diff --git a/src/include/switch_rtp.h b/src/include/switch_rtp.h index e778c5916d..51f9e69779 100644 --- a/src/include/switch_rtp.h +++ b/src/include/switch_rtp.h @@ -55,6 +55,9 @@ typedef void (*switch_rtp_invalid_handler)(switch_rtp *rtp_session, switch_sockaddr_t *from_addr); +SWITCH_DECLARE(void) switch_rtp_init(switch_memory_pool *pool); +SWITCH_DECLARE(switch_port_t) switch_rtp_request_port(void); + SWITCH_DECLARE(switch_rtp *)switch_rtp_new(char *rx_ip, switch_port_t rx_port, char *tx_ip, diff --git a/src/include/switch_stun.h b/src/include/switch_stun.h index 0326b3966c..84ebfd5b7b 100644 --- a/src/include/switch_stun.h +++ b/src/include/switch_stun.h @@ -36,7 +36,7 @@ */ #ifndef _SWITCH_STUN_PARSER_H #define _SWITCH_STUN_PARSER_H - +#define SWITCH_STUN_DEFAULT_PORT 3478 #define SWITCH_STUN_PACKET_MIN_LEN 20 typedef enum { @@ -196,6 +196,23 @@ SWITCH_DECLARE(uint8_t) switch_stun_packet_attribute_add_username(switch_stun_pa */ SWITCH_DECLARE(uint8_t) switch_stun_packet_attribute_add_binded_address(switch_stun_packet_t *packet, char *ipstr, uint16_t port); +/*! + \brief Perform a stun lookup + \param ip the local ip to use (replaced with stun results) + \param port the local port to use (replaced with stun results) + \param stunip the ip of the stun server + \param pool the memory pool to use + \return SUCCESS or FAIL +*/ +SWITCH_DECLARE(switch_status) switch_stun_lookup (char **ip, + switch_port_t *port, + char *stunip, + switch_port_t stunport, + char **err, + switch_memory_pool *pool); + + + /*! \brief set a switch_stun_packet_attribute_t pointer to point at the first attribute in a packet \param packet the packet in question diff --git a/src/mod/endpoints/mod_dingaling/mod_dingaling.c b/src/mod/endpoints/mod_dingaling/mod_dingaling.c index 508427570c..9c240c23f7 100644 --- a/src/mod/endpoints/mod_dingaling/mod_dingaling.c +++ b/src/mod/endpoints/mod_dingaling/mod_dingaling.c @@ -68,7 +68,6 @@ static struct { int codec_rates_last; unsigned int flags; unsigned int init; - uint16_t rtp_port; switch_hash *profile_hash; int running; int handles; @@ -95,7 +94,6 @@ struct private_object { struct switch_frame read_frame; struct switch_frame cng_frame; struct mdl_profile *profile; - switch_sockaddr_t *switch_stun_addr; unsigned char read_buf[SWITCH_RECCOMMENDED_BUFFER_SIZE]; unsigned char cng_buf[SWITCH_RECCOMMENDED_BUFFER_SIZE]; switch_core_session *session; @@ -108,7 +106,8 @@ struct private_object { struct switch_rtp *rtp_session; ldl_session_t *dlsession; char *remote_ip; - uint16_t remote_port; + switch_port_t local_port; + switch_port_t remote_port; char local_user[16]; char *remote_user; unsigned int cand_id; @@ -135,12 +134,6 @@ struct rfc2833_digit { int duration; }; -static uint16_t next_rtp_port(void) -{ - uint16_t ret = globals.rtp_port++; - globals.rtp_port++; - return ret; -} SWITCH_DECLARE_GLOBAL_STRING_FUNC(set_global_dialplan, globals.dialplan) SWITCH_DECLARE_GLOBAL_STRING_FUNC(set_global_codec_string, globals.codec_string) @@ -280,7 +273,7 @@ static void *SWITCH_THREAD_FUNC negotiate_thread_run(switch_thread *thread, void cand[0].name = "rtp"; cand[0].address = advip; - cand[0].port = next_rtp_port(); + cand[0].port = tech_pvt->local_port; cand[0].username = tech_pvt->local_user; cand[0].password = tech_pvt->local_user; cand[0].pref = 1; @@ -878,13 +871,13 @@ static switch_status channel_outgoing_channel(switch_core_session *session, swit } switch_core_session_add_stream(*new_session, NULL); - if ((tech_pvt = - (struct private_object *) switch_core_session_alloc(*new_session, sizeof(struct private_object))) != 0) { + if ((tech_pvt = (struct private_object *) switch_core_session_alloc(*new_session, sizeof(struct private_object))) != 0) { memset(tech_pvt, 0, sizeof(*tech_pvt)); channel = switch_core_session_get_channel(*new_session); switch_core_session_set_private(*new_session, tech_pvt); tech_pvt->session = *new_session; tech_pvt->codec_index = -1; + tech_pvt->local_port = switch_rtp_request_port(); } else { switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Hey where is my memory pool?\n"); switch_core_session_destroy(new_session); @@ -1009,7 +1002,6 @@ static switch_status load_config(void) int lastcat = -1; memset(&globals, 0, sizeof(globals)); - globals.rtp_port = 10000; globals.running = 1; switch_core_hash_init(&globals.profile_hash, module_pool); @@ -1134,6 +1126,7 @@ static ldl_status handle_signalling(ldl_handle_t *handle, ldl_session_t *dlsessi tech_pvt->session = session; tech_pvt->codec_index = -1; tech_pvt->profile = profile; + tech_pvt->local_port = switch_rtp_request_port(); } else { switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Hey where is my memory pool?\n"); switch_core_session_destroy(&session); @@ -1219,7 +1212,7 @@ static ldl_status handle_signalling(ldl_handle_t *handle, ldl_session_t *dlsessi if (signal) { ldl_candidate_t *candidates; unsigned int len = 0; - + char *err; if (ldl_session_get_candidates(dlsession, &candidates, &len) == LDL_STATUS_SUCCESS) { @@ -1257,17 +1250,7 @@ static ldl_status handle_signalling(ldl_handle_t *handle, ldl_session_t *dlsessi tech_pvt->remote_port = candidates[x].port; tech_pvt->remote_user = switch_core_session_strdup(session, candidates[x].username); - if (switch_sockaddr_info_get(&tech_pvt->switch_stun_addr, - tech_pvt->remote_ip, - SWITCH_UNSPEC, - tech_pvt->remote_port, - 0, - switch_core_session_get_pool(session)) != SWITCH_STATUS_SUCCESS) { - switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Address Error!\n"); - return LDL_STATUS_FALSE; - } - - + if (tech_pvt->codec_index < 0) { switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Don't have my codec yet here's one\n"); tech_pvt->codec_name = tech_pvt->codecs[0]->iananame; @@ -1286,24 +1269,44 @@ static ldl_status handle_signalling(ldl_handle_t *handle, ldl_session_t *dlsessi memset(cand, 0, sizeof(cand)); switch_stun_random_string(tech_pvt->local_user, 16, NULL); - cand[0].name = "rtp"; + + cand[0].port = tech_pvt->local_port; cand[0].address = advip; - cand[0].port = next_rtp_port(); + + if (!strncasecmp(advip, "stun:", 5)) { + cand[0].address = profile->ip; + if (switch_stun_lookup(&cand[0].address, + &cand[0].port, + advip + 5, + SWITCH_STUN_DEFAULT_PORT, + &err, + switch_core_session_get_pool(tech_pvt->session)) != SWITCH_STATUS_SUCCESS) { + switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Stun Failed! %s:%d [%s]\n", advip + 5, SWITCH_STUN_DEFAULT_PORT, err); + switch_channel_hangup(channel); + return LDL_STATUS_FALSE; + } + switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Stun Success %s:%d\n", cand[0].address, cand[0].port); + cand[0].type = "stun"; + } else { + cand[0].type = "local"; + } + + cand[0].name = "rtp"; cand[0].username = tech_pvt->local_user; cand[0].password = tech_pvt->local_user; cand[0].pref = 1; cand[0].protocol = "udp"; - cand[0].type = "local"; + tech_pvt->cand_id = ldl_session_candidates(dlsession, cand, 1); tech_pvt->desc_id = ldl_session_describe(dlsession, payloads, 1, LDL_DESCRIPTION_ACCEPT); if (!tech_pvt->rtp_session) { const char *err; - switch_console_printf(SWITCH_CHANNEL_CONSOLE, "SETUP RTP %s:%d -> %s:%d\n", profile->ip, cand[0].port, tech_pvt->remote_ip, tech_pvt->remote_port); + switch_console_printf(SWITCH_CHANNEL_CONSOLE, "SETUP RTP %s:%d -> %s:%d\n", profile->ip, tech_pvt->local_port, tech_pvt->remote_ip, tech_pvt->remote_port); if (!(tech_pvt->rtp_session = switch_rtp_new(profile->ip, - cand[0].port, + tech_pvt->local_port, tech_pvt->remote_ip, tech_pvt->remote_port, tech_pvt->codec_num, diff --git a/src/mod/endpoints/mod_exosip/mod_exosip.c b/src/mod/endpoints/mod_exosip/mod_exosip.c index 72fa52c554..1e519d9490 100644 --- a/src/mod/endpoints/mod_exosip/mod_exosip.c +++ b/src/mod/endpoints/mod_exosip/mod_exosip.c @@ -80,13 +80,10 @@ static struct { int bytes_per_frame; char *dialplan; int port; - switch_port_t rtp_start; - switch_port_t rtp_end; char *codec_string; char *codec_order[SWITCH_MAX_CODECS]; int codec_order_last; switch_hash *call_hash; - switch_mutex_t *port_lock; int running; int codec_ms; int dtmf_duration; @@ -134,20 +131,6 @@ struct private_object { }; -static switch_port_t next_rtp_port(void) -{ - switch_port_t port; - - switch_mutex_lock(globals.port_lock); - port = globals.rtp_start; - globals.rtp_start += 2; - if (port >= globals.rtp_end) { - port = globals.rtp_start; - } - switch_mutex_unlock(globals.port_lock); - return port; -} - struct rfc2833_digit { char digit; int duration; @@ -268,7 +251,7 @@ static switch_status exosip_on_init(switch_core_session *session) osip_rfc3264_init(&tech_pvt->sdp_config); /* Decide on local IP and rtp port */ strncpy(tech_pvt->local_sdp_audio_ip, localip, sizeof(tech_pvt->local_sdp_audio_ip)); - tech_pvt->local_sdp_audio_port = next_rtp_port(); + tech_pvt->local_sdp_audio_port = switch_rtp_request_port(); /* Initialize SDP */ sdp_message_init(&tech_pvt->local_sdp); sdp_message_v_version_set(tech_pvt->local_sdp, "0"); @@ -1028,7 +1011,6 @@ SWITCH_MOD_DECLARE(switch_status) switch_module_load(const switch_loadable_modul return SWITCH_STATUS_TERM; } - switch_mutex_init(&globals.port_lock, SWITCH_MUTEX_NESTED, module_pool); switch_core_hash_init(&globals.call_hash, module_pool); /* connect my internal structure to the blank pointer passed to me */ @@ -1116,7 +1098,7 @@ static switch_status exosip_create_call(eXosip_event_t * event) } eXosip_guess_localip(AF_INET, tech_pvt->local_sdp_audio_ip, 50); - tech_pvt->local_sdp_audio_port = next_rtp_port(); + tech_pvt->local_sdp_audio_port = switch_rtp_request_port(); osip_rfc3264_init(&tech_pvt->sdp_config); /* Add in what codecs we support locally */ @@ -1565,8 +1547,6 @@ static int config_exosip(int reload) return SWITCH_STATUS_TERM; } - globals.rtp_start = 16384; - globals.rtp_end = 32768; globals.dtmf_duration = 100; while (switch_config_next_pair(&cfg, &var, &val)) { @@ -1583,12 +1563,7 @@ static int config_exosip(int reload) set_global_dialplan(val); } else if (!strcmp(var, "codec_prefs")) { set_global_codec_string(val); - globals.codec_order_last = - switch_separate_string(globals.codec_string, ',', globals.codec_order, SWITCH_MAX_CODECS); - } else if (!strcmp(var, "rtp_min_port")) { - globals.rtp_start = (switch_port_t)atoi(val); - } else if (!strcmp(var, "rtp_max_port")) { - globals.rtp_end = (switch_port_t)atoi(val); + globals.codec_order_last = switch_separate_string(globals.codec_string, ',', globals.codec_order, SWITCH_MAX_CODECS); } else if (!strcmp(var, "codec_ms")) { globals.codec_ms = atoi(val); } else if (!strcmp(var, "dtmf_duration")) { diff --git a/src/switch_core.c b/src/switch_core.c index 2df845c1fb..7ae5a74717 100644 --- a/src/switch_core.c +++ b/src/switch_core.c @@ -2414,11 +2414,10 @@ SWITCH_DECLARE(switch_status) switch_core_init(char *console) } - + assert(runtime.memory_pool != NULL); switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Allocated memory pool. Sessions are %u bytes\n", sizeof(struct switch_core_session)); switch_event_init(runtime.memory_pool); - - assert(runtime.memory_pool != NULL); + switch_rtp_init(runtime.memory_pool); /* Activate SQL database */ if ((runtime.db = switch_core_db_handle()) == 0 ) { diff --git a/src/switch_ivr.c b/src/switch_ivr.c index c4be7bc168..29b4b5b578 100644 --- a/src/switch_ivr.c +++ b/src/switch_ivr.c @@ -780,6 +780,9 @@ static void *audio_bridge_thread(switch_thread *thread, void *obj) switch_channel_hangup(chan_b); } switch_channel_clear_flag(chan_a, CF_ORIGINATOR); + } else if (!switch_channel_test_flag(chan_a, CF_ORIGINATOR) && !switch_channel_test_flag(chan_a, CF_TRANSFER)) { + switch_core_session_kill_channel(session_a, SWITCH_SIG_KILL); + switch_channel_hangup(chan_a); } while (his_thread->running > 0) { diff --git a/src/switch_rtp.c b/src/switch_rtp.c index c218a33769..9cf1260186 100644 --- a/src/switch_rtp.c +++ b/src/switch_rtp.c @@ -42,6 +42,11 @@ #define MAX_KEY_LEN 64 #define rtp_header_len 12 #define RTP_MAX_BUF_LEN 16384 +#define RTP_START_PORT 16384 +#define RTP_END_PORT 32768 + +static switch_port_t NEXT_PORT = RTP_START_PORT; +static switch_mutex_t *port_lock = NULL; typedef srtp_hdr_t rtp_hdr_t; @@ -160,16 +165,31 @@ static void handle_ice(switch_rtp *rtp_session, void *data, switch_size_t len) } -static void init_rtp(void) +SWITCH_DECLARE(void) switch_rtp_init(switch_memory_pool *pool) { if (global_init) { return; } srtp_init(); + switch_mutex_init(&port_lock, SWITCH_MUTEX_NESTED, pool); global_init = 1; } +SWITCH_DECLARE(switch_port_t) switch_rtp_request_port(void) +{ + switch_port_t port; + + switch_mutex_lock(port_lock); + port = NEXT_PORT; + NEXT_PORT += 2; + if (port > RTP_END_PORT) { + port = RTP_START_PORT; + } + switch_mutex_unlock(port_lock); + return port; +} + SWITCH_DECLARE(switch_rtp *)switch_rtp_new(char *rx_ip, switch_port_t rx_port, char *tx_ip, @@ -187,9 +207,6 @@ SWITCH_DECLARE(switch_rtp *)switch_rtp_new(char *rx_ip, char key[MAX_KEY_LEN]; uint32_t ssrc = rand() & 0xffff; - if (!global_init) { - init_rtp(); - } if (switch_sockaddr_info_get(&rx_addr, rx_ip, SWITCH_UNSPEC, rx_port, 0, pool) != SWITCH_STATUS_SUCCESS) { *err = "RX Address Error!"; @@ -281,6 +298,12 @@ SWITCH_DECLARE(switch_status) switch_rtp_activate_ice(switch_rtp *rtp_session, c rtp_session->ice_user = switch_core_strdup(rtp_session->pool, ice_user); rtp_session->user_ice = switch_core_strdup(rtp_session->pool, user_ice); + if (rtp_session->ice_user) { + if (ice_out(rtp_session) != SWITCH_STATUS_SUCCESS) { + return -1; + } + } + return SWITCH_STATUS_SUCCESS; } @@ -319,7 +342,7 @@ SWITCH_DECLARE(int) switch_rtp_read(switch_rtp *rtp_session, void *data, uint32_ bytes = sizeof(rtp_msg_t); switch_socket_recvfrom(rtp_session->from_addr, rtp_session->sock, 0, (void *)&rtp_session->recv_msg, &bytes); - + if (bytes <= 0) { return 0; } diff --git a/src/switch_stun.c b/src/switch_stun.c index e640838fb3..832133defd 100644 --- a/src/switch_stun.c +++ b/src/switch_stun.c @@ -266,3 +266,105 @@ SWITCH_DECLARE(uint8_t) switch_stun_packet_attribute_add_username(switch_stun_pa packet->header.length += htons(sizeof(switch_stun_packet_attribute_t)) + attribute->length; return 1; } + +SWITCH_DECLARE(switch_status) switch_stun_lookup (char **ip, + switch_port_t *port, + char *stunip, + switch_port_t stunport, + char **err, + switch_memory_pool *pool) + +{ + switch_sockaddr_t *local_addr = NULL, *remote_addr = NULL, *from_addr = NULL; + switch_socket_t *sock = NULL; + uint8_t buf[256] = {0}; + switch_stun_packet_t *packet; + switch_stun_packet_attribute_t *attr; + switch_size_t bytes = 0; + char username[33] = {0}; + char rip[16]; + uint16_t rport = 0; + switch_time_t started = 0; + unsigned int elapsed = 0; + + *err = "Success"; + + switch_sockaddr_info_get(&from_addr, NULL, SWITCH_UNSPEC, 0, 0, pool); + + if (switch_sockaddr_info_get(&local_addr, *ip, SWITCH_UNSPEC, *port, 0, pool) != SWITCH_STATUS_SUCCESS) { + *err = "Local Address Error!"; + return SWITCH_STATUS_FALSE; + } + + if (switch_sockaddr_info_get(&remote_addr, stunip, SWITCH_UNSPEC, stunport, 0, pool) != SWITCH_STATUS_SUCCESS) { + *err = "Remote Address Error!"; + return SWITCH_STATUS_FALSE; + } + + if (switch_socket_create(&sock, AF_INET, SOCK_DGRAM, 0, pool) != SWITCH_STATUS_SUCCESS) { + *err = "Socket Error!"; + return SWITCH_STATUS_FALSE; + } + + if (switch_socket_bind(sock, local_addr) != SWITCH_STATUS_SUCCESS) { + *err = "Bind Error!"; + return SWITCH_STATUS_FALSE; + } + + switch_socket_opt_set(sock, APR_SO_NONBLOCK, TRUE); + packet = switch_stun_packet_build_header(SWITCH_STUN_BINDING_REQUEST, NULL, buf); + switch_stun_random_string(username, 32, NULL); + switch_stun_packet_attribute_add_username(packet, username, 32); + bytes = switch_stun_packet_length(packet); + switch_socket_sendto(sock, remote_addr, 0, (void *)packet, &bytes); + started = switch_time_now(); + + *ip = NULL; + *port = 0; + + + for(;;) { + bytes = sizeof(buf); + if (switch_socket_recvfrom(from_addr, sock, 0, (char *)&buf, &bytes) == SWITCH_STATUS_SUCCESS && bytes > 0) { + break; + } + + if ((elapsed = (unsigned int)((switch_time_now() - started) / 1000)) > 5000) { + *err = "Timeout"; + switch_socket_shutdown(sock, APR_SHUTDOWN_READWRITE); + switch_socket_close(sock); + return SWITCH_STATUS_TIMEOUT; + } + switch_yield(1000); + } + switch_socket_close(sock); + + packet = switch_stun_packet_parse(buf, sizeof(buf)); + switch_stun_packet_first_attribute(packet, attr); + + do { + switch(attr->type) { + case SWITCH_STUN_ATTR_MAPPED_ADDRESS: + if (attr->type) { + switch_stun_packet_attribute_get_mapped_address(attr, rip, &rport); + } + break; + case SWITCH_STUN_ATTR_USERNAME: + if(attr->type) { + switch_stun_packet_attribute_get_username(attr, username, 32); + } + break; + } + } while (switch_stun_packet_next_attribute(attr)); + + if (packet->header.type == SWITCH_STUN_BINDING_RESPONSE) { + *ip = switch_core_strdup(pool, rip); + *port = rport; + return SWITCH_STATUS_SUCCESS; + } else { + *err = "Invalid Reply"; + } + + + return SWITCH_STATUS_FALSE; +}