Index: channels/chan_sip.c =================================================================== --- channels/chan_sip.c (revision 372708) +++ channels/chan_sip.c (working copy) @@ -1388,7 +1388,7 @@ static int process_sdp_a_video(const char *a, struct sip_pvt *p, struct ast_rtp_codecs *newvideortp, int *last_rtpmap_codec); static int process_sdp_a_text(const char *a, struct sip_pvt *p, struct ast_rtp_codecs *newtextrtp, char *red_fmtp, int *red_num_gen, int *red_data_pt, int *last_rtpmap_codec); static int process_sdp_a_image(const char *a, struct sip_pvt *p); -static void add_ice_to_sdp(struct ast_rtp_instance *instance, struct ast_str **a_buf); +static void add_ice_to_sdp(struct ast_rtp_instance *instance, int is_webrtc, struct ast_str **a_buf); static void start_ice(struct ast_rtp_instance *instance); static void add_codec_to_sdp(const struct sip_pvt *p, struct ast_format *codec, struct ast_str **m_buf, struct ast_str **a_buf, @@ -1681,6 +1681,9 @@ static int sip_subscribe_mwi_do(const void *data); static int __sip_subscribe_mwi_do(struct sip_subscription_mwi *mwi); + +static int sip_pvt_is_webrtc(struct sip_pvt *p); + /*! \brief Definition of this channel for PBX channel registration */ struct ast_channel_tech sip_tech = { .type = "SIP", @@ -10412,9 +10415,10 @@ { struct ast_rtp_engine_ice *ice; int found = FALSE; - char ufrag[256], pwd[256], foundation[32], transport[4], address[46], cand_type[6], relay_address[46] = ""; + char ufrag[256], pwd[256], foundation[32], transport[4], address[46], cand_type[6], next_att[24] = "", relay_address[46] = ""; struct ast_rtp_engine_ice_candidate candidate = { 0, }; int port, relay_port = 0; + int generation = -1; if (!instance || !(ice = ast_rtp_instance_get_ice(instance))) { return found; @@ -10426,10 +10430,11 @@ } else if (sscanf(a, "ice-pwd: %255s", pwd) == 1) { ice->set_authentication(instance, NULL, pwd); found = TRUE; - } else if (sscanf(a, "candidate: %31s %30u %3s %30u %23s %30u typ %5s %*s %23s %*s %30u", foundation, &candidate.id, transport, &candidate.priority, - address, &port, cand_type, relay_address, &relay_port) >= 7) { + } else if (sscanf(a, "candidate: %31s %30u %3s %30u %23s %30u typ %5s %s %23s %*s %30u", foundation, &candidate.id, transport, &candidate.priority, + address, &port, cand_type, next_att, relay_address, &relay_port) >= 7) { candidate.foundation = foundation; candidate.transport = transport; + candidate.is_webrtc = (sip_pvt_is_webrtc(p) || (!strcasecmp(next_att, "generation") || !strcasecmp(next_att, "name"))); // (WS, WSS or UDP+webrtc4IE) ast_sockaddr_parse(&candidate.address, address, PARSE_PORT_FORBID); ast_sockaddr_set_port(&candidate.address, port); @@ -11990,7 +11995,7 @@ } /*! \brief Add ICE attributes to SDP */ -static void add_ice_to_sdp(struct ast_rtp_instance *instance, struct ast_str **a_buf) +static void add_ice_to_sdp(struct ast_rtp_instance *instance, int is_webrtc, struct ast_str **a_buf) { struct ast_rtp_engine_ice *ice = ast_rtp_instance_get_ice(instance); const char *username, *password; @@ -12013,6 +12018,13 @@ i = ao2_iterator_init(candidates, 0); while ((candidate = ao2_iterator_next(&i))) { + if((candidate->is_webrtc = is_webrtc)){ + int i; + int len = strlen(candidate->transport); + for(i = 0; i < len; ++i){ + candidate->transport[i] = tolower(candidate->transport[i]); + } + } ast_str_append(a_buf, 0, "a=candidate:%s %d %s %d ", candidate->foundation, candidate->id, candidate->transport, candidate->priority); ast_str_append(a_buf, 0, "%s ", ast_sockaddr_stringify_host(&candidate->address)); ast_str_append(a_buf, 0, "%s typ ", ast_sockaddr_stringify_port(&candidate->address)); @@ -12030,6 +12042,13 @@ ast_str_append(a_buf, 0, "rport %s", ast_sockaddr_stringify_port(&candidate->relay_address)); } + if(is_webrtc){ + static const int generation = 0; + static const int svn = 10; + // add generation attribute in ICE candidate + ast_str_append(a_buf, 0, " generation %d svn %d", generation, svn); + } + ast_str_append(a_buf, 0, "\r\n"); } @@ -12517,7 +12536,7 @@ } if (!doing_directmedia && ast_test_flag(&p->flags[2], SIP_PAGE3_ICE_SUPPORT)) { - add_ice_to_sdp(p->vrtp, &a_video); + add_ice_to_sdp(p->vrtp, sip_pvt_is_webrtc(p), &a_video); } } @@ -12534,7 +12553,7 @@ } if (!doing_directmedia && ast_test_flag(&p->flags[2], SIP_PAGE3_ICE_SUPPORT)) { - add_ice_to_sdp(p->trtp, &a_text); + add_ice_to_sdp(p->trtp, sip_pvt_is_webrtc(p), &a_text); } } @@ -12633,7 +12652,7 @@ ast_str_append(&a_text, 0, "a=ptime:%d\r\n", min_text_packet_size); if (!doing_directmedia && ast_test_flag(&p->flags[2], SIP_PAGE3_ICE_SUPPORT)) { - add_ice_to_sdp(p->rtp, &a_audio); + add_ice_to_sdp(p->rtp, sip_pvt_is_webrtc(p), &a_audio); } if (m_audio->len - m_audio->used < 2 || m_video->len - m_video->used < 2 || @@ -15416,6 +15435,9 @@ ast_string_field_set(peer, useragent, useragent); ast_verb(4, "Saved useragent \"%s\" for peer %s\n", peer->useragent, peer->name); } + + ast_verb(4, "User Agent transport = %s", get_transport_pvt(pvt)); + return PARSE_REGISTER_UPDATE; } @@ -32882,6 +32904,14 @@ return 0; } +static int sip_pvt_is_webrtc(struct sip_pvt *p) +{ + // FIXME: For now, we pretend that all clients connected using WebSocket transport are Chrome and we need to enable ICE-JINGLE + // instead of RFC 5245 + return (!strncasecmp(get_transport_pvt(p), "WSS", 3) || !strncasecmp(get_transport_pvt(p), "WS", 2)); + // return (p->useragent && strstr(p->useragent, "chrome")); +} + static const struct ast_data_handler peers_data_provider = { .version = AST_DATA_HANDLER_VERSION, .get = peers_data_provider_get Index: channels/sip/include/sip.h =================================================================== --- channels/sip/include/sip.h (revision 372708) +++ channels/sip/include/sip.h (working copy) @@ -1212,6 +1212,8 @@ struct ast_cc_config_params *cc_params; struct sip_epa_entry *epa_entry; int fromdomainport; /*!< Domain port to show in from field */ + + int is_webrtc; /*!< Whether this dialog is generated by a WebRTC client */ }; /*! \brief sip packet - raw format for outbound packets that are sent or scheduled for transmission Index: include/asterisk/rtp_engine.h =================================================================== --- include/asterisk/rtp_engine.h (revision 372708) +++ include/asterisk/rtp_engine.h (working copy) @@ -331,6 +331,7 @@ struct ast_sockaddr address; /*!< Address of the candidate */ struct ast_sockaddr relay_address; /*!< Relay address for the candidate */ enum ast_rtp_ice_candidate_type type; /*!< Type of candidate */ + int is_webrtc; /*!< Whether the candidate is generated from a bugus WebRTC client using ICE-JINGLE */ }; /*! \brief Structure that represents the optional ICE support within an RTP engine */ Index: res/pjproject/pjnath/include/pjnath/stun_auth.h =================================================================== --- res/pjproject/pjnath/include/pjnath/stun_auth.h (revision 372708) +++ res/pjproject/pjnath/include/pjnath/stun_auth.h (working copy) @@ -57,8 +57,14 @@ /** * Authentication using long term credential. */ - PJ_STUN_AUTH_LONG_TERM = 2 + PJ_STUN_AUTH_LONG_TERM = 2, + /** + * Chrome ICE implementation + */ + PJ_STUN_AUTH_WEBRTC = 3 + + } pj_stun_auth_type; @@ -80,8 +86,14 @@ * performing server side authentication where server does not know * in advance the identity of the user requesting authentication. */ - PJ_STUN_AUTH_CRED_DYNAMIC + PJ_STUN_AUTH_CRED_DYNAMIC, + + /** + * Chrome credential type (ICE-Jingle) + */ + PJ_STUN_AUTH_CRED_WEBRTC + } pj_stun_auth_cred_type; @@ -159,6 +171,20 @@ } static_cred; + struct + { + /** + * The username of the credential for outgoing messages. + */ + pj_str_t tx_username; + + /** + * The username of the credential for incoming messages. + */ + pj_str_t rx_username; + + } webrtc_cred; + /** * This structure contains callback to be called by the framework * to authenticate the incoming message. Index: res/pjproject/pjnath/include/pjnath/stun_session.h =================================================================== --- res/pjproject/pjnath/include/pjnath/stun_session.h (revision 372708) +++ res/pjproject/pjnath/include/pjnath/stun_session.h (working copy) @@ -751,6 +751,9 @@ pj_stun_tx_data *tdata); + +PJ_DECL(enum pj_stun_auth_type) pj_stun_session_get_auth_type(pj_stun_session *sess); + /** * @} */ Index: res/pjproject/pjnath/include/pjnath/stun_config.h =================================================================== --- res/pjproject/pjnath/include/pjnath/stun_config.h (revision 372708) +++ res/pjproject/pjnath/include/pjnath/stun_config.h (working copy) @@ -81,6 +81,13 @@ */ unsigned res_cache_msec; + /** + * Software name to be included in all STUN requests and responses. + * + * Default: PJNATH_STUN_SOFTWARE_NAME. + */ + pj_str_t software_name; + } pj_stun_config; @@ -102,6 +109,7 @@ cfg->timer_heap = timer_heap; cfg->rto_msec = PJ_STUN_RTO_VALUE; cfg->res_cache_msec = PJ_STUN_RES_CACHE_DURATION; + cfg->software_name = pj_str((char*)PJNATH_STUN_SOFTWARE_NAME); } Index: res/pjproject/pjnath/include/pjnath/ice_session.h =================================================================== --- res/pjproject/pjnath/include/pjnath/ice_session.h (revision 372708) +++ res/pjproject/pjnath/include/pjnath/ice_session.h (working copy) @@ -594,6 +594,11 @@ */ int controlled_agent_want_nom_timeout; + /** + * Specify whether remote peer is a WebRTC client. + */ + pj_bool_t is_webrtc; + } pj_ice_sess_options; Index: res/pjproject/pjnath/include/pjnath/config.h =================================================================== --- res/pjproject/pjnath/include/pjnath/config.h (revision 372708) +++ res/pjproject/pjnath/include/pjnath/config.h (working copy) @@ -487,6 +487,21 @@ # define PJNATH_POOL_INC_TURN_SOCK 1000 #endif +/** Default STUN software name */ +#ifndef PJNATH_STUN_SOFTWARE_NAME +# define PJNATH_MAKE_SW_NAME(a,b,c,d) "pjnath-" #a "." #b "." #c d +# define PJNATH_MAKE_SW_NAME2(a,b,c,d) PJNATH_MAKE_SW_NAME(a,b,c,d) +# define PJNATH_STUN_SOFTWARE_NAME PJNATH_MAKE_SW_NAME2( \ + PJ_VERSION_NUM_MAJOR, \ + PJ_VERSION_NUM_MINOR, \ + PJ_VERSION_NUM_REV, \ + PJ_VERSION_NUM_EXTRA) +#endif + +#ifndef PJ_STUN_ERROR_WEBRTC_NOTREADY +# define PJ_STUN_ERROR_WEBRTC_NOTREADY -3891 +#endif + /** * @} */ Index: res/pjproject/pjnath/include/pjnath/turn_sock.h =================================================================== --- res/pjproject/pjnath/include/pjnath/turn_sock.h (revision 372708) +++ res/pjproject/pjnath/include/pjnath/turn_sock.h (working copy) @@ -109,6 +109,13 @@ typedef struct pj_turn_sock_cfg { /** + * Packet buffer size. + * + * Default value is PJ_TURN_MAX_PKT_LEN. + */ + unsigned max_pkt_size; + + /** * QoS traffic type to be set on this transport. When application wants * to apply QoS tagging to the transport, it's preferable to set this * field rather than \a qos_param fields since this is more portable. Index: res/pjproject/pjnath/include/pjnath/stun_sock.h =================================================================== --- res/pjproject/pjnath/include/pjnath/stun_sock.h (revision 372708) +++ res/pjproject/pjnath/include/pjnath/stun_sock.h (working copy) @@ -218,7 +218,9 @@ typedef struct pj_stun_sock_cfg { /** - * Packet buffer size. Default value is PJ_STUN_SOCK_PKT_LEN. + * Packet buffer size. + * + * Default value is PJ_STUN_SOCK_PKT_LEN. */ unsigned max_pkt_size; Index: res/pjproject/pjnath/src/pjnath/ice_session.c =================================================================== --- res/pjproject/pjnath/src/pjnath/ice_session.c (revision 372708) +++ res/pjproject/pjnath/src/pjnath/ice_session.c (working copy) @@ -301,12 +301,15 @@ /* Init STUN authentication credential */ pj_bzero(&auth_cred, sizeof(auth_cred)); - auth_cred.type = PJ_STUN_AUTH_CRED_DYNAMIC; - auth_cred.data.dyn_cred.get_auth = &stun_auth_get_auth; - auth_cred.data.dyn_cred.get_cred = &stun_auth_get_cred; - auth_cred.data.dyn_cred.get_password = &stun_auth_get_password; - auth_cred.data.dyn_cred.user_data = comp->stun_sess; - pj_stun_session_set_credential(comp->stun_sess, PJ_STUN_AUTH_SHORT_TERM, + auth_cred.type = ice->opt.is_webrtc ? PJ_STUN_AUTH_CRED_WEBRTC : PJ_STUN_AUTH_CRED_DYNAMIC; + if(auth_cred.type == PJ_STUN_AUTH_CRED_DYNAMIC){ + auth_cred.data.dyn_cred.get_auth = &stun_auth_get_auth; + auth_cred.data.dyn_cred.get_cred = &stun_auth_get_cred; + auth_cred.data.dyn_cred.get_password = &stun_auth_get_password; + auth_cred.data.dyn_cred.user_data = comp->stun_sess; + } + + pj_stun_session_set_credential(comp->stun_sess, ice->opt.is_webrtc ? PJ_STUN_AUTH_WEBRTC : PJ_STUN_AUTH_SHORT_TERM, &auth_cred); return PJ_SUCCESS; @@ -320,6 +323,7 @@ opt->nominated_check_delay = PJ_ICE_NOMINATED_CHECK_DELAY; opt->controlled_agent_want_nom_timeout = ICE_CONTROLLED_AGENT_WAIT_NOMINATION_TIMEOUT; + opt->is_webrtc = PJ_TRUE; } /* @@ -435,8 +439,20 @@ PJ_DEF(pj_status_t) pj_ice_sess_set_options(pj_ice_sess *ice, const pj_ice_sess_options *opt) { + pj_bool_t update; PJ_ASSERT_RETURN(ice && opt, PJ_EINVAL); - pj_memcpy(&ice->opt, opt, sizeof(*opt)); + update = (ice->opt.is_webrtc != opt->is_webrtc); + pj_memcpy(&ice->opt, opt, sizeof(*opt)); + if(update){ + unsigned int i; + for (i=0; icomp_cnt; ++i) { + pj_ice_sess_comp *comp; + comp = &ice->comp[i]; + if(!comp->valid_check && !comp->nominated_check){ + init_comp(ice, i+1, comp); + } + } + } LOG5((ice->obj_name, "ICE nomination type set to %s", (ice->opt.aggressive ? "aggressive" : "regular"))); return PJ_SUCCESS; @@ -929,6 +945,10 @@ pj_ice_sess_check_state st, pj_status_t err_code) { + if(ice->opt.is_webrtc && check->state >= PJ_ICE_SESS_CHECK_STATE_SUCCEEDED){ + return;// return WebRTC keepAlive + } + pj_assert(check->state < PJ_ICE_SESS_CHECK_STATE_SUCCEEDED); LOG5((ice->obj_name, "Check %s: state changed from %s to %s", @@ -1150,6 +1170,7 @@ * Need to do it here just in case app destroy the session * in the callback. */ + if (ice->ice_status == PJ_SUCCESS) ice_keep_alive(ice, PJ_FALSE); @@ -1621,9 +1642,11 @@ /* Save credentials */ username.ptr = buf; - + pj_strcpy(&username, rem_ufrag); - pj_strcat2(&username, ":"); + if(!ice->opt.is_webrtc){ + pj_strcat2(&username, ":"); + } pj_strcat(&username, &ice->rx_ufrag); pj_strdup(ice->pool, &ice->tx_uname, &username); @@ -1631,7 +1654,9 @@ pj_strdup(ice->pool, &ice->tx_pass, rem_passwd); pj_strcpy(&username, &ice->rx_ufrag); - pj_strcat2(&username, ":"); + if(!ice->opt.is_webrtc){ + pj_strcat2(&username, ":"); + } pj_strcat(&username, rem_ufrag); pj_strdup(ice->pool, &ice->rx_uname, &username); @@ -1709,6 +1734,21 @@ } ice->comp_cnt = highest_comp; + // copy username for chrome authentication + if(ice->opt.is_webrtc){ + struct pj_stun_auth_cred auth_cred; + pj_bzero(&auth_cred, sizeof(auth_cred)); + auth_cred.type = PJ_STUN_AUTH_CRED_WEBRTC; + pj_strdup(ice->pool, &auth_cred.data.webrtc_cred.rx_username, &ice->rx_uname); + pj_strdup(ice->pool, &auth_cred.data.webrtc_cred.tx_username, &ice->tx_uname); + + for(i = 0; i < ice->comp_cnt ; ++i){ + pj_stun_session_set_credential(ice->comp[i].stun_sess, + PJ_STUN_AUTH_WEBRTC, + &auth_cred); + } + } + /* Init timer entry in the checklist. Initially the timer ID is FALSE * because timer is not running. */ @@ -2156,6 +2196,7 @@ pj_ice_sess_cand *lcand; pj_ice_sess_checklist *clist; pj_stun_xor_mapped_addr_attr *xaddr; + pj_stun_mapped_addr_attr *addr = 0; unsigned i; PJ_UNUSED_ARG(stun_sess); @@ -2167,6 +2208,10 @@ clist = msg_data->data.req.clist; check = &clist->checks[msg_data->data.req.ckid]; + //FIXME: Find why WebRTC keepalive requests cause tdata mismatch + if(tdata != check->tdata){ + return; + } /* Mark STUN transaction as complete */ pj_assert(tdata == check->tdata); @@ -2291,18 +2336,23 @@ /* Get the STUN XOR-MAPPED-ADDRESS attribute. */ xaddr = (pj_stun_xor_mapped_addr_attr*) pj_stun_msg_find_attr(response, PJ_STUN_ATTR_XOR_MAPPED_ADDR,0); - if (!xaddr) { - check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_FAILED, - PJNATH_ESTUNNOMAPPEDADDR); - on_check_complete(ice, check); - pj_mutex_unlock(ice->mutex); - return; + /* Chrome returns mapped address only */ + if (!xaddr && pj_stun_session_get_auth_type(stun_sess) == PJ_STUN_AUTH_WEBRTC) { + addr = (pj_stun_mapped_addr_attr*) pj_stun_msg_find_attr(response, PJ_STUN_ATTR_MAPPED_ADDR, 0); + } + + if (!xaddr && !addr) { + check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_FAILED, + PJNATH_ESTUNNOMAPPEDADDR); + on_check_complete(ice, check); + pj_mutex_unlock(ice->mutex); + return; } /* Find local candidate that matches the XOR-MAPPED-ADDRESS */ pj_assert(lcand == NULL); for (i=0; ilcand_cnt; ++i) { - if (sockaddr_cmp(&xaddr->sockaddr, &ice->lcand[i].addr) == 0) { + if ((xaddr && sockaddr_cmp(&xaddr->sockaddr, &ice->lcand[i].addr) == 0) || (addr && sockaddr_cmp(&addr->sockaddr, &ice->lcand[i].addr) == 0)) { /* Match */ lcand = &ice->lcand[i]; break; @@ -2335,7 +2385,7 @@ msg_data->transport_id, PJ_ICE_CAND_TYPE_PRFLX, 65535, &foundation, - &xaddr->sockaddr, + xaddr ? &xaddr->sockaddr : &addr->sockaddr, &check->lcand->base_addr, &check->lcand->base_addr, sizeof(pj_sockaddr_in), &cand_id); @@ -2461,7 +2511,7 @@ /* Get PRIORITY attribute */ prio_attr = (pj_stun_priority_attr*) pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_PRIORITY, 0); - if (prio_attr == NULL) { + if (prio_attr == NULL && pj_stun_session_get_auth_type(sess) != PJ_STUN_AUTH_WEBRTC) { LOG5((ice->obj_name, "Received Binding request with no PRIORITY")); pj_mutex_unlock(ice->mutex); return PJ_SUCCESS; @@ -2544,6 +2594,11 @@ PJ_STUN_ATTR_XOR_MAPPED_ADDR, PJ_TRUE, src_addr, src_addr_len); + /* Add MAPPED-ADDRESS attribute */ + status = pj_stun_msg_add_sockaddr_attr(tdata->pool, tdata->msg, + PJ_STUN_ATTR_MAPPED_ADDR, + PJ_FALSE, src_addr, src_addr_len); + /* Create a msg_data to be associated with this response */ msg_data = PJ_POOL_ZALLOC_T(tdata->pool, pj_ice_msg_data); msg_data->transport_id = ((pj_ice_msg_data*)token)->transport_id; @@ -2574,7 +2629,9 @@ rcheck->src_addr_len = src_addr_len; pj_memcpy(&rcheck->src_addr, src_addr, src_addr_len); rcheck->use_candidate = (uc_attr != NULL); - rcheck->priority = prio_attr->value; + if(prio_attr){ + rcheck->priority = prio_attr->value; + } rcheck->role_attr = role_attr; if (ice->rcand_cnt == 0) { @@ -2752,6 +2809,29 @@ pj_bool_t complete; unsigned j; + // WebRTC STUN refreshness + if(ice->opt.is_webrtc && PJ_FALSE){ // FIXME: No longer needed in newest chrome and make Asterisk to overrun + pj_status_t status; + pj_ice_msg_data* msg_data; + + status = pj_stun_session_create_req(comp->stun_sess, + PJ_STUN_BINDING_REQUEST, PJ_STUN_MAGIC, + NULL, &c->tdata); + if(status == PJ_SUCCESS){ + msg_data = PJ_POOL_ZALLOC_T(c->tdata->pool, pj_ice_msg_data); + if(msg_data){ + msg_data->transport_id = lcand->transport_id; + msg_data->has_req_data = PJ_TRUE; + msg_data->data.req.ice = ice; + msg_data->data.req.clist = &ice->clist; + msg_data->data.req.ckid = i; + status = pj_stun_session_send_msg(comp->stun_sess, msg_data, PJ_FALSE, + PJ_TRUE, &rcand->addr, + sizeof(pj_sockaddr_in), c->tdata); + } + } + } + /* If this check is nominated, scan the valid_list for the * same check and update the nominated flag. A controlled * agent might have finished the check earlier. @@ -2785,7 +2865,7 @@ } } - } + } /* If the pair is not already on the check list: * - The pair is inserted into the check list based on its priority. * - Its state is set to In-Progress Index: res/pjproject/pjnath/src/pjnath/turn_sock.c =================================================================== --- res/pjproject/pjnath/src/pjnath/turn_sock.c (revision 372708) +++ res/pjproject/pjnath/src/pjnath/turn_sock.c (working copy) @@ -97,6 +97,7 @@ PJ_DEF(void) pj_turn_sock_cfg_default(pj_turn_sock_cfg *cfg) { pj_bzero(cfg, sizeof(*cfg)); + cfg->max_pkt_size = PJ_TURN_MAX_PKT_LEN; cfg->qos_type = PJ_QOS_TYPE_BEST_EFFORT; cfg->qos_ignore_error = PJ_TRUE; } @@ -210,6 +211,7 @@ } if (turn_sock->active_sock) { + pj_activesock_set_user_data(turn_sock->active_sock, NULL); pj_activesock_close(turn_sock->active_sock); turn_sock->active_sock = NULL; } @@ -462,7 +464,17 @@ pj_turn_sock *turn_sock; turn_sock = (pj_turn_sock*) pj_activesock_get_user_data(asock); + if (!turn_sock) + return PJ_FALSE; + /* TURN session may have already been destroyed here. + * See ticket #1557 (http://trac.pjsip.org/repos/ticket/1557). + */ + if (!turn_sock->sess) { + sess_fail(turn_sock, "TURN session already destroyed", status); + return PJ_FALSE; + } + if (status != PJ_SUCCESS) { sess_fail(turn_sock, "TCP connect() error", status); return PJ_FALSE; @@ -474,7 +486,7 @@ /* Kick start pending read operation */ status = pj_activesock_start_read(asock, turn_sock->pool, - PJ_TURN_MAX_PKT_LEN, 0); + turn_sock->setting.max_pkt_size, 0); /* Init send_key */ pj_ioqueue_op_key_init(&turn_sock->send_key, sizeof(turn_sock->send_key)); Index: res/pjproject/pjnath/src/pjnath/ice_strans.c =================================================================== --- res/pjproject/pjnath/src/pjnath/ice_strans.c (revision 372708) +++ res/pjproject/pjnath/src/pjnath/ice_strans.c (working copy) @@ -503,6 +503,13 @@ add_update_turn(ice_st, comp); } + /* It's possible that we end up without any candidates */ + if (comp->cand_cnt == 0) { + PJ_LOG(4,(ice_st->obj_name, + "Error: no candidate is created due to settings")); + return PJ_EINVAL; + } + return PJ_SUCCESS; } @@ -1145,6 +1152,8 @@ */ PJ_DEF(pj_status_t) pj_ice_strans_stop_ice(pj_ice_strans *ice_st) { + PJ_ASSERT_RETURN(ice_st, PJ_EINVAL); + if (ice_st->ice) { pj_ice_sess_destroy(ice_st->ice); ice_st->ice = NULL; @@ -1546,7 +1555,7 @@ if (comp->default_cand > idx) { --comp->default_cand; } else if (comp->default_cand == idx) { - comp->default_cand = !idx; + comp->default_cand = 0; } /* Remove srflx candidate */ @@ -1574,7 +1583,7 @@ /* May not have cand, e.g. when error during init */ if (cand) cand->status = status; - if (!ice_st->cfg.stun.ignore_stun_error) { + if (!ice_st->cfg.stun.ignore_stun_error || comp->cand_cnt==1) { sess_fail(ice_st, PJ_ICE_STRANS_OP_INIT, "STUN binding request failed", status); } else { Index: res/pjproject/pjnath/src/pjnath/stun_auth.c =================================================================== --- res/pjproject/pjnath/src/pjnath/stun_auth.c (revision 372708) +++ res/pjproject/pjnath/src/pjnath/stun_auth.c (working copy) @@ -34,25 +34,31 @@ pj_stun_auth_cred *dst, const pj_stun_auth_cred *src) { - dst->type = src->type; + dst->type = src->type; - switch (src->type) { - case PJ_STUN_AUTH_CRED_STATIC: - pj_strdup(pool, &dst->data.static_cred.realm, - &src->data.static_cred.realm); - pj_strdup(pool, &dst->data.static_cred.username, - &src->data.static_cred.username); - dst->data.static_cred.data_type = src->data.static_cred.data_type; - pj_strdup(pool, &dst->data.static_cred.data, - &src->data.static_cred.data); - pj_strdup(pool, &dst->data.static_cred.nonce, - &src->data.static_cred.nonce); - break; - case PJ_STUN_AUTH_CRED_DYNAMIC: - pj_memcpy(&dst->data.dyn_cred, &src->data.dyn_cred, - sizeof(src->data.dyn_cred)); - break; - } + switch (src->type) { + case PJ_STUN_AUTH_CRED_WEBRTC: + pj_strdup(pool, &dst->data.webrtc_cred.rx_username, + &src->data.webrtc_cred.rx_username); + pj_strdup(pool, &dst->data.webrtc_cred.tx_username, + &src->data.webrtc_cred.tx_username); + break; + case PJ_STUN_AUTH_CRED_STATIC: + pj_strdup(pool, &dst->data.static_cred.realm, + &src->data.static_cred.realm); + pj_strdup(pool, &dst->data.static_cred.username, + &src->data.static_cred.username); + dst->data.static_cred.data_type = src->data.static_cred.data_type; + pj_strdup(pool, &dst->data.static_cred.data, + &src->data.static_cred.data); + pj_strdup(pool, &dst->data.static_cred.nonce, + &src->data.static_cred.nonce); + break; + case PJ_STUN_AUTH_CRED_DYNAMIC: + pj_memcpy(&dst->data.dyn_cred, &src->data.dyn_cred, + sizeof(src->data.dyn_cred)); + break; + } } @@ -231,7 +237,7 @@ pj_uint8_t digest[PJ_SHA1_DIGEST_SIZE]; pj_stun_status err_code; const char *err_text = NULL; - pj_status_t status; + pj_status_t status; /* msg and credential MUST be specified */ PJ_ASSERT_RETURN(pkt && pkt_len && msg && cred, PJ_EINVAL); @@ -252,7 +258,10 @@ /* Get realm and nonce from credential */ p_info->realm.slen = p_info->nonce.slen = 0; - if (cred->type == PJ_STUN_AUTH_CRED_STATIC) { + if(cred->type == PJ_STUN_AUTH_CRED_WEBRTC){ + /* no realm or nonce for webrtc */ + } + else if (cred->type == PJ_STUN_AUTH_CRED_STATIC) { p_info->realm = cred->data.static_cred.realm; p_info->nonce = cred->data.static_cred.nonce; } else if (cred->type == PJ_STUN_AUTH_CRED_DYNAMIC) { @@ -318,7 +327,20 @@ } /* Check if username match */ - if (cred->type == PJ_STUN_AUTH_CRED_STATIC) { + if(cred->type == PJ_STUN_AUTH_CRED_WEBRTC){ + pj_bool_t username_ok = !pj_strcmp(&auser->value, &cred->data.webrtc_cred.rx_username); + if (username_ok) { + pj_strdup(pool, &p_info->username, + &cred->data.webrtc_cred.rx_username); + //pj_stun_create_key(pool, &p_info->auth_key, &p_info->realm, + // &auser->value, cred->data.webrtc_cred.data_type, + // &cred->data.webrtc_cred.data); + // For webrtc do not check other fields + return PJ_SUCCESS; + } + err_code = PJ_STUN_SC_UNAUTHORIZED; + goto on_auth_failed; + } else if (cred->type == PJ_STUN_AUTH_CRED_STATIC) { pj_bool_t username_ok; username_ok = !pj_strcmp(&auser->value, &cred->data.static_cred.username); Index: res/pjproject/pjnath/src/pjnath/turn_session.c =================================================================== --- res/pjproject/pjnath/src/pjnath/turn_session.c (revision 372708) +++ res/pjproject/pjnath/src/pjnath/turn_session.c (working copy) @@ -421,7 +421,10 @@ /* This may recursively call this function again with * state==PJ_TURN_STATE_DEALLOCATED. */ + /* No need to deallocate as we're already deallocating! + * See https://trac.pjsip.org/repos/ticket/1551 send_refresh(sess, 0); + */ break; case PJ_TURN_STATE_DEALLOCATED: case PJ_TURN_STATE_DESTROYING: Index: res/pjproject/pjnath/src/pjnath/stun_session.c =================================================================== --- res/pjproject/pjnath/src/pjnath/stun_session.c (revision 372708) +++ res/pjproject/pjnath/src/pjnath/stun_session.c (working copy) @@ -500,11 +500,9 @@ pj_memcpy(&sess->cb, cb, sizeof(*cb)); sess->use_fingerprint = fingerprint; sess->log_flag = 0xFFFF; - - sess->srv_name.ptr = (char*) pj_pool_alloc(pool, 32); - sess->srv_name.slen = pj_ansi_snprintf(sess->srv_name.ptr, 32, - "pjnath-%s", pj_get_version()); + pj_stun_session_set_software_name(sess, &cfg->software_name); + sess->rx_pool = pj_pool_create(sess->cfg->pf, name, PJNATH_POOL_LEN_STUN_TDATA, PJNATH_POOL_INC_STUN_TDATA, NULL); @@ -657,7 +655,10 @@ static pj_status_t get_auth(pj_stun_session *sess, pj_stun_tx_data *tdata) { - if (sess->cred.type == PJ_STUN_AUTH_CRED_STATIC) { + if(sess->cred.type == PJ_STUN_AUTH_CRED_WEBRTC){ + tdata->auth_info.username = sess->cred.data.webrtc_cred.tx_username; + } + else if (sess->cred.type == PJ_STUN_AUTH_CRED_STATIC) { //tdata->auth_info.realm = sess->cred.data.static_cred.realm; tdata->auth_info.realm = sess->server_realm; tdata->auth_info.username = sess->cred.data.static_cred.username; @@ -728,9 +729,9 @@ /* Get authentication information for the request */ if (sess->auth_type == PJ_STUN_AUTH_NONE) { - /* No authentication */ + /* No authentication or chrome auth */ - } else if (sess->auth_type == PJ_STUN_AUTH_SHORT_TERM) { + } else if (sess->auth_type == PJ_STUN_AUTH_SHORT_TERM || sess->auth_type == PJ_STUN_AUTH_WEBRTC) { /* MUST put authentication in request */ status = get_auth(sess, tdata); if (status != PJ_SUCCESS) { @@ -821,6 +822,14 @@ /* copy the credential found in the request */ pj_stun_req_cred_info_dup(tdata->pool, &tdata->auth_info, &rdata->info); + /* Add USERNAME */ + if(sess->cred.type == PJ_STUN_AUTH_CRED_WEBRTC){ + status = pj_stun_msg_add_string_attr(tdata->pool, tdata->msg, + PJ_STUN_ATTR_USERNAME, + &sess->cred.data.webrtc_cred.rx_username); + PJ_ASSERT_RETURN(status==PJ_SUCCESS, status); + } + *p_tdata = tdata; return PJ_SUCCESS; @@ -1138,11 +1147,17 @@ { return PJ_SUCCESS; } - + status = pj_stun_authenticate_request(pkt, pkt_len, rdata->msg, &sess->cred, tmp_pool, &rdata->info, &response); if (status != PJ_SUCCESS && response != NULL) { + if(sess->cred.type == PJ_STUN_AUTH_CRED_WEBRTC){ + // Fix for http://code.google.com/p/sipml5/issues/detail?id=36 + // Do not send error to chrome + return (!sess->cred.data.webrtc_cred.rx_username.slen || !sess->cred.data.webrtc_cred.tx_username.slen) ? PJ_STUN_ERROR_WEBRTC_NOTREADY : PJ_SUCCESS; + } + PJ_LOG(5,(SNAME(sess), "Message authentication failed")); send_response(sess, token, tmp_pool, response, &rdata->info, PJ_FALSE, src_addr, src_addr_len); @@ -1174,7 +1189,7 @@ if (sess->auth_type == PJ_STUN_AUTH_NONE) options |= PJ_STUN_NO_AUTHENTICATE; - + /* Authenticate the message, unless PJ_STUN_NO_AUTHENTICATE * is specified in the option. */ @@ -1261,7 +1276,7 @@ if (sess->auth_type == PJ_STUN_AUTH_NONE) options |= PJ_STUN_NO_AUTHENTICATE; - + /* Authenticate the message, unless PJ_STUN_NO_AUTHENTICATE * is specified in the option. */ @@ -1434,3 +1449,7 @@ return status; } +enum pj_stun_auth_type pj_stun_session_get_auth_type(pj_stun_session *sess) +{ + return sess->auth_type; +} \ No newline at end of file Index: res/res_rtp_asterisk.c =================================================================== --- res/res_rtp_asterisk.c (revision 372708) +++ res/res_rtp_asterisk.c (working copy) @@ -411,6 +411,7 @@ ast_sockaddr_copy(&remote_candidate->address, &candidate->address); ast_sockaddr_copy(&remote_candidate->relay_address, &candidate->relay_address); remote_candidate->type = candidate->type; + remote_candidate->is_webrtc = candidate->is_webrtc; ao2_link(rtp->remote_candidates, remote_candidate); ao2_ref(remote_candidate, -1); @@ -462,6 +463,7 @@ struct ao2_iterator i; struct ast_rtp_engine_ice_candidate *candidate; int cand_cnt = 0; + int is_webrtc = 0; if (!rtp->ice || !rtp->remote_candidates || rtp->ice_started) { return; @@ -498,11 +500,31 @@ pj_turn_sock_set_perm(rtp->turn_rtcp, 1, &candidates[cand_cnt].addr, 1); } + // WebRTC candidates if at least one candidate is marked as it + if(candidate->is_webrtc){ + is_webrtc = 1; + } + cand_cnt++; } ao2_iterator_destroy(&i); + + // WebRTC option is enabled by default and have to be disabled if not needed + { + pj_status_t status; + pj_ice_sess_options opt; + status = pj_ice_sess_get_options(rtp->ice, &opt); + if(status == PJ_SUCCESS){ + opt.is_webrtc = is_webrtc; + status = pj_ice_sess_set_options(rtp->ice, &opt); + if(status != PJ_SUCCESS){ + // Print Error message + } + } + } + if (pj_ice_sess_create_check_list(rtp->ice, &ufrag, &passwd, ao2_container_count(rtp->remote_candidates), &candidates[0]) == PJ_SUCCESS) { pj_ice_sess_start_check(rtp->ice); pj_timer_heap_poll(timerheap, NULL); @@ -891,7 +913,7 @@ status = pj_ice_sess_on_rx_pkt(rtp->ice, rtcp ? COMPONENT_RTCP : COMPONENT_RTP, rtcp ? TRANSPORT_SOCKET_RTCP : TRANSPORT_SOCKET_RTP, buf, len, &address, pj_sockaddr_get_len(&address)); - if (status != PJ_SUCCESS) { + if (status != PJ_SUCCESS && status != PJ_STUN_ERROR_WEBRTC_NOTREADY) { char buf[100]; pj_strerror(status, buf, sizeof(buf));