static int udp_resolve( udp_connection_t *uc, struct sockaddr_storage *ss, const char *host, int port, int *multicast, int receiver ) { struct addrinfo hints, *res, *ressave, *use = NULL; char port_buf[6]; int x; snprintf(port_buf, 6, "%d", port); memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_flags = (receiver ? AI_PASSIVE : 0) | AI_NUMERICSERV; hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_DGRAM; x = getaddrinfo(host, port_buf, &hints, &res); if (x < 0) { tvhlog(LOG_ERR, uc->subsystem, "getaddrinfo: %s: %s", host != NULL ? host : "*", x == EAI_SYSTEM ? strerror(errno) : gai_strerror(x)); return -1; } ressave = res; while (res) { if (res->ai_family == tcp_preferred_address_family) { use = res; break; } else if (use == NULL) { use = res; } res = res->ai_next; } if (use->ai_family == AF_INET6) { ss->ss_family = AF_INET6; IP_AS_V6(*ss, port) = htons(port); memcpy(&IP_AS_V6(*ss, addr), &((struct sockaddr_in6 *)use->ai_addr)->sin6_addr, sizeof(struct in6_addr)); *multicast = !!IN6_IS_ADDR_MULTICAST(&IP_AS_V6(*ss, addr)); } else if (use->ai_family == AF_INET) { ss->ss_family = AF_INET; IP_AS_V4(*ss, port) = htons(port); IP_AS_V4(*ss, addr) = ((struct sockaddr_in *)use->ai_addr)->sin_addr; *multicast = !!IN_MULTICAST(ntohl(IP_AS_V4(*ss, addr.s_addr))); } freeaddrinfo(ressave); if (ss->ss_family != AF_INET && ss->ss_family != AF_INET6) { tvherror(uc->subsystem, "%s - failed to process host '%s'", uc->name, uc->host); return -1; } return 0; }
udp_connection_t * udp_bind ( int subsystem, const char *name, const char *bindaddr, int port, const char *multicast_src, const char *ifname, int rxsize, int txsize ) { int fd, ifindex, reuse = 1; udp_connection_t *uc; char buf[256]; socklen_t addrlen; uc = calloc(1, sizeof(udp_connection_t)); uc->fd = -1; uc->host = bindaddr ? strdup(bindaddr) : NULL; uc->port = port; uc->ifname = ifname ? strdup(ifname) : NULL; uc->subsystem = subsystem; uc->name = name ? strdup(name) : NULL; uc->rxtxsize = rxsize; if (udp_resolve(uc, &uc->ip, uc->host, port, &uc->multicast, 1)) { udp_close(uc); return UDP_FATAL_ERROR; } /* Open socket */ if ((fd = tvh_socket(uc->ip.ss_family, SOCK_DGRAM, 0)) == -1) { tvherror(subsystem, "%s - failed to create socket [%s]", name, strerror(errno)); udp_close(uc); return UDP_FATAL_ERROR; } uc->fd = fd; /* Mark reuse address */ if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse))) { tvherror(subsystem, "%s - failed to reuse address for socket [%s]", name, strerror(errno)); udp_close(uc); return UDP_FATAL_ERROR; } /* Bind to interface */ ifindex = udp_ifindex_required(uc) ? udp_get_ifindex(ifname) : 0; if (ifindex < 0) { tvherror(subsystem, "%s - could not find interface %s", name, ifname); goto error; } /* IPv4 */ if (uc->ip.ss_family == AF_INET) { /* Bind */ if (bind(fd, (struct sockaddr *)&uc->ip, sizeof(struct sockaddr_in))) { inet_ntop(AF_INET, &IP_AS_V4(uc->ip, addr), buf, sizeof(buf)); tvherror(subsystem, "%s - cannot bind %s:%hu [e=%s]", name, buf, ntohs(IP_AS_V4(uc->ip, port)), strerror(errno)); goto error; } if (uc->multicast) { /* Join multicast group */ if (multicast_src && *multicast_src) { /* Join with specific source address (SSM) */ struct ip_mreq_source ms; memset(&ms, 0, sizeof(ms)); ms.imr_multiaddr = IP_AS_V4(uc->ip, addr); /* Note, ip_mreq_source does not support the ifindex parameter, so we have to resolve to the ip of the interface on all platforms. */ if (udp_get_ifaddr(fd, ifname, &ms.imr_interface) == -1) { tvherror(subsystem, "%s - cannot find ip address for interface %s [e=%s]", name, ifname, strerror(errno)); goto error; } if (inet_pton(AF_INET, multicast_src, &ms.imr_sourceaddr) < 1) { tvherror(subsystem, "%s - invalid ipv4 address '%s' specified as multicast source [e=%s]", name, multicast_src, strerror(errno)); goto error; } if (setsockopt(fd, udp_get_solip(), IP_ADD_SOURCE_MEMBERSHIP, &ms, sizeof(ms)) < 0) { tvherror(subsystem, "%s - setsockopt IP_ADD_SOURCE_MEMBERSHIP failed [e=%s]", name, strerror(errno)); goto error; } } else { /* Standard multicast join (non-SSM) */ #if defined(PLATFORM_DARWIN) struct ip_mreq m; #else struct ip_mreqn m; #endif memset(&m, 0, sizeof(m)); m.imr_multiaddr = IP_AS_V4(uc->ip, addr); #if !defined(PLATFORM_DARWIN) m.imr_address.s_addr = 0; m.imr_ifindex = ifindex; #else if (udp_get_ifaddr(fd, ifname, &m.imr_interface) == -1) { tvherror(subsystem, "%s - cannot find ip address for interface %s [e=%s]", name, ifname, strerror(errno)); goto error; } #endif if (setsockopt(fd, udp_get_solip(), IP_ADD_MEMBERSHIP, &m, sizeof(m))) { inet_ntop(AF_INET, &m.imr_multiaddr, buf, sizeof(buf)); tvhwarn(subsystem, "%s - cannot join %s [%s]", name, buf, strerror(errno)); } } } /* Bind to IPv6 group */ } else { struct ipv6_mreq m; memset(&m, 0, sizeof(m)); /* Bind */ if (bind(fd, (struct sockaddr *)&uc->ip, sizeof(struct sockaddr_in6))) { inet_ntop(AF_INET6, &IP_AS_V6(uc->ip, addr), buf, sizeof(buf)); tvherror(subsystem, "%s - cannot bind %s:%hu [e=%s]", name, buf, ntohs(IP_AS_V6(uc->ip, port)), strerror(errno)); goto error; } if (uc->multicast) { /* Join group */ m.ipv6mr_multiaddr = IP_AS_V6(uc->ip, addr); m.ipv6mr_interface = ifindex; #ifdef SOL_IPV6 if (setsockopt(fd, SOL_IPV6, IPV6_ADD_MEMBERSHIP, &m, sizeof(m))) { inet_ntop(AF_INET, &m.ipv6mr_multiaddr, buf, sizeof(buf)); tvhwarn(subsystem, "%s - cannot join %s [%s]", name, buf, strerror(errno)); } #else tvherror(subsystem, "IPv6 multicast not supported"); goto error; #endif } } addrlen = sizeof(uc->ip); if (getsockname(fd, (struct sockaddr *)&uc->ip, &addrlen)) { tvherror(subsystem, "%s - cannot obtain socket name [%s]", name, strerror(errno)); goto error; } /* Increase/Decrease RX buffer size */ if (rxsize > 0 && setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &rxsize, sizeof(rxsize)) == -1) tvhwarn(subsystem, "%s - cannot change UDP rx buffer size [%s]", name, strerror(errno)); /* Increase/Decrease TX buffer size */ if (txsize > 0 && setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &txsize, sizeof(txsize)) == -1) tvhwarn(subsystem, "%s - cannot change UDP tx buffer size [%s]", name, strerror(errno)); return uc; error: udp_close(uc); return NULL; }
udp_connection_t * udp_bind ( const char *subsystem, const char *name, const char *bindaddr, int port, const char *ifname, int rxsize ) { int fd, ifindex, reuse = 1; udp_connection_t *uc; char buf[256]; socklen_t addrlen; uc = calloc(1, sizeof(udp_connection_t)); uc->fd = -1; uc->host = bindaddr ? strdup(bindaddr) : NULL; uc->port = port; uc->ifname = ifname ? strdup(ifname) : NULL; uc->subsystem = subsystem ? strdup(subsystem) : NULL; uc->name = name ? strdup(name) : NULL; uc->rxtxsize = rxsize; if (udp_resolve(uc, 1) < 0) { udp_close(uc); return UDP_FATAL_ERROR; } /* Open socket */ if ((fd = tvh_socket(uc->ip.ss_family, SOCK_DGRAM, 0)) == -1) { tvherror(subsystem, "%s - failed to create socket [%s]", name, strerror(errno)); udp_close(uc); return UDP_FATAL_ERROR; } /* Mark reuse address */ setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)); /* Bind to interface */ ifindex = udp_ifindex_required(uc) ? udp_get_ifindex(ifname) : 0; if (ifindex < 0) { tvherror(subsystem, "%s - could not find interface %s", name, ifname); goto error; } /* IPv4 */ if (uc->ip.ss_family == AF_INET) { #if defined(PLATFORM_DARWIN) struct ip_mreq m; #else struct ip_mreqn m; #endif memset(&m, 0, sizeof(m)); /* Bind */ if (bind(fd, (struct sockaddr *)&uc->ip, sizeof(struct sockaddr_in)) == -1) { inet_ntop(AF_INET, &IP_AS_V4(uc->ip, addr), buf, sizeof(buf)); tvherror(subsystem, "%s - cannot bind %s:%hu [e=%s]", name, buf, ntohs(IP_AS_V4(uc->ip, port)), strerror(errno)); goto error; } if (uc->multicast) { /* Join group */ m.imr_multiaddr = IP_AS_V4(uc->ip, addr); #if !defined(PLATFORM_DARWIN) m.imr_address.s_addr = 0; m.imr_ifindex = ifindex; #else if (udp_get_ifaddr(fd, ifname, &m.imr_interface) == -1) { tvherror(subsystem, "%s - cannot find ip address for interface %s [e=%s]", name, ifname, strerror(errno)); goto error; } #endif if (setsockopt(fd, udp_get_solip(), IP_ADD_MEMBERSHIP, &m, sizeof(m))) { inet_ntop(AF_INET, &m.imr_multiaddr, buf, sizeof(buf)); tvhwarn(subsystem, "%s - cannot join %s [%s]", name, buf, strerror(errno)); } } /* Bind to IPv6 group */ } else { struct ipv6_mreq m; memset(&m, 0, sizeof(m)); /* Bind */ if (bind(fd, (struct sockaddr *)&uc->ip, sizeof(struct sockaddr_in6)) == -1) { inet_ntop(AF_INET6, &IP_AS_V6(uc->ip, addr), buf, sizeof(buf)); tvherror(subsystem, "%s - cannot bind %s:%hu [e=%s]", name, buf, ntohs(IP_AS_V6(uc->ip, port)), strerror(errno)); goto error; } if (uc->multicast) { /* Join group */ m.ipv6mr_multiaddr = IP_AS_V6(uc->ip, addr); m.ipv6mr_interface = ifindex; #ifdef SOL_IPV6 if (setsockopt(fd, SOL_IPV6, IPV6_ADD_MEMBERSHIP, &m, sizeof(m))) { inet_ntop(AF_INET, &m.ipv6mr_multiaddr, buf, sizeof(buf)); tvhwarn(subsystem, "%s - cannot join %s [%s]", name, buf, strerror(errno)); } #else tvherror(subsystem, "IPv6 multicast not supported"); goto error; #endif } } addrlen = sizeof(uc->ip); getsockname(fd, (struct sockaddr *)&uc->ip, &addrlen); /* Increase RX buffer size */ if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &rxsize, sizeof(rxsize)) == -1) tvhwarn(subsystem, "%s - cannot increase UDP rx buffer size [%s]", name, strerror(errno)); uc->fd = fd; return uc; error: udp_close(uc); return NULL; }