📄 udp.c
字号:
/***************************************************************************** * udp.c: ***************************************************************************** * Copyright (C) 2004-2006 the VideoLAN team * Copyright © 2006-2007 Rémi Denis-Courmont * * $Id$ * * Authors: Laurent Aimar <fenrir@videolan.org> * Rémi Denis-Courmont <rem # videolan.org> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************//***************************************************************************** * Preamble *****************************************************************************/#ifdef HAVE_CONFIG_H# include "config.h"#endif#include <vlc_common.h>#include <errno.h>#ifdef HAVE_SYS_TIME_H# include <sys/time.h>#endif#include <vlc_network.h>#ifdef WIN32# if defined(UNDER_CE)# undef IP_MULTICAST_TTL# define IP_MULTICAST_TTL 3# undef IP_ADD_MEMBERSHIP# define IP_ADD_MEMBERSHIP 5# endif# define EAFNOSUPPORT WSAEAFNOSUPPORT# define if_nametoindex( str ) atoi( str )#else# include <unistd.h># ifdef HAVE_NET_IF_H# include <net/if.h># endif#endif#ifdef HAVE_LINUX_DCCP_H# include <linux/dccp.h># ifndef SOCK_DCCP /* provisional API */# define SOCK_DCCP 6# endif#endif#ifndef SOL_IP# define SOL_IP IPPROTO_IP#endif#ifndef SOL_IPV6# define SOL_IPV6 IPPROTO_IPV6#endif#ifndef IPPROTO_IPV6# define IPPROTO_IPV6 41 /* IANA */#endif#ifndef SOL_DCCP# define SOL_DCCP IPPROTO_DCCP#endif#ifndef IPPROTO_DCCP# define IPPROTO_DCCP 33 /* IANA */#endif#ifndef SOL_UDPLITE# define SOL_UDPLITE IPPROTO_UDPLITE#endif#ifndef IPPROTO_UDPLITE# define IPPROTO_UDPLITE 136 /* IANA */#endif#if defined (HAVE_NETINET_UDPLITE_H)# include <netinet/udplite.h>#elif defined (__linux__)/* still missing from glibc 2.6 */# define UDPLITE_SEND_CSCOV 10# define UDPLITE_RECV_CSCOV 11#endifextern int net_Socket( vlc_object_t *p_this, int i_family, int i_socktype, int i_protocol );/* */static int net_SetupDgramSocket( vlc_object_t *p_obj, int fd, const struct addrinfo *ptr ){#ifdef SO_REUSEPORT setsockopt (fd, SOL_SOCKET, SO_REUSEPORT, &(int){ 1 }, sizeof (int));#endif#ifdef SO_RCVBUF /* Increase the receive buffer size to 1/2MB (8Mb/s during 1/2s) * to avoid packet loss caused in case of scheduling hiccups */ setsockopt (fd, SOL_SOCKET, SO_RCVBUF, (void *)&(int){ 0x80000 }, sizeof (int)); setsockopt (fd, SOL_SOCKET, SO_SNDBUF, (void *)&(int){ 0x80000 }, sizeof (int));#endif#if defined (WIN32) || defined (UNDER_CE) if (net_SockAddrIsMulticast (ptr->ai_addr, ptr->ai_addrlen) && (sizeof (struct sockaddr_storage) >= ptr->ai_addrlen)) { // This works for IPv4 too - don't worry! struct sockaddr_in6 dumb = { .sin6_family = ptr->ai_addr->sa_family, .sin6_port = ((struct sockaddr_in *)(ptr->ai_addr))->sin_port }; bind (fd, (struct sockaddr *)&dumb, ptr->ai_addrlen); } else#endif if (bind (fd, ptr->ai_addr, ptr->ai_addrlen)) { msg_Err( p_obj, "socket bind error (%m)" ); net_Close (fd); return -1; } return fd;}/* */static int net_ListenSingle (vlc_object_t *obj, const char *host, int port, int family, int protocol){ struct addrinfo hints, *res; memset (&hints, 0, sizeof( hints )); hints.ai_family = family; hints.ai_socktype = SOCK_DGRAM; hints.ai_flags = AI_PASSIVE; if (host && !*host) host = NULL; msg_Dbg (obj, "net: opening %s datagram port %d", host ?: "any", port); int val = vlc_getaddrinfo (obj, host, port, &hints, &res); if (val) { msg_Err (obj, "Cannot resolve %s port %d : %s", host, port, vlc_gai_strerror (val)); return -1; } val = -1; for (const struct addrinfo *ptr = res; ptr != NULL; ptr = ptr->ai_next) { int fd = net_Socket (obj, ptr->ai_family, ptr->ai_socktype, protocol ?: ptr->ai_protocol); if (fd == -1) { msg_Dbg (obj, "socket error: %m"); continue; } if (ptr->ai_next != NULL) {#ifdef IPV6_V6ONLY if ((ptr->ai_family != AF_INET6) || setsockopt (fd, SOL_IPV6, IPV6_V6ONLY, &(int){ 0 }, sizeof (int)))#endif { msg_Err (obj, "Multiple network protocols present"); msg_Err (obj, "Please select network protocol manually"); } } fd = net_SetupDgramSocket( obj, fd, ptr ); if( fd == -1 ) continue; if (net_SockAddrIsMulticast (ptr->ai_addr, ptr->ai_addrlen) && net_Subscribe (obj, fd, ptr->ai_addr, ptr->ai_addrlen)) { net_Close (fd); continue; } val = fd; break; } vlc_freeaddrinfo (res); return val;}static int net_SetMcastHopLimit( vlc_object_t *p_this, int fd, int family, int hlim ){ int proto, cmd; /* There is some confusion in the world whether IP_MULTICAST_TTL * takes a byte or an int as an argument. * BSD seems to indicate byte so we are going with that and use * int as a fallback to be safe */ switch( family ) {#ifdef IP_MULTICAST_TTL case AF_INET: proto = SOL_IP; cmd = IP_MULTICAST_TTL; break;#endif#ifdef IPV6_MULTICAST_HOPS case AF_INET6: proto = SOL_IPV6; cmd = IPV6_MULTICAST_HOPS; break;#endif default: errno = EAFNOSUPPORT; msg_Warn( p_this, "%m" ); return VLC_EGENERIC; } if( setsockopt( fd, proto, cmd, &hlim, sizeof( hlim ) ) < 0 ) { /* BSD compatibility */ unsigned char buf; buf = (unsigned char)(( hlim > 255 ) ? 255 : hlim); if( setsockopt( fd, proto, cmd, &buf, sizeof( buf ) ) ) return VLC_EGENERIC; } return VLC_SUCCESS;}static int net_SetMcastOutIface (int fd, int family, int scope){ switch (family) {#ifdef IPV6_MULTICAST_IF case AF_INET6: return setsockopt (fd, SOL_IPV6, IPV6_MULTICAST_IF, &scope, sizeof (scope));#endif#ifdef __linux__ case AF_INET: { struct ip_mreqn req = { .imr_ifindex = scope }; return setsockopt (fd, SOL_IP, IP_MULTICAST_IF, &req, sizeof (req)); }#endif } errno = EAFNOSUPPORT; return -1;}static inline int net_SetMcastOutIPv4 (int fd, struct in_addr ipv4){#ifdef IP_MULTICAST_IF return setsockopt( fd, SOL_IP, IP_MULTICAST_IF, &ipv4, sizeof (ipv4));#else errno = EAFNOSUPPORT; return -1;#endif}static int net_SetMcastOut (vlc_object_t *p_this, int fd, int family, const char *iface, const char *addr){ if (iface != NULL) { int scope = if_nametoindex (iface); if (scope == 0) { msg_Err (p_this, "invalid multicast interface: %s", iface); return -1; } if (net_SetMcastOutIface (fd, family, scope) == 0) return 0; msg_Err (p_this, "%s: %m", iface); } if (addr != NULL) { if (family == AF_INET) { struct in_addr ipv4; if (inet_pton (AF_INET, addr, &ipv4) <= 0) { msg_Err (p_this, "invalid IPv4 address for multicast: %s", addr); return -1; } if (net_SetMcastOutIPv4 (fd, ipv4) == 0) return 0; msg_Err (p_this, "%s: %m", addr); } } return -1;}/** * Old-style any-source multicast join. * In use on Windows XP/2003 and older. */static intnet_IPv4Join (vlc_object_t *obj, int fd, const struct sockaddr_in *src, const struct sockaddr_in *grp){#ifdef IP_ADD_MEMBERSHIP union { struct ip_mreq gr4;# ifdef IP_ADD_SOURCE_MEMBERSHIP struct ip_mreq_source gsr4;# endif } opt; int cmd; struct in_addr id = { .s_addr = INADDR_ANY }; socklen_t optlen; /* Multicast interface IPv4 address */ char *iface = var_CreateGetNonEmptyString (obj, "miface-addr"); if ((iface != NULL) && (inet_pton (AF_INET, iface, &id) <= 0)) { msg_Err (obj, "invalid multicast interface address %s", iface); free (iface); goto error; } free (iface); memset (&opt, 0, sizeof (opt)); if (src != NULL) {# ifdef IP_ADD_SOURCE_MEMBERSHIP cmd = IP_ADD_SOURCE_MEMBERSHIP; opt.gsr4.imr_multiaddr = grp->sin_addr; opt.gsr4.imr_sourceaddr = src->sin_addr; opt.gsr4.imr_interface = id; optlen = sizeof (opt.gsr4);# else errno = ENOSYS; goto error;# endif } else { cmd = IP_ADD_MEMBERSHIP; opt.gr4.imr_multiaddr = grp->sin_addr; opt.gr4.imr_interface = id; optlen = sizeof (opt.gr4); } msg_Dbg (obj, "IP_ADD_%sMEMBERSHIP multicast request", (src != NULL) ? "SOURCE_" : ""); if (setsockopt (fd, SOL_IP, cmd, &opt, optlen) == 0) return 0;error:#endif msg_Err (obj, "cannot join IPv4 multicast group (%m)"); return -1;}static intnet_IPv6Join (vlc_object_t *obj, int fd, const struct sockaddr_in6 *src){#ifdef IPV6_JOIN_GROUP struct ipv6_mreq gr6; memset (&gr6, 0, sizeof (gr6)); gr6.ipv6mr_interface = src->sin6_scope_id; memcpy (&gr6.ipv6mr_multiaddr, &src->sin6_addr, 16); msg_Dbg (obj, "IPV6_JOIN_GROUP multicast request"); if (!setsockopt (fd, SOL_IPV6, IPV6_JOIN_GROUP, &gr6, sizeof (gr6))) return 0;#else errno = ENOSYS;#endif msg_Err (obj, "cannot join IPv6 any-source multicast group (%m)"); return -1;}#if defined (WIN32) && !defined (MCAST_JOIN_SOURCE_GROUP)/* * I hate manual definitions: Error-prone. Portability hell. * Developers shall use UP-TO-DATE compilers. Full point. * If you remove the warning, you remove the whole ifndef. */# warning Your C headers are out-of-date. Please update.# define MCAST_JOIN_GROUP 41struct group_req{ ULONG gr_interface; struct sockaddr_storage gr_group;};# define MCAST_JOIN_SOURCE_GROUP 45 /* from <ws2ipdef.h> */struct group_source_req{ uint32_t gsr_interface; struct sockaddr_storage gsr_group; struct sockaddr_storage gsr_source;};
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -