]> git.sesse.net Git - vlc/blob - src/network/udp.c
HTTPd: do not reveal the username through as the realm (fixes: #2993)
[vlc] / src / network / udp.c
1 /*****************************************************************************
2  * udp.c:
3  *****************************************************************************
4  * Copyright (C) 2004-2006 the VideoLAN team
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
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation; either version 2 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 General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, 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
38 #ifdef HAVE_SYS_TIME_H
39 #    include <sys/time.h>
40 #endif
41
42 #include <vlc_network.h>
43
44 #ifdef WIN32
45 #   if defined(UNDER_CE)
46 #       undef IP_MULTICAST_TTL
47 #       define IP_MULTICAST_TTL 3
48 #       undef IP_ADD_MEMBERSHIP
49 #       define IP_ADD_MEMBERSHIP 5
50 #   endif
51 #   define EAFNOSUPPORT WSAEAFNOSUPPORT
52 #   define if_nametoindex( str ) atoi( str )
53 #else
54 #   include <unistd.h>
55 #   ifdef HAVE_NET_IF_H
56 #       include <net/if.h>
57 #   endif
58 #endif
59
60 #ifdef HAVE_LINUX_DCCP_H
61 # include <linux/dccp.h>
62 # ifndef SOCK_DCCP /* provisional API */
63 #  define SOCK_DCCP 6
64 # endif
65 #endif
66
67 #ifndef SOL_IP
68 # define SOL_IP IPPROTO_IP
69 #endif
70 #ifndef SOL_IPV6
71 # define SOL_IPV6 IPPROTO_IPV6
72 #endif
73 #ifndef IPPROTO_IPV6
74 # define IPPROTO_IPV6 41 /* IANA */
75 #endif
76 #ifndef SOL_DCCP
77 # define SOL_DCCP IPPROTO_DCCP
78 #endif
79 #ifndef IPPROTO_DCCP
80 # define IPPROTO_DCCP 33 /* IANA */
81 #endif
82 #ifndef SOL_UDPLITE
83 # define SOL_UDPLITE IPPROTO_UDPLITE
84 #endif
85 #ifndef IPPROTO_UDPLITE
86 # define IPPROTO_UDPLITE 136 /* IANA */
87 #endif
88
89 #if defined (HAVE_NETINET_UDPLITE_H)
90 # include <netinet/udplite.h>
91 #elif defined (__linux__)
92 /* still missing from glibc 2.6 */
93 # define UDPLITE_SEND_CSCOV     10
94 # define UDPLITE_RECV_CSCOV     11
95 #endif
96
97 extern int net_Socket( vlc_object_t *p_this, int i_family, int i_socktype,
98                        int i_protocol );
99
100 /* */
101 static int net_SetupDgramSocket( vlc_object_t *p_obj, int fd, const struct addrinfo *ptr )
102 {
103 #ifdef SO_REUSEPORT
104     setsockopt (fd, SOL_SOCKET, SO_REUSEPORT, &(int){ 1 }, sizeof (int));
105 #endif
106
107 #ifdef SO_RCVBUF
108     /* Increase the receive buffer size to 1/2MB (8Mb/s during 1/2s)
109      * to avoid packet loss caused in case of scheduling hiccups */
110     setsockopt (fd, SOL_SOCKET, SO_RCVBUF,
111                 (void *)&(int){ 0x80000 }, sizeof (int));
112     setsockopt (fd, SOL_SOCKET, SO_SNDBUF,
113                 (void *)&(int){ 0x80000 }, sizeof (int));
114 #endif
115
116 #if defined (WIN32) || defined (UNDER_CE)
117     if (net_SockAddrIsMulticast (ptr->ai_addr, ptr->ai_addrlen)
118      && (sizeof (struct sockaddr_storage) >= ptr->ai_addrlen))
119     {
120         // This works for IPv4 too - don't worry!
121         struct sockaddr_in6 dumb =
122         {
123             .sin6_family = ptr->ai_addr->sa_family,
124             .sin6_port =  ((struct sockaddr_in *)(ptr->ai_addr))->sin_port
125         };
126
127         bind (fd, (struct sockaddr *)&dumb, ptr->ai_addrlen);
128     }
129     else
130 #endif
131     if (bind (fd, ptr->ai_addr, ptr->ai_addrlen))
132     {
133         msg_Err( p_obj, "socket bind error (%m)" );
134         net_Close (fd);
135         return -1;
136     }
137     return fd;
138 }
139
140 /* */
141 static int net_ListenSingle (vlc_object_t *obj, const char *host, int port,
142                              int family, int protocol)
143 {
144     struct addrinfo hints, *res;
145
146     memset (&hints, 0, sizeof( hints ));
147     hints.ai_family = family;
148     hints.ai_socktype = SOCK_DGRAM;
149     hints.ai_flags = AI_PASSIVE;
150
151     if (host && !*host)
152         host = NULL;
153
154     msg_Dbg (obj, "net: opening %s datagram port %d",
155              host ? host : "any", port);
156
157     int val = vlc_getaddrinfo (obj, host, port, &hints, &res);
158     if (val)
159     {
160         msg_Err (obj, "Cannot resolve %s port %d : %s", host, port,
161                  vlc_gai_strerror (val));
162         return -1;
163     }
164
165     val = -1;
166
167     int fd6 = -1;
168     for (const struct addrinfo *ptr = res; ptr != NULL; ptr = ptr->ai_next)
169     {
170         int fd = net_Socket (obj, ptr->ai_family, ptr->ai_socktype,
171                              protocol ? protocol : ptr->ai_protocol);
172         if (fd == -1)
173         {
174             msg_Dbg (obj, "socket error: %m");
175             continue;
176         }
177
178 #ifdef IPV6_V6ONLY
179         /* If IPv6 was forced, set IPv6-only mode.
180          * If IPv4 was forced, do nothing extraordinary.
181          * If nothing was forced, try dual-mode IPv6. */
182         if (ptr->ai_family == AF_INET6)
183         {
184             int on = (family == AF_INET6);
185             setsockopt (fd, SOL_IPV6, IPV6_V6ONLY, &on, sizeof (on));
186         }
187         else if (ptr->ai_family == AF_INET && family == AF_UNSPEC)
188         {
189             for (const struct addrinfo *p = ptr; p != NULL; p = p->ai_next)
190                 if (p->ai_family == AF_INET6)
191                 {
192                     net_Close (fd);
193                     fd = -1;
194                     break;
195                 }
196             if (fd == -1)
197                 continue;
198         }
199 #else
200         if (family == AF_UNSPEC && ptr->ai_next != NULL)
201         {
202             msg_Warn (obj, "ambiguous network protocol specification");
203             msg_Warn (obj, "please select IP version explicitly");
204         }
205 #endif
206
207         fd = net_SetupDgramSocket( obj, fd, ptr );
208         if( fd == -1 )
209             continue;
210
211         if (net_SockAddrIsMulticast (ptr->ai_addr, ptr->ai_addrlen)
212          && net_Subscribe (obj, fd, ptr->ai_addr, ptr->ai_addrlen))
213         {
214             net_Close (fd);
215             continue;
216         }
217
218         val = fd;
219         break;
220     }
221
222     vlc_freeaddrinfo (res);
223     return val;
224 }
225
226
227 static int net_SetMcastHopLimit( vlc_object_t *p_this,
228                                  int fd, int family, int hlim )
229 {
230     int proto, cmd;
231
232     /* There is some confusion in the world whether IP_MULTICAST_TTL
233      * takes a byte or an int as an argument.
234      * BSD seems to indicate byte so we are going with that and use
235      * int as a fallback to be safe */
236     switch( family )
237     {
238 #ifdef IP_MULTICAST_TTL
239         case AF_INET:
240             proto = SOL_IP;
241             cmd = IP_MULTICAST_TTL;
242             break;
243 #endif
244
245 #ifdef IPV6_MULTICAST_HOPS
246         case AF_INET6:
247             proto = SOL_IPV6;
248             cmd = IPV6_MULTICAST_HOPS;
249             break;
250 #endif
251
252         default:
253             errno = EAFNOSUPPORT;
254             msg_Warn( p_this, "%m" );
255             return VLC_EGENERIC;
256     }
257
258     if( setsockopt( fd, proto, cmd, &hlim, sizeof( hlim ) ) < 0 )
259     {
260         /* BSD compatibility */
261         unsigned char buf;
262
263         buf = (unsigned char)(( hlim > 255 ) ? 255 : hlim);
264         if( setsockopt( fd, proto, cmd, &buf, sizeof( buf ) ) )
265             return VLC_EGENERIC;
266     }
267
268     return VLC_SUCCESS;
269 }
270
271
272 static int net_SetMcastOutIface (int fd, int family, int scope)
273 {
274     switch (family)
275     {
276 #ifdef IPV6_MULTICAST_IF
277         case AF_INET6:
278             return setsockopt (fd, SOL_IPV6, IPV6_MULTICAST_IF,
279                                &scope, sizeof (scope));
280 #endif
281
282 #ifdef __linux__
283         case AF_INET:
284         {
285             struct ip_mreqn req = { .imr_ifindex = scope };
286
287             return setsockopt (fd, SOL_IP, IP_MULTICAST_IF, &req,
288                                sizeof (req));
289         }
290 #endif
291     }
292
293     errno = EAFNOSUPPORT;
294     return -1;
295 }
296
297
298 static inline int net_SetMcastOutIPv4 (int fd, struct in_addr ipv4)
299 {
300 #ifdef IP_MULTICAST_IF
301     return setsockopt( fd, SOL_IP, IP_MULTICAST_IF, &ipv4, sizeof (ipv4));
302 #else
303     errno = EAFNOSUPPORT;
304     return -1;
305 #endif
306 }
307
308
309 static int net_SetMcastOut (vlc_object_t *p_this, int fd, int family,
310                             const char *iface, const char *addr)
311 {
312     if (iface != NULL)
313     {
314         int scope = if_nametoindex (iface);
315         if (scope == 0)
316         {
317             msg_Err (p_this, "invalid multicast interface: %s", iface);
318             return -1;
319         }
320
321         if (net_SetMcastOutIface (fd, family, scope) == 0)
322             return 0;
323
324         msg_Err (p_this, "%s: %m", iface);
325     }
326
327     if (addr != NULL)
328     {
329         if (family == AF_INET)
330         {
331             struct in_addr ipv4;
332             if (inet_pton (AF_INET, addr, &ipv4) <= 0)
333             {
334                 msg_Err (p_this, "invalid IPv4 address for multicast: %s",
335                          addr);
336                 return -1;
337             }
338
339             if (net_SetMcastOutIPv4 (fd, ipv4) == 0)
340                 return 0;
341
342             msg_Err (p_this, "%s: %m", addr);
343         }
344     }
345
346     return -1;
347 }
348
349
350 /**
351  * Old-style any-source multicast join.
352  * In use on Windows XP/2003 and older.
353  */
354 static int
355 net_IPv4Join (vlc_object_t *obj, int fd,
356               const struct sockaddr_in *src, const struct sockaddr_in *grp)
357 {
358 #ifdef IP_ADD_MEMBERSHIP
359     union
360     {
361         struct ip_mreq gr4;
362 # ifdef IP_ADD_SOURCE_MEMBERSHIP
363         struct ip_mreq_source gsr4;
364 # endif
365     } opt;
366     int cmd;
367     struct in_addr id = { .s_addr = INADDR_ANY };
368     socklen_t optlen;
369
370     /* Multicast interface IPv4 address */
371     char *iface = var_CreateGetNonEmptyString (obj, "miface-addr");
372     if ((iface != NULL)
373      && (inet_pton (AF_INET, iface, &id) <= 0))
374     {
375         msg_Err (obj, "invalid multicast interface address %s", iface);
376         free (iface);
377         goto error;
378     }
379     free (iface);
380
381     memset (&opt, 0, sizeof (opt));
382     if (src != NULL)
383     {
384 # ifdef IP_ADD_SOURCE_MEMBERSHIP
385         cmd = IP_ADD_SOURCE_MEMBERSHIP;
386         opt.gsr4.imr_multiaddr = grp->sin_addr;
387         opt.gsr4.imr_sourceaddr = src->sin_addr;
388         opt.gsr4.imr_interface = id;
389         optlen = sizeof (opt.gsr4);
390 # else
391         errno = ENOSYS;
392         goto error;
393 # endif
394     }
395     else
396     {
397         cmd = IP_ADD_MEMBERSHIP;
398         opt.gr4.imr_multiaddr = grp->sin_addr;
399         opt.gr4.imr_interface = id;
400         optlen = sizeof (opt.gr4);
401     }
402
403     msg_Dbg (obj, "IP_ADD_%sMEMBERSHIP multicast request",
404              (src != NULL) ? "SOURCE_" : "");
405
406     if (setsockopt (fd, SOL_IP, cmd, &opt, optlen) == 0)
407         return 0;
408
409 error:
410 #endif
411
412     msg_Err (obj, "cannot join IPv4 multicast group (%m)");
413     return -1;
414 }
415
416
417 static int
418 net_IPv6Join (vlc_object_t *obj, int fd, const struct sockaddr_in6 *src)
419 {
420 #ifdef IPV6_JOIN_GROUP
421     struct ipv6_mreq gr6;
422     memset (&gr6, 0, sizeof (gr6));
423     gr6.ipv6mr_interface = src->sin6_scope_id;
424     memcpy (&gr6.ipv6mr_multiaddr, &src->sin6_addr, 16);
425
426     msg_Dbg (obj, "IPV6_JOIN_GROUP multicast request");
427
428     if (!setsockopt (fd, SOL_IPV6, IPV6_JOIN_GROUP, &gr6, sizeof (gr6)))
429         return 0;
430 #else
431     errno = ENOSYS;
432 #endif
433
434     msg_Err (obj, "cannot join IPv6 any-source multicast group (%m)");
435     return -1;
436 }
437
438
439 #if defined (WIN32) && !defined (MCAST_JOIN_SOURCE_GROUP)
440 /*
441  * I hate manual definitions: Error-prone. Portability hell.
442  * Developers shall use UP-TO-DATE compilers. Full point.
443  * If you remove the warning, you remove the whole ifndef.
444  */
445 #  warning Your C headers are out-of-date. Please update.
446
447 #  define MCAST_JOIN_GROUP 41
448 struct group_req
449 {
450     ULONG gr_interface;
451     struct sockaddr_storage gr_group;
452 };
453
454 #  define MCAST_JOIN_SOURCE_GROUP 45 /* from <ws2ipdef.h> */
455 struct group_source_req
456 {
457     uint32_t gsr_interface;
458     struct sockaddr_storage gsr_group;
459     struct sockaddr_storage gsr_source;
460 };
461 #endif
462
463 /**
464  * IP-agnostic multicast join,
465  * with fallback to old APIs, and fallback from SSM to ASM.
466  */
467 static int
468 net_SourceSubscribe (vlc_object_t *obj, int fd,
469                      const struct sockaddr *src, socklen_t srclen,
470                      const struct sockaddr *grp, socklen_t grplen)
471 {
472     int level, iid = 0;
473
474     char *iface = var_CreateGetNonEmptyString (obj, "miface");
475     if (iface != NULL)
476     {
477         iid = if_nametoindex (iface);
478         if (iid == 0)
479         {
480             msg_Err (obj, "invalid multicast interface: %s", iface);
481             free (iface);
482             return -1;
483         }
484         free (iface);
485     }
486
487     switch (grp->sa_family)
488     {
489 #ifdef AF_INET6
490         case AF_INET6:
491             level = SOL_IPV6;
492             if (((const struct sockaddr_in6 *)grp)->sin6_scope_id)
493                 iid = ((const struct sockaddr_in6 *)grp)->sin6_scope_id;
494             break;
495 #endif
496
497         case AF_INET:
498             level = SOL_IP;
499             break;
500
501         default:
502             errno = EAFNOSUPPORT;
503             return -1;
504     }
505
506     if (src != NULL)
507         switch (src->sa_family)
508         {
509 #ifdef AF_INET6
510             case AF_INET6:
511                 if (memcmp (&((const struct sockaddr_in6 *)src)->sin6_addr,
512                             &in6addr_any, sizeof (in6addr_any)) == 0)
513                     src = NULL;
514             break;
515 #endif
516
517             case AF_INET:
518                 if (((const struct sockaddr_in *)src)->sin_addr.s_addr
519                      == INADDR_ANY)
520                     src = NULL;
521                 break;
522         }
523
524
525     /* Agnostic ASM/SSM multicast join */
526 #ifdef MCAST_JOIN_SOURCE_GROUP
527     union
528     {
529         struct group_req gr;
530         struct group_source_req gsr;
531     } opt;
532     socklen_t optlen;
533
534     memset (&opt, 0, sizeof (opt));
535
536     if (src != NULL)
537     {
538         if ((grplen > sizeof (opt.gsr.gsr_group))
539          || (srclen > sizeof (opt.gsr.gsr_source)))
540             return -1;
541
542         opt.gsr.gsr_interface = iid;
543         memcpy (&opt.gsr.gsr_source, src, srclen);
544         memcpy (&opt.gsr.gsr_group,  grp, grplen);
545         optlen = sizeof (opt.gsr);
546     }
547     else
548     {
549         if (grplen > sizeof (opt.gr.gr_group))
550             return -1;
551
552         opt.gr.gr_interface = iid;
553         memcpy (&opt.gr.gr_group, grp, grplen);
554         optlen = sizeof (opt.gr);
555     }
556
557     msg_Dbg (obj, "Multicast %sgroup join request", src ? "source " : "");
558
559     if (setsockopt (fd, level,
560                     src ? MCAST_JOIN_SOURCE_GROUP : MCAST_JOIN_GROUP,
561                     (void *)&opt, optlen) == 0)
562         return 0;
563 #endif
564
565     /* Fallback to IPv-specific APIs */
566     if ((src != NULL) && (src->sa_family != grp->sa_family))
567         return -1;
568
569     switch (grp->sa_family)
570     {
571         case AF_INET:
572             if ((grplen < sizeof (struct sockaddr_in))
573              || ((src != NULL) && (srclen < sizeof (struct sockaddr_in))))
574                 return -1;
575
576             if (net_IPv4Join (obj, fd, (const struct sockaddr_in *)src,
577                               (const struct sockaddr_in *)grp) == 0)
578                 return 0;
579             break;
580
581 #ifdef AF_INET6
582         case AF_INET6:
583             if ((grplen < sizeof (struct sockaddr_in6))
584              || ((src != NULL) && (srclen < sizeof (struct sockaddr_in6))))
585                 return -1;
586
587             /* IPv6-specific SSM API does not exist. So if we're here
588              * it means IPv6 SSM is not supported on this OS and we
589              * directly fallback to ASM */
590
591             if (net_IPv6Join (obj, fd, (const struct sockaddr_in6 *)grp) == 0)
592                 return 0;
593             break;
594 #endif
595     }
596
597     msg_Err (obj, "Multicast group join error (%m)");
598
599     if (src != NULL)
600     {
601         msg_Warn (obj, "Trying ASM instead of SSM...");
602         return net_Subscribe (obj, fd, grp, grplen);
603     }
604
605     msg_Err (obj, "Multicast not supported");
606     return -1;
607 }
608
609
610 int net_Subscribe (vlc_object_t *obj, int fd,
611                    const struct sockaddr *addr, socklen_t addrlen)
612 {
613     return net_SourceSubscribe (obj, fd, NULL, 0, addr, addrlen);
614 }
615
616
617 static int net_SetDSCP( int fd, uint8_t dscp )
618 {
619     struct sockaddr_storage addr;
620     if( getsockname( fd, (struct sockaddr *)&addr, &(socklen_t){ sizeof (addr) }) )
621         return -1;
622
623     int level, cmd;
624
625     switch( addr.ss_family )
626     {
627 #ifdef IPV6_TCLASS
628         case AF_INET6:
629             level = SOL_IPV6;
630             cmd = IPV6_TCLASS;
631             break;
632 #endif
633
634         case AF_INET:
635             level = SOL_IP;
636             cmd = IP_TOS;
637             break;
638
639         default:
640 #ifdef ENOPROTOOPT
641             errno = ENOPROTOOPT;
642 #endif
643             return -1;
644     }
645
646     return setsockopt( fd, level, cmd, &(int){ dscp }, sizeof (int));
647 }
648
649
650 /*****************************************************************************
651  * __net_ConnectDgram:
652  *****************************************************************************
653  * Open a datagram socket to send data to a defined destination, with an
654  * optional hop limit.
655  *****************************************************************************/
656 int __net_ConnectDgram( vlc_object_t *p_this, const char *psz_host, int i_port,
657                         int i_hlim, int proto )
658 {
659     struct addrinfo hints, *res, *ptr;
660     int             i_val, i_handle = -1;
661     bool      b_unreach = false;
662
663     if( i_hlim < 0 )
664         i_hlim = var_CreateGetInteger( p_this, "ttl" );
665
666     memset( &hints, 0, sizeof( hints ) );
667     hints.ai_socktype = SOCK_DGRAM;
668
669     msg_Dbg( p_this, "net: connecting to [%s]:%d", psz_host, i_port );
670
671     i_val = vlc_getaddrinfo( p_this, psz_host, i_port, &hints, &res );
672     if( i_val )
673     {
674         msg_Err( p_this, "cannot resolve [%s]:%d : %s", psz_host, i_port,
675                  vlc_gai_strerror( i_val ) );
676         return -1;
677     }
678
679     for( ptr = res; ptr != NULL; ptr = ptr->ai_next )
680     {
681         char *str;
682         int fd = net_Socket (p_this, ptr->ai_family, ptr->ai_socktype,
683                              proto ? proto : ptr->ai_protocol);
684         if (fd == -1)
685             continue;
686
687 #if !defined( SYS_BEOS )
688         /* Increase the receive buffer size to 1/2MB (8Mb/s during 1/2s)
689         * to avoid packet loss caused by scheduling problems */
690         setsockopt (fd, SOL_SOCKET, SO_RCVBUF, &(int){ 0x80000 }, sizeof (int));
691         setsockopt (fd, SOL_SOCKET, SO_SNDBUF, &(int){ 0x80000 }, sizeof (int));
692
693         /* Allow broadcast sending */
694         setsockopt (fd, SOL_SOCKET, SO_BROADCAST, &(int){ 1 }, sizeof (int));
695 #endif
696
697         if( i_hlim >= 0 )
698             net_SetMcastHopLimit( p_this, fd, ptr->ai_family, i_hlim );
699
700         str = var_CreateGetNonEmptyString (p_this, "miface");
701         if (str != NULL)
702         {
703             net_SetMcastOut (p_this, fd, ptr->ai_family, str, NULL);
704             free (str);
705         }
706
707         str = var_CreateGetNonEmptyString (p_this, "miface-addr");
708         if (str != NULL)
709         {
710             net_SetMcastOut (p_this, fd, ptr->ai_family, NULL, str);
711             free (str);
712         }
713
714         net_SetDSCP (fd, var_CreateGetInteger (p_this, "dscp"));
715
716         if( connect( fd, ptr->ai_addr, ptr->ai_addrlen ) == 0 )
717         {
718             /* success */
719             i_handle = fd;
720             break;
721         }
722
723 #if defined( WIN32 ) || defined( UNDER_CE )
724         if( WSAGetLastError () == WSAENETUNREACH )
725 #else
726         if( errno == ENETUNREACH )
727 #endif
728             b_unreach = true;
729         else
730         {
731             msg_Warn( p_this, "%s port %d : %m", psz_host, i_port);
732             net_Close( fd );
733             continue;
734         }
735     }
736
737     vlc_freeaddrinfo( res );
738
739     if( i_handle == -1 )
740     {
741         if( b_unreach )
742             msg_Err( p_this, "Host %s port %d is unreachable", psz_host,
743                      i_port );
744         return -1;
745     }
746
747     return i_handle;
748 }
749
750
751 /*****************************************************************************
752  * __net_OpenDgram:
753  *****************************************************************************
754  * OpenDgram a datagram socket and return a handle
755  *****************************************************************************/
756 int __net_OpenDgram( vlc_object_t *obj, const char *psz_bind, int i_bind,
757                      const char *psz_server, int i_server,
758                      int family, int protocol )
759 {
760     if ((psz_server == NULL) || (psz_server[0] == '\0'))
761         return net_ListenSingle (obj, psz_bind, i_bind, family, protocol);
762
763     msg_Dbg (obj, "net: connecting to [%s]:%d from [%s]:%d",
764              psz_server, i_server, psz_bind, i_bind);
765
766     struct addrinfo hints, *loc, *rem;
767     int val;
768
769     memset (&hints, 0, sizeof (hints));
770     hints.ai_family = family;
771     hints.ai_socktype = SOCK_DGRAM;
772
773     val = vlc_getaddrinfo (obj, psz_server, i_server, &hints, &rem);
774     if (val)
775     {
776         msg_Err (obj, "cannot resolve %s port %d : %s", psz_bind, i_bind,
777                  vlc_gai_strerror (val));
778         return -1;
779     }
780
781     hints.ai_flags = AI_PASSIVE;
782     val = vlc_getaddrinfo (obj, psz_bind, i_bind, &hints, &loc);
783     if (val)
784     {
785         msg_Err (obj, "cannot resolve %s port %d : %s", psz_bind, i_bind,
786                  vlc_gai_strerror (val));
787         vlc_freeaddrinfo (rem);
788         return -1;
789     }
790
791     for (struct addrinfo *ptr = loc; ptr != NULL; ptr = ptr->ai_next)
792     {
793         int fd = net_Socket (obj, ptr->ai_family, ptr->ai_socktype,
794                              protocol ? protocol : ptr->ai_protocol);
795         if (fd == -1)
796             continue; // usually, address family not supported
797
798         fd = net_SetupDgramSocket( obj, fd, ptr );
799         if( fd == -1 )
800             continue;
801
802         val = -1;
803         for (struct addrinfo *ptr2 = rem; ptr2 != NULL; ptr2 = ptr2->ai_next)
804         {
805             if ((ptr2->ai_family != ptr->ai_family)
806              || (ptr2->ai_socktype != ptr->ai_socktype)
807              || (ptr2->ai_protocol != ptr->ai_protocol))
808                 continue;
809
810             if (net_SockAddrIsMulticast (ptr->ai_addr, ptr->ai_addrlen)
811               ? net_SourceSubscribe (obj, fd,
812                                      ptr2->ai_addr, ptr2->ai_addrlen,
813                                      ptr->ai_addr, ptr->ai_addrlen)
814               : connect (fd, ptr2->ai_addr, ptr2->ai_addrlen))
815             {
816                 msg_Err (obj, "cannot connect to %s port %d: %m",
817                          psz_server, i_server);
818                 continue;
819             }
820             val = fd;
821             break;
822         }
823
824         if (val != -1)
825             break;
826
827         net_Close (fd);
828     }
829
830     vlc_freeaddrinfo (rem);
831     vlc_freeaddrinfo (loc);
832     return val;
833 }
834
835
836 /**
837  * net_SetCSCov:
838  * Sets the send and receive checksum coverage of a socket:
839  * @param fd socket
840  * @param sendcov payload coverage of sent packets (bytes), -1 for full
841  * @param recvcov minimum payload coverage of received packets, -1 for full
842  */
843 int net_SetCSCov (int fd, int sendcov, int recvcov)
844 {
845     int type;
846
847     if (getsockopt (fd, SOL_SOCKET, SO_TYPE,
848                     &type, &(socklen_t){ sizeof (type) }))
849         return VLC_EGENERIC;
850
851     switch (type)
852     {
853 #ifdef UDPLITE_RECV_CSCOV
854         case SOCK_DGRAM: /* UDP-Lite */
855             if (sendcov == -1)
856                 sendcov = 0;
857             else
858                 sendcov += 8; /* partial */
859             if (setsockopt (fd, SOL_UDPLITE, UDPLITE_SEND_CSCOV, &sendcov,
860                             sizeof (sendcov)))
861                 return VLC_EGENERIC;
862
863             if (recvcov == -1)
864                 recvcov = 0;
865             else
866                 recvcov += 8;
867             if (setsockopt (fd, SOL_UDPLITE, UDPLITE_RECV_CSCOV,
868                             &recvcov, sizeof (recvcov)))
869                 return VLC_EGENERIC;
870
871             return VLC_SUCCESS;
872 #endif
873 #ifdef DCCP_SOCKOPT_SEND_CSCOV
874         case SOCK_DCCP: /* DCCP and its ill-named socket type */
875             if ((sendcov == -1) || (sendcov > 56))
876                 sendcov = 0;
877             else
878                 sendcov = (sendcov + 3) / 4;
879             if (setsockopt (fd, SOL_DCCP, DCCP_SOCKOPT_SEND_CSCOV,
880                             &sendcov, sizeof (sendcov)))
881                 return VLC_EGENERIC;
882
883             if ((recvcov == -1) || (recvcov > 56))
884                 recvcov = 0;
885             else
886                 recvcov = (recvcov + 3) / 4;
887             if (setsockopt (fd, SOL_DCCP, DCCP_SOCKOPT_RECV_CSCOV,
888                             &recvcov, sizeof (recvcov)))
889                 return VLC_EGENERIC;
890
891             return VLC_SUCCESS;
892 #endif
893     }
894 #if !defined( UDPLITE_RECV_CSCOV ) && !defined( DCCP_SOCKOPT_SEND_CSCOV )
895     VLC_UNUSED(sendcov);
896     VLC_UNUSED(recvcov);
897 #endif
898
899     return VLC_EGENERIC;
900 }