network: undef EAFNOSUPPORT before overriding it.
[vlc.git] / src / network / udp.c
1 /*****************************************************************************
2  * udp.c:
3  *****************************************************************************
4  * Copyright (C) 2004-2006 VLC authors and VideoLAN
5  * Copyright © 2006-2007 Rémi Denis-Courmont
6  *
7  * $Id$
8  *
9  * Authors: Laurent Aimar <fenrir@videolan.org>
10  *          Rémi Denis-Courmont <rem # videolan.org>
11  *
12  * This program is free software; you can redistribute it and/or modify it
13  * under the terms of the GNU Lesser General Public License as published by
14  * the Free Software Foundation; either version 2.1 of the License, or
15  * (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20  * GNU Lesser General Public License for more details.
21  *
22  * You should have received a copy of the GNU Lesser General Public License
23  * along with this program; if not, write to the Free Software Foundation,
24  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
25  *****************************************************************************/
26
27 /*****************************************************************************
28  * Preamble
29  *****************************************************************************/
30 #ifdef HAVE_CONFIG_H
31 # include "config.h"
32 #endif
33
34 #include <vlc_common.h>
35
36 #include <errno.h>
37 #include <assert.h>
38
39 #include <vlc_network.h>
40
41 #ifdef _WIN32
42 #   undef EAFNOSUPPORT
43 #   define EAFNOSUPPORT WSAEAFNOSUPPORT
44 #else
45 #   include <unistd.h>
46 #   ifdef HAVE_NET_IF_H
47 #       include <net/if.h>
48 #   endif
49 #endif
50
51 #ifdef HAVE_LINUX_DCCP_H
52 # include <linux/dccp.h>
53 # ifndef SOCK_DCCP /* provisional API */
54 #  define SOCK_DCCP 6
55 # endif
56 #endif
57
58 #ifndef SOL_IP
59 # define SOL_IP IPPROTO_IP
60 #endif
61 #ifndef SOL_IPV6
62 # define SOL_IPV6 IPPROTO_IPV6
63 #endif
64 #ifndef IPPROTO_IPV6
65 # define IPPROTO_IPV6 41 /* IANA */
66 #endif
67 #ifndef SOL_DCCP
68 # define SOL_DCCP IPPROTO_DCCP
69 #endif
70 #ifndef IPPROTO_DCCP
71 # define IPPROTO_DCCP 33 /* IANA */
72 #endif
73 #ifndef SOL_UDPLITE
74 # define SOL_UDPLITE IPPROTO_UDPLITE
75 #endif
76 #ifndef IPPROTO_UDPLITE
77 # define IPPROTO_UDPLITE 136 /* IANA */
78 #endif
79
80 #if defined (HAVE_NETINET_UDPLITE_H)
81 # include <netinet/udplite.h>
82 #elif defined (__linux__)
83 /* still missing from glibc 2.6 */
84 # define UDPLITE_SEND_CSCOV     10
85 # define UDPLITE_RECV_CSCOV     11
86 #endif
87
88 extern int net_Socket( vlc_object_t *p_this, int i_family, int i_socktype,
89                        int i_protocol );
90
91 /* */
92 static int net_SetupDgramSocket (vlc_object_t *p_obj, int fd,
93                                  const struct addrinfo *ptr)
94 {
95 #ifdef SO_REUSEPORT
96     setsockopt (fd, SOL_SOCKET, SO_REUSEPORT, &(int){ 1 }, sizeof (int));
97 #endif
98
99 #if defined (_WIN32)
100     if (net_SockAddrIsMulticast (ptr->ai_addr, ptr->ai_addrlen)
101      && (sizeof (struct sockaddr_storage) >= ptr->ai_addrlen))
102     {
103         // This works for IPv4 too - don't worry!
104         struct sockaddr_in6 dumb =
105         {
106             .sin6_family = ptr->ai_addr->sa_family,
107             .sin6_port =  ((struct sockaddr_in *)(ptr->ai_addr))->sin_port
108         };
109
110         bind (fd, (struct sockaddr *)&dumb, ptr->ai_addrlen);
111     }
112     else
113 #endif
114     if (bind (fd, ptr->ai_addr, ptr->ai_addrlen))
115     {
116         msg_Err( p_obj, "socket bind error (%m)" );
117         net_Close (fd);
118         return -1;
119     }
120     return fd;
121 }
122
123 /* */
124 static int net_ListenSingle (vlc_object_t *obj, const char *host, int port,
125                              int protocol)
126 {
127     struct addrinfo hints = {
128         .ai_socktype = SOCK_DGRAM,
129         .ai_protocol = protocol,
130         .ai_flags = AI_PASSIVE | AI_NUMERICSERV | AI_IDN,
131     }, *res;
132
133     if (host && !*host)
134         host = NULL;
135
136     msg_Dbg (obj, "net: opening %s datagram port %d",
137              host ? host : "any", port);
138
139     int val = vlc_getaddrinfo (host, port, &hints, &res);
140     if (val)
141     {
142         msg_Err (obj, "Cannot resolve %s port %d : %s", host, port,
143                  gai_strerror (val));
144         return -1;
145     }
146
147     val = -1;
148
149     for (const struct addrinfo *ptr = res; ptr != NULL; ptr = ptr->ai_next)
150     {
151         int fd = net_Socket (obj, ptr->ai_family, ptr->ai_socktype,
152                              ptr->ai_protocol);
153         if (fd == -1)
154         {
155             msg_Dbg (obj, "socket error: %m");
156             continue;
157         }
158
159 #ifdef IPV6_V6ONLY
160         /* Try dual-mode IPv6 if available. */
161         if (ptr->ai_family == AF_INET6)
162             setsockopt (fd, SOL_IPV6, IPV6_V6ONLY, &(int){ 0 }, sizeof (int));
163 #endif
164         fd = net_SetupDgramSocket( obj, fd, ptr );
165         if( fd == -1 )
166             continue;
167
168         if (net_SockAddrIsMulticast (ptr->ai_addr, ptr->ai_addrlen)
169          && net_Subscribe (obj, fd, ptr->ai_addr, ptr->ai_addrlen))
170         {
171             net_Close (fd);
172             continue;
173         }
174
175         val = fd;
176         break;
177     }
178
179     freeaddrinfo (res);
180     return val;
181 }
182
183
184 static int net_SetMcastHopLimit( vlc_object_t *p_this,
185                                  int fd, int family, int hlim )
186 {
187     int proto, cmd;
188
189     /* There is some confusion in the world whether IP_MULTICAST_TTL
190      * takes a byte or an int as an argument.
191      * BSD seems to indicate byte so we are going with that and use
192      * int as a fallback to be safe */
193     switch( family )
194     {
195 #ifdef IP_MULTICAST_TTL
196         case AF_INET:
197             proto = SOL_IP;
198             cmd = IP_MULTICAST_TTL;
199             break;
200 #endif
201
202 #ifdef IPV6_MULTICAST_HOPS
203         case AF_INET6:
204             proto = SOL_IPV6;
205             cmd = IPV6_MULTICAST_HOPS;
206             break;
207 #endif
208
209         default:
210             errno = EAFNOSUPPORT;
211             msg_Warn( p_this, "%m" );
212             return VLC_EGENERIC;
213     }
214
215     if( setsockopt( fd, proto, cmd, &hlim, sizeof( hlim ) ) < 0 )
216     {
217         /* BSD compatibility */
218         unsigned char buf;
219
220         msg_Dbg( p_this, "cannot set hop limit (%d): %m", hlim );
221         buf = (unsigned char)(( hlim > 255 ) ? 255 : hlim);
222         if( setsockopt( fd, proto, cmd, &buf, sizeof( buf ) ) )
223         {
224             msg_Err( p_this, "cannot set hop limit (%d): %m", hlim );
225             return VLC_EGENERIC;
226         }
227     }
228
229     return VLC_SUCCESS;
230 }
231
232
233 static int net_SetMcastOut (vlc_object_t *p_this, int fd, int family,
234                             const char *iface)
235 {
236     int scope = if_nametoindex (iface);
237     if (scope == 0)
238     {
239         msg_Err (p_this, "invalid multicast interface: %s", iface);
240         return -1;
241     }
242
243     switch (family)
244     {
245 #ifdef IPV6_MULTICAST_IF
246         case AF_INET6:
247             if (setsockopt (fd, SOL_IPV6, IPV6_MULTICAST_IF,
248                             &scope, sizeof (scope)) == 0)
249                 return 0;
250             break;
251 #endif
252
253 #ifdef __linux__
254         case AF_INET:
255         {
256             struct ip_mreqn req = { .imr_ifindex = scope };
257             if (setsockopt (fd, SOL_IP, IP_MULTICAST_IF,
258                             &req, sizeof (req)) == 0)
259                 return 0;
260             break;
261         }
262 #endif
263         default:
264             errno = EAFNOSUPPORT;
265     }
266     msg_Err (p_this, "cannot force multicast interface %s: %m", iface);
267     return -1;
268 }
269
270
271 static unsigned var_GetIfIndex (vlc_object_t *obj)
272 {
273     char *ifname = var_InheritString (obj, "miface");
274     if (ifname == NULL)
275         return 0;
276
277     unsigned ifindex = if_nametoindex (ifname);
278     if (ifindex == 0)
279         msg_Err (obj, "invalid multicast interface: %s", ifname);
280     free (ifname);
281     return ifindex;
282 }
283
284
285 /**
286  * IP-agnostic multicast join,
287  * with fallback to old APIs, and fallback from SSM to ASM.
288  */
289 static int
290 net_SourceSubscribe (vlc_object_t *obj, int fd,
291                      const struct sockaddr *src, socklen_t srclen,
292                      const struct sockaddr *grp, socklen_t grplen)
293 {
294 #ifdef MCAST_JOIN_SOURCE_GROUP
295     /* Agnostic SSM multicast join */
296     int level;
297     struct group_source_req gsr;
298
299     memset (&gsr, 0, sizeof (gsr));
300     gsr.gsr_interface = var_GetIfIndex (obj);
301
302     switch (grp->sa_family)
303     {
304 #ifdef AF_INET6
305         case AF_INET6:
306         {
307             const struct sockaddr_in6 *g6 = (const struct sockaddr_in6 *)grp;
308
309             level = SOL_IPV6;
310             assert (grplen >= sizeof (struct sockaddr_in6));
311             if (g6->sin6_scope_id != 0)
312                 gsr.gsr_interface = g6->sin6_scope_id;
313             break;
314         }
315 #endif
316         case AF_INET:
317             level = SOL_IP;
318             break;
319         default:
320             errno = EAFNOSUPPORT;
321             return -1;
322     }
323
324     assert (grplen <= sizeof (gsr.gsr_group));
325     memcpy (&gsr.gsr_source, src, srclen);
326     assert (srclen <= sizeof (gsr.gsr_source));
327     memcpy (&gsr.gsr_group,  grp, grplen);
328     if (setsockopt (fd, level, MCAST_JOIN_SOURCE_GROUP,
329                     &gsr, sizeof (gsr)) == 0)
330         return 0;
331
332 #else
333     if (src->sa_family != grp->sa_family)
334     {
335         errno = EAFNOSUPPORT;
336         return -1;
337     }
338
339     switch (grp->sa_family)
340     {
341 # ifdef IP_ADD_SOURCE_MEMBERSHIP
342         /* IPv4-specific API */
343         case AF_INET:
344         {
345             struct ip_mreq_source imr;
346
347             memset (&imr, 0, sizeof (imr));
348             assert (grplen >= sizeof (struct sockaddr_in));
349             imr.imr_multiaddr = ((const struct sockaddr_in *)grp)->sin_addr;
350             assert (srclen >= sizeof (struct sockaddr_in));
351             imr.imr_sourceaddr = ((const struct sockaddr_in *)src)->sin_addr;
352             if (setsockopt (fd, SOL_IP, IP_ADD_SOURCE_MEMBERSHIP,
353                             &imr, sizeof (imr)) == 0)
354                 return 0;
355             break;
356         }
357 # endif
358         default:
359             errno = EAFNOSUPPORT;
360     }
361
362 #endif
363     msg_Err (obj, "cannot join source multicast group: %m");
364     msg_Warn (obj, "trying ASM instead of SSM...");
365     return net_Subscribe (obj, fd, grp, grplen);
366 }
367
368
369 int net_Subscribe (vlc_object_t *obj, int fd,
370                    const struct sockaddr *grp, socklen_t grplen)
371 {
372 /* MCAST_JOIN_GROUP was introduced to OS X in v10.7, but it doesn't work,
373  * so ignore it to use the same code as on 10.5 or 10.6 */
374 #if defined (MCAST_JOIN_GROUP) && !defined (__APPLE__)
375     /* Agnostic SSM multicast join */
376     int level;
377     struct group_req gr;
378
379     memset (&gr, 0, sizeof (gr));
380     gr.gr_interface = var_GetIfIndex (obj);
381
382     switch (grp->sa_family)
383     {
384 #ifdef AF_INET6
385         case AF_INET6:
386         {
387             const struct sockaddr_in6 *g6 = (const struct sockaddr_in6 *)grp;
388
389             level = SOL_IPV6;
390             assert (grplen >= sizeof (struct sockaddr_in6));
391             if (g6->sin6_scope_id != 0)
392                 gr.gr_interface = g6->sin6_scope_id;
393             break;
394         }
395 #endif
396         case AF_INET:
397             level = SOL_IP;
398             break;
399         default:
400             errno = EAFNOSUPPORT;
401             return -1;
402     }
403
404     assert (grplen <= sizeof (gr.gr_group));
405     memcpy (&gr.gr_group, grp, grplen);
406     if (setsockopt (fd, level, MCAST_JOIN_GROUP, &gr, sizeof (gr)) == 0)
407         return 0;
408
409 #else
410     switch (grp->sa_family)
411     {
412 # ifdef IPV6_JOIN_GROUP
413         case AF_INET6:
414         {
415             struct ipv6_mreq ipv6mr;
416             const struct sockaddr_in6 *g6 = (const struct sockaddr_in6 *)grp;
417
418             memset (&ipv6mr, 0, sizeof (ipv6mr));
419             assert (grplen >= sizeof (struct sockaddr_in6));
420             ipv6mr.ipv6mr_multiaddr = g6->sin6_addr;
421             ipv6mr.ipv6mr_interface = g6->sin6_scope_id;
422             if (!setsockopt (fd, SOL_IPV6, IPV6_JOIN_GROUP,
423                              &ipv6mr, sizeof (ipv6mr)))
424                 return 0;
425             break;
426         }
427 # endif
428 # ifdef IP_ADD_MEMBERSHIP
429         case AF_INET:
430         {
431             struct ip_mreq imr;
432
433             memset (&imr, 0, sizeof (imr));
434             assert (grplen >= sizeof (struct sockaddr_in));
435             imr.imr_multiaddr = ((const struct sockaddr_in *)grp)->sin_addr;
436             if (setsockopt (fd, SOL_IP, IP_ADD_MEMBERSHIP,
437                             &imr, sizeof (imr)) == 0)
438                 return 0;
439             break;
440         }
441 # endif
442         default:
443             errno = EAFNOSUPPORT;
444     }
445
446 #endif
447     msg_Err (obj, "cannot join multicast group: %m");
448     return -1;
449 }
450
451
452 static int net_SetDSCP( int fd, uint8_t dscp )
453 {
454     struct sockaddr_storage addr;
455     if( getsockname( fd, (struct sockaddr *)&addr, &(socklen_t){ sizeof (addr) }) )
456         return -1;
457
458     int level, cmd;
459
460     switch( addr.ss_family )
461     {
462 #ifdef IPV6_TCLASS
463         case AF_INET6:
464             level = SOL_IPV6;
465             cmd = IPV6_TCLASS;
466             break;
467 #endif
468
469         case AF_INET:
470             level = SOL_IP;
471             cmd = IP_TOS;
472             break;
473
474         default:
475 #ifdef ENOPROTOOPT
476             errno = ENOPROTOOPT;
477 #endif
478             return -1;
479     }
480
481     return setsockopt( fd, level, cmd, &(int){ dscp }, sizeof (int));
482 }
483
484 #undef net_ConnectDgram
485 /*****************************************************************************
486  * net_ConnectDgram:
487  *****************************************************************************
488  * Open a datagram socket to send data to a defined destination, with an
489  * optional hop limit.
490  *****************************************************************************/
491 int net_ConnectDgram( vlc_object_t *p_this, const char *psz_host, int i_port,
492                       int i_hlim, int proto )
493 {
494     struct addrinfo hints = {
495         .ai_socktype = SOCK_DGRAM,
496         .ai_protocol = proto,
497         .ai_flags = AI_NUMERICSERV | AI_IDN,
498     }, *res;
499     int       i_handle = -1;
500     bool      b_unreach = false;
501
502     if( i_hlim < 0 )
503         i_hlim = var_InheritInteger( p_this, "ttl" );
504
505     msg_Dbg( p_this, "net: connecting to [%s]:%d", psz_host, i_port );
506
507     int val = vlc_getaddrinfo (psz_host, i_port, &hints, &res);
508     if (val)
509     {
510         msg_Err (p_this, "cannot resolve [%s]:%d : %s", psz_host, i_port,
511                  gai_strerror (val));
512         return -1;
513     }
514
515     for (struct addrinfo *ptr = res; ptr != NULL; ptr = ptr->ai_next)
516     {
517         char *str;
518         int fd = net_Socket (p_this, ptr->ai_family, ptr->ai_socktype,
519                              ptr->ai_protocol);
520         if (fd == -1)
521             continue;
522
523         /* Allow broadcast sending */
524         setsockopt (fd, SOL_SOCKET, SO_BROADCAST, &(int){ 1 }, sizeof (int));
525
526         if( i_hlim >= 0 )
527             net_SetMcastHopLimit( p_this, fd, ptr->ai_family, i_hlim );
528
529         str = var_InheritString (p_this, "miface");
530         if (str != NULL)
531         {
532             net_SetMcastOut (p_this, fd, ptr->ai_family, str);
533             free (str);
534         }
535
536         net_SetDSCP (fd, var_InheritInteger (p_this, "dscp"));
537
538         if( connect( fd, ptr->ai_addr, ptr->ai_addrlen ) == 0 )
539         {
540             /* success */
541             i_handle = fd;
542             break;
543         }
544
545 #if defined( _WIN32 )
546         if( WSAGetLastError () == WSAENETUNREACH )
547 #else
548         if( errno == ENETUNREACH )
549 #endif
550             b_unreach = true;
551         else
552             msg_Warn( p_this, "%s port %d : %m", psz_host, i_port);
553         net_Close( fd );
554     }
555
556     freeaddrinfo( res );
557
558     if( i_handle == -1 )
559     {
560         if( b_unreach )
561             msg_Err( p_this, "Host %s port %d is unreachable", psz_host,
562                      i_port );
563         return -1;
564     }
565
566     return i_handle;
567 }
568
569 #undef net_OpenDgram
570 /*****************************************************************************
571  * net_OpenDgram:
572  *****************************************************************************
573  * OpenDgram a datagram socket and return a handle
574  *****************************************************************************/
575 int net_OpenDgram( vlc_object_t *obj, const char *psz_bind, int i_bind,
576                    const char *psz_server, int i_server, int protocol )
577 {
578     if ((psz_server == NULL) || (psz_server[0] == '\0'))
579         return net_ListenSingle (obj, psz_bind, i_bind, protocol);
580
581     msg_Dbg (obj, "net: connecting to [%s]:%d from [%s]:%d",
582              psz_server, i_server, psz_bind, i_bind);
583
584     struct addrinfo hints = {
585         .ai_socktype = SOCK_DGRAM,
586         .ai_protocol = protocol,
587         .ai_flags = AI_NUMERICSERV | AI_IDN,
588     }, *loc, *rem;
589
590     int val = vlc_getaddrinfo (psz_server, i_server, &hints, &rem);
591     if (val)
592     {
593         msg_Err (obj, "cannot resolve %s port %d : %s", psz_bind, i_bind,
594                  gai_strerror (val));
595         return -1;
596     }
597
598     hints.ai_flags |= AI_PASSIVE;
599     val = vlc_getaddrinfo (psz_bind, i_bind, &hints, &loc);
600     if (val)
601     {
602         msg_Err (obj, "cannot resolve %s port %d : %s", psz_bind, i_bind,
603                  gai_strerror (val));
604         freeaddrinfo (rem);
605         return -1;
606     }
607
608     val = -1;
609     for (struct addrinfo *ptr = loc; ptr != NULL; ptr = ptr->ai_next)
610     {
611         int fd = net_Socket (obj, ptr->ai_family, ptr->ai_socktype,
612                              ptr->ai_protocol);
613         if (fd == -1)
614             continue; // usually, address family not supported
615
616         fd = net_SetupDgramSocket( obj, fd, ptr );
617         if( fd == -1 )
618             continue;
619
620         for (struct addrinfo *ptr2 = rem; ptr2 != NULL; ptr2 = ptr2->ai_next)
621         {
622             if ((ptr2->ai_family != ptr->ai_family)
623              || (ptr2->ai_socktype != ptr->ai_socktype)
624              || (ptr2->ai_protocol != ptr->ai_protocol))
625                 continue;
626
627             if (net_SockAddrIsMulticast (ptr->ai_addr, ptr->ai_addrlen)
628               ? net_SourceSubscribe (obj, fd,
629                                      ptr2->ai_addr, ptr2->ai_addrlen,
630                                      ptr->ai_addr, ptr->ai_addrlen)
631               : connect (fd, ptr2->ai_addr, ptr2->ai_addrlen))
632             {
633                 msg_Err (obj, "cannot connect to %s port %d: %m",
634                          psz_server, i_server);
635                 continue;
636             }
637             val = fd;
638             break;
639         }
640
641         if (val != -1)
642             break;
643
644         net_Close (fd);
645     }
646
647     freeaddrinfo (rem);
648     freeaddrinfo (loc);
649     return val;
650 }
651
652
653 /**
654  * net_SetCSCov:
655  * Sets the send and receive checksum coverage of a socket:
656  * @param fd socket
657  * @param sendcov payload coverage of sent packets (bytes), -1 for full
658  * @param recvcov minimum payload coverage of received packets, -1 for full
659  */
660 int net_SetCSCov (int fd, int sendcov, int recvcov)
661 {
662     int type;
663
664     if (getsockopt (fd, SOL_SOCKET, SO_TYPE,
665                     &type, &(socklen_t){ sizeof (type) }))
666         return VLC_EGENERIC;
667
668     switch (type)
669     {
670 #ifdef UDPLITE_RECV_CSCOV
671         case SOCK_DGRAM: /* UDP-Lite */
672             if (sendcov == -1)
673                 sendcov = 0;
674             else
675                 sendcov += 8; /* partial */
676             if (setsockopt (fd, SOL_UDPLITE, UDPLITE_SEND_CSCOV, &sendcov,
677                             sizeof (sendcov)))
678                 return VLC_EGENERIC;
679
680             if (recvcov == -1)
681                 recvcov = 0;
682             else
683                 recvcov += 8;
684             if (setsockopt (fd, SOL_UDPLITE, UDPLITE_RECV_CSCOV,
685                             &recvcov, sizeof (recvcov)))
686                 return VLC_EGENERIC;
687
688             return VLC_SUCCESS;
689 #endif
690 #ifdef DCCP_SOCKOPT_SEND_CSCOV
691         case SOCK_DCCP: /* DCCP and its ill-named socket type */
692             if ((sendcov == -1) || (sendcov > 56))
693                 sendcov = 0;
694             else
695                 sendcov = (sendcov + 3) / 4;
696             if (setsockopt (fd, SOL_DCCP, DCCP_SOCKOPT_SEND_CSCOV,
697                             &sendcov, sizeof (sendcov)))
698                 return VLC_EGENERIC;
699
700             if ((recvcov == -1) || (recvcov > 56))
701                 recvcov = 0;
702             else
703                 recvcov = (recvcov + 3) / 4;
704             if (setsockopt (fd, SOL_DCCP, DCCP_SOCKOPT_RECV_CSCOV,
705                             &recvcov, sizeof (recvcov)))
706                 return VLC_EGENERIC;
707
708             return VLC_SUCCESS;
709 #endif
710     }
711 #if !defined( UDPLITE_RECV_CSCOV ) && !defined( DCCP_SOCKOPT_SEND_CSCOV )
712     VLC_UNUSED(sendcov);
713     VLC_UNUSED(recvcov);
714 #endif
715
716     return VLC_EGENERIC;
717 }