diff --git a/src/include/switch_utils.h b/src/include/switch_utils.h index b667d4f0f4..cfbb372bfd 100644 --- a/src/include/switch_utils.h +++ b/src/include/switch_utils.h @@ -118,10 +118,44 @@ SWITCH_DECLARE(switch_status_t) switch_find_local_ip(_Out_opt_bytecapcount_(len) \brief find the char representation of an ip adress \param buf the buffer to write the ip adress found into \param len the length of the buf - \param in the struct in_addr * to get the adress from + \param sa the struct sockaddr * to get the adress from + \param salen the length of sa \return the ip adress string */ -SWITCH_DECLARE(char *) get_addr(char *buf, switch_size_t len, struct in_addr *in); +SWITCH_DECLARE(char *) get_addr(char *buf, switch_size_t len, struct sockaddr *sa, socklen_t salen); + +/*! + \brief get the port number of an ip address + \param sa the struct sockaddr * to get the port from + \return the ip adress string +*/ +SWITCH_DECLARE(unsigned short) get_port(struct sockaddr *sa); + +/*! + \brief flags to be used with switch_build_uri() + */ +enum switch_uri_flags { + SWITCH_URI_NUMERIC_HOST = 1, + SWITCH_URI_NUMERIC_PORT = 2, + SWITCH_URI_NO_SCOPE = 4, +}; + +/*! + \brief build a URI string from components + \param uri output string + \param size maximum size of output string (including trailing null) + \param scheme URI scheme + \param user user part or null if none + \param sa host address + \param flags logical OR-ed combination of flags from \ref switch_uri_flags + \return number of characters printed (not including the trailing null) + */ +SWITCH_DECLARE(int) switch_build_uri(char *uri, + switch_size_t size, + const char *scheme, + const char *user, + const switch_sockaddr_t *sa, + int flags); #define SWITCH_STATUS_IS_BREAK(x) (x == SWITCH_STATUS_BREAK || x == 730035 || x == 35) diff --git a/src/mod/endpoints/mod_sofia/mod_sofia.c b/src/mod/endpoints/mod_sofia/mod_sofia.c index d39ecb1ffc..4cb388a8f6 100644 --- a/src/mod/endpoints/mod_sofia/mod_sofia.c +++ b/src/mod/endpoints/mod_sofia/mod_sofia.c @@ -926,7 +926,8 @@ static switch_status_t sofia_receive_message(switch_core_session_t *session, swi char ref_to[128] = ""; if (!strstr(msg->string_arg, "sip:")) { - switch_snprintf(ref_to, sizeof(ref_to), "sip:%s@%s", msg->string_arg, tech_pvt->profile->sipip); + const char *format = strchr(tech_pvt->profile->sipip, ':') ? "sip:%s@[%s]" : "sip:%s@%s"; + switch_snprintf(ref_to, sizeof(ref_to), format, msg->string_arg, tech_pvt->profile->sipip); } else { switch_set_string(ref_to, msg->string_arg); } diff --git a/src/mod/endpoints/mod_sofia/sofia.c b/src/mod/endpoints/mod_sofia/sofia.c index d3ffd2eef2..b021ecfed8 100644 --- a/src/mod/endpoints/mod_sofia/sofia.c +++ b/src/mod/endpoints/mod_sofia/sofia.c @@ -215,7 +215,8 @@ void sofia_event_callback(nua_event_t event, if (authorization) { char network_ip[80]; - get_addr(network_ip, sizeof(network_ip), &((struct sockaddr_in *) msg_addrinfo(nua_current_request(nua))->ai_addr)->sin_addr); + su_addrinfo_t *addrinfo = msg_addrinfo(nua_current_request(nua)); + get_addr(network_ip, sizeof(network_ip), addrinfo->ai_addr, addrinfo->ai_addrlen); auth_res = sofia_reg_parse_auth(profile, authorization, sip, (char *) sip->sip_request->rq_method_name, tech_pvt->key, strlen(tech_pvt->key), network_ip, NULL, 0, REG_INVITE, NULL); @@ -749,6 +750,7 @@ static void parse_gateways(sofia_profile_t *profile, switch_xml_t gateways_tag) switch_mutex_unlock(mod_sofia_globals.hash_mutex); if ((gateway = switch_core_alloc(profile->pool, sizeof(*gateway)))) { + const char *sipip, *format; char *register_str = "true", *scheme = "Digest", *realm = NULL, *username = NULL, @@ -893,8 +895,11 @@ static void parse_gateways(sofia_profile_t *profile, switch_xml_t gateways_tag) gateway->register_url = switch_core_sprintf(gateway->pool, "sip:%s;transport=%s", register_proxy, register_transport); gateway->register_from = switch_core_sprintf(gateway->pool, "", from_user, from_domain, register_transport); - gateway->register_contact = switch_core_sprintf(gateway->pool, "", extension, - profile->extsipip ? profile->extsipip : profile->sipip, + + sipip = profile->extsipip ? profile->extsipip : profile->sipip; + format = strchr(sipip, ':') ? "" : ""; + gateway->register_contact = switch_core_sprintf(gateway->pool, format, extension, + sipip, sofia_glue_transport_has_tls(gateway->register_transport) ? profile->tls_sip_port : profile-> sip_port, params); @@ -1389,10 +1394,22 @@ switch_status_t config_sofia(int reload, char *profile_name) profile->sipdomain = switch_core_strdup(profile->pool, profile->sipip); } if (profile->extsipip) { - profile->url = switch_core_sprintf(profile->pool, "sip:mod_sofia@%s:%d", profile->extsipip, profile->sip_port); + char *ipv6 = strchr(profile->extsipip, ':'); + profile->url = switch_core_sprintf(profile->pool, + "sip:mod_sofia@%s%s%s:%d", + ipv6 ? "[" : "", + profile->extsipip, + ipv6 ? "]" : "", + profile->sip_port); profile->bindurl = switch_core_sprintf(profile->pool, "%s;maddr=%s", profile->url, profile->sipip); } else { - profile->url = switch_core_sprintf(profile->pool, "sip:mod_sofia@%s:%d", profile->sipip, profile->sip_port); + char *ipv6 = strchr(profile->sipip, ':'); + profile->url = switch_core_sprintf(profile->pool, + "sip:mod_sofia@%s%s%s:%d", + ipv6 ? "[" : "", + profile->sipip, + ipv6 ? "]" : "", + profile->sip_port); profile->bindurl = profile->url; } @@ -1410,12 +1427,37 @@ switch_status_t config_sofia(int reload, char *profile_name) } if (profile->extsipip) { - profile->tls_url = switch_core_sprintf(profile->pool, "sip:mod_sofia@%s:%d", profile->extsipip, profile->tls_sip_port); + char *ipv6 = strchr(profile->extsipip, ':'); + profile->tls_url = + switch_core_sprintf(profile->pool, + "sip:mod_sofia@%s%s%s:%d", + ipv6 ? "[" : "", + profile->extsipip, ipv6 ? "]" : "", + profile->tls_sip_port); profile->tls_bindurl = - switch_core_sprintf(profile->pool, "sips:mod_sofia@%s:%d;maddr=%s", profile->extsipip, profile->tls_sip_port, profile->sipip); + switch_core_sprintf(profile->pool, + "sips:mod_sofia@%s%s%s:%d;maddr=%s", + ipv6 ? "[" : "", + profile->extsipip, + ipv6 ? "]" : "", + profile->tls_sip_port, + profile->sipip); } else { - profile->tls_url = switch_core_sprintf(profile->pool, "sip:mod_sofia@%s:%d", profile->sipip, profile->tls_sip_port); - profile->tls_bindurl = switch_core_sprintf(profile->pool, "sips:mod_sofia@%s:%d", profile->sipip, profile->tls_sip_port); + char *ipv6 = strchr(profile->sipip, ':'); + profile->tls_url = + switch_core_sprintf(profile->pool, + "sip:mod_sofia@%s%s%s:%d", + ipv6 ? "[" : "", + profile->sipip, + ipv6 ? "]" : "", + profile->tls_sip_port); + profile->tls_bindurl = + switch_core_sprintf(profile->pool, + "sips:mod_sofia@%s%s%s:%d", + ipv6 ? "[" : "", + profile->sipip, + ipv6 ? "]" : "", + profile->tls_sip_port); } if (profile->tls_bind_params) { @@ -2637,7 +2679,7 @@ void sofia_handle_sip_i_invite(nua_t *nua, sofia_profile_t *profile, nua_handle_ return; } - get_addr(network_ip, sizeof(network_ip), &((struct sockaddr_in *) my_addrinfo->ai_addr)->sin_addr); + get_addr(network_ip, sizeof(network_ip), my_addrinfo->ai_addr, my_addrinfo->ai_addrlen); network_port = ntohs(((struct sockaddr_in *) msg_addrinfo(nua_current_request(nua))->ai_addr)->sin_port); if ((profile->pflags & PFLAG_AGGRESSIVE_NAT_DETECTION)) { @@ -2754,9 +2796,17 @@ void sofia_handle_sip_i_invite(nua_t *nua, sofia_profile_t *profile, nua_handle_ char tmp[35] = ""; sofia_transport_t transport = sofia_glue_url2transport(sip->sip_contact->m_url); - tech_pvt->record_route = switch_core_session_sprintf(session, "sip:%s@%s:%d;transport=%s", - sip->sip_contact->m_url->url_user, tech_pvt->remote_ip, - tech_pvt->remote_port, sofia_glue_transport2str(transport)); + const char *ipv6 = strchr(tech_pvt->remote_ip, ':'); + tech_pvt->record_route = + switch_core_session_sprintf(session, + "sip:%s@%s%s%s:%d;transport=%s", + sip->sip_contact->m_url->url_user, + ipv6 ? "[" : "", + tech_pvt->remote_ip, + ipv6 ? "]" : "", + tech_pvt->remote_port, + sofia_glue_transport2str(transport)); + switch_channel_set_variable(channel, "sip_received_ip", tech_pvt->remote_ip); snprintf(tmp, sizeof(tmp), "%d", tech_pvt->remote_port); switch_channel_set_variable(channel, "sip_received_port", tmp); @@ -2871,6 +2921,7 @@ void sofia_handle_sip_i_invite(nua_t *nua, sofia_profile_t *profile, nua_handle_ url_set_chanvars(session, sip->sip_to->a_url, sip_to); if (switch_channel_get_variable(channel, "sip_to_uri")) { + const char *ipv6; host = switch_channel_get_variable(channel, "sip_to_host"); user = switch_channel_get_variable(channel, "sip_to_user"); @@ -2887,7 +2938,14 @@ void sofia_handle_sip_i_invite(nua_t *nua, sofia_profile_t *profile, nua_handle_ port = sofia_glue_transport_has_tls(transport) ? profile->tls_sip_port : profile->sip_port; } - tech_pvt->to_uri = switch_core_session_sprintf(session, "sip:%s@%s:%d;transport=%s", user, host, port, sofia_glue_transport2str(transport)); + ipv6 = strchr(host, ':'); + tech_pvt->to_uri = + switch_core_session_sprintf(session, + "sip:%s@%s%s%s:%d;transport=%s", + user, ipv6 ? "[" : "", + host, ipv6 ? "]" : "", + port, + sofia_glue_transport2str(transport)); if (profile->ndlb & PFLAG_NDLB_TO_IN_200_CONTACT) { if (strchr(tech_pvt->to_uri, '>')) { diff --git a/src/mod/endpoints/mod_sofia/sofia_glue.c b/src/mod/endpoints/mod_sofia/sofia_glue.c index 85f4ba2d02..2401d35790 100644 --- a/src/mod/endpoints/mod_sofia/sofia_glue.c +++ b/src/mod/endpoints/mod_sofia/sofia_glue.c @@ -44,6 +44,7 @@ void sofia_glue_set_local_sdp(private_object_t *tech_pvt, const char *ip, uint32 uint32_t v_port; int use_cng = 1; const char *val; + const char *family; const char *pass_fmtp = switch_channel_get_variable(tech_pvt->channel, "sip_video_fmtp"); const char *ov_fmtp = switch_channel_get_variable(tech_pvt->channel, "sip_force_video_fmtp"); @@ -82,13 +83,14 @@ void sofia_glue_set_local_sdp(private_object_t *tech_pvt, const char *ip, uint32 tech_pvt->session_id++; + family = strchr(ip, ':') ? "IP6" : "IP4"; switch_snprintf(buf, sizeof(buf), "v=0\n" - "o=FreeSWITCH %010u %010u IN IP4 %s\n" + "o=FreeSWITCH %010u %010u IN %s %s\n" "s=FreeSWITCH\n" - "c=IN IP4 %s\n" "t=0 0\n" + "c=IN %s %s\n" "t=0 0\n" "a=%s\n" - "m=audio %d RTP/%sAVP", tech_pvt->owner_id, tech_pvt->session_id, ip, ip, sr, port, + "m=audio %d RTP/%sAVP", tech_pvt->owner_id, tech_pvt->session_id, family, ip, family, ip, sr, port, (!switch_strlen_zero(tech_pvt->local_crypto_key) && switch_test_flag(tech_pvt, TFLAG_SECURE)) ? "S" : ""); @@ -730,7 +732,8 @@ switch_status_t sofia_glue_tech_proxy_remote_addr(private_object_t *tech_pvt) return SWITCH_STATUS_FALSE; } - if ((p = (char *) switch_stristr("c=IN IP4 ", tech_pvt->remote_sdp_str))) { + if ((p = (char *) switch_stristr("c=IN IP4 ", tech_pvt->remote_sdp_str)) || + (p = (char *) switch_stristr("c=IN IP6 ", tech_pvt->remote_sdp_str))) { ip_ptr = p + 9; } @@ -833,7 +836,8 @@ void sofia_glue_tech_patch_sdp(private_object_t *tech_pvt) len = strlen(tech_pvt->local_sdp_str) + 384; - if ((p = (char *) switch_stristr("c=IN IP4 ", tech_pvt->local_sdp_str))) { + if ((p = (char *) switch_stristr("c=IN IP4 ", tech_pvt->local_sdp_str)) || + (p = (char *) switch_stristr("c=IN IP6 ", tech_pvt->local_sdp_str))) { ip_ptr = p + 9; } @@ -956,11 +960,15 @@ switch_status_t sofia_glue_do_invite(switch_core_session_t *session) check_decode(cid_num, session); if (!tech_pvt->from_str) { - tech_pvt->from_str = switch_core_session_sprintf(tech_pvt->session, "\"%s\" ", - cid_name, - cid_num, - !switch_strlen_zero(cid_num) ? "@" : "", - tech_pvt->profile->extsipip ? tech_pvt->profile->extsipip : tech_pvt->profile->sipip); + const char* sipip = tech_pvt->profile->extsipip ? tech_pvt->profile->extsipip : tech_pvt->profile->sipip; + const char* format = strchr(sipip, ':') ? "\"%s\" " : "\"%s\" "; + tech_pvt->from_str = + switch_core_session_sprintf(tech_pvt->session, + format, + cid_name, + cid_num, + !switch_strlen_zero(cid_num) ? "@" : "", + sipip); } if ((alertbuf = switch_channel_get_variable(channel, "alert_info"))) { @@ -1223,15 +1231,17 @@ void sofia_glue_do_xfer_invite(switch_core_session_t *session) private_object_t *tech_pvt = switch_core_session_get_private(session); switch_channel_t *channel = switch_core_session_get_channel(session); switch_caller_profile_t *caller_profile; + const char *sipip, *format; switch_assert(tech_pvt != NULL); caller_profile = switch_channel_get_caller_profile(channel); - if ((tech_pvt->from_str = switch_core_session_sprintf(session, "\"%s\" ", - caller_profile->caller_id_name, - caller_profile->caller_id_number, - tech_pvt->profile->extsipip ? tech_pvt->profile->extsipip : tech_pvt->profile->sipip))) { + sipip = tech_pvt->profile->extsipip ? tech_pvt->profile->extsipip : tech_pvt->profile->sipip; + format = strchr(sipip, ':') ? "\"%s\" " : "\"%s\" "; + if ((tech_pvt->from_str = switch_core_session_sprintf(session, format, + caller_profile->caller_id_name, + caller_profile->caller_id_number, sipip))) { const char *rep = switch_channel_get_variable(channel, SOFIA_REPLACES_HEADER); @@ -1637,7 +1647,7 @@ switch_status_t sofia_glue_activate_rtp(private_object_t *tech_pvt, switch_rtp_f } if (!switch_channel_test_flag(tech_pvt->channel, CF_PROXY_MEDIA)) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "AUDIO RTP [%s] %s:%d->%s:%d codec: %u ms: %d\n", + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "AUDIO RTP [%s] %s port %d -> %s port %d codec: %u ms: %d\n", switch_channel_get_name(tech_pvt->channel), tech_pvt->local_sdp_audio_ip, tech_pvt->local_sdp_audio_port, diff --git a/src/mod/endpoints/mod_sofia/sofia_presence.c b/src/mod/endpoints/mod_sofia/sofia_presence.c index f3ef3491bc..361c6606a8 100644 --- a/src/mod/endpoints/mod_sofia/sofia_presence.c +++ b/src/mod/endpoints/mod_sofia/sofia_presence.c @@ -1203,13 +1203,14 @@ void sofia_presence_handle_sip_i_subscribe(int status, char *port; char new_port[25] = ""; char *is_nat = NULL; + const char *ipv6; if (!(contact && sip->sip_contact->m_url)) { nua_respond(nh, 481, "INVALID SUBSCRIPTION", TAG_END()); return; } - get_addr(network_ip, sizeof(network_ip), &((struct sockaddr_in *) my_addrinfo->ai_addr)->sin_addr); + get_addr(network_ip, sizeof(network_ip), my_addrinfo->ai_addr, my_addrinfo->ai_addrlen); network_port = ntohs(((struct sockaddr_in *) msg_addrinfo(nua_current_request(nua))->ai_addr)->sin_port); tl_gets(tags, NUTAG_SUBSTATE_REF(sub_state), TAG_END()); @@ -1279,11 +1280,25 @@ void sofia_presence_handle_sip_i_subscribe(int status, switch_snprintf(new_port, sizeof(new_port), ":%s", port); } + ipv6 = strchr(contact_host, ':'); if (contact->m_url->url_params) { - contact_str = switch_mprintf("%s %s", - display, contact->m_url->url_user, contact_host, new_port, contact->m_url->url_params, is_nat ? ";nat" : ""); + contact_str = switch_mprintf("%s %s", + display, contact->m_url->url_user, + ipv6 ? "[" : "", + contact_host, + ipv6 ? "]" : "", + new_port, + contact->m_url->url_params, + is_nat ? ";nat" : ""); } else { - contact_str = switch_mprintf("%s %s", display, contact->m_url->url_user, contact_host, new_port, is_nat ? ";nat" : ""); + contact_str = switch_mprintf("%s %s", + display, + contact->m_url->url_user, + ipv6 ? "[" : "", + contact_host, + ipv6 ? "]" : "", + new_port, + is_nat ? ";nat" : ""); } @@ -1398,7 +1413,13 @@ void sofia_presence_handle_sip_i_subscribe(int status, char *sticky = NULL; if (is_nat) { - sticky = switch_mprintf("sip:%s@%s:%d", contact_user, network_ip, network_port); + const char *ipv6 = strchr(network_ip, ':'); + sticky = switch_mprintf("sip:%s@%s%s%s:%d", + contact_user, + ipv6 ? "[" : "", + network_ip, + ipv6 ? "]" : "", + network_port); } nua_respond(nh, SIP_202_ACCEPTED, NUTAG_WITH_THIS(nua), SIPTAG_SUBSCRIPTION_STATE_STR(sstr), TAG_IF(sticky, NUTAG_PROXY(sticky)), diff --git a/src/mod/endpoints/mod_sofia/sofia_reg.c b/src/mod/endpoints/mod_sofia/sofia_reg.c index 46f7d1f955..d7ff882d69 100644 --- a/src/mod/endpoints/mod_sofia/sofia_reg.c +++ b/src/mod/endpoints/mod_sofia/sofia_reg.c @@ -403,6 +403,7 @@ uint8_t sofia_reg_handle_register(nua_t *nua, sofia_profile_t *profile, nua_hand const char *rpid = "unknown"; const char *display = "\"user\""; char network_ip[80]; + char url_ip[80]; char *register_gateway = NULL; int network_port; const char *reg_desc = "Registered"; @@ -410,12 +411,15 @@ uint8_t sofia_reg_handle_register(nua_t *nua, sofia_profile_t *profile, nua_hand char *force_user; char received_data[128] = ""; char *path_val = NULL; - + su_addrinfo_t *my_addrinfo = msg_addrinfo(nua_current_request(nua)); + /* all callers must confirm that sip, sip->sip_request and sip->sip_contact are not NULL */ switch_assert(sip != NULL && sip->sip_contact != NULL && sip->sip_request != NULL); - get_addr(network_ip, sizeof(network_ip), &((struct sockaddr_in *) msg_addrinfo(nua_current_request(nua))->ai_addr)->sin_addr); - network_port = ntohs(((struct sockaddr_in *) msg_addrinfo(nua_current_request(nua))->ai_addr)->sin_port); + get_addr(network_ip, sizeof(network_ip), my_addrinfo->ai_addr,my_addrinfo->ai_addrlen); + network_port = get_port(my_addrinfo->ai_addr); + + snprintf(url_ip, sizeof(url_ip), my_addrinfo->ai_addr->sa_family == AF_INET6 ? "[%s]" : "%s", network_ip); expires = sip->sip_expires; authorization = sip->sip_authorization; @@ -466,7 +470,7 @@ uint8_t sofia_reg_handle_register(nua_t *nua, sofia_profile_t *profile, nua_hand } else { reg_desc = "Registered(UDP-NAT)"; } - contact_host = network_ip; + contact_host = url_ip; switch_snprintf(new_port, sizeof(new_port), ":%d", network_port); port = NULL; } else { @@ -501,7 +505,7 @@ uint8_t sofia_reg_handle_register(nua_t *nua, sofia_profile_t *profile, nua_hand } if (is_nat && (profile->pflags & PFLAG_RECIEVED_IN_NAT_REG_CONTACT)) { - switch_snprintf(received_data, sizeof(received_data), ";received=\"%s:%d\"", network_ip, network_port); + switch_snprintf(received_data, sizeof(received_data), ";received=\"%s:%d\"", url_ip, network_port); } if (contact->m_url->url_params) { @@ -556,16 +560,16 @@ uint8_t sofia_reg_handle_register(nua_t *nua, sofia_profile_t *profile, nua_hand if ((v_contact_str = switch_event_get_header(*v_event, "sip-force-contact"))) { if (*received_data && (profile->pflags & PFLAG_RECIEVED_IN_NAT_REG_CONTACT)) { - switch_snprintf(received_data, sizeof(received_data), ";received=\"%s:%d\"", network_ip, network_port); + switch_snprintf(received_data, sizeof(received_data), ";received=\"%s:%d\"", url_ip, network_port); } if (!strcasecmp(v_contact_str, "nat-connectile-dysfunction") || !strcasecmp(v_contact_str, "NDLB-connectile-dysfunction") || !strcasecmp(v_contact_str, "NDLB-tls-connectile-dysfunction")) { if (contact->m_url->url_params) { switch_snprintf(contact_str, sizeof(contact_str), "%s ", - display, contact->m_url->url_user, network_ip, network_port, contact->m_url->url_params, received_data); + display, contact->m_url->url_user, url_ip, network_port, contact->m_url->url_params, received_data); } else { - switch_snprintf(contact_str, sizeof(contact_str), "%s ", display, contact->m_url->url_user, network_ip, + switch_snprintf(contact_str, sizeof(contact_str), "%s ", display, contact->m_url->url_user, url_ip, network_port, received_data); } if (strstr(v_contact_str, "tls")) { @@ -762,8 +766,8 @@ void sofia_reg_handle_sip_i_register(nua_t *nua, sofia_profile_t *profile, nua_h char *is_nat = NULL; - get_addr(network_ip, sizeof(network_ip), &((struct sockaddr_in *) my_addrinfo->ai_addr)->sin_addr); - network_port = ntohs(((struct sockaddr_in *) msg_addrinfo(nua_current_request(nua))->ai_addr)->sin_port); + get_addr(network_ip, sizeof(network_ip), my_addrinfo->ai_addr, my_addrinfo->ai_addrlen); + network_port = get_port(msg_addrinfo(nua_current_request(nua))->ai_addr); if (!(sip->sip_contact && sip->sip_contact->m_url)) { diff --git a/src/switch_apr.c b/src/switch_apr.c index 9ad6e25133..b76b616d71 100644 --- a/src/switch_apr.c +++ b/src/switch_apr.c @@ -630,7 +630,7 @@ SWITCH_DECLARE(const char *) switch_get_addr(char *buf, switch_size_t len, switc if (!in) { return ""; } - return get_addr(buf, len, &in->sa.sin.sin_addr); + return get_addr(buf, len, (struct sockaddr*)&in->sa, in->salen); } SWITCH_DECLARE(uint16_t) switch_sockaddr_get_port(switch_sockaddr_t *sa) diff --git a/src/switch_rtp.c b/src/switch_rtp.c index 25913a673e..a60cae1741 100644 --- a/src/switch_rtp.c +++ b/src/switch_rtp.c @@ -121,7 +121,13 @@ struct switch_rtp_rfc2833_data { }; struct switch_rtp { - switch_socket_t *sock; + /* + * Two sockets are needed because we might be transcoding protocol families + * (e.g. receive over IPv4 and send over IPv6). In case the protocol + * families are equal, sock_input == sock_output and only one socket is + * used. + */ + switch_socket_t *sock_input, *sock_output; switch_sockaddr_t *local_addr; rtp_msg_t send_msg; @@ -220,7 +226,7 @@ static switch_status_t ice_out(switch_rtp_t *rtp_session) packet = switch_stun_packet_build_header(SWITCH_STUN_BINDING_REQUEST, NULL, buf); switch_stun_packet_attribute_add_username(packet, rtp_session->ice_user, 32); bytes = switch_stun_packet_length(packet); - switch_socket_sendto(rtp_session->sock, rtp_session->remote_addr, 0, (void *) packet, &bytes); + switch_socket_sendto(rtp_session->sock_output, rtp_session->remote_addr, 0, (void *) packet, &bytes); rtp_session->stuncount = 25; end: @@ -296,7 +302,7 @@ static void handle_ice(switch_rtp_t *rtp_session, void *data, switch_size_t len) remote_ip = switch_get_addr(ipbuf, sizeof(ipbuf), rtp_session->from_addr); switch_stun_packet_attribute_add_binded_address(rpacket, (char *) remote_ip, switch_sockaddr_get_port(rtp_session->from_addr)); bytes = switch_stun_packet_length(rpacket); - switch_socket_sendto(rtp_session->sock, rtp_session->from_addr, 0, (void *) rpacket, &bytes); + switch_socket_sendto(rtp_session->sock_output, rtp_session->from_addr, 0, (void *) rpacket, &bytes); } end: @@ -440,11 +446,11 @@ SWITCH_DECLARE(switch_status_t) switch_rtp_set_local_address(switch_rtp_t *rtp_s goto done; } - if (rtp_session->sock) { + if (rtp_session->sock_input) { switch_rtp_kill_socket(rtp_session); } - if (switch_socket_create(&new_sock, AF_INET, SOCK_DGRAM, 0, rtp_session->pool) != SWITCH_STATUS_SUCCESS) { + if (switch_socket_create(&new_sock, switch_sockaddr_get_family(rtp_session->local_addr), SOCK_DGRAM, 0, rtp_session->pool) != SWITCH_STATUS_SUCCESS) { *err = "Socket Error!"; goto done; } @@ -483,12 +489,12 @@ SWITCH_DECLARE(switch_status_t) switch_rtp_set_local_address(switch_rtp_t *rtp_s #endif - old_sock = rtp_session->sock; - rtp_session->sock = new_sock; + old_sock = rtp_session->sock_input; + rtp_session->sock_input = new_sock; new_sock = NULL; if (switch_test_flag(rtp_session, SWITCH_RTP_FLAG_USE_TIMER) || switch_test_flag(rtp_session, SWITCH_RTP_FLAG_NOBLOCK)) { - switch_socket_opt_set(rtp_session->sock, SWITCH_SO_NONBLOCK, TRUE); + switch_socket_opt_set(rtp_session->sock_input, SWITCH_SO_NONBLOCK, TRUE); switch_set_flag_locked(rtp_session, SWITCH_RTP_FLAG_NOBLOCK); } @@ -522,6 +528,7 @@ SWITCH_DECLARE(void) switch_rtp_set_max_missed_packets(switch_rtp_t *rtp_session SWITCH_DECLARE(switch_status_t) switch_rtp_set_remote_address(switch_rtp_t *rtp_session, const char *host, switch_port_t port, const char **err) { switch_sockaddr_t *remote_addr; + switch_status_t status = SWITCH_STATUS_SUCCESS; *err = "Success"; if (switch_sockaddr_info_get(&remote_addr, host, SWITCH_UNSPEC, port, 0, rtp_session->pool) != SWITCH_STATUS_SUCCESS || !remote_addr) { @@ -530,11 +537,32 @@ SWITCH_DECLARE(switch_status_t) switch_rtp_set_remote_address(switch_rtp_t *rtp_ } switch_mutex_lock(rtp_session->write_mutex); + rtp_session->remote_addr = remote_addr; rtp_session->remote_port = port; + + if (rtp_session->sock_input && + switch_sockaddr_get_family(rtp_session->remote_addr) == + switch_sockaddr_get_family(rtp_session->local_addr)) { + rtp_session->sock_output = rtp_session->sock_input; + } + else { + if (rtp_session->sock_output && + rtp_session->sock_output != rtp_session->sock_input) { + switch_socket_close(rtp_session->sock_output); + } + if ((status = switch_socket_create(&rtp_session->sock_output, + switch_sockaddr_get_family(rtp_session->remote_addr), + SOCK_DGRAM, 0, rtp_session->pool)) != + SWITCH_STATUS_SUCCESS) + { + *err = "Socket Error!"; + } + } + switch_mutex_unlock(rtp_session->write_mutex); - return SWITCH_STATUS_SUCCESS; + return status; } SWITCH_DECLARE(switch_status_t) switch_rtp_add_crypto_key(switch_rtp_t *rtp_session, @@ -829,9 +857,9 @@ SWITCH_DECLARE(void) switch_rtp_break(switch_rtp_t *rtp_session) switch_assert(rtp_session != NULL); switch_mutex_lock(rtp_session->flag_mutex); - if (rtp_session->sock) { + if (rtp_session->sock_input) { switch_set_flag_locked(rtp_session, SWITCH_RTP_FLAG_BREAK); - switch_socket_sendto(rtp_session->sock, rtp_session->local_addr, 0, (void *) &o, &len); + switch_socket_sendto(rtp_session->sock_input, rtp_session->local_addr, 0, (void *) &o, &len); } switch_mutex_unlock(rtp_session->flag_mutex); } @@ -842,8 +870,11 @@ SWITCH_DECLARE(void) switch_rtp_kill_socket(switch_rtp_t *rtp_session) switch_mutex_lock(rtp_session->flag_mutex); if (switch_test_flag(rtp_session, SWITCH_RTP_FLAG_IO)) { switch_clear_flag(rtp_session, SWITCH_RTP_FLAG_IO); - if (rtp_session->sock) { - switch_socket_shutdown(rtp_session->sock, SWITCH_SHUTDOWN_READWRITE); + if (rtp_session->sock_input) { + switch_socket_shutdown(rtp_session->sock_input, SWITCH_SHUTDOWN_READWRITE); + } + if (rtp_session->sock_output && rtp_session->sock_output != rtp_session->sock_input) { + switch_socket_shutdown(rtp_session->sock_output, SWITCH_SHUTDOWN_READWRITE); } } switch_mutex_unlock(rtp_session->flag_mutex); @@ -858,7 +889,7 @@ SWITCH_DECLARE(uint8_t) switch_rtp_ready(switch_rtp_t *rtp_session) } switch_mutex_lock(rtp_session->flag_mutex); - ret = (switch_test_flag(rtp_session, SWITCH_RTP_FLAG_IO) && rtp_session->sock && rtp_session->remote_addr && rtp_session->ready == 2) ? 1 : 0; + ret = (switch_test_flag(rtp_session, SWITCH_RTP_FLAG_IO) && rtp_session->sock_input && rtp_session->sock_output && rtp_session->remote_addr && rtp_session->ready == 2) ? 1 : 0; switch_mutex_unlock(rtp_session->flag_mutex); return ret; @@ -899,10 +930,15 @@ SWITCH_DECLARE(void) switch_rtp_destroy(switch_rtp_t **rtp_session) stfu_n_destroy(&(*rtp_session)->jb); } - sock = (*rtp_session)->sock; - (*rtp_session)->sock = NULL; + sock = (*rtp_session)->sock_input; + (*rtp_session)->sock_input = NULL; switch_socket_close(sock); + if ( (*rtp_session)->sock_output != sock ) { + sock = (*rtp_session)->sock_output; + (*rtp_session)->sock_output = NULL; + switch_socket_close(sock); + } if (switch_test_flag((*rtp_session), SWITCH_RTP_FLAG_VAD)) { switch_rtp_disable_vad(*rtp_session); @@ -932,7 +968,7 @@ SWITCH_DECLARE(void) switch_rtp_destroy(switch_rtp_t **rtp_session) SWITCH_DECLARE(switch_socket_t *) switch_rtp_get_rtp_socket(switch_rtp_t *rtp_session) { - return rtp_session->sock; + return rtp_session->sock_input; } SWITCH_DECLARE(void) switch_rtp_set_default_samples_per_interval(switch_rtp_t *rtp_session, uint16_t samples_per_interval) @@ -1096,7 +1132,7 @@ static int rtp_common_read(switch_rtp_t *rtp_session, switch_payload_t *payload_ int do_cng = 0; bytes = sizeof(rtp_msg_t); - status = switch_socket_recvfrom(rtp_session->from_addr, rtp_session->sock, 0, (void *) &rtp_session->recv_msg, &bytes); + status = switch_socket_recvfrom(rtp_session->from_addr, rtp_session->sock_input, 0, (void *) &rtp_session->recv_msg, &bytes); if (bytes < 0) { ret = (int) bytes; @@ -1184,7 +1220,7 @@ static int rtp_common_read(switch_rtp_t *rtp_session, switch_payload_t *payload_ } if (rtp_session->invalid_handler) { - rtp_session->invalid_handler(rtp_session, rtp_session->sock, (void *) &rtp_session->recv_msg, bytes, rtp_session->from_addr); + rtp_session->invalid_handler(rtp_session, rtp_session->sock_input, (void *) &rtp_session->recv_msg, bytes, rtp_session->from_addr); } memset(data, 0, 2); @@ -1783,7 +1819,7 @@ static int rtp_common_write(switch_rtp_t *rtp_session, bytes = sbytes; } - if (switch_socket_sendto(rtp_session->sock, rtp_session->remote_addr, 0, (void *) send_msg, &bytes) != SWITCH_STATUS_SUCCESS) { + if (switch_socket_sendto(rtp_session->sock_output, rtp_session->remote_addr, 0, (void *) send_msg, &bytes) != SWITCH_STATUS_SUCCESS) { rtp_session->seq--; ret = -1; goto end; @@ -1889,7 +1925,7 @@ SWITCH_DECLARE(int) switch_rtp_write_frame(switch_rtp_t *rtp_session, switch_fra return 0; } bytes = frame->packetlen; - if (switch_socket_sendto(rtp_session->sock, rtp_session->remote_addr, 0, frame->packet, &bytes) != SWITCH_STATUS_SUCCESS) { + if (switch_socket_sendto(rtp_session->sock_output, rtp_session->remote_addr, 0, frame->packet, &bytes) != SWITCH_STATUS_SUCCESS) { return -1; } return (int) bytes; @@ -1971,7 +2007,7 @@ SWITCH_DECLARE(int) switch_rtp_write_manual(switch_rtp_t *rtp_session, bytes = sbytes; } - if (switch_socket_sendto(rtp_session->sock, rtp_session->remote_addr, 0, (void *) &rtp_session->write_msg, &bytes) != SWITCH_STATUS_SUCCESS) { + if (switch_socket_sendto(rtp_session->sock_output, rtp_session->remote_addr, 0, (void *) &rtp_session->write_msg, &bytes) != SWITCH_STATUS_SUCCESS) { rtp_session->seq--; ret = -1; goto end; diff --git a/src/switch_utils.c b/src/switch_utils.c index bf703088a5..10d2d3bda8 100644 --- a/src/switch_utils.c +++ b/src/switch_utils.c @@ -757,7 +757,7 @@ SWITCH_DECLARE(switch_status_t) switch_find_local_ip(char *buf, int len, int fam goto doh; } - switch_copy_string(buf, get_addr(abuf, sizeof(abuf), &iface_out.sin_addr), len); + switch_copy_string(buf, get_addr(abuf, sizeof(abuf), (struct sockaddr*)&iface_out, sizeof(iface_out)), len); status = SWITCH_STATUS_SUCCESS; } break; @@ -768,17 +768,13 @@ SWITCH_DECLARE(switch_status_t) switch_find_local_ip(char *buf, int len, int fam memset(&remote, 0, sizeof(struct sockaddr_in6)); remote.sin6_family = AF_INET6; - switch_inet_pton(AF_INET6, buf, &remote.sin6_addr); + switch_inet_pton(AF_INET6, base, &remote.sin6_addr); remote.sin6_port = htons(4242); memset(&iface_out, 0, sizeof(iface_out)); tmp_socket = socket(AF_INET6, SOCK_DGRAM, 0); - if (setsockopt(tmp_socket, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)) == -1) { - goto doh; - } - - if (connect(tmp_socket, (struct sockaddr *) &remote, sizeof(struct sockaddr_in)) == -1) { + if (connect(tmp_socket, (struct sockaddr *) &remote, sizeof(remote)) == -1) { goto doh; } @@ -1054,16 +1050,66 @@ static const char *switch_inet_ntop6(unsigned char const *src, char *dst, size_t #endif -SWITCH_DECLARE(char *) get_addr(char *buf, switch_size_t len, struct in_addr *in) +SWITCH_DECLARE(char *) get_addr(char *buf, switch_size_t len, struct sockaddr *sa, socklen_t salen) { switch_assert(buf); *buf = '\0'; - if (in) { - switch_inet_ntop(AF_INET, in, buf, len); + if (sa) { + getnameinfo(sa, salen, buf, len, NULL, 0, NI_NUMERICHOST); } return buf; } +SWITCH_DECLARE(unsigned short) get_port(struct sockaddr *sa) +{ + unsigned short port = 0; + if (sa) { + switch (sa->sa_family) { + case AF_INET: + port = ntohs(((struct sockaddr_in*)sa)->sin_port); + break; + case AF_INET6: + port = ntohs(((struct sockaddr_in6*)sa)->sin6_port); + break; + } + } + return port; +} + +SWITCH_DECLARE(int) switch_build_uri(char *uri, + switch_size_t size, + const char *scheme, + const char *user, + const switch_sockaddr_t *sa, + int flags) +{ + char host[NI_MAXHOST], serv[NI_MAXSERV]; + struct sockaddr_storage ss; + const struct sockaddr *addr; + const char *colon; + + if (flags & SWITCH_URI_NO_SCOPE && sa->family == AF_INET6) { + memcpy(&ss, &sa->sa, sa->salen); + ((struct sockaddr_in6*) &ss)->sin6_scope_id = 0; + addr = (const struct sockaddr*) &ss; + } else { + addr = (const struct sockaddr*) &sa->sa; + } + + if (getnameinfo(addr, sa->salen, host, sizeof(host), serv, sizeof(serv), + (flags & SWITCH_URI_NUMERIC_HOST) ? NI_NUMERICHOST : 0 | + (flags & SWITCH_URI_NUMERIC_PORT) ? NI_NUMERICSERV : 0) != 0) { + return 0; + } + + colon = strchr(host, ':'); + + return switch_snprintf(uri, size, "%s:%s%s%s%s%s%s%s", scheme, + user ? user : "", user ? "@" : "", + colon ? "[" : "", host, colon ? "]" : "", + serv[0] ? ":" : "", serv[0] ? serv : ""); +} + SWITCH_DECLARE(char) switch_rfc2833_to_char(int event) { if (event > -1 && event < (int32_t) sizeof(RFC2833_CHARS)) {