1 /*****************************************************************************
3 *****************************************************************************
4 * Copyright (C) 2004-2006 the VideoLAN team
5 * Copyright © 2006-2007 Rémi Denis-Courmont
9 * Authors: Laurent Aimar <fenrir@videolan.org>
10 * Rémi Denis-Courmont <rem # videolan.org>
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.
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.
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 *****************************************************************************/
27 /*****************************************************************************
29 *****************************************************************************/
34 #include <vlc_common.h>
38 #include <vlc_network.h>
41 # if defined(UNDER_CE)
42 # undef IP_MULTICAST_TTL
43 # define IP_MULTICAST_TTL 3
44 # undef IP_ADD_MEMBERSHIP
45 # define IP_ADD_MEMBERSHIP 5
47 # define EAFNOSUPPORT WSAEAFNOSUPPORT
55 #ifdef HAVE_LINUX_DCCP_H
56 # include <linux/dccp.h>
57 # ifndef SOCK_DCCP /* provisional API */
63 # define SOL_IP IPPROTO_IP
66 # define SOL_IPV6 IPPROTO_IPV6
69 # define IPPROTO_IPV6 41 /* IANA */
72 # define SOL_DCCP IPPROTO_DCCP
75 # define IPPROTO_DCCP 33 /* IANA */
78 # define SOL_UDPLITE IPPROTO_UDPLITE
80 #ifndef IPPROTO_UDPLITE
81 # define IPPROTO_UDPLITE 136 /* IANA */
84 #if defined (HAVE_NETINET_UDPLITE_H)
85 # include <netinet/udplite.h>
86 #elif defined (__linux__)
87 /* still missing from glibc 2.6 */
88 # define UDPLITE_SEND_CSCOV 10
89 # define UDPLITE_RECV_CSCOV 11
92 extern int net_Socket( vlc_object_t *p_this, int i_family, int i_socktype,
96 static int net_SetupDgramSocket( vlc_object_t *p_obj, int fd, const struct addrinfo *ptr )
99 setsockopt (fd, SOL_SOCKET, SO_REUSEPORT, &(int){ 1 }, sizeof (int));
103 /* Increase the receive buffer size to 1/2MB (8Mb/s during 1/2s)
104 * to avoid packet loss caused in case of scheduling hiccups */
105 setsockopt (fd, SOL_SOCKET, SO_RCVBUF,
106 (void *)&(int){ 0x80000 }, sizeof (int));
107 setsockopt (fd, SOL_SOCKET, SO_SNDBUF,
108 (void *)&(int){ 0x80000 }, sizeof (int));
111 #if defined (WIN32) || defined (UNDER_CE)
112 if (net_SockAddrIsMulticast (ptr->ai_addr, ptr->ai_addrlen)
113 && (sizeof (struct sockaddr_storage) >= ptr->ai_addrlen))
115 // This works for IPv4 too - don't worry!
116 struct sockaddr_in6 dumb =
118 .sin6_family = ptr->ai_addr->sa_family,
119 .sin6_port = ((struct sockaddr_in *)(ptr->ai_addr))->sin_port
122 bind (fd, (struct sockaddr *)&dumb, ptr->ai_addrlen);
126 if (bind (fd, ptr->ai_addr, ptr->ai_addrlen))
128 msg_Err( p_obj, "socket bind error (%m)" );
136 static int net_ListenSingle (vlc_object_t *obj, const char *host, int port,
137 int family, int protocol)
139 struct addrinfo hints, *res;
141 memset (&hints, 0, sizeof( hints ));
142 hints.ai_family = family;
143 hints.ai_socktype = SOCK_DGRAM;
144 hints.ai_protocol = protocol;
145 hints.ai_flags = AI_PASSIVE;
150 msg_Dbg (obj, "net: opening %s datagram port %d",
151 host ? host : "any", port);
153 int val = vlc_getaddrinfo (obj, host, port, &hints, &res);
156 msg_Err (obj, "Cannot resolve %s port %d : %s", host, port,
163 for (const struct addrinfo *ptr = res; ptr != NULL; ptr = ptr->ai_next)
165 int fd = net_Socket (obj, ptr->ai_family, ptr->ai_socktype,
169 msg_Dbg (obj, "socket error: %m");
174 /* If IPv6 was forced, set IPv6-only mode.
175 * If IPv4 was forced, do nothing extraordinary.
176 * If nothing was forced, try dual-mode IPv6. */
177 if (ptr->ai_family == AF_INET6)
179 int on = (family == AF_INET6);
180 setsockopt (fd, SOL_IPV6, IPV6_V6ONLY, &on, sizeof (on));
182 if (ptr->ai_family == AF_INET)
184 if (family == AF_UNSPEC && ptr->ai_next != NULL)
186 msg_Warn (obj, "ambiguous network protocol specification");
187 msg_Warn (obj, "please select IP version explicitly");
190 fd = net_SetupDgramSocket( obj, fd, ptr );
194 if (net_SockAddrIsMulticast (ptr->ai_addr, ptr->ai_addrlen)
195 && net_Subscribe (obj, fd, ptr->ai_addr, ptr->ai_addrlen))
210 static int net_SetMcastHopLimit( vlc_object_t *p_this,
211 int fd, int family, int hlim )
215 /* There is some confusion in the world whether IP_MULTICAST_TTL
216 * takes a byte or an int as an argument.
217 * BSD seems to indicate byte so we are going with that and use
218 * int as a fallback to be safe */
221 #ifdef IP_MULTICAST_TTL
224 cmd = IP_MULTICAST_TTL;
228 #ifdef IPV6_MULTICAST_HOPS
231 cmd = IPV6_MULTICAST_HOPS;
236 errno = EAFNOSUPPORT;
237 msg_Warn( p_this, "%m" );
241 if( setsockopt( fd, proto, cmd, &hlim, sizeof( hlim ) ) < 0 )
243 /* BSD compatibility */
246 msg_Dbg( p_this, "cannot set hop limit (%d): %m", hlim );
247 buf = (unsigned char)(( hlim > 255 ) ? 255 : hlim);
248 if( setsockopt( fd, proto, cmd, &buf, sizeof( buf ) ) )
250 msg_Err( p_this, "cannot set hop limit (%d): %m", hlim );
259 static int net_SetMcastOutIface (int fd, int family, int scope)
263 #ifdef IPV6_MULTICAST_IF
265 return setsockopt (fd, SOL_IPV6, IPV6_MULTICAST_IF,
266 &scope, sizeof (scope));
272 struct ip_mreqn req = { .imr_ifindex = scope };
274 return setsockopt (fd, SOL_IP, IP_MULTICAST_IF, &req,
280 errno = EAFNOSUPPORT;
285 static inline int net_SetMcastOutIPv4 (int fd, struct in_addr ipv4)
287 #ifdef IP_MULTICAST_IF
288 return setsockopt( fd, SOL_IP, IP_MULTICAST_IF, &ipv4, sizeof (ipv4));
290 errno = EAFNOSUPPORT;
296 static int net_SetMcastOut (vlc_object_t *p_this, int fd, int family,
297 const char *iface, const char *addr)
301 int scope = if_nametoindex (iface);
304 msg_Err (p_this, "invalid multicast interface: %s", iface);
308 if (net_SetMcastOutIface (fd, family, scope) == 0)
311 msg_Err (p_this, "%s: %m", iface);
316 if (family == AF_INET)
319 if (inet_pton (AF_INET, addr, &ipv4) <= 0)
321 msg_Err (p_this, "invalid IPv4 address for multicast: %s",
326 if (net_SetMcastOutIPv4 (fd, ipv4) == 0)
329 msg_Err (p_this, "%s: %m", addr);
338 * Old-style any-source multicast join.
339 * In use on Windows XP/2003 and older.
342 net_IPv4Join (vlc_object_t *obj, int fd,
343 const struct sockaddr_in *src, const struct sockaddr_in *grp)
345 #ifdef IP_ADD_MEMBERSHIP
349 # ifdef IP_ADD_SOURCE_MEMBERSHIP
350 struct ip_mreq_source gsr4;
354 struct in_addr id = { .s_addr = INADDR_ANY };
357 /* Multicast interface IPv4 address */
358 char *iface = var_InheritString (obj, "miface-addr");
360 && (inet_pton (AF_INET, iface, &id) <= 0))
362 msg_Err (obj, "invalid multicast interface address %s", iface);
368 memset (&opt, 0, sizeof (opt));
371 # if defined( IP_ADD_SOURCE_MEMBERSHIP ) && !defined( __ANDROID__ )
372 cmd = IP_ADD_SOURCE_MEMBERSHIP;
373 opt.gsr4.imr_multiaddr = grp->sin_addr;
374 opt.gsr4.imr_sourceaddr = src->sin_addr;
375 opt.gsr4.imr_interface = id;
376 optlen = sizeof (opt.gsr4);
384 cmd = IP_ADD_MEMBERSHIP;
385 opt.gr4.imr_multiaddr = grp->sin_addr;
386 opt.gr4.imr_interface = id;
387 optlen = sizeof (opt.gr4);
390 msg_Dbg (obj, "IP_ADD_%sMEMBERSHIP multicast request",
391 (src != NULL) ? "SOURCE_" : "");
393 if (setsockopt (fd, SOL_IP, cmd, &opt, optlen) == 0)
399 msg_Err (obj, "cannot join IPv4 multicast group (%m)");
405 net_IPv6Join (vlc_object_t *obj, int fd, const struct sockaddr_in6 *src)
407 #ifdef IPV6_JOIN_GROUP
408 struct ipv6_mreq gr6;
409 memset (&gr6, 0, sizeof (gr6));
410 gr6.ipv6mr_interface = src->sin6_scope_id;
411 memcpy (&gr6.ipv6mr_multiaddr, &src->sin6_addr, 16);
413 msg_Dbg (obj, "IPV6_JOIN_GROUP multicast request");
415 if (!setsockopt (fd, SOL_IPV6, IPV6_JOIN_GROUP, &gr6, sizeof (gr6)))
421 msg_Err (obj, "cannot join IPv6 any-source multicast group (%m)");
426 #if defined (WIN32) && !defined (MCAST_JOIN_SOURCE_GROUP)
428 * I hate manual definitions: Error-prone. Portability hell.
429 * Developers shall use UP-TO-DATE compilers. Full point.
430 * If you remove the warning, you remove the whole ifndef.
432 # warning Your C headers are out-of-date. Please update.
434 # define MCAST_JOIN_GROUP 41
438 struct sockaddr_storage gr_group;
441 # define MCAST_JOIN_SOURCE_GROUP 45 /* from <ws2ipdef.h> */
442 struct group_source_req
444 uint32_t gsr_interface;
445 struct sockaddr_storage gsr_group;
446 struct sockaddr_storage gsr_source;
451 * IP-agnostic multicast join,
452 * with fallback to old APIs, and fallback from SSM to ASM.
455 net_SourceSubscribe (vlc_object_t *obj, int fd,
456 const struct sockaddr *src, socklen_t srclen,
457 const struct sockaddr *grp, socklen_t grplen)
461 char *iface = var_InheritString (obj, "miface");
464 iid = if_nametoindex (iface);
467 msg_Err (obj, "invalid multicast interface: %s", iface);
474 switch (grp->sa_family)
479 if (((const struct sockaddr_in6 *)grp)->sin6_scope_id)
480 iid = ((const struct sockaddr_in6 *)grp)->sin6_scope_id;
489 errno = EAFNOSUPPORT;
494 switch (src->sa_family)
498 if (memcmp (&((const struct sockaddr_in6 *)src)->sin6_addr,
499 &in6addr_any, sizeof (in6addr_any)) == 0)
505 if (((const struct sockaddr_in *)src)->sin_addr.s_addr
512 /* Agnostic ASM/SSM multicast join */
513 #ifdef MCAST_JOIN_SOURCE_GROUP
517 struct group_source_req gsr;
521 memset (&opt, 0, sizeof (opt));
525 if ((grplen > sizeof (opt.gsr.gsr_group))
526 || (srclen > sizeof (opt.gsr.gsr_source)))
529 opt.gsr.gsr_interface = iid;
530 memcpy (&opt.gsr.gsr_source, src, srclen);
531 memcpy (&opt.gsr.gsr_group, grp, grplen);
532 optlen = sizeof (opt.gsr);
536 if (grplen > sizeof (opt.gr.gr_group))
539 opt.gr.gr_interface = iid;
540 memcpy (&opt.gr.gr_group, grp, grplen);
541 optlen = sizeof (opt.gr);
544 msg_Dbg (obj, "Multicast %sgroup join request", src ? "source " : "");
546 if (setsockopt (fd, level,
547 src ? MCAST_JOIN_SOURCE_GROUP : MCAST_JOIN_GROUP,
548 (void *)&opt, optlen) == 0)
552 /* Fallback to IPv-specific APIs */
553 if ((src != NULL) && (src->sa_family != grp->sa_family))
556 switch (grp->sa_family)
559 if ((grplen < sizeof (struct sockaddr_in))
560 || ((src != NULL) && (srclen < sizeof (struct sockaddr_in))))
563 if (net_IPv4Join (obj, fd, (const struct sockaddr_in *)src,
564 (const struct sockaddr_in *)grp) == 0)
570 if ((grplen < sizeof (struct sockaddr_in6))
571 || ((src != NULL) && (srclen < sizeof (struct sockaddr_in6))))
574 /* IPv6-specific SSM API does not exist. So if we're here
575 * it means IPv6 SSM is not supported on this OS and we
576 * directly fallback to ASM */
578 if (net_IPv6Join (obj, fd, (const struct sockaddr_in6 *)grp) == 0)
584 msg_Err (obj, "Multicast group join error (%m)");
588 msg_Warn (obj, "Trying ASM instead of SSM...");
589 return net_Subscribe (obj, fd, grp, grplen);
592 msg_Err (obj, "Multicast not supported");
597 int net_Subscribe (vlc_object_t *obj, int fd,
598 const struct sockaddr *addr, socklen_t addrlen)
600 return net_SourceSubscribe (obj, fd, NULL, 0, addr, addrlen);
604 static int net_SetDSCP( int fd, uint8_t dscp )
606 struct sockaddr_storage addr;
607 if( getsockname( fd, (struct sockaddr *)&addr, &(socklen_t){ sizeof (addr) }) )
612 switch( addr.ss_family )
633 return setsockopt( fd, level, cmd, &(int){ dscp }, sizeof (int));
636 #undef net_ConnectDgram
637 /*****************************************************************************
639 *****************************************************************************
640 * Open a datagram socket to send data to a defined destination, with an
641 * optional hop limit.
642 *****************************************************************************/
643 int net_ConnectDgram( vlc_object_t *p_this, const char *psz_host, int i_port,
644 int i_hlim, int proto )
646 struct addrinfo hints, *res, *ptr;
647 int i_val, i_handle = -1;
648 bool b_unreach = false;
651 i_hlim = var_InheritInteger( p_this, "ttl" );
653 memset( &hints, 0, sizeof( hints ) );
654 hints.ai_socktype = SOCK_DGRAM;
655 hints.ai_protocol = proto;
657 msg_Dbg( p_this, "net: connecting to [%s]:%d", psz_host, i_port );
659 i_val = vlc_getaddrinfo( p_this, psz_host, i_port, &hints, &res );
662 msg_Err( p_this, "cannot resolve [%s]:%d : %s", psz_host, i_port,
663 gai_strerror( i_val ) );
667 for( ptr = res; ptr != NULL; ptr = ptr->ai_next )
670 int fd = net_Socket (p_this, ptr->ai_family, ptr->ai_socktype,
675 /* Increase the receive buffer size to 1/2MB (8Mb/s during 1/2s)
676 * to avoid packet loss caused by scheduling problems */
677 setsockopt (fd, SOL_SOCKET, SO_RCVBUF, &(int){ 0x80000 }, sizeof (int));
678 setsockopt (fd, SOL_SOCKET, SO_SNDBUF, &(int){ 0x80000 }, sizeof (int));
680 /* Allow broadcast sending */
681 setsockopt (fd, SOL_SOCKET, SO_BROADCAST, &(int){ 1 }, sizeof (int));
684 net_SetMcastHopLimit( p_this, fd, ptr->ai_family, i_hlim );
686 str = var_InheritString (p_this, "miface");
689 net_SetMcastOut (p_this, fd, ptr->ai_family, str, NULL);
693 str = var_InheritString (p_this, "miface-addr");
696 net_SetMcastOut (p_this, fd, ptr->ai_family, NULL, str);
700 net_SetDSCP (fd, var_InheritInteger (p_this, "dscp"));
702 if( connect( fd, ptr->ai_addr, ptr->ai_addrlen ) == 0 )
709 #if defined( WIN32 ) || defined( UNDER_CE )
710 if( WSAGetLastError () == WSAENETUNREACH )
712 if( errno == ENETUNREACH )
717 msg_Warn( p_this, "%s port %d : %m", psz_host, i_port);
728 msg_Err( p_this, "Host %s port %d is unreachable", psz_host,
737 /*****************************************************************************
739 *****************************************************************************
740 * OpenDgram a datagram socket and return a handle
741 *****************************************************************************/
742 int net_OpenDgram( vlc_object_t *obj, const char *psz_bind, int i_bind,
743 const char *psz_server, int i_server,
744 int family, int protocol )
746 if ((psz_server == NULL) || (psz_server[0] == '\0'))
747 return net_ListenSingle (obj, psz_bind, i_bind, family, protocol);
749 msg_Dbg (obj, "net: connecting to [%s]:%d from [%s]:%d",
750 psz_server, i_server, psz_bind, i_bind);
752 struct addrinfo hints, *loc, *rem;
755 memset (&hints, 0, sizeof (hints));
756 hints.ai_family = family;
757 hints.ai_socktype = SOCK_DGRAM;
758 hints.ai_protocol = protocol;
760 val = vlc_getaddrinfo (obj, psz_server, i_server, &hints, &rem);
763 msg_Err (obj, "cannot resolve %s port %d : %s", psz_bind, i_bind,
768 hints.ai_flags = AI_PASSIVE;
769 val = vlc_getaddrinfo (obj, psz_bind, i_bind, &hints, &loc);
772 msg_Err (obj, "cannot resolve %s port %d : %s", psz_bind, i_bind,
779 for (struct addrinfo *ptr = loc; ptr != NULL; ptr = ptr->ai_next)
781 int fd = net_Socket (obj, ptr->ai_family, ptr->ai_socktype,
784 continue; // usually, address family not supported
786 fd = net_SetupDgramSocket( obj, fd, ptr );
790 for (struct addrinfo *ptr2 = rem; ptr2 != NULL; ptr2 = ptr2->ai_next)
792 if ((ptr2->ai_family != ptr->ai_family)
793 || (ptr2->ai_socktype != ptr->ai_socktype)
794 || (ptr2->ai_protocol != ptr->ai_protocol))
797 if (net_SockAddrIsMulticast (ptr->ai_addr, ptr->ai_addrlen)
798 ? net_SourceSubscribe (obj, fd,
799 ptr2->ai_addr, ptr2->ai_addrlen,
800 ptr->ai_addr, ptr->ai_addrlen)
801 : connect (fd, ptr2->ai_addr, ptr2->ai_addrlen))
803 msg_Err (obj, "cannot connect to %s port %d: %m",
804 psz_server, i_server);
825 * Sets the send and receive checksum coverage of a socket:
827 * @param sendcov payload coverage of sent packets (bytes), -1 for full
828 * @param recvcov minimum payload coverage of received packets, -1 for full
830 int net_SetCSCov (int fd, int sendcov, int recvcov)
834 if (getsockopt (fd, SOL_SOCKET, SO_TYPE,
835 &type, &(socklen_t){ sizeof (type) }))
840 #ifdef UDPLITE_RECV_CSCOV
841 case SOCK_DGRAM: /* UDP-Lite */
845 sendcov += 8; /* partial */
846 if (setsockopt (fd, SOL_UDPLITE, UDPLITE_SEND_CSCOV, &sendcov,
854 if (setsockopt (fd, SOL_UDPLITE, UDPLITE_RECV_CSCOV,
855 &recvcov, sizeof (recvcov)))
860 #ifdef DCCP_SOCKOPT_SEND_CSCOV
861 case SOCK_DCCP: /* DCCP and its ill-named socket type */
862 if ((sendcov == -1) || (sendcov > 56))
865 sendcov = (sendcov + 3) / 4;
866 if (setsockopt (fd, SOL_DCCP, DCCP_SOCKOPT_SEND_CSCOV,
867 &sendcov, sizeof (sendcov)))
870 if ((recvcov == -1) || (recvcov > 56))
873 recvcov = (recvcov + 3) / 4;
874 if (setsockopt (fd, SOL_DCCP, DCCP_SOCKOPT_RECV_CSCOV,
875 &recvcov, sizeof (recvcov)))
881 #if !defined( UDPLITE_RECV_CSCOV ) && !defined( DCCP_SOCKOPT_SEND_CSCOV )