static bool config_ParseHost( output_config_t *p_config, char *psz_string ) { struct addrinfo *p_ai; int i_mtu; p_config->psz_displayname = strdup( psz_string ); p_ai = ParseNodeService( psz_string, &psz_string, DEFAULT_PORT ); if ( p_ai == NULL ) return false; memcpy( &p_config->connect_addr, p_ai->ai_addr, p_ai->ai_addrlen ); freeaddrinfo( p_ai ); p_config->i_family = p_config->connect_addr.ss_family; if ( p_config->i_family == AF_UNSPEC ) return false; if ( psz_string == NULL || !*psz_string ) goto end; if ( *psz_string == '@' ) { psz_string++; p_ai = ParseNodeService( psz_string, &psz_string, 0 ); if ( p_ai == NULL || p_ai->ai_family != p_config->i_family ) msg_Warn( NULL, "invalid bind address" ); else memcpy( &p_config->bind_addr, p_ai->ai_addr, p_ai->ai_addrlen ); freeaddrinfo( p_ai ); } while ( (psz_string = strchr( psz_string, '/' )) != NULL ) { *psz_string++ = '\0'; #define IS_OPTION( option ) (!strncasecmp( psz_string, option, strlen(option) )) #define ARG_OPTION( option ) (psz_string + strlen(option)) if ( IS_OPTION("udp") ) p_config->i_config |= OUTPUT_UDP; else if ( IS_OPTION("dvb") ) p_config->i_config |= OUTPUT_DVB; else if ( IS_OPTION("epg") ) p_config->i_config |= OUTPUT_EPG; else if ( IS_OPTION("tsid=") ) p_config->i_tsid = strtol( ARG_OPTION("tsid="), NULL, 0 ); else if ( IS_OPTION("retention=") ) p_config->i_max_retention = strtoll( ARG_OPTION("retention="), NULL, 0 ) * 1000; else if ( IS_OPTION("latency=") ) p_config->i_output_latency = strtoll( ARG_OPTION("latency="), NULL, 0 ) * 1000; else if ( IS_OPTION("ttl=") ) p_config->i_ttl = strtol( ARG_OPTION("ttl="), NULL, 0 ); else if ( IS_OPTION("tos=") ) p_config->i_tos = strtol( ARG_OPTION("tos="), NULL, 0 ); else if ( IS_OPTION("mtu=") ) p_config->i_mtu = strtol( ARG_OPTION("mtu="), NULL, 0 ); else if ( IS_OPTION("ifindex=") ) p_config->i_if_index_v6 = strtol( ARG_OPTION("ifindex="), NULL, 0 ); else if ( IS_OPTION("networkid=") ) p_config->i_network_id = strtol( ARG_OPTION("networkid="), NULL, 0 ); else if ( IS_OPTION("networkname=") ) { config_strdvb( &p_config->network_name, ARG_OPTION("networkname=") ); } else if ( IS_OPTION("srvname=") ) { config_strdvb( &p_config->service_name, ARG_OPTION("srvname=") ); } else if ( IS_OPTION("srvprovider=") ) { config_strdvb( &p_config->provider_name, ARG_OPTION("srvprovider=") ); } else if ( IS_OPTION("srcaddr=") ) { if ( p_config->i_family != AF_INET ) { msg_Err( NULL, "RAW sockets currently implemented for ipv4 only"); return false; } free( p_config->psz_srcaddr ); p_config->psz_srcaddr = config_stropt( ARG_OPTION("srcaddr=") ); p_config->i_config |= OUTPUT_RAW; } else if ( IS_OPTION("srcport=") ) p_config->i_srcport = strtol( ARG_OPTION("srcport="), NULL, 0 ); else if ( IS_OPTION("ssrc=") ) { in_addr_t i_addr = inet_addr( ARG_OPTION("ssrc=") ); memcpy( p_config->pi_ssrc, &i_addr, 4 * sizeof(uint8_t) ); } else if ( IS_OPTION("pidmap=") ) { char *str1; char *saveptr = NULL; char *tok = NULL; int i, i_newpid; for (i = 0, str1 = config_stropt( (ARG_OPTION("pidmap="))); i < N_MAP_PIDS; i++, str1 = NULL) { tok = strtok_r(str1, ",", &saveptr); if ( !tok ) break; i_newpid = strtoul(tok, NULL, 0); p_config->pi_confpids[i] = i_newpid; } p_config->b_do_remap = true; } else if ( IS_OPTION("newsid=") ) p_config->i_new_sid = strtol( ARG_OPTION("newsid="), NULL, 0 ); else msg_Warn( NULL, "unrecognized option %s", psz_string ); #undef IS_OPTION #undef ARG_OPTION } end: i_mtu = p_config->i_family == AF_INET6 ? DEFAULT_IPV6_MTU : DEFAULT_IPV4_MTU; if ( !p_config->i_mtu ) p_config->i_mtu = i_mtu; else if ( p_config->i_mtu < TS_SIZE + RTP_HEADER_SIZE ) { msg_Warn( NULL, "invalid MTU %d, setting %d", p_config->i_mtu, i_mtu ); p_config->i_mtu = i_mtu; } return true; }
/** @internal @This parses _uri and opens IPv4 & IPv6 sockets * * @param upipe description structure of the pipe * @param _uri socket URI * @param ttl packets time-to-live * @param bind_port bind port * @param connect_port connect port * @param weight weight (UNUSED) * @param use_tcp Set this to open a tcp socket (instead of udp) * @param use_raw open RAW socket (udp) * @param raw_header user-provided buffer for RAW header (ip+udp) * @return socket fd, or -1 in case of error */ int upipe_udp_open_socket(struct upipe *upipe, const char *_uri, int ttl, uint16_t bind_port, uint16_t connect_port, unsigned int *weight, bool *use_tcp, bool *use_raw, uint8_t *raw_header) { union sockaddru bind_addr, connect_addr; int fd, i; char *uri = strdup(_uri); char *token = uri; char *token2 = NULL; int bind_if_index = 0, connect_if_index = 0; in_addr_t if_addr = INADDR_ANY; in_addr_t src_addr = INADDR_ANY; uint16_t src_port = 4242; int tos = 0; bool b_tcp; bool b_raw; int family; socklen_t sockaddr_len; #if !defined(__APPLE__) && !defined(__native_client__) char *ifname = NULL; #endif if (!uri) return -1; memset(&bind_addr, 0, sizeof(union sockaddru)); memset(&connect_addr, 0, sizeof(union sockaddru)); bind_addr.ss.ss_family = AF_UNSPEC; connect_addr.ss.ss_family = AF_UNSPEC; if (use_tcp == NULL) { use_tcp = &b_tcp; } *use_tcp = false; if (use_raw == NULL) { use_raw = &b_raw; } *use_raw = false; token2 = strrchr(uri, ','); if (token2) { *token2++ = '\0'; if (weight) { *weight = strtoul(token2, NULL, 0); } } else if (weight) { *weight = 1; } token2 = strchr(uri, '/'); if (token2) { *token2 = '\0'; } if (*token == '\0') { free(uri); return -1; } /* Hosts */ if (token[0] != '@') { if (!upipe_udp_parse_node_service(upipe, token, &token, connect_port, &connect_if_index, &connect_addr.ss)) { free(uri); return -1; } /* required on some architectures */ memset(&connect_addr.sin.sin_zero, 0, sizeof(connect_addr.sin.sin_zero)); } if (token[0] == '@') { token++; if (!upipe_udp_parse_node_service(upipe, token, &token, bind_port, &bind_if_index, &bind_addr.ss)) { free(uri); return -1; } /* required on some architectures */ memset(&bind_addr.sin.sin_zero, 0, sizeof(bind_addr.sin.sin_zero)); } if (bind_addr.ss.ss_family == AF_UNSPEC && connect_addr.ss.ss_family == AF_UNSPEC) { free(uri); return -1; } upipe_udp_print_socket(upipe, "socket definition:", &bind_addr, &connect_addr); /* Weights and options */ if (token2) { do { *token2++ = '\0'; #define IS_OPTION(option) (!strncasecmp(token2, option, strlen(option))) #define ARG_OPTION(option) (token2 + strlen(option)) if (IS_OPTION("ifindex=")) { bind_if_index = connect_if_index = strtol(ARG_OPTION("ifindex="), NULL, 0); } else if (IS_OPTION("ifaddr=")) { char *option = config_stropt(ARG_OPTION("ifaddr=")); if_addr = inet_addr(option); free( option ); #if !defined(__APPLE__) && !defined(__native_client__) } else if ( IS_OPTION("ifname=") ) { ifname = config_stropt( ARG_OPTION("ifname=") ); if (strlen(ifname) >= IFNAMSIZ) { ifname[IFNAMSIZ-1] = '\0'; } #endif } else if (IS_OPTION("srcaddr=")) { char *option = config_stropt(ARG_OPTION("srcaddr=")); src_addr = inet_addr(option); free(option); *use_raw = true; } else if (IS_OPTION("srcport=")) { src_port = strtol(ARG_OPTION("srcport="), NULL, 0); } else if (IS_OPTION("ttl=")) { ttl = strtol(ARG_OPTION("ttl="), NULL, 0); } else if (IS_OPTION("tos=")) { tos = strtol(ARG_OPTION("tos="), NULL, 0); } else if (IS_OPTION("tcp")) { *use_tcp = true; } else { upipe_warn_va(upipe, "unrecognized option %s", token2); } #undef IS_OPTION #undef ARG_OPTION } while ((token2 = strchr(token2, '/')) != NULL); } if (unlikely(*use_tcp && *use_raw)) { upipe_warn(upipe, "RAW sockets not implemented for tcp"); free(uri); return -1; } free(uri); /* Sanity checks */ if (bind_addr.ss.ss_family != AF_UNSPEC && connect_addr.ss.ss_family != AF_UNSPEC && bind_addr.ss.ss_family != connect_addr.ss.ss_family) { upipe_err(upipe, "incompatible address types"); return -1; } if (bind_addr.ss.ss_family != AF_UNSPEC) { family = bind_addr.ss.ss_family; } else if (connect_addr.ss.ss_family != AF_UNSPEC) { family = connect_addr.ss.ss_family; } else { upipe_err(upipe, "ambiguous address declaration"); return -1; } sockaddr_len = (family == AF_INET) ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6); if (bind_if_index && connect_if_index && bind_if_index != connect_if_index) { upipe_err(upipe, "incompatible bind and connect interfaces"); return -1; } if (connect_if_index) bind_if_index = connect_if_index; else connect_if_index = bind_if_index; /* RAW header */ if (*use_raw && raw_header) { upipe_udp_raw_fill_headers(upipe, raw_header, src_addr, connect_addr.sin.sin_addr.s_addr, src_port, ntohs(connect_addr.sin.sin_port), ttl, tos, 0); } /* Socket configuration */ int sock_type = SOCK_DGRAM; if (*use_tcp) sock_type = SOCK_STREAM; if (*use_raw) sock_type = SOCK_RAW; int sock_proto = (*use_raw ? IPPROTO_RAW : 0); if ((fd = socket(family, sock_type, sock_proto)) < 0) { upipe_err_va(upipe, "unable to open socket (%m)"); return -1; } #if !defined(__APPLE__) && !defined(__native_client__) if (*use_raw) { int hincl = 1; if (setsockopt(fd, IPPROTO_IP, IP_HDRINCL, &hincl, sizeof(hincl)) < 0) { upipe_err_va(upipe, "unable to set IP_HDRINCL"); close(fd); return -1; } } #endif i = 1; if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *)&i, sizeof(i)) == -1) { upipe_err_va(upipe, "unable to set socket (%m)"); close(fd); return -1; } if (family == AF_INET6) { if (bind_if_index && setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, (void *)&bind_if_index, sizeof(bind_if_index)) < 0) { upipe_err(upipe, "couldn't set interface index"); upipe_udp_print_socket(upipe, "socket definition:", &bind_addr, &connect_addr); close(fd); return -1; } if (bind_addr.ss.ss_family != AF_UNSPEC) { #if !defined(__APPLE__) && !defined(__native_client__) if (IN6_IS_ADDR_MULTICAST(&bind_addr.sin6.sin6_addr)) { struct ipv6_mreq imr; union sockaddru bind_addr_any = bind_addr; bind_addr_any.sin6.sin6_addr = in6addr_any; if (bind(fd, &bind_addr_any.so, sizeof(bind_addr_any)) < 0) { upipe_err(upipe, "couldn't bind"); upipe_udp_print_socket(upipe, "socket definition:", &bind_addr, &connect_addr); close(fd); return -1; } imr.ipv6mr_multiaddr = bind_addr.sin6.sin6_addr; imr.ipv6mr_interface = bind_if_index; /* Join Multicast group without source filter */ if (setsockopt(fd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, (char *)&imr, sizeof(struct ipv6_mreq)) < 0) { upipe_err(upipe, "couldn't join multicast group"); upipe_udp_print_socket(upipe, "socket definition:", &bind_addr, &connect_addr); close(fd); return -1; } } else #endif goto normal_bind; } } else if (bind_addr.ss.ss_family != AF_UNSPEC) { normal_bind: if (bind(fd, &bind_addr.so, sockaddr_len) < 0) { upipe_err(upipe, "couldn't bind"); upipe_udp_print_socket(upipe, "socket definition:", &bind_addr, &connect_addr); close(fd); return -1; } } if (!*use_tcp) { /* Increase the receive buffer size to 1/2MB (8Mb/s during 1/2s) to * avoid packet loss caused by scheduling problems */ i = 0x80000; if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (void *) &i, sizeof(i))) upipe_warn(upipe, "fail to increase receive buffer"); /* Join the multicast group if the socket is a multicast address */ if (bind_addr.ss.ss_family == AF_INET && IN_MULTICAST(ntohl(bind_addr.sin.sin_addr.s_addr))) { #ifndef __native_client__ if (connect_addr.ss.ss_family != AF_UNSPEC) { /* Source-specific multicast */ struct ip_mreq_source imr; imr.imr_multiaddr = bind_addr.sin.sin_addr; imr.imr_interface.s_addr = if_addr; imr.imr_sourceaddr = connect_addr.sin.sin_addr; if (bind_if_index) { upipe_warn(upipe, "ignoring ifindex option in SSM"); } if (setsockopt(fd, IPPROTO_IP, IP_ADD_SOURCE_MEMBERSHIP, (char *)&imr, sizeof(struct ip_mreq_source)) < 0) { upipe_err_va(upipe, "couldn't join multicast group (%m)"); upipe_udp_print_socket(upipe, "socket definition:", &bind_addr, &connect_addr); close(fd); return -1; } } else if (bind_if_index) { /* Linux-specific interface-bound multicast */ struct ip_mreqn imr; imr.imr_multiaddr = bind_addr.sin.sin_addr; imr.imr_address.s_addr = if_addr; imr.imr_ifindex = bind_if_index; if (setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&imr, sizeof(struct ip_mreqn)) < 0) { upipe_err_va(upipe, "couldn't join multicast group (%m)"); upipe_udp_print_socket(upipe, "socket definition:", &bind_addr, &connect_addr); close(fd); return -1; } } else #endif { /* Regular multicast */ struct ip_mreq imr; imr.imr_multiaddr = bind_addr.sin.sin_addr; imr.imr_interface.s_addr = if_addr; if (setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&imr, sizeof(struct ip_mreq)) < 0) { upipe_err_va(upipe, "couldn't join multicast group (%m)"); upipe_udp_print_socket(upipe, "socket definition:", &bind_addr, &connect_addr); close(fd); return -1; } } #ifdef SO_BINDTODEVICE if (ifname) { /* linux specific, needs root or CAP_NET_RAW */ if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, ifname, strlen(ifname) + 1) < 0) { upipe_err_va(upipe, "couldn't bind to device %s (%m)", ifname); free(ifname); close(fd); return -1; } ubase_clean_str(&ifname); } #endif } } if (connect_addr.ss.ss_family != AF_UNSPEC) { if (connect(fd, &connect_addr.so, sockaddr_len) < 0) { upipe_err_va(upipe, "cannot connect socket (%m)"); upipe_udp_print_socket(upipe, "socket definition:", &bind_addr, &connect_addr); close(fd); return -1; } if (!*use_tcp) { if (ttl) { if (family == AF_INET && IN_MULTICAST(ntohl(connect_addr.sin.sin_addr.s_addr))) { if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, (void *)&ttl, sizeof(ttl)) == -1) { upipe_err_va(upipe, "couldn't set TTL (%m)"); upipe_udp_print_socket(upipe, "socket definition:", &bind_addr, &connect_addr); close(fd); return -1; } } if (family == AF_INET6 && IN6_IS_ADDR_MULTICAST(&connect_addr.sin6.sin6_addr)) { if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (void *)&ttl, sizeof(ttl)) == -1) { upipe_err_va(upipe, "couldn't set TTL (%m)"); upipe_udp_print_socket(upipe, "socket definition:", &bind_addr, &connect_addr); close(fd); return -1; } } } if (tos) { if (setsockopt(fd, IPPROTO_IP, IP_TOS, (void *)&tos, sizeof(tos)) == -1) { upipe_err_va(upipe, "couldn't set TOS (%m)"); upipe_udp_print_socket(upipe, "socket definition:", &bind_addr, &connect_addr); close(fd); return -1; } } } } else if (*use_tcp) { /* Open in listen mode - wait for an incoming connection */ int new_fd; if (listen(fd, 1) < 0) { upipe_err_va(upipe, "couldn't listen (%m)"); upipe_udp_print_socket(upipe, "socket definition:", &bind_addr, &connect_addr); close(fd); return -1; } while ((new_fd = accept(fd, NULL, NULL)) < 0) { if (errno != EINTR) { upipe_err_va(upipe, "couldn't accept (%m)"); upipe_udp_print_socket(upipe, "socket definition:", &bind_addr, &connect_addr); close(fd); return -1; } } close(fd); return new_fd; } return fd; }
/***************************************************************************** * udp_Open *****************************************************************************/ void udp_Open( void ) { int i_family; struct addrinfo *p_connect_ai = NULL, *p_bind_ai; int i_if_index = 0; in_addr_t i_if_addr = INADDR_ANY; int i_mtu = 0; char *psz_bind, *psz_string = strdup( psz_udp_src ); char *psz_save = psz_string; int i = 1; /* Parse configuration. */ if ( (psz_bind = strchr( psz_string, '@' )) != NULL ) { *psz_bind++ = '\0'; p_connect_ai = ParseNodeService( psz_string, NULL, 0 ); } else psz_bind = psz_string; p_bind_ai = ParseNodeService( psz_bind, &psz_string, DEFAULT_PORT ); if ( p_bind_ai == NULL ) { msg_Err( NULL, "couldn't parse %s", psz_bind ); exit(EXIT_FAILURE); } i_family = p_bind_ai->ai_family; if ( p_connect_ai != NULL && p_connect_ai->ai_family != i_family ) { msg_Warn( NULL, "invalid connect address" ); freeaddrinfo( p_connect_ai ); p_connect_ai = NULL; } while ( (psz_string = strchr( psz_string, '/' )) != NULL ) { *psz_string++ = '\0'; #define IS_OPTION( option ) (!strncasecmp( psz_string, option, strlen(option) )) #define ARG_OPTION( option ) (psz_string + strlen(option)) if ( IS_OPTION("udp") ) b_udp = true; else if ( IS_OPTION("mtu=") ) i_mtu = strtol( ARG_OPTION("mtu="), NULL, 0 ); else if ( IS_OPTION("ifindex=") ) i_if_index = strtol( ARG_OPTION("ifindex="), NULL, 0 ); else if ( IS_OPTION("ifaddr=") ) i_if_addr = inet_addr( ARG_OPTION("ifaddr=") ); else msg_Warn( NULL, "unrecognized option %s", psz_string ); #undef IS_OPTION #undef ARG_OPTION } if ( !i_mtu ) i_mtu = i_family == AF_INET6 ? DEFAULT_IPV6_MTU : DEFAULT_IPV4_MTU; i_block_cnt = (i_mtu - (b_udp ? 0 : RTP_HEADER_SIZE)) / TS_SIZE; /* Do stuff. */ if ( (i_handle = socket( i_family, SOCK_DGRAM, IPPROTO_UDP )) < 0 ) { msg_Err( NULL, "couldn't create socket (%s)", strerror(errno) ); exit(EXIT_FAILURE); } setsockopt( i_handle, SOL_SOCKET, SO_REUSEADDR, (void *) &i, sizeof( i ) ); /* Increase the receive buffer size to 1/2MB (8Mb/s during 1/2s) to avoid * packet loss caused by scheduling problems */ i = 0x80000; setsockopt( i_handle, SOL_SOCKET, SO_RCVBUF, (void *) &i, sizeof( i ) ); if ( bind( i_handle, p_bind_ai->ai_addr, p_bind_ai->ai_addrlen ) < 0 ) { msg_Err( NULL, "couldn't bind (%s)", strerror(errno) ); close( i_handle ); exit(EXIT_FAILURE); } if ( p_connect_ai != NULL ) { uint16_t i_port; if ( i_family == AF_INET6 ) i_port = ((struct sockaddr_in6 *)p_connect_ai->ai_addr)->sin6_port; else i_port = ((struct sockaddr_in *)p_connect_ai->ai_addr)->sin_port; if ( i_port != 0 && connect( i_handle, p_connect_ai->ai_addr, p_connect_ai->ai_addrlen ) < 0 ) msg_Warn( NULL, "couldn't connect socket (%s)", strerror(errno) ); } /* Join the multicast group if the socket is a multicast address */ if ( i_family == AF_INET6 ) { struct sockaddr_in6 *p_addr = (struct sockaddr_in6 *)p_bind_ai->ai_addr; if ( IN6_IS_ADDR_MULTICAST( &p_addr->sin6_addr ) ) { struct ipv6_mreq imr; imr.ipv6mr_multiaddr = p_addr->sin6_addr; imr.ipv6mr_interface = i_if_index; if ( i_if_addr != INADDR_ANY ) msg_Warn( NULL, "ignoring ifaddr option in IPv6" ); if ( setsockopt( i_handle, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, (char *)&imr, sizeof(struct ipv6_mreq) ) < 0 ) msg_Warn( NULL, "couldn't join multicast group (%s)", strerror(errno) ); } } else { struct sockaddr_in *p_addr = (struct sockaddr_in *)p_bind_ai->ai_addr; if ( IN_MULTICAST( ntohl(p_addr->sin_addr.s_addr)) ) { if ( p_connect_ai != NULL ) { #ifndef IP_ADD_SOURCE_MEMBERSHIP msg_Err( NULL, "IP_ADD_SOURCE_MEMBERSHIP is unsupported." ); #else /* Source-specific multicast */ struct sockaddr_in *p_src = (struct sockaddr_in *)&p_connect_ai->ai_addr; struct ip_mreq_source imr; imr.imr_multiaddr = p_addr->sin_addr; imr.imr_interface.s_addr = i_if_addr; imr.imr_sourceaddr = p_src->sin_addr; if ( i_if_index ) msg_Warn( NULL, "ignoring ifindex option in SSM" ); if ( setsockopt( i_handle, IPPROTO_IP, IP_ADD_SOURCE_MEMBERSHIP, (char *)&imr, sizeof(struct ip_mreq_source) ) < 0 ) msg_Warn( NULL, "couldn't join multicast group (%s)", strerror(errno) ); #endif } else if ( i_if_index ) { /* Linux-specific interface-bound multicast */ struct ip_mreqn imr; imr.imr_multiaddr = p_addr->sin_addr; #if defined(__linux__) imr.imr_address.s_addr = i_if_addr; imr.imr_ifindex = i_if_index; #endif if ( setsockopt( i_handle, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&imr, sizeof(struct ip_mreqn) ) < 0 ) msg_Warn( NULL, "couldn't join multicast group (%s)", strerror(errno) ); } else { /* Regular multicast */ struct ip_mreq imr; imr.imr_multiaddr = p_addr->sin_addr; imr.imr_interface.s_addr = i_if_addr; if ( setsockopt( i_handle, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&imr, sizeof(struct ip_mreq) ) == -1 ) msg_Warn( NULL, "couldn't join multicast group (%s)", strerror(errno) ); } } } freeaddrinfo( p_bind_ai ); if ( p_connect_ai != NULL ) freeaddrinfo( p_connect_ai ); free( psz_save ); msg_Dbg( NULL, "binding socket to %s", psz_udp_src ); }
bool config_ParseHost( output_config_t *p_config, char *psz_string ) { struct addrinfo *p_ai; int i_mtu; p_config->psz_displayname = strdup( psz_string ); p_ai = ParseNodeService( psz_string, &psz_string, DEFAULT_PORT ); if ( p_ai == NULL ) return false; memcpy( &p_config->connect_addr, p_ai->ai_addr, p_ai->ai_addrlen ); freeaddrinfo( p_ai ); p_config->i_family = p_config->connect_addr.ss_family; if ( p_config->i_family == AF_UNSPEC ) return false; if ( psz_string == NULL || !*psz_string ) goto end; if ( *psz_string == '@' ) { psz_string++; p_ai = ParseNodeService( psz_string, &psz_string, 0 ); if ( p_ai == NULL || p_ai->ai_family != p_config->i_family ) msg_Warn( NULL, "invalid bind address" ); else memcpy( &p_config->bind_addr, p_ai->ai_addr, p_ai->ai_addrlen ); freeaddrinfo( p_ai ); } while ( (psz_string = strchr( psz_string, '/' )) != NULL ) { *psz_string++ = '\0'; #define IS_OPTION( option ) (!strncasecmp( psz_string, option, strlen(option) )) #define ARG_OPTION( option ) (psz_string + strlen(option)) if ( IS_OPTION("udp") ) p_config->i_config |= OUTPUT_UDP; else if ( IS_OPTION("dvb") ) p_config->i_config |= OUTPUT_DVB; else if ( IS_OPTION("epg") ) p_config->i_config |= OUTPUT_EPG; else if ( IS_OPTION("tsid=") ) p_config->i_tsid = strtol( ARG_OPTION("tsid="), NULL, 0 ); else if ( IS_OPTION("retention=") ) p_config->i_max_retention = strtoll( ARG_OPTION("retention="), NULL, 0 ) * 1000; else if ( IS_OPTION("latency=") ) p_config->i_output_latency = strtoll( ARG_OPTION("latency="), NULL, 0 ) * 1000; else if ( IS_OPTION("ttl=") ) p_config->i_ttl = strtol( ARG_OPTION("ttl="), NULL, 0 ); else if ( IS_OPTION("tos=") ) p_config->i_tos = strtol( ARG_OPTION("tos="), NULL, 0 ); else if ( IS_OPTION("mtu=") ) p_config->i_mtu = strtol( ARG_OPTION("mtu="), NULL, 0 ); else if ( IS_OPTION("ifindex=") ) p_config->i_if_index_v6 = strtol( ARG_OPTION("ifindex="), NULL, 0 ); else if ( IS_OPTION("srvname=") ) { if ( p_config->psz_service_name ) free( p_config->psz_service_name ); p_config->psz_service_name = config_stropt( ARG_OPTION("srvname=") ); } else if ( IS_OPTION("srvprovider=") ) { if ( !p_config->psz_service_provider ) free( p_config->psz_service_provider ); p_config->psz_service_provider = config_stropt( ARG_OPTION("srvprovider=") ); } else if ( IS_OPTION("ssrc=") ) { in_addr_t i_addr = inet_addr( ARG_OPTION("ssrc=") ); memcpy( p_config->pi_ssrc, &i_addr, 4 * sizeof(uint8_t) ); } else msg_Warn( NULL, "unrecognized option %s", psz_string ); #undef IS_OPTION #undef ARG_OPTION } if ( !p_config->psz_service_provider && psz_provider_name ) p_config->psz_service_provider = strdup( psz_provider_name ); end: i_mtu = p_config->i_family == AF_INET6 ? DEFAULT_IPV6_MTU : DEFAULT_IPV4_MTU; if ( !p_config->i_mtu ) p_config->i_mtu = i_mtu; else if ( p_config->i_mtu < TS_SIZE + RTP_HEADER_SIZE ) { msg_Warn( NULL, "invalid MTU %d, setting %d", p_config->i_mtu, i_mtu ); p_config->i_mtu = i_mtu; } return true; }