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