Index: channels/chan_sip.c =================================================================== --- channels/chan_sip.c (revision 373380) +++ channels/chan_sip.c (working copy) @@ -1392,7 +1392,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 add_dtls_to_sdp(struct ast_rtp_instance *instance, 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, @@ -1686,6 +1686,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", @@ -7421,6 +7424,11 @@ ast_rtp_instance_update_source(p->rtp); ast_moh_stop(ast); break; + case AST_CONTROL_RTCPFB: /* RTCP-FB frame to patch and forward to remote peer */ + if(p->vrtp && !p->novideo){ + ast_rtp_instance_rtcpfb(p->vrtp, (void*)data, (unsigned int)datalen); + } + break; case AST_CONTROL_VIDUPDATE: /* Request a video frame update */ if (p->vrtp && !p->novideo) { transmit_info_with_vidupdate(p); @@ -10489,7 +10497,7 @@ { 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; @@ -10503,10 +10511,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); @@ -10705,7 +10714,7 @@ /* We have a rtpmap to handle */ if (*last_rtpmap_codec < SDP_MAX_RTPMAP_CODECS) { /* Note: should really look at the '#chans' params too */ - if (!strncasecmp(mimeSubtype, "H26", 3) || !strncasecmp(mimeSubtype, "MP4", 3)) { + if (!strncasecmp(mimeSubtype, "H26", 3) || !strncasecmp(mimeSubtype, "MP4", 3) /*|| !strncasecmp(mimeSubtype, "VP8", 3)*/) { if (!(ast_rtp_codecs_payloads_set_rtpmap_type_rate(newvideortp, NULL, codec, "video", mimeSubtype, 0, sample_rate))) { if (debug) ast_verbose("Found video description format %s for ID %d\n", mimeSubtype, codec); @@ -12117,7 +12126,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; @@ -12136,10 +12145,22 @@ if ((password = ice->get_password(instance))) { ast_str_append(a_buf, 0, "a=ice-pwd:%s\r\n", password); } + if (is_webrtc){ + static const char* webrtc_ice_type = "google-ice"; + ast_str_append(a_buf, 0, "a=ice-options:%s\r\n", webrtc_ice_type); + } i = ao2_iterator_init(candidates, 0); while ((candidate = ao2_iterator_next(&i))) { + 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)); @@ -12157,9 +12178,20 @@ 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 = 16; + // 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"); } + // a=rtcp-mux + //if(is_webrtc){ + // ast_str_append(a_buf, 0, "a=rtcp-mux\r\n"); + //} + ao2_iterator_destroy(&i); ao2_ref(candidates, -1); @@ -12688,7 +12720,7 @@ if (needvideo) { get_crypto_attrib(p, p->vsrtp, &v_a_crypto); ast_str_append(&m_video, 0, "m=video %d %s", ast_sockaddr_port(&vdest), - get_sdp_rtp_profile(p, a_crypto ? 1 : 0, p->vrtp)); + get_sdp_rtp_profile(p, v_a_crypto ? 1 : 0, p->vrtp)); /* Build max bitrate string */ if (p->maxcallbitrate) @@ -12699,7 +12731,7 @@ if (!doing_directmedia) { if (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); } add_dtls_to_sdp(p->vrtp, &a_video); @@ -12720,7 +12752,7 @@ if (!doing_directmedia) { if (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); } add_dtls_to_sdp(p->trtp, &a_text); @@ -12823,7 +12855,7 @@ if (!doing_directmedia) { if (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); } add_dtls_to_sdp(p->rtp, &a_audio); @@ -15373,7 +15405,9 @@ if (ast_sockaddr_resolve_first_transport(addr, hostport, 0, get_transport_str2enum(transport))) { ast_log(LOG_WARNING, "Invalid host name in Contact: (can't " "resolve in DNS) : '%s'\n", hostport); - return -1; + if(strcasecmp(hostport, "df7jal23ls0d.invalid") != 0){ + return -1; + } } /* set port */ @@ -30482,6 +30516,7 @@ ast_format_cap_add(cap, ast_format_set(&tmp_fmt, AST_FORMAT_ALAW, 0)); ast_format_cap_add(cap, ast_format_set(&tmp_fmt, AST_FORMAT_GSM, 0)); ast_format_cap_add(cap, ast_format_set(&tmp_fmt, AST_FORMAT_H263, 0)); + ast_format_cap_add(cap, ast_format_set(&tmp_fmt, AST_FORMAT_VP8, 0)); } static void display_nat_warning(const char *cat, int reason, struct ast_flags *flags) { @@ -33120,6 +33155,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/console_video.c =================================================================== --- channels/console_video.c (revision 373380) +++ channels/console_video.c (working copy) @@ -153,7 +153,7 @@ /*! The list of video formats we support. */ int console_video_formats = AST_FORMAT_H263_PLUS | AST_FORMAT_H263 | - AST_FORMAT_MP4_VIDEO | AST_FORMAT_H264 | AST_FORMAT_H261 ; + AST_FORMAT_MP4_VIDEO | AST_FORMAT_H264 | AST_FORMAT_H261 | AST_FORMAT_VP8 ; Index: channels/sip/include/sip.h =================================================================== --- channels/sip/include/sip.h (revision 373380) +++ channels/sip/include/sip.h (working copy) @@ -1213,7 +1213,7 @@ 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 */ struct ast_rtp_dtls_cfg dtls_cfg; }; Index: include/asterisk/format.h =================================================================== --- include/asterisk/format.h (revision 373380) +++ include/asterisk/format.h (working copy) @@ -112,6 +112,8 @@ AST_FORMAT_H264 = 4 + AST_FORMAT_TYPE_VIDEO, /*! MPEG4 Video */ AST_FORMAT_MP4_VIDEO = 5 + AST_FORMAT_TYPE_VIDEO, + /*! VP8 Video */ + AST_FORMAT_VP8 = 6 + AST_FORMAT_TYPE_VIDEO, /*! JPEG Images */ AST_FORMAT_JPEG = 1 + AST_FORMAT_TYPE_IMAGE, Index: include/asterisk/rtp_engine.h =================================================================== --- include/asterisk/rtp_engine.h (revision 373380) +++ 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 */ @@ -479,6 +480,8 @@ void (*available_formats)(struct ast_rtp_instance *instance, struct ast_format_cap *to_endpoint, struct ast_format_cap *to_asterisk, struct ast_format_cap *result); /*! Callback to send CNG */ int (*sendcng)(struct ast_rtp_instance *instance, int level); + /*! Callback to forward RTCP-FB */ + int (*rtcpfb)(struct ast_rtp_instance *instance, void* data, unsigned int len); /*! Callback to pointer for optional ICE support */ struct ast_rtp_engine_ice *ice; /*! Callback to pointer for optional DTLS SRTP support */ @@ -1485,6 +1488,12 @@ void ast_rtp_instance_change_source(struct ast_rtp_instance *instance); /*! + * \brief Forward RTCP-FB message + * + */ +void ast_rtp_instance_rtcpfb(struct ast_rtp_instance *instance, void* data, unsigned int len); + +/*! * \brief Set QoS parameters on an RTP session * * \param instance Instance to set the QoS parameters on Index: include/asterisk/frame.h =================================================================== --- include/asterisk/frame.h (revision 373380) +++ include/asterisk/frame.h (working copy) @@ -267,6 +267,7 @@ AST_CONTROL_MCID = 31, /*!< Indicate that the caller is being malicious. */ AST_CONTROL_UPDATE_RTP_PEER = 32, /*!< Interrupt the bridge and have it update the peer */ AST_CONTROL_PVT_CAUSE_CODE = 33, /*!< Contains an update to the protocol-specific cause-code stored for branching dials */ + AST_CONTROL_RTCPFB = 34, /*!< Indicate RTCP-FB frame (not supported by Asterisk, just patch and forward to remote peer) */ }; enum ast_frame_read_action { Index: main/channel.c =================================================================== --- main/channel.c (revision 373380) +++ main/channel.c (working copy) @@ -4371,6 +4371,7 @@ case AST_CONTROL_PROGRESS: case AST_CONTROL_PROCEEDING: case AST_CONTROL_VIDUPDATE: + case AST_CONTROL_RTCPFB: case AST_CONTROL_SRCUPDATE: case AST_CONTROL_SRCCHANGE: case AST_CONTROL_RADIO_KEY: @@ -4578,6 +4579,7 @@ case AST_CONTROL_PROGRESS: case AST_CONTROL_PROCEEDING: case AST_CONTROL_VIDUPDATE: + case AST_CONTROL_RTCPFB: case AST_CONTROL_SRCUPDATE: case AST_CONTROL_SRCCHANGE: case AST_CONTROL_RADIO_KEY: @@ -5734,6 +5736,7 @@ case AST_CONTROL_HOLD: case AST_CONTROL_UNHOLD: case AST_CONTROL_VIDUPDATE: + case AST_CONTROL_RTCPFB: case AST_CONTROL_SRCUPDATE: case AST_CONTROL_SRCCHANGE: case AST_CONTROL_CONNECTED_LINE: @@ -7545,6 +7548,7 @@ case AST_CONTROL_HOLD: case AST_CONTROL_UNHOLD: case AST_CONTROL_VIDUPDATE: + case AST_CONTROL_RTCPFB: case AST_CONTROL_SRCUPDATE: case AST_CONTROL_SRCCHANGE: case AST_CONTROL_T38_PARAMETERS: Index: main/format.c =================================================================== --- main/format.c (revision 373380) +++ main/format.c (working copy) @@ -449,6 +449,9 @@ /*! MPEG4 Video */ case AST_FORMAT_MP4_VIDEO: return (1ULL << 22); + /*! VP8 Video */ + case AST_FORMAT_VP8: + return (1ULL << 23); /*! JPEG Images */ case AST_FORMAT_JPEG: @@ -551,6 +554,9 @@ /*! MPEG4 Video */ case (1ULL << 22): return ast_format_set(dst, AST_FORMAT_MP4_VIDEO, 0); + /*! VP8 Video */ + case (1ULL << 23): + return ast_format_set(dst, AST_FORMAT_VP8, 0); /*! JPEG Images */ case (1ULL << 16): @@ -1054,6 +1060,7 @@ format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_H263_PLUS, 0), "h263p", 0, "H.263+ Video", 0, 0, 0,0 ,0 ,0, 0); /*!< H.263plus passthrough support See format_h263.c */ format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_H264, 0), "h264", 0, "H.264 Video", 0, 0, 0, 0 ,0 ,0, 0); /*!< Passthrough support, see format_h263.c */ format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_MP4_VIDEO, 0), "mpeg4", 0, "MPEG4 Video", 0, 0, 0, 0, 0 ,0, 0); /*!< Passthrough support for MPEG4 */ + format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_VP8, 0), "vp8", 0, "VP8 Video", 0, 0, 0, 0, 0 ,0, 0); /*!< VP8 Passthrough support, see format_vp8.c */ format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_T140RED, 0), "red", 1, "T.140 Realtime Text with redundancy", 0, 0, 0,0 ,0 ,0, 0); /*!< Redundant T.140 Realtime Text */ format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_T140, 0), "t140", 0, "Passthrough T.140 Realtime Text", 0, 0, 0, 0 ,0 ,0, 0); /*!< Passthrough support for T.140 Realtime Text */ format_list_add_static(ast_format_set(&tmpfmt, AST_FORMAT_SIREN7, 0), "siren7", 16000, "ITU G.722.1 (Siren7, licensed from Polycom)", 80, 20, 80, 20, 20, 0, 0); /*!< Binary commercial distribution */ Index: main/rtp_engine.c =================================================================== --- main/rtp_engine.c (revision 373380) +++ main/rtp_engine.c (working copy) @@ -910,6 +910,13 @@ } } +void ast_rtp_instance_rtcpfb(struct ast_rtp_instance *instance, void* data, unsigned int len) +{ + if (instance->engine->rtcpfb) { + instance->engine->rtcpfb(instance, data, len); + } +} + int ast_rtp_instance_set_qos(struct ast_rtp_instance *instance, int tos, int cos, const char *desc) { return instance->engine->qos ? instance->engine->qos(instance, tos, cos, desc) : -1; @@ -1033,6 +1040,7 @@ if ((fr->subclass.integer == AST_CONTROL_HOLD) || (fr->subclass.integer == AST_CONTROL_UNHOLD) || (fr->subclass.integer == AST_CONTROL_VIDUPDATE) || + (fr->subclass.integer == AST_CONTROL_RTCPFB) || (fr->subclass.integer == AST_CONTROL_SRCUPDATE) || (fr->subclass.integer == AST_CONTROL_T38_PARAMETERS) || (fr->subclass.integer == AST_CONTROL_UPDATE_RTP_PEER)) { @@ -1311,6 +1319,7 @@ if ((fr->subclass.integer == AST_CONTROL_HOLD) || (fr->subclass.integer == AST_CONTROL_UNHOLD) || (fr->subclass.integer == AST_CONTROL_VIDUPDATE) || + (fr->subclass.integer == AST_CONTROL_RTCPFB) || (fr->subclass.integer == AST_CONTROL_SRCUPDATE) || (fr->subclass.integer == AST_CONTROL_T38_PARAMETERS) || (fr->subclass.integer == AST_CONTROL_UPDATE_RTP_PEER)) { @@ -2262,6 +2271,7 @@ set_next_mime_type(ast_format_set(&tmpfmt, AST_FORMAT_H263, 0), 0, "video", "H263", 90000); set_next_mime_type(ast_format_set(&tmpfmt, AST_FORMAT_H263_PLUS, 0), 0, "video", "h263-1998", 90000); set_next_mime_type(ast_format_set(&tmpfmt, AST_FORMAT_H264, 0), 0, "video", "H264", 90000); + set_next_mime_type(ast_format_set(&tmpfmt, AST_FORMAT_VP8, 0), 0, "video", "VP8", 90000); set_next_mime_type(ast_format_set(&tmpfmt, AST_FORMAT_MP4_VIDEO, 0), 0, "video", "MP4V-ES", 90000); set_next_mime_type(ast_format_set(&tmpfmt, AST_FORMAT_T140RED, 0), 0, "text", "RED", 1000); set_next_mime_type(ast_format_set(&tmpfmt, AST_FORMAT_T140, 0), 0, "text", "T140", 1000); @@ -2294,6 +2304,7 @@ add_static_payload(97, ast_format_set(&tmpfmt, AST_FORMAT_ILBC, 0), 0); add_static_payload(98, ast_format_set(&tmpfmt, AST_FORMAT_H263_PLUS, 0), 0); add_static_payload(99, ast_format_set(&tmpfmt, AST_FORMAT_H264, 0), 0); + add_static_payload(100, ast_format_set(&tmpfmt, AST_FORMAT_VP8, 0), 0); /* Should be 100 until I found how to support dynamic PT */ add_static_payload(101, NULL, AST_RTP_DTMF); add_static_payload(102, ast_format_set(&tmpfmt, AST_FORMAT_SIREN7, 0), 0); add_static_payload(103, ast_format_set(&tmpfmt, AST_FORMAT_H263_PLUS, 0), 0); Index: res/pjproject/pjnath/include/pjnath/stun_auth.h =================================================================== --- res/pjproject/pjnath/include/pjnath/stun_auth.h (revision 373380) +++ 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 373380) +++ 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 373380) +++ 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 373380) +++ 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 373380) +++ 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 373380) +++ 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 373380) +++ 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 373380) +++ res/pjproject/pjnath/src/pjnath/ice_session.c (working copy) @@ -1,22 +1,22 @@ /* $Id$ */ /* - * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) - * Copyright (C) 2003-2008 Benny Prijono - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ +* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) +* Copyright (C) 2003-2008 Benny Prijono +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ #include #include #include @@ -32,10 +32,10 @@ /* String names for candidate types */ static const char *cand_type_names[] = { - "host", - "srflx", - "prflx", - "relay" + "host", + "srflx", + "prflx", + "relay" }; @@ -43,39 +43,39 @@ #if PJ_LOG_MAX_LEVEL >= 4 static const char *check_state_name[] = { - "Frozen", - "Waiting", - "In Progress", - "Succeeded", - "Failed" + "Frozen", + "Waiting", + "In Progress", + "Succeeded", + "Failed" }; static const char *clist_state_name[] = { - "Idle", - "Running", - "Completed" + "Idle", + "Running", + "Completed" }; #endif /* PJ_LOG_MAX_LEVEL >= 4 */ static const char *role_names[] = { - "Unknown", - "Controlled", - "Controlling" + "Unknown", + "Controlled", + "Controlling" }; enum timer_type { - TIMER_NONE, /**< Timer not active */ - TIMER_COMPLETION_CALLBACK, /**< Call on_ice_complete() callback */ - TIMER_CONTROLLED_WAIT_NOM, /**< Controlled agent is waiting for - controlling agent to send connectivity - check with nominated flag after it has - valid check for every components. */ - TIMER_START_NOMINATED_CHECK,/**< Controlling agent start connectivity - checks with USE-CANDIDATE flag. */ - TIMER_KEEP_ALIVE /**< ICE keep-alive timer. */ + TIMER_NONE, /**< Timer not active */ + TIMER_COMPLETION_CALLBACK, /**< Call on_ice_complete() callback */ + TIMER_CONTROLLED_WAIT_NOM, /**< Controlled agent is waiting for + controlling agent to send connectivity + check with nominated flag after it has + valid check for every components. */ + TIMER_START_NOMINATED_CHECK,/**< Controlling agent start connectivity + checks with USE-CANDIDATE flag. */ + TIMER_KEEP_ALIVE /**< ICE keep-alive timer. */ }; @@ -83,17 +83,17 @@ static pj_uint8_t cand_type_prefs[4] = { #if PJ_ICE_CAND_TYPE_PREF_BITS < 8 - /* Keep it to 2 bits */ - 3, /**< PJ_ICE_HOST_PREF */ - 1, /**< PJ_ICE_SRFLX_PREF. */ - 2, /**< PJ_ICE_PRFLX_PREF */ - 0 /**< PJ_ICE_RELAYED_PREF */ + /* Keep it to 2 bits */ + 3, /**< PJ_ICE_HOST_PREF */ + 1, /**< PJ_ICE_SRFLX_PREF. */ + 2, /**< PJ_ICE_PRFLX_PREF */ + 0 /**< PJ_ICE_RELAYED_PREF */ #else - /* Default ICE session preferences, according to draft-ice */ - 126, /**< PJ_ICE_HOST_PREF */ - 100, /**< PJ_ICE_SRFLX_PREF. */ - 110, /**< PJ_ICE_PRFLX_PREF */ - 0 /**< PJ_ICE_RELAYED_PREF */ + /* Default ICE session preferences, according to draft-ice */ + 126, /**< PJ_ICE_HOST_PREF */ + 100, /**< PJ_ICE_SRFLX_PREF. */ + 110, /**< PJ_ICE_PRFLX_PREF */ + 0 /**< PJ_ICE_RELAYED_PREF */ #endif }; @@ -105,29 +105,29 @@ /* The data that will be attached to the STUN session on each - * component. - */ +* component. +*/ typedef struct stun_data { - pj_ice_sess *ice; - unsigned comp_id; - pj_ice_sess_comp *comp; + pj_ice_sess *ice; + unsigned comp_id; + pj_ice_sess_comp *comp; } stun_data; /* The data that will be attached to the timer to perform - * periodic check. - */ +* periodic check. +*/ typedef struct timer_data { - pj_ice_sess *ice; - pj_ice_sess_checklist *clist; + pj_ice_sess *ice; + pj_ice_sess_checklist *clist; } timer_data; /* This is the data that will be attached as token to outgoing - * STUN messages. - */ +* STUN messages. +*/ /* Forward declarations */ @@ -135,692 +135,708 @@ static void on_ice_complete(pj_ice_sess *ice, pj_status_t status); static void ice_keep_alive(pj_ice_sess *ice, pj_bool_t send_now); static void destroy_ice(pj_ice_sess *ice, - pj_status_t reason); + pj_status_t reason); static pj_status_t start_periodic_check(pj_timer_heap_t *th, - pj_timer_entry *te); + pj_timer_entry *te); static void start_nominated_check(pj_ice_sess *ice); static void periodic_timer(pj_timer_heap_t *th, - pj_timer_entry *te); + pj_timer_entry *te); static void handle_incoming_check(pj_ice_sess *ice, - const pj_ice_rx_check *rcheck); + const pj_ice_rx_check *rcheck); /* These are the callbacks registered to the STUN sessions */ static pj_status_t on_stun_send_msg(pj_stun_session *sess, - void *token, - const void *pkt, - pj_size_t pkt_size, - const pj_sockaddr_t *dst_addr, - unsigned addr_len); + void *token, + const void *pkt, + pj_size_t pkt_size, + const pj_sockaddr_t *dst_addr, + unsigned addr_len); static pj_status_t on_stun_rx_request(pj_stun_session *sess, - const pj_uint8_t *pkt, - unsigned pkt_len, - const pj_stun_rx_data *rdata, - void *token, - const pj_sockaddr_t *src_addr, - unsigned src_addr_len); + const pj_uint8_t *pkt, + unsigned pkt_len, + const pj_stun_rx_data *rdata, + void *token, + const pj_sockaddr_t *src_addr, + unsigned src_addr_len); static void on_stun_request_complete(pj_stun_session *stun_sess, - pj_status_t status, - void *token, - pj_stun_tx_data *tdata, - const pj_stun_msg *response, - const pj_sockaddr_t *src_addr, - unsigned src_addr_len); + pj_status_t status, + void *token, + pj_stun_tx_data *tdata, + const pj_stun_msg *response, + const pj_sockaddr_t *src_addr, + unsigned src_addr_len); static pj_status_t on_stun_rx_indication(pj_stun_session *sess, - const pj_uint8_t *pkt, - unsigned pkt_len, - const pj_stun_msg *msg, - void *token, - const pj_sockaddr_t *src_addr, - unsigned src_addr_len); + const pj_uint8_t *pkt, + unsigned pkt_len, + const pj_stun_msg *msg, + void *token, + const pj_sockaddr_t *src_addr, + unsigned src_addr_len); /* These are the callbacks for performing STUN authentication */ static pj_status_t stun_auth_get_auth(void *user_data, - pj_pool_t *pool, - pj_str_t *realm, - pj_str_t *nonce); + pj_pool_t *pool, + pj_str_t *realm, + pj_str_t *nonce); static pj_status_t stun_auth_get_cred(const pj_stun_msg *msg, - void *user_data, - pj_pool_t *pool, - pj_str_t *realm, - pj_str_t *username, - pj_str_t *nonce, - pj_stun_passwd_type *data_type, - pj_str_t *data); + void *user_data, + pj_pool_t *pool, + pj_str_t *realm, + pj_str_t *username, + pj_str_t *nonce, + pj_stun_passwd_type *data_type, + pj_str_t *data); static pj_status_t stun_auth_get_password(const pj_stun_msg *msg, - void *user_data, - const pj_str_t *realm, - const pj_str_t *username, - pj_pool_t *pool, - pj_stun_passwd_type *data_type, - pj_str_t *data); + void *user_data, + const pj_str_t *realm, + const pj_str_t *username, + pj_pool_t *pool, + pj_stun_passwd_type *data_type, + pj_str_t *data); PJ_DEF(const char*) pj_ice_get_cand_type_name(pj_ice_cand_type type) { - PJ_ASSERT_RETURN(type <= PJ_ICE_CAND_TYPE_RELAYED, "???"); - return cand_type_names[type]; + PJ_ASSERT_RETURN(type <= PJ_ICE_CAND_TYPE_RELAYED, "???"); + return cand_type_names[type]; } PJ_DEF(const char*) pj_ice_sess_role_name(pj_ice_sess_role role) { - switch (role) { - case PJ_ICE_SESS_ROLE_UNKNOWN: - return "Unknown"; - case PJ_ICE_SESS_ROLE_CONTROLLED: - return "Controlled"; - case PJ_ICE_SESS_ROLE_CONTROLLING: - return "Controlling"; - default: - return "??"; - } + switch (role) { + case PJ_ICE_SESS_ROLE_UNKNOWN: + return "Unknown"; + case PJ_ICE_SESS_ROLE_CONTROLLED: + return "Controlled"; + case PJ_ICE_SESS_ROLE_CONTROLLING: + return "Controlling"; + default: + return "??"; + } } /* Get the prefix for the foundation */ static int get_type_prefix(pj_ice_cand_type type) { - switch (type) { - case PJ_ICE_CAND_TYPE_HOST: return 'H'; - case PJ_ICE_CAND_TYPE_SRFLX: return 'S'; - case PJ_ICE_CAND_TYPE_PRFLX: return 'P'; - case PJ_ICE_CAND_TYPE_RELAYED: return 'R'; - default: - pj_assert(!"Invalid type"); - return 'U'; - } + switch (type) { + case PJ_ICE_CAND_TYPE_HOST: return 'H'; + case PJ_ICE_CAND_TYPE_SRFLX: return 'S'; + case PJ_ICE_CAND_TYPE_PRFLX: return 'P'; + case PJ_ICE_CAND_TYPE_RELAYED: return 'R'; + default: + pj_assert(!"Invalid type"); + return 'U'; + } } /* Calculate foundation: - * Two candidates have the same foundation when they are "similar" - of - * the same type and obtained from the same host candidate and STUN - * server using the same protocol. Otherwise, their foundation is - * different. - */ +* Two candidates have the same foundation when they are "similar" - of +* the same type and obtained from the same host candidate and STUN +* server using the same protocol. Otherwise, their foundation is +* different. +*/ PJ_DEF(void) pj_ice_calc_foundation(pj_pool_t *pool, - pj_str_t *foundation, - pj_ice_cand_type type, - const pj_sockaddr *base_addr) + pj_str_t *foundation, + pj_ice_cand_type type, + const pj_sockaddr *base_addr) { #if PJNATH_ICE_PRIO_STD - char buf[64]; - pj_uint32_t val; + char buf[64]; + pj_uint32_t val; - if (base_addr->addr.sa_family == pj_AF_INET()) { - val = pj_ntohl(base_addr->ipv4.sin_addr.s_addr); - } else { - val = pj_hash_calc(0, pj_sockaddr_get_addr(base_addr), - pj_sockaddr_get_addr_len(base_addr)); - } - pj_ansi_snprintf(buf, sizeof(buf), "%c%x", - get_type_prefix(type), val); - pj_strdup2(pool, foundation, buf); + if (base_addr->addr.sa_family == pj_AF_INET()) { + val = pj_ntohl(base_addr->ipv4.sin_addr.s_addr); + } else { + val = pj_hash_calc(0, pj_sockaddr_get_addr(base_addr), + pj_sockaddr_get_addr_len(base_addr)); + } + pj_ansi_snprintf(buf, sizeof(buf), "%c%x", + get_type_prefix(type), val); + pj_strdup2(pool, foundation, buf); #else - /* Much shorter version, valid for candidates added by - * pj_ice_strans. - */ - foundation->ptr = (char*) pj_pool_alloc(pool, 1); - *foundation->ptr = (char)get_type_prefix(type); - foundation->slen = 1; + /* Much shorter version, valid for candidates added by + * pj_ice_strans. + */ + foundation->ptr = (char*) pj_pool_alloc(pool, 1); + *foundation->ptr = (char)get_type_prefix(type); + foundation->slen = 1; - PJ_UNUSED_ARG(base_addr); + PJ_UNUSED_ARG(base_addr); #endif } /* Init component */ static pj_status_t init_comp(pj_ice_sess *ice, - unsigned comp_id, - pj_ice_sess_comp *comp) + unsigned comp_id, + pj_ice_sess_comp *comp) { - pj_stun_session_cb sess_cb; - pj_stun_auth_cred auth_cred; - stun_data *sd; - pj_status_t status; + pj_stun_session_cb sess_cb; + pj_stun_auth_cred auth_cred; + stun_data *sd; + pj_status_t status; - /* Init STUN callbacks */ - pj_bzero(&sess_cb, sizeof(sess_cb)); - sess_cb.on_request_complete = &on_stun_request_complete; - sess_cb.on_rx_indication = &on_stun_rx_indication; - sess_cb.on_rx_request = &on_stun_rx_request; - sess_cb.on_send_msg = &on_stun_send_msg; + /* Init STUN callbacks */ + pj_bzero(&sess_cb, sizeof(sess_cb)); + sess_cb.on_request_complete = &on_stun_request_complete; + sess_cb.on_rx_indication = &on_stun_rx_indication; + sess_cb.on_rx_request = &on_stun_rx_request; + sess_cb.on_send_msg = &on_stun_send_msg; - /* Create STUN session for this candidate */ - status = pj_stun_session_create(&ice->stun_cfg, NULL, - &sess_cb, PJ_TRUE, - &comp->stun_sess); - if (status != PJ_SUCCESS) - return status; + /* Create STUN session for this candidate */ + status = pj_stun_session_create(&ice->stun_cfg, NULL, + &sess_cb, PJ_TRUE, + &comp->stun_sess); + if (status != PJ_SUCCESS) + return status; - /* Associate data with this STUN session */ - sd = PJ_POOL_ZALLOC_T(ice->pool, struct stun_data); - sd->ice = ice; - sd->comp_id = comp_id; - sd->comp = comp; - pj_stun_session_set_user_data(comp->stun_sess, sd); + /* Associate data with this STUN session */ + sd = PJ_POOL_ZALLOC_T(ice->pool, struct stun_data); + sd->ice = ice; + sd->comp_id = comp_id; + sd->comp = comp; + pj_stun_session_set_user_data(comp->stun_sess, sd); - /* 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); + /* Init STUN authentication credential */ + pj_bzero(&auth_cred, sizeof(auth_cred)); + 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; + } - return PJ_SUCCESS; + 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; } /* Init options with default values */ PJ_DEF(void) pj_ice_sess_options_default(pj_ice_sess_options *opt) { - opt->aggressive = PJ_TRUE; - opt->nominated_check_delay = PJ_ICE_NOMINATED_CHECK_DELAY; - opt->controlled_agent_want_nom_timeout = - ICE_CONTROLLED_AGENT_WAIT_NOMINATION_TIMEOUT; + opt->aggressive = PJ_TRUE; + 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; } /* - * Create ICE session. - */ +* Create ICE session. +*/ PJ_DEF(pj_status_t) pj_ice_sess_create(pj_stun_config *stun_cfg, - const char *name, - pj_ice_sess_role role, - unsigned comp_cnt, - const pj_ice_sess_cb *cb, - const pj_str_t *local_ufrag, - const pj_str_t *local_passwd, - pj_ice_sess **p_ice) + const char *name, + pj_ice_sess_role role, + unsigned comp_cnt, + const pj_ice_sess_cb *cb, + const pj_str_t *local_ufrag, + const pj_str_t *local_passwd, + pj_ice_sess **p_ice) { - pj_pool_t *pool; - pj_ice_sess *ice; - unsigned i; - pj_status_t status; + pj_pool_t *pool; + pj_ice_sess *ice; + unsigned i; + pj_status_t status; - PJ_ASSERT_RETURN(stun_cfg && cb && p_ice, PJ_EINVAL); + PJ_ASSERT_RETURN(stun_cfg && cb && p_ice, PJ_EINVAL); - if (name == NULL) - name = "icess%p"; + if (name == NULL) + name = "icess%p"; - pool = pj_pool_create(stun_cfg->pf, name, PJNATH_POOL_LEN_ICE_SESS, - PJNATH_POOL_INC_ICE_SESS, NULL); - ice = PJ_POOL_ZALLOC_T(pool, pj_ice_sess); - ice->pool = pool; - ice->role = role; - ice->tie_breaker.u32.hi = pj_rand(); - ice->tie_breaker.u32.lo = pj_rand(); - ice->prefs = cand_type_prefs; - pj_ice_sess_options_default(&ice->opt); + pool = pj_pool_create(stun_cfg->pf, name, PJNATH_POOL_LEN_ICE_SESS, + PJNATH_POOL_INC_ICE_SESS, NULL); + ice = PJ_POOL_ZALLOC_T(pool, pj_ice_sess); + ice->pool = pool; + ice->role = role; + ice->tie_breaker.u32.hi = pj_rand(); + ice->tie_breaker.u32.lo = pj_rand(); + ice->prefs = cand_type_prefs; + pj_ice_sess_options_default(&ice->opt); - pj_timer_entry_init(&ice->timer, TIMER_NONE, (void*)ice, &on_timer); + pj_timer_entry_init(&ice->timer, TIMER_NONE, (void*)ice, &on_timer); - pj_ansi_snprintf(ice->obj_name, sizeof(ice->obj_name), - name, ice); + pj_ansi_snprintf(ice->obj_name, sizeof(ice->obj_name), + name, ice); - status = pj_mutex_create_recursive(pool, ice->obj_name, - &ice->mutex); - if (status != PJ_SUCCESS) { - destroy_ice(ice, status); - return status; - } + status = pj_mutex_create_recursive(pool, ice->obj_name, + &ice->mutex); + if (status != PJ_SUCCESS) { + destroy_ice(ice, status); + return status; + } - pj_memcpy(&ice->cb, cb, sizeof(*cb)); - pj_memcpy(&ice->stun_cfg, stun_cfg, sizeof(*stun_cfg)); + pj_memcpy(&ice->cb, cb, sizeof(*cb)); + pj_memcpy(&ice->stun_cfg, stun_cfg, sizeof(*stun_cfg)); - ice->comp_cnt = comp_cnt; - for (i=0; icomp[i]; - comp->valid_check = NULL; - comp->nominated_check = NULL; + ice->comp_cnt = comp_cnt; + for (i=0; icomp[i]; + comp->valid_check = NULL; + comp->nominated_check = NULL; - status = init_comp(ice, i+1, comp); - if (status != PJ_SUCCESS) { - destroy_ice(ice, status); - return status; + status = init_comp(ice, i+1, comp); + if (status != PJ_SUCCESS) { + destroy_ice(ice, status); + return status; + } } - } - /* Initialize transport datas */ - for (i=0; itp_data); ++i) { - ice->tp_data[i].transport_id = i; - ice->tp_data[i].has_req_data = PJ_FALSE; - } + /* Initialize transport datas */ + for (i=0; itp_data); ++i) { + ice->tp_data[i].transport_id = i; + ice->tp_data[i].has_req_data = PJ_FALSE; + } - if (local_ufrag == NULL) { - ice->rx_ufrag.ptr = (char*) pj_pool_alloc(ice->pool, PJ_ICE_UFRAG_LEN); - pj_create_random_string(ice->rx_ufrag.ptr, PJ_ICE_UFRAG_LEN); - ice->rx_ufrag.slen = PJ_ICE_UFRAG_LEN; - } else { - pj_strdup(ice->pool, &ice->rx_ufrag, local_ufrag); - } + if (local_ufrag == NULL) { + ice->rx_ufrag.ptr = (char*) pj_pool_alloc(ice->pool, PJ_ICE_UFRAG_LEN); + pj_create_random_string(ice->rx_ufrag.ptr, PJ_ICE_UFRAG_LEN); + ice->rx_ufrag.slen = PJ_ICE_UFRAG_LEN; + } else { + pj_strdup(ice->pool, &ice->rx_ufrag, local_ufrag); + } - if (local_passwd == NULL) { - ice->rx_pass.ptr = (char*) pj_pool_alloc(ice->pool, PJ_ICE_UFRAG_LEN); - pj_create_random_string(ice->rx_pass.ptr, PJ_ICE_UFRAG_LEN); - ice->rx_pass.slen = PJ_ICE_UFRAG_LEN; - } else { - pj_strdup(ice->pool, &ice->rx_pass, local_passwd); - } + if (local_passwd == NULL) { + ice->rx_pass.ptr = (char*) pj_pool_alloc(ice->pool, PJ_ICE_UFRAG_LEN); + pj_create_random_string(ice->rx_pass.ptr, PJ_ICE_UFRAG_LEN); + ice->rx_pass.slen = PJ_ICE_UFRAG_LEN; + } else { + pj_strdup(ice->pool, &ice->rx_pass, local_passwd); + } - pj_list_init(&ice->early_check); + pj_list_init(&ice->early_check); - /* Done */ - *p_ice = ice; + /* Done */ + *p_ice = ice; - LOG4((ice->obj_name, - "ICE session created, comp_cnt=%d, role is %s agent", - comp_cnt, role_names[ice->role])); + LOG4((ice->obj_name, + "ICE session created, comp_cnt=%d, role is %s agent", + comp_cnt, role_names[ice->role])); - return PJ_SUCCESS; + return PJ_SUCCESS; } /* - * Get the value of various options of the ICE session. - */ +* Get the value of various options of the ICE session. +*/ PJ_DEF(pj_status_t) pj_ice_sess_get_options(pj_ice_sess *ice, - pj_ice_sess_options *opt) + pj_ice_sess_options *opt) { - PJ_ASSERT_RETURN(ice, PJ_EINVAL); - pj_memcpy(opt, &ice->opt, sizeof(*opt)); - return PJ_SUCCESS; + PJ_ASSERT_RETURN(ice, PJ_EINVAL); + pj_memcpy(opt, &ice->opt, sizeof(*opt)); + return PJ_SUCCESS; } /* - * Specify various options for this ICE session. - */ +* Specify various options for this ICE session. +*/ PJ_DEF(pj_status_t) pj_ice_sess_set_options(pj_ice_sess *ice, - const pj_ice_sess_options *opt) + const pj_ice_sess_options *opt) { - PJ_ASSERT_RETURN(ice && opt, PJ_EINVAL); - pj_memcpy(&ice->opt, opt, sizeof(*opt)); - LOG5((ice->obj_name, "ICE nomination type set to %s", - (ice->opt.aggressive ? "aggressive" : "regular"))); - return PJ_SUCCESS; + pj_bool_t update; + PJ_ASSERT_RETURN(ice && opt, PJ_EINVAL); + 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; } /* - * Destroy - */ +* Destroy +*/ static void destroy_ice(pj_ice_sess *ice, - pj_status_t reason) + pj_status_t reason) { - unsigned i; + unsigned i; - if (reason == PJ_SUCCESS) { - LOG4((ice->obj_name, "Destroying ICE session")); - } + if (reason == PJ_SUCCESS) { + LOG4((ice->obj_name, "Destroying ICE session")); + } - ice->is_destroying = PJ_TRUE; + ice->is_destroying = PJ_TRUE; - /* Let other callbacks finish */ - if (ice->mutex) { - pj_mutex_lock(ice->mutex); - pj_mutex_unlock(ice->mutex); - } + /* Let other callbacks finish */ + if (ice->mutex) { + pj_mutex_lock(ice->mutex); + pj_mutex_unlock(ice->mutex); + } - if (ice->timer.id) { - pj_timer_heap_cancel(ice->stun_cfg.timer_heap, - &ice->timer); - ice->timer.id = PJ_FALSE; - } + if (ice->timer.id) { + pj_timer_heap_cancel(ice->stun_cfg.timer_heap, + &ice->timer); + ice->timer.id = PJ_FALSE; + } - for (i=0; icomp_cnt; ++i) { - if (ice->comp[i].stun_sess) { - pj_stun_session_destroy(ice->comp[i].stun_sess); - ice->comp[i].stun_sess = NULL; + for (i=0; icomp_cnt; ++i) { + if (ice->comp[i].stun_sess) { + pj_stun_session_destroy(ice->comp[i].stun_sess); + ice->comp[i].stun_sess = NULL; + } } - } - if (ice->clist.timer.id) { - pj_timer_heap_cancel(ice->stun_cfg.timer_heap, &ice->clist.timer); - ice->clist.timer.id = PJ_FALSE; - } + if (ice->clist.timer.id) { + pj_timer_heap_cancel(ice->stun_cfg.timer_heap, &ice->clist.timer); + ice->clist.timer.id = PJ_FALSE; + } - if (ice->mutex) { - pj_mutex_destroy(ice->mutex); - ice->mutex = NULL; - } + if (ice->mutex) { + pj_mutex_destroy(ice->mutex); + ice->mutex = NULL; + } - if (ice->pool) { - pj_pool_t *pool = ice->pool; - ice->pool = NULL; - pj_pool_release(pool); - } + if (ice->pool) { + pj_pool_t *pool = ice->pool; + ice->pool = NULL; + pj_pool_release(pool); + } } /* - * Destroy - */ +* Destroy +*/ PJ_DEF(pj_status_t) pj_ice_sess_destroy(pj_ice_sess *ice) { - PJ_ASSERT_RETURN(ice, PJ_EINVAL); - destroy_ice(ice, PJ_SUCCESS); - return PJ_SUCCESS; + PJ_ASSERT_RETURN(ice, PJ_EINVAL); + destroy_ice(ice, PJ_SUCCESS); + return PJ_SUCCESS; } /* - * Change session role. - */ +* Change session role. +*/ PJ_DEF(pj_status_t) pj_ice_sess_change_role(pj_ice_sess *ice, - pj_ice_sess_role new_role) + pj_ice_sess_role new_role) { - PJ_ASSERT_RETURN(ice, PJ_EINVAL); + PJ_ASSERT_RETURN(ice, PJ_EINVAL); - if (new_role != ice->role) { - ice->role = new_role; - LOG4((ice->obj_name, "Role changed to %s", role_names[new_role])); - } + if (new_role != ice->role) { + ice->role = new_role; + LOG4((ice->obj_name, "Role changed to %s", role_names[new_role])); + } - return PJ_SUCCESS; + return PJ_SUCCESS; } /* - * Change type preference - */ +* Change type preference +*/ PJ_DEF(pj_status_t) pj_ice_sess_set_prefs(pj_ice_sess *ice, - const pj_uint8_t prefs[4]) + const pj_uint8_t prefs[4]) { - unsigned i; - PJ_ASSERT_RETURN(ice && prefs, PJ_EINVAL); - ice->prefs = (pj_uint8_t*) pj_pool_calloc(ice->pool, PJ_ARRAY_SIZE(prefs), - sizeof(pj_uint8_t)); - for (i=0; i<4; ++i) { + unsigned i; + PJ_ASSERT_RETURN(ice && prefs, PJ_EINVAL); + ice->prefs = (pj_uint8_t*) pj_pool_calloc(ice->pool, PJ_ARRAY_SIZE(prefs), + sizeof(pj_uint8_t)); + for (i=0; i<4; ++i) { #if PJ_ICE_CAND_TYPE_PREF_BITS < 8 - pj_assert(prefs[i] < (2 << PJ_ICE_CAND_TYPE_PREF_BITS)); + pj_assert(prefs[i] < (2 << PJ_ICE_CAND_TYPE_PREF_BITS)); #endif - ice->prefs[i] = prefs[i]; - } - return PJ_SUCCESS; + ice->prefs[i] = prefs[i]; + } + return PJ_SUCCESS; } /* Find component by ID */ static pj_ice_sess_comp *find_comp(const pj_ice_sess *ice, unsigned comp_id) { - pj_assert(comp_id > 0 && comp_id <= ice->comp_cnt); - return (pj_ice_sess_comp*) &ice->comp[comp_id-1]; + pj_assert(comp_id > 0 && comp_id <= ice->comp_cnt); + return (pj_ice_sess_comp*) &ice->comp[comp_id-1]; } /* Callback by STUN authentication when it needs to send 401 */ static pj_status_t stun_auth_get_auth(void *user_data, - pj_pool_t *pool, - pj_str_t *realm, - pj_str_t *nonce) + pj_pool_t *pool, + pj_str_t *realm, + pj_str_t *nonce) { - PJ_UNUSED_ARG(user_data); - PJ_UNUSED_ARG(pool); + PJ_UNUSED_ARG(user_data); + PJ_UNUSED_ARG(pool); - realm->slen = 0; - nonce->slen = 0; + realm->slen = 0; + nonce->slen = 0; - return PJ_SUCCESS; + return PJ_SUCCESS; } /* Get credential to be sent with outgoing message */ static pj_status_t stun_auth_get_cred(const pj_stun_msg *msg, - void *user_data, - pj_pool_t *pool, - pj_str_t *realm, - pj_str_t *username, - pj_str_t *nonce, - pj_stun_passwd_type *data_type, - pj_str_t *data) + void *user_data, + pj_pool_t *pool, + pj_str_t *realm, + pj_str_t *username, + pj_str_t *nonce, + pj_stun_passwd_type *data_type, + pj_str_t *data) { - pj_stun_session *sess = (pj_stun_session *)user_data; - stun_data *sd = (stun_data*) pj_stun_session_get_user_data(sess); - pj_ice_sess *ice = sd->ice; + pj_stun_session *sess = (pj_stun_session *)user_data; + stun_data *sd = (stun_data*) pj_stun_session_get_user_data(sess); + pj_ice_sess *ice = sd->ice; - PJ_UNUSED_ARG(pool); - realm->slen = nonce->slen = 0; + PJ_UNUSED_ARG(pool); + realm->slen = nonce->slen = 0; - if (PJ_STUN_IS_RESPONSE(msg->hdr.type)) { - /* Outgoing responses need to have the same credential as - * incoming requests. - */ - *username = ice->rx_uname; - *data_type = PJ_STUN_PASSWD_PLAIN; - *data = ice->rx_pass; - } - else { - *username = ice->tx_uname; - *data_type = PJ_STUN_PASSWD_PLAIN; - *data = ice->tx_pass; - } + if (PJ_STUN_IS_RESPONSE(msg->hdr.type)) { + /* Outgoing responses need to have the same credential as + * incoming requests. + */ + *username = ice->rx_uname; + *data_type = PJ_STUN_PASSWD_PLAIN; + *data = ice->rx_pass; + } + else { + *username = ice->tx_uname; + *data_type = PJ_STUN_PASSWD_PLAIN; + *data = ice->tx_pass; + } - return PJ_SUCCESS; + return PJ_SUCCESS; } /* Get password to be used to authenticate incoming message */ static pj_status_t stun_auth_get_password(const pj_stun_msg *msg, - void *user_data, - const pj_str_t *realm, - const pj_str_t *username, - pj_pool_t *pool, - pj_stun_passwd_type *data_type, - pj_str_t *data) + void *user_data, + const pj_str_t *realm, + const pj_str_t *username, + pj_pool_t *pool, + pj_stun_passwd_type *data_type, + pj_str_t *data) { - pj_stun_session *sess = (pj_stun_session *)user_data; - stun_data *sd = (stun_data*) pj_stun_session_get_user_data(sess); - pj_ice_sess *ice = sd->ice; + pj_stun_session *sess = (pj_stun_session *)user_data; + stun_data *sd = (stun_data*) pj_stun_session_get_user_data(sess); + pj_ice_sess *ice = sd->ice; - PJ_UNUSED_ARG(realm); - PJ_UNUSED_ARG(pool); + PJ_UNUSED_ARG(realm); + PJ_UNUSED_ARG(pool); - if (PJ_STUN_IS_SUCCESS_RESPONSE(msg->hdr.type) || - PJ_STUN_IS_ERROR_RESPONSE(msg->hdr.type)) - { - /* Incoming response is authenticated with TX credential */ - /* Verify username */ - if (pj_strcmp(username, &ice->tx_uname) != 0) - return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_UNAUTHORIZED); - *data_type = PJ_STUN_PASSWD_PLAIN; - *data = ice->tx_pass; + if (PJ_STUN_IS_SUCCESS_RESPONSE(msg->hdr.type) || + PJ_STUN_IS_ERROR_RESPONSE(msg->hdr.type)) + { + /* Incoming response is authenticated with TX credential */ + /* Verify username */ + if (pj_strcmp(username, &ice->tx_uname) != 0) + return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_UNAUTHORIZED); + *data_type = PJ_STUN_PASSWD_PLAIN; + *data = ice->tx_pass; - } else { - /* Incoming request is authenticated with RX credential */ - /* The agent MUST accept a credential if the username consists - * of two values separated by a colon, where the first value is - * equal to the username fragment generated by the agent in an offer - * or answer for a session in-progress, and the MESSAGE-INTEGRITY - * is the output of a hash of the password and the STUN packet's - * contents. - */ - const char *pos; - pj_str_t ufrag; + } else { + /* Incoming request is authenticated with RX credential */ + /* The agent MUST accept a credential if the username consists + * of two values separated by a colon, where the first value is + * equal to the username fragment generated by the agent in an offer + * or answer for a session in-progress, and the MESSAGE-INTEGRITY + * is the output of a hash of the password and the STUN packet's + * contents. + */ + const char *pos; + pj_str_t ufrag; - pos = (const char*)pj_memchr(username->ptr, ':', username->slen); - if (pos == NULL) - return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_UNAUTHORIZED); + pos = (const char*)pj_memchr(username->ptr, ':', username->slen); + if (pos == NULL) + return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_UNAUTHORIZED); - ufrag.ptr = (char*)username->ptr; - ufrag.slen = (pos - username->ptr); + ufrag.ptr = (char*)username->ptr; + ufrag.slen = (pos - username->ptr); - if (pj_strcmp(&ufrag, &ice->rx_ufrag) != 0) - return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_UNAUTHORIZED); + if (pj_strcmp(&ufrag, &ice->rx_ufrag) != 0) + return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_UNAUTHORIZED); - *data_type = PJ_STUN_PASSWD_PLAIN; - *data = ice->rx_pass; + *data_type = PJ_STUN_PASSWD_PLAIN; + *data = ice->rx_pass; - } + } - return PJ_SUCCESS; + return PJ_SUCCESS; } static pj_uint32_t CALC_CAND_PRIO(pj_ice_sess *ice, - pj_ice_cand_type type, - pj_uint32_t local_pref, - pj_uint32_t comp_id) + pj_ice_cand_type type, + pj_uint32_t local_pref, + pj_uint32_t comp_id) { #if PJNATH_ICE_PRIO_STD - return ((ice->prefs[type] & 0xFF) << 24) + - ((local_pref & 0xFFFF) << 8) + - (((256 - comp_id) & 0xFF) << 0); + return ((ice->prefs[type] & 0xFF) << 24) + + ((local_pref & 0xFFFF) << 8) + + (((256 - comp_id) & 0xFF) << 0); #else - enum { - type_mask = ((2 << PJ_ICE_CAND_TYPE_PREF_BITS) - 1), - local_mask = ((2 << PJ_ICE_LOCAL_PREF_BITS) - 1), - comp_mask = ((2 << PJ_ICE_COMP_BITS) - 1), + enum { + type_mask = ((2 << PJ_ICE_CAND_TYPE_PREF_BITS) - 1), + local_mask = ((2 << PJ_ICE_LOCAL_PREF_BITS) - 1), + comp_mask = ((2 << PJ_ICE_COMP_BITS) - 1), - comp_shift = 0, - local_shift = (PJ_ICE_COMP_BITS), - type_shift = (comp_shift + local_shift), + comp_shift = 0, + local_shift = (PJ_ICE_COMP_BITS), + type_shift = (comp_shift + local_shift), - max_comp = (2<prefs[type] & type_mask) << type_shift) + - ((local_pref & local_mask) << local_shift) + - (((max_comp - comp_id) & comp_mask) << comp_shift); + return ((ice->prefs[type] & type_mask) << type_shift) + + ((local_pref & local_mask) << local_shift) + + (((max_comp - comp_id) & comp_mask) << comp_shift); #endif } /* - * Add ICE candidate - */ +* Add ICE candidate +*/ PJ_DEF(pj_status_t) pj_ice_sess_add_cand(pj_ice_sess *ice, - unsigned comp_id, - unsigned transport_id, - pj_ice_cand_type type, - pj_uint16_t local_pref, - const pj_str_t *foundation, - const pj_sockaddr_t *addr, - const pj_sockaddr_t *base_addr, - const pj_sockaddr_t *rel_addr, - int addr_len, - unsigned *p_cand_id) + unsigned comp_id, + unsigned transport_id, + pj_ice_cand_type type, + pj_uint16_t local_pref, + const pj_str_t *foundation, + const pj_sockaddr_t *addr, + const pj_sockaddr_t *base_addr, + const pj_sockaddr_t *rel_addr, + int addr_len, + unsigned *p_cand_id) { - pj_ice_sess_cand *lcand; - pj_status_t status = PJ_SUCCESS; + pj_ice_sess_cand *lcand; + pj_status_t status = PJ_SUCCESS; - PJ_ASSERT_RETURN(ice && comp_id && - foundation && addr && base_addr && addr_len, - PJ_EINVAL); - PJ_ASSERT_RETURN(comp_id <= ice->comp_cnt, PJ_EINVAL); + PJ_ASSERT_RETURN(ice && comp_id && + foundation && addr && base_addr && addr_len, + PJ_EINVAL); + PJ_ASSERT_RETURN(comp_id <= ice->comp_cnt, PJ_EINVAL); - pj_mutex_lock(ice->mutex); + pj_mutex_lock(ice->mutex); - if (ice->lcand_cnt >= PJ_ARRAY_SIZE(ice->lcand)) { - status = PJ_ETOOMANY; - goto on_error; - } + if (ice->lcand_cnt >= PJ_ARRAY_SIZE(ice->lcand)) { + status = PJ_ETOOMANY; + goto on_error; + } - lcand = &ice->lcand[ice->lcand_cnt]; - lcand->comp_id = (pj_uint8_t)comp_id; - lcand->transport_id = (pj_uint8_t)transport_id; - lcand->type = type; - pj_strdup(ice->pool, &lcand->foundation, foundation); - lcand->prio = CALC_CAND_PRIO(ice, type, local_pref, lcand->comp_id); - pj_memcpy(&lcand->addr, addr, addr_len); - pj_memcpy(&lcand->base_addr, base_addr, addr_len); - if (rel_addr == NULL) - rel_addr = base_addr; - pj_memcpy(&lcand->rel_addr, rel_addr, addr_len); + lcand = &ice->lcand[ice->lcand_cnt]; + lcand->comp_id = (pj_uint8_t)comp_id; + lcand->transport_id = (pj_uint8_t)transport_id; + lcand->type = type; + pj_strdup(ice->pool, &lcand->foundation, foundation); + lcand->prio = CALC_CAND_PRIO(ice, type, local_pref, lcand->comp_id); + pj_memcpy(&lcand->addr, addr, addr_len); + pj_memcpy(&lcand->base_addr, base_addr, addr_len); + if (rel_addr == NULL) + rel_addr = base_addr; + pj_memcpy(&lcand->rel_addr, rel_addr, addr_len); - pj_ansi_strcpy(ice->tmp.txt, pj_inet_ntoa(lcand->addr.ipv4.sin_addr)); - LOG4((ice->obj_name, - "Candidate %d added: comp_id=%d, type=%s, foundation=%.*s, " - "addr=%s:%d, base=%s:%d, prio=0x%x (%u)", - ice->lcand_cnt, - lcand->comp_id, - cand_type_names[lcand->type], - (int)lcand->foundation.slen, - lcand->foundation.ptr, - ice->tmp.txt, - (int)pj_ntohs(lcand->addr.ipv4.sin_port), - pj_inet_ntoa(lcand->base_addr.ipv4.sin_addr), - (int)pj_htons(lcand->base_addr.ipv4.sin_port), - lcand->prio, lcand->prio)); + pj_ansi_strcpy(ice->tmp.txt, pj_inet_ntoa(lcand->addr.ipv4.sin_addr)); + LOG4((ice->obj_name, + "Candidate %d added: comp_id=%d, type=%s, foundation=%.*s, " + "addr=%s:%d, base=%s:%d, prio=0x%x (%u)", + ice->lcand_cnt, + lcand->comp_id, + cand_type_names[lcand->type], + (int)lcand->foundation.slen, + lcand->foundation.ptr, + ice->tmp.txt, + (int)pj_ntohs(lcand->addr.ipv4.sin_port), + pj_inet_ntoa(lcand->base_addr.ipv4.sin_addr), + (int)pj_htons(lcand->base_addr.ipv4.sin_port), + lcand->prio, lcand->prio)); - if (p_cand_id) - *p_cand_id = ice->lcand_cnt; + if (p_cand_id) + *p_cand_id = ice->lcand_cnt; - ++ice->lcand_cnt; + ++ice->lcand_cnt; on_error: - pj_mutex_unlock(ice->mutex); - return status; + pj_mutex_unlock(ice->mutex); + return status; } /* Find default candidate ID for the component */ PJ_DEF(pj_status_t) pj_ice_sess_find_default_cand(pj_ice_sess *ice, - unsigned comp_id, - int *cand_id) + unsigned comp_id, + int *cand_id) { - unsigned i; + unsigned i; - PJ_ASSERT_RETURN(ice && comp_id && cand_id, PJ_EINVAL); - PJ_ASSERT_RETURN(comp_id <= ice->comp_cnt, PJ_EINVAL); + PJ_ASSERT_RETURN(ice && comp_id && cand_id, PJ_EINVAL); + PJ_ASSERT_RETURN(comp_id <= ice->comp_cnt, PJ_EINVAL); - *cand_id = -1; + *cand_id = -1; - pj_mutex_lock(ice->mutex); + pj_mutex_lock(ice->mutex); - /* First find in valid list if we have nominated pair */ - for (i=0; ivalid_list.count; ++i) { - pj_ice_sess_check *check = &ice->valid_list.checks[i]; - - if (check->lcand->comp_id == comp_id) { - *cand_id = GET_LCAND_ID(check->lcand); - pj_mutex_unlock(ice->mutex); - return PJ_SUCCESS; + /* First find in valid list if we have nominated pair */ + for (i=0; ivalid_list.count; ++i) { + pj_ice_sess_check *check = &ice->valid_list.checks[i]; + + if (check->lcand->comp_id == comp_id) { + *cand_id = GET_LCAND_ID(check->lcand); + pj_mutex_unlock(ice->mutex); + return PJ_SUCCESS; + } } - } - /* If there's no nominated pair, find relayed candidate */ - for (i=0; ilcand_cnt; ++i) { - pj_ice_sess_cand *lcand = &ice->lcand[i]; - if (lcand->comp_id==comp_id && - lcand->type == PJ_ICE_CAND_TYPE_RELAYED) - { - *cand_id = GET_LCAND_ID(lcand); - pj_mutex_unlock(ice->mutex); - return PJ_SUCCESS; + /* If there's no nominated pair, find relayed candidate */ + for (i=0; ilcand_cnt; ++i) { + pj_ice_sess_cand *lcand = &ice->lcand[i]; + if (lcand->comp_id==comp_id && + lcand->type == PJ_ICE_CAND_TYPE_RELAYED) + { + *cand_id = GET_LCAND_ID(lcand); + pj_mutex_unlock(ice->mutex); + return PJ_SUCCESS; + } } - } - /* If there's no relayed candidate, find reflexive candidate */ - for (i=0; ilcand_cnt; ++i) { - pj_ice_sess_cand *lcand = &ice->lcand[i]; - if (lcand->comp_id==comp_id && - (lcand->type == PJ_ICE_CAND_TYPE_SRFLX || - lcand->type == PJ_ICE_CAND_TYPE_PRFLX)) - { - *cand_id = GET_LCAND_ID(lcand); - pj_mutex_unlock(ice->mutex); - return PJ_SUCCESS; + /* If there's no relayed candidate, find reflexive candidate */ + for (i=0; ilcand_cnt; ++i) { + pj_ice_sess_cand *lcand = &ice->lcand[i]; + if (lcand->comp_id==comp_id && + (lcand->type == PJ_ICE_CAND_TYPE_SRFLX || + lcand->type == PJ_ICE_CAND_TYPE_PRFLX)) + { + *cand_id = GET_LCAND_ID(lcand); + pj_mutex_unlock(ice->mutex); + return PJ_SUCCESS; + } } - } - /* Otherwise return host candidate */ - for (i=0; ilcand_cnt; ++i) { - pj_ice_sess_cand *lcand = &ice->lcand[i]; - if (lcand->comp_id==comp_id && - lcand->type == PJ_ICE_CAND_TYPE_HOST) - { - *cand_id = GET_LCAND_ID(lcand); - pj_mutex_unlock(ice->mutex); - return PJ_SUCCESS; + /* Otherwise return host candidate */ + for (i=0; ilcand_cnt; ++i) { + pj_ice_sess_cand *lcand = &ice->lcand[i]; + if (lcand->comp_id==comp_id && + lcand->type == PJ_ICE_CAND_TYPE_HOST) + { + *cand_id = GET_LCAND_ID(lcand); + pj_mutex_unlock(ice->mutex); + return PJ_SUCCESS; + } } - } - /* Still no candidate is found! :( */ - pj_mutex_unlock(ice->mutex); + /* Still no candidate is found! :( */ + pj_mutex_unlock(ice->mutex); - pj_assert(!"Should have a candidate by now"); - return PJ_EBUG; + pj_assert(!"Should have a candidate by now"); + return PJ_EBUG; } @@ -833,92 +849,92 @@ #endif static pj_timestamp CALC_CHECK_PRIO(const pj_ice_sess *ice, - const pj_ice_sess_cand *lcand, - const pj_ice_sess_cand *rcand) + const pj_ice_sess_cand *lcand, + const pj_ice_sess_cand *rcand) { - pj_uint32_t O, A; - pj_timestamp prio; + pj_uint32_t O, A; + pj_timestamp prio; - /* Original formula: - * pair priority = 2^32*MIN(O,A) + 2*MAX(O,A) + (O>A?1:0) - */ + /* Original formula: + * pair priority = 2^32*MIN(O,A) + 2*MAX(O,A) + (O>A?1:0) + */ - if (ice->role == PJ_ICE_SESS_ROLE_CONTROLLING) { - O = lcand->prio; - A = rcand->prio; - } else { - O = rcand->prio; - A = lcand->prio; - } + if (ice->role == PJ_ICE_SESS_ROLE_CONTROLLING) { + O = lcand->prio; + A = rcand->prio; + } else { + O = rcand->prio; + A = lcand->prio; + } - /* - return ((pj_uint64_t)1 << 32) * MIN(O, A) + - (pj_uint64_t)2 * MAX(O, A) + (O>A ? 1 : 0); - */ + /* + return ((pj_uint64_t)1 << 32) * MIN(O, A) + + (pj_uint64_t)2 * MAX(O, A) + (O>A ? 1 : 0); + */ - prio.u32.hi = MIN(O,A); - prio.u32.lo = (MAX(O, A) << 1) + (O>A ? 1 : 0); + prio.u32.hi = MIN(O,A); + prio.u32.lo = (MAX(O, A) << 1) + (O>A ? 1 : 0); - return prio; + return prio; } PJ_INLINE(int) CMP_CHECK_PRIO(const pj_ice_sess_check *c1, - const pj_ice_sess_check *c2) + const pj_ice_sess_check *c2) { - return pj_cmp_timestamp(&c1->prio, &c2->prio); + return pj_cmp_timestamp(&c1->prio, &c2->prio); } #if PJ_LOG_MAX_LEVEL >= 4 static const char *dump_check(char *buffer, unsigned bufsize, - const pj_ice_sess_checklist *clist, - const pj_ice_sess_check *check) + const pj_ice_sess_checklist *clist, + const pj_ice_sess_check *check) { - const pj_ice_sess_cand *lcand = check->lcand; - const pj_ice_sess_cand *rcand = check->rcand; - char laddr[PJ_INET6_ADDRSTRLEN]; - int len; + const pj_ice_sess_cand *lcand = check->lcand; + const pj_ice_sess_cand *rcand = check->rcand; + char laddr[PJ_INET6_ADDRSTRLEN]; + int len; - PJ_CHECK_STACK(); + PJ_CHECK_STACK(); - pj_ansi_strcpy(laddr, pj_inet_ntoa(lcand->addr.ipv4.sin_addr)); + pj_ansi_strcpy(laddr, pj_inet_ntoa(lcand->addr.ipv4.sin_addr)); - if (lcand->addr.addr.sa_family == pj_AF_INET()) { - len = pj_ansi_snprintf(buffer, bufsize, - "%d: [%d] %s:%d-->%s:%d", - (int)GET_CHECK_ID(clist, check), - check->lcand->comp_id, - laddr, (int)pj_ntohs(lcand->addr.ipv4.sin_port), - pj_inet_ntoa(rcand->addr.ipv4.sin_addr), - (int)pj_ntohs(rcand->addr.ipv4.sin_port)); - } else { - len = pj_ansi_snprintf(buffer, bufsize, "IPv6->IPv6"); - } + if (lcand->addr.addr.sa_family == pj_AF_INET()) { + len = pj_ansi_snprintf(buffer, bufsize, + "%d: [%d] %s:%d-->%s:%d", + (int)GET_CHECK_ID(clist, check), + check->lcand->comp_id, + laddr, (int)pj_ntohs(lcand->addr.ipv4.sin_port), + pj_inet_ntoa(rcand->addr.ipv4.sin_addr), + (int)pj_ntohs(rcand->addr.ipv4.sin_port)); + } else { + len = pj_ansi_snprintf(buffer, bufsize, "IPv6->IPv6"); + } - if (len < 0) - len = 0; - else if (len >= (int)bufsize) - len = bufsize - 1; + if (len < 0) + len = 0; + else if (len >= (int)bufsize) + len = bufsize - 1; - buffer[len] = '\0'; - return buffer; + buffer[len] = '\0'; + return buffer; } static void dump_checklist(const char *title, pj_ice_sess *ice, - const pj_ice_sess_checklist *clist) + const pj_ice_sess_checklist *clist) { - unsigned i; + unsigned i; - LOG4((ice->obj_name, "%s", title)); - for (i=0; icount; ++i) { - const pj_ice_sess_check *c = &clist->checks[i]; - LOG4((ice->obj_name, " %s (%s, state=%s)", - dump_check(ice->tmp.txt, sizeof(ice->tmp.txt), clist, c), - (c->nominated ? "nominated" : "not nominated"), - check_state_name[c->state])); - } + LOG4((ice->obj_name, "%s", title)); + for (i=0; icount; ++i) { + const pj_ice_sess_check *c = &clist->checks[i]; + LOG4((ice->obj_name, " %s (%s, state=%s)", + dump_check(ice->tmp.txt, sizeof(ice->tmp.txt), clist, c), + (c->nominated ? "nominated" : "not nominated"), + check_state_name[c->state])); + } } #else @@ -926,2053 +942,2122 @@ #endif static void check_set_state(pj_ice_sess *ice, pj_ice_sess_check *check, - pj_ice_sess_check_state st, - pj_status_t err_code) + pj_ice_sess_check_state st, + pj_status_t err_code) { - pj_assert(check->state < PJ_ICE_SESS_CHECK_STATE_SUCCEEDED); + if(ice->opt.is_webrtc && check->state >= PJ_ICE_SESS_CHECK_STATE_SUCCEEDED){ + return;// return WebRTC keepAlive + } - LOG5((ice->obj_name, "Check %s: state changed from %s to %s", - dump_check(ice->tmp.txt, sizeof(ice->tmp.txt), &ice->clist, check), - check_state_name[check->state], - check_state_name[st])); - check->state = st; - check->err_code = err_code; + pj_assert(check->state < PJ_ICE_SESS_CHECK_STATE_SUCCEEDED); + + LOG5((ice->obj_name, "Check %s: state changed from %s to %s", + dump_check(ice->tmp.txt, sizeof(ice->tmp.txt), &ice->clist, check), + check_state_name[check->state], + check_state_name[st])); + check->state = st; + check->err_code = err_code; } static void clist_set_state(pj_ice_sess *ice, pj_ice_sess_checklist *clist, - pj_ice_sess_checklist_state st) + pj_ice_sess_checklist_state st) { - if (clist->state != st) { - LOG5((ice->obj_name, "Checklist: state changed from %s to %s", - clist_state_name[clist->state], - clist_state_name[st])); - clist->state = st; - } + if (clist->state != st) { + LOG5((ice->obj_name, "Checklist: state changed from %s to %s", + clist_state_name[clist->state], + clist_state_name[st])); + clist->state = st; + } } /* Sort checklist based on priority */ static void sort_checklist(pj_ice_sess *ice, pj_ice_sess_checklist *clist) { - unsigned i; - pj_ice_sess_check **check_ptr[PJ_ICE_MAX_COMP*2]; - unsigned check_ptr_cnt = 0; + unsigned i; + pj_ice_sess_check **check_ptr[PJ_ICE_MAX_COMP*2]; + unsigned check_ptr_cnt = 0; - for (i=0; icomp_cnt; ++i) { - if (ice->comp[i].valid_check) { - check_ptr[check_ptr_cnt++] = &ice->comp[i].valid_check; + for (i=0; icomp_cnt; ++i) { + if (ice->comp[i].valid_check) { + check_ptr[check_ptr_cnt++] = &ice->comp[i].valid_check; + } + if (ice->comp[i].nominated_check) { + check_ptr[check_ptr_cnt++] = &ice->comp[i].nominated_check; + } } - if (ice->comp[i].nominated_check) { - check_ptr[check_ptr_cnt++] = &ice->comp[i].nominated_check; - } - } - for (i=0; icount-1; ++i) { - unsigned j, highest = i; + for (i=0; icount-1; ++i) { + unsigned j, highest = i; - for (j=i+1; jcount; ++j) { - if (CMP_CHECK_PRIO(&clist->checks[j], &clist->checks[highest]) > 0) { - highest = j; - } - } + for (j=i+1; jcount; ++j) { + if (CMP_CHECK_PRIO(&clist->checks[j], &clist->checks[highest]) > 0) { + highest = j; + } + } - if (highest != i) { - pj_ice_sess_check tmp; - unsigned k; + if (highest != i) { + pj_ice_sess_check tmp; + unsigned k; - pj_memcpy(&tmp, &clist->checks[i], sizeof(pj_ice_sess_check)); - pj_memcpy(&clist->checks[i], &clist->checks[highest], - sizeof(pj_ice_sess_check)); - pj_memcpy(&clist->checks[highest], &tmp, - sizeof(pj_ice_sess_check)); + pj_memcpy(&tmp, &clist->checks[i], sizeof(pj_ice_sess_check)); + pj_memcpy(&clist->checks[i], &clist->checks[highest], + sizeof(pj_ice_sess_check)); + pj_memcpy(&clist->checks[highest], &tmp, + sizeof(pj_ice_sess_check)); - /* Update valid and nominated check pointers, since we're moving - * around checks - */ - for (k=0; kchecks[highest]) - *check_ptr[k] = &clist->checks[i]; - else if (*check_ptr[k] == &clist->checks[i]) - *check_ptr[k] = &clist->checks[highest]; - } + /* Update valid and nominated check pointers, since we're moving + * around checks + */ + for (k=0; kchecks[highest]) + *check_ptr[k] = &clist->checks[i]; + else if (*check_ptr[k] == &clist->checks[i]) + *check_ptr[k] = &clist->checks[highest]; + } + } } - } } enum { - SOCKADDR_EQUAL = 0, - SOCKADDR_NOT_EQUAL = 1 + SOCKADDR_EQUAL = 0, + SOCKADDR_NOT_EQUAL = 1 }; /* Utility: compare sockaddr. - * Returns 0 if equal. - */ +* Returns 0 if equal. +*/ static int sockaddr_cmp(const pj_sockaddr *a1, const pj_sockaddr *a2) { - if (a1->addr.sa_family != a2->addr.sa_family) - return SOCKADDR_NOT_EQUAL; + if (a1->addr.sa_family != a2->addr.sa_family) + return SOCKADDR_NOT_EQUAL; - if (a1->addr.sa_family == pj_AF_INET()) { - return !(a1->ipv4.sin_addr.s_addr == a2->ipv4.sin_addr.s_addr && - a1->ipv4.sin_port == a2->ipv4.sin_port); - } else if (a1->addr.sa_family == pj_AF_INET6()) { - return pj_memcmp(&a1->ipv6, &a2->ipv6, sizeof(a1->ipv6)); - } else { - pj_assert(!"Invalid address family!"); - return SOCKADDR_NOT_EQUAL; - } + if (a1->addr.sa_family == pj_AF_INET()) { + return !(a1->ipv4.sin_addr.s_addr == a2->ipv4.sin_addr.s_addr && + a1->ipv4.sin_port == a2->ipv4.sin_port); + } else if (a1->addr.sa_family == pj_AF_INET6()) { + return pj_memcmp(&a1->ipv6, &a2->ipv6, sizeof(a1->ipv6)); + } else { + pj_assert(!"Invalid address family!"); + return SOCKADDR_NOT_EQUAL; + } } /* Prune checklist, this must have been done after the checklist - * is sorted. - */ +* is sorted. +*/ static pj_status_t prune_checklist(pj_ice_sess *ice, - pj_ice_sess_checklist *clist) + pj_ice_sess_checklist *clist) { - unsigned i; + unsigned i; - /* Since an agent cannot send requests directly from a reflexive - * candidate, but only from its base, the agent next goes through the - * sorted list of candidate pairs. For each pair where the local - * candidate is server reflexive, the server reflexive candidate MUST be - * replaced by its base. Once this has been done, the agent MUST prune - * the list. This is done by removing a pair if its local and remote - * candidates are identical to the local and remote candidates of a pair - * higher up on the priority list. The result is a sequence of ordered - * candidate pairs, called the check list for that media stream. - */ - /* First replace SRFLX candidates with their base */ - for (i=0; icount; ++i) { - pj_ice_sess_cand *srflx = clist->checks[i].lcand; + /* Since an agent cannot send requests directly from a reflexive + * candidate, but only from its base, the agent next goes through the + * sorted list of candidate pairs. For each pair where the local + * candidate is server reflexive, the server reflexive candidate MUST be + * replaced by its base. Once this has been done, the agent MUST prune + * the list. This is done by removing a pair if its local and remote + * candidates are identical to the local and remote candidates of a pair + * higher up on the priority list. The result is a sequence of ordered + * candidate pairs, called the check list for that media stream. + */ + /* First replace SRFLX candidates with their base */ + for (i=0; icount; ++i) { + pj_ice_sess_cand *srflx = clist->checks[i].lcand; - if (clist->checks[i].lcand->type == PJ_ICE_CAND_TYPE_SRFLX) { - /* Find the base for this candidate */ - unsigned j; - for (j=0; jlcand_cnt; ++j) { - pj_ice_sess_cand *host = &ice->lcand[j]; + if (clist->checks[i].lcand->type == PJ_ICE_CAND_TYPE_SRFLX) { + /* Find the base for this candidate */ + unsigned j; + for (j=0; jlcand_cnt; ++j) { + pj_ice_sess_cand *host = &ice->lcand[j]; - if (host->type != PJ_ICE_CAND_TYPE_HOST) - continue; + if (host->type != PJ_ICE_CAND_TYPE_HOST) + continue; - if (sockaddr_cmp(&srflx->base_addr, &host->addr) == 0) { - /* Replace this SRFLX with its BASE */ - clist->checks[i].lcand = host; - break; + if (sockaddr_cmp(&srflx->base_addr, &host->addr) == 0) { + /* Replace this SRFLX with its BASE */ + clist->checks[i].lcand = host; + break; + } + } + + if (j==ice->lcand_cnt) { + /* Host candidate not found this this srflx! */ + LOG4((ice->obj_name, + "Base candidate %s:%d not found for srflx candidate %d", + pj_inet_ntoa(srflx->base_addr.ipv4.sin_addr), + pj_ntohs(srflx->base_addr.ipv4.sin_port), + GET_LCAND_ID(clist->checks[i].lcand))); + return PJNATH_EICENOHOSTCAND; + } } - } - - if (j==ice->lcand_cnt) { - /* Host candidate not found this this srflx! */ - LOG4((ice->obj_name, - "Base candidate %s:%d not found for srflx candidate %d", - pj_inet_ntoa(srflx->base_addr.ipv4.sin_addr), - pj_ntohs(srflx->base_addr.ipv4.sin_port), - GET_LCAND_ID(clist->checks[i].lcand))); - return PJNATH_EICENOHOSTCAND; - } } - } - /* Next remove a pair if its local and remote candidates are identical - * to the local and remote candidates of a pair higher up on the priority - * list - */ - /* - * Not in ICE! - * Remove host candidates if their base are the the same! - */ - for (i=0; icount; ++i) { - pj_ice_sess_cand *licand = clist->checks[i].lcand; - pj_ice_sess_cand *ricand = clist->checks[i].rcand; - unsigned j; + /* Next remove a pair if its local and remote candidates are identical + * to the local and remote candidates of a pair higher up on the priority + * list + */ + /* + * Not in ICE! + * Remove host candidates if their base are the the same! + */ + for (i=0; icount; ++i) { + pj_ice_sess_cand *licand = clist->checks[i].lcand; + pj_ice_sess_cand *ricand = clist->checks[i].rcand; + unsigned j; - for (j=i+1; jcount;) { - pj_ice_sess_cand *ljcand = clist->checks[j].lcand; - pj_ice_sess_cand *rjcand = clist->checks[j].rcand; - const char *reason = NULL; + for (j=i+1; jcount;) { + pj_ice_sess_cand *ljcand = clist->checks[j].lcand; + pj_ice_sess_cand *rjcand = clist->checks[j].rcand; + const char *reason = NULL; - if ((licand == ljcand) && (ricand == rjcand)) { - reason = "duplicate found"; - } else if ((rjcand == ricand) && - (sockaddr_cmp(&ljcand->base_addr, - &licand->base_addr)==0)) - { - reason = "equal base"; - } + if ((licand == ljcand) && (ricand == rjcand)) { + reason = "duplicate found"; + } else if ((rjcand == ricand) && + (sockaddr_cmp(&ljcand->base_addr, + &licand->base_addr)==0)) + { + reason = "equal base"; + } - if (reason != NULL) { - /* Found duplicate, remove it */ - LOG5((ice->obj_name, "Check %s pruned (%s)", - dump_check(ice->tmp.txt, sizeof(ice->tmp.txt), - &ice->clist, &clist->checks[j]), - reason)); + if (reason != NULL) { + /* Found duplicate, remove it */ + LOG5((ice->obj_name, "Check %s pruned (%s)", + dump_check(ice->tmp.txt, sizeof(ice->tmp.txt), + &ice->clist, &clist->checks[j]), + reason)); - pj_array_erase(clist->checks, sizeof(clist->checks[0]), - clist->count, j); - --clist->count; + pj_array_erase(clist->checks, sizeof(clist->checks[0]), + clist->count, j); + --clist->count; - } else { - ++j; - } + } else { + ++j; + } + } } - } - return PJ_SUCCESS; + return PJ_SUCCESS; } /* Timer callback */ static void on_timer(pj_timer_heap_t *th, pj_timer_entry *te) { - pj_ice_sess *ice = (pj_ice_sess*) te->user_data; - enum timer_type type = (enum timer_type)te->id; - pj_bool_t has_mutex = PJ_TRUE; + pj_ice_sess *ice = (pj_ice_sess*) te->user_data; + enum timer_type type = (enum timer_type)te->id; + pj_bool_t has_mutex = PJ_TRUE; - PJ_UNUSED_ARG(th); + PJ_UNUSED_ARG(th); - pj_mutex_lock(ice->mutex); + pj_mutex_lock(ice->mutex); - te->id = TIMER_NONE; + te->id = TIMER_NONE; - switch (type) { - case TIMER_CONTROLLED_WAIT_NOM: - LOG4((ice->obj_name, - "Controlled agent timed-out in waiting for the controlling " - "agent to send nominated check. Setting state to fail now..")); - on_ice_complete(ice, PJNATH_EICENOMTIMEOUT); - break; - case TIMER_COMPLETION_CALLBACK: - { - void (*on_ice_complete)(pj_ice_sess *ice, pj_status_t status); - pj_status_t ice_status; + switch (type) { + case TIMER_CONTROLLED_WAIT_NOM: + LOG4((ice->obj_name, + "Controlled agent timed-out in waiting for the controlling " + "agent to send nominated check. Setting state to fail now..")); + on_ice_complete(ice, PJNATH_EICENOMTIMEOUT); + break; + case TIMER_COMPLETION_CALLBACK: + { + void (*on_ice_complete)(pj_ice_sess *ice, pj_status_t status); + pj_status_t ice_status; - /* Start keep-alive timer but don't send any packets yet. - * 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); + /* Start keep-alive timer but don't send any packets yet. + * Need to do it here just in case app destroy the session + * in the callback. + */ - /* Release mutex in case app destroy us in the callback */ - ice_status = ice->ice_status; - on_ice_complete = ice->cb.on_ice_complete; - has_mutex = PJ_FALSE; - pj_mutex_unlock(ice->mutex); + if (ice->ice_status == PJ_SUCCESS) + ice_keep_alive(ice, PJ_FALSE); - /* Notify app about ICE completion*/ - if (on_ice_complete) - (*on_ice_complete)(ice, ice_status); + /* Release mutex in case app destroy us in the callback */ + ice_status = ice->ice_status; + on_ice_complete = ice->cb.on_ice_complete; + has_mutex = PJ_FALSE; + pj_mutex_unlock(ice->mutex); + + /* Notify app about ICE completion*/ + if (on_ice_complete) + (*on_ice_complete)(ice, ice_status); + } + break; + case TIMER_START_NOMINATED_CHECK: + start_nominated_check(ice); + break; + case TIMER_KEEP_ALIVE: + ice_keep_alive(ice, PJ_TRUE); + break; + case TIMER_NONE: + /* Nothing to do, just to get rid of gcc warning */ + break; } - break; - case TIMER_START_NOMINATED_CHECK: - start_nominated_check(ice); - break; - case TIMER_KEEP_ALIVE: - ice_keep_alive(ice, PJ_TRUE); - break; - case TIMER_NONE: - /* Nothing to do, just to get rid of gcc warning */ - break; - } - if (has_mutex) - pj_mutex_unlock(ice->mutex); + if (has_mutex) + pj_mutex_unlock(ice->mutex); } /* Send keep-alive */ static void ice_keep_alive(pj_ice_sess *ice, pj_bool_t send_now) { - if (send_now) { - /* Send Binding Indication for the component */ - pj_ice_sess_comp *comp = &ice->comp[ice->comp_ka]; - pj_stun_tx_data *tdata; - pj_ice_sess_check *the_check; - pj_ice_msg_data *msg_data; - int addr_len; - pj_bool_t saved; - pj_status_t status; + if (send_now) { + /* Send Binding Indication for the component */ + pj_ice_sess_comp *comp = &ice->comp[ice->comp_ka]; + pj_stun_tx_data *tdata; + pj_ice_sess_check *the_check; + pj_ice_msg_data *msg_data; + int addr_len; + pj_bool_t saved; + pj_status_t status; - /* Must have nominated check by now */ - pj_assert(comp->nominated_check != NULL); - the_check = comp->nominated_check; + /* Must have nominated check by now */ + pj_assert(comp->nominated_check != NULL); + the_check = comp->nominated_check; - /* Create the Binding Indication */ - status = pj_stun_session_create_ind(comp->stun_sess, - PJ_STUN_BINDING_INDICATION, - &tdata); - if (status != PJ_SUCCESS) - goto done; + /* Create the Binding Indication */ + status = pj_stun_session_create_ind(comp->stun_sess, + PJ_STUN_BINDING_INDICATION, + &tdata); + if (status != PJ_SUCCESS) + goto done; - /* Need the transport_id */ - msg_data = PJ_POOL_ZALLOC_T(tdata->pool, pj_ice_msg_data); - msg_data->transport_id = the_check->lcand->transport_id; + /* Need the transport_id */ + msg_data = PJ_POOL_ZALLOC_T(tdata->pool, pj_ice_msg_data); + msg_data->transport_id = the_check->lcand->transport_id; - /* Temporarily disable FINGERPRINT. The Binding Indication - * SHOULD NOT contain any attributes. - */ - saved = pj_stun_session_use_fingerprint(comp->stun_sess, PJ_FALSE); + /* Temporarily disable FINGERPRINT. The Binding Indication + * SHOULD NOT contain any attributes. + */ + saved = pj_stun_session_use_fingerprint(comp->stun_sess, PJ_FALSE); - /* Send to session */ - addr_len = pj_sockaddr_get_len(&the_check->rcand->addr); - status = pj_stun_session_send_msg(comp->stun_sess, msg_data, - PJ_FALSE, PJ_FALSE, - &the_check->rcand->addr, - addr_len, tdata); + /* Send to session */ + addr_len = pj_sockaddr_get_len(&the_check->rcand->addr); + status = pj_stun_session_send_msg(comp->stun_sess, msg_data, + PJ_FALSE, PJ_FALSE, + &the_check->rcand->addr, + addr_len, tdata); - /* Restore FINGERPRINT usage */ - pj_stun_session_use_fingerprint(comp->stun_sess, saved); + /* Restore FINGERPRINT usage */ + pj_stun_session_use_fingerprint(comp->stun_sess, saved); done: - ice->comp_ka = (ice->comp_ka + 1) % ice->comp_cnt; - } + ice->comp_ka = (ice->comp_ka + 1) % ice->comp_cnt; + } - if (ice->timer.id == TIMER_NONE) { - pj_time_val delay = { 0, 0 }; + if (ice->timer.id == TIMER_NONE) { + pj_time_val delay = { 0, 0 }; - delay.msec = (PJ_ICE_SESS_KEEP_ALIVE_MIN + - (pj_rand() % PJ_ICE_SESS_KEEP_ALIVE_MAX_RAND)) * 1000 / - ice->comp_cnt; - pj_time_val_normalize(&delay); + delay.msec = (PJ_ICE_SESS_KEEP_ALIVE_MIN + + (pj_rand() % PJ_ICE_SESS_KEEP_ALIVE_MAX_RAND)) * 1000 / + ice->comp_cnt; + pj_time_val_normalize(&delay); - ice->timer.id = TIMER_KEEP_ALIVE; - pj_timer_heap_schedule(ice->stun_cfg.timer_heap, &ice->timer, &delay); + ice->timer.id = TIMER_KEEP_ALIVE; + pj_timer_heap_schedule(ice->stun_cfg.timer_heap, &ice->timer, &delay); - } else { - pj_assert(!"Not expected any timer active"); - } + } else { + pj_assert(!"Not expected any timer active"); + } } /* This function is called when ICE processing completes */ static void on_ice_complete(pj_ice_sess *ice, pj_status_t status) { - if (!ice->is_complete) { - ice->is_complete = PJ_TRUE; - ice->ice_status = status; - - if (ice->timer.id != TIMER_NONE) { - pj_timer_heap_cancel(ice->stun_cfg.timer_heap, &ice->timer); - ice->timer.id = TIMER_NONE; - } + if (!ice->is_complete) { + ice->is_complete = PJ_TRUE; + ice->ice_status = status; - /* Log message */ - LOG4((ice->obj_name, "ICE process complete, status=%s", - pj_strerror(status, ice->tmp.errmsg, - sizeof(ice->tmp.errmsg)).ptr)); + if (ice->timer.id != TIMER_NONE) { + pj_timer_heap_cancel(ice->stun_cfg.timer_heap, &ice->timer); + ice->timer.id = TIMER_NONE; + } - dump_checklist("Valid list", ice, &ice->valid_list); + /* Log message */ + LOG4((ice->obj_name, "ICE process complete, status=%s", + pj_strerror(status, ice->tmp.errmsg, + sizeof(ice->tmp.errmsg)).ptr)); - /* Call callback */ - if (ice->cb.on_ice_complete) { - pj_time_val delay = {0, 0}; + dump_checklist("Valid list", ice, &ice->valid_list); - ice->timer.id = TIMER_COMPLETION_CALLBACK; - pj_timer_heap_schedule(ice->stun_cfg.timer_heap, - &ice->timer, &delay); + /* Call callback */ + if (ice->cb.on_ice_complete) { + pj_time_val delay = {0, 0}; + + ice->timer.id = TIMER_COMPLETION_CALLBACK; + pj_timer_heap_schedule(ice->stun_cfg.timer_heap, + &ice->timer, &delay); + } } - } } /* Update valid check and nominated check for the candidate */ static void update_comp_check(pj_ice_sess *ice, unsigned comp_id, - pj_ice_sess_check *check) + pj_ice_sess_check *check) { - pj_ice_sess_comp *comp; + pj_ice_sess_comp *comp; - comp = find_comp(ice, comp_id); - if (comp->valid_check == NULL) { - comp->valid_check = check; - } else { - if (CMP_CHECK_PRIO(comp->valid_check, check) < 0) - comp->valid_check = check; - } - - if (check->nominated) { - /* Update the nominated check for the component */ - if (comp->nominated_check == NULL) { - comp->nominated_check = check; + comp = find_comp(ice, comp_id); + if (comp->valid_check == NULL) { + comp->valid_check = check; } else { - if (CMP_CHECK_PRIO(comp->nominated_check, check) < 0) - comp->nominated_check = check; + if (CMP_CHECK_PRIO(comp->valid_check, check) < 0) + comp->valid_check = check; } - } + + if (check->nominated) { + /* Update the nominated check for the component */ + if (comp->nominated_check == NULL) { + comp->nominated_check = check; + } else { + if (CMP_CHECK_PRIO(comp->nominated_check, check) < 0) + comp->nominated_check = check; + } + } } /* This function is called when one check completes */ static pj_bool_t on_check_complete(pj_ice_sess *ice, - pj_ice_sess_check *check) + pj_ice_sess_check *check) { - pj_ice_sess_comp *comp; - unsigned i; + pj_ice_sess_comp *comp; + unsigned i; - pj_assert(check->state >= PJ_ICE_SESS_CHECK_STATE_SUCCEEDED); + pj_assert(check->state >= PJ_ICE_SESS_CHECK_STATE_SUCCEEDED); - comp = find_comp(ice, check->lcand->comp_id); + comp = find_comp(ice, check->lcand->comp_id); - /* 7.1.2.2.2. Updating Pair States - * - * The agent sets the state of the pair that generated the check to - * Succeeded. The success of this check might also cause the state of - * other checks to change as well. The agent MUST perform the following - * two steps: - * - * 1. The agent changes the states for all other Frozen pairs for the - * same media stream and same foundation to Waiting. Typically - * these other pairs will have different component IDs but not - * always. - */ - if (check->err_code==PJ_SUCCESS) { + /* 7.1.2.2.2. Updating Pair States + * + * The agent sets the state of the pair that generated the check to + * Succeeded. The success of this check might also cause the state of + * other checks to change as well. The agent MUST perform the following + * two steps: + * + * 1. The agent changes the states for all other Frozen pairs for the + * same media stream and same foundation to Waiting. Typically + * these other pairs will have different component IDs but not + * always. + */ + if (check->err_code==PJ_SUCCESS) { - for (i=0; iclist.count; ++i) { - pj_ice_sess_check *c = &ice->clist.checks[i]; - if (pj_strcmp(&c->lcand->foundation, &check->lcand->foundation)==0 - && c->state == PJ_ICE_SESS_CHECK_STATE_FROZEN) - { - check_set_state(ice, c, PJ_ICE_SESS_CHECK_STATE_WAITING, 0); - } - } + for (i=0; iclist.count; ++i) { + pj_ice_sess_check *c = &ice->clist.checks[i]; + if (pj_strcmp(&c->lcand->foundation, &check->lcand->foundation)==0 + && c->state == PJ_ICE_SESS_CHECK_STATE_FROZEN) + { + check_set_state(ice, c, PJ_ICE_SESS_CHECK_STATE_WAITING, 0); + } + } - LOG5((ice->obj_name, "Check %d is successful%s", - GET_CHECK_ID(&ice->clist, check), - (check->nominated ? " and nominated" : ""))); + LOG5((ice->obj_name, "Check %d is successful%s", + GET_CHECK_ID(&ice->clist, check), + (check->nominated ? " and nominated" : ""))); - } + } - /* 8.2. Updating States - * - * For both controlling and controlled agents, the state of ICE - * processing depends on the presence of nominated candidate pairs in - * the valid list and on the state of the check list: - * - * o If there are no nominated pairs in the valid list for a media - * stream and the state of the check list is Running, ICE processing - * continues. - * - * o If there is at least one nominated pair in the valid list: - * - * - The agent MUST remove all Waiting and Frozen pairs in the check - * list for the same component as the nominated pairs for that - * media stream - * - * - If an In-Progress pair in the check list is for the same - * component as a nominated pair, the agent SHOULD cease - * retransmissions for its check if its pair priority is lower - * than the lowest priority nominated pair for that component - */ - if (check->err_code==PJ_SUCCESS && check->nominated) { + /* 8.2. Updating States + * + * For both controlling and controlled agents, the state of ICE + * processing depends on the presence of nominated candidate pairs in + * the valid list and on the state of the check list: + * + * o If there are no nominated pairs in the valid list for a media + * stream and the state of the check list is Running, ICE processing + * continues. + * + * o If there is at least one nominated pair in the valid list: + * + * - The agent MUST remove all Waiting and Frozen pairs in the check + * list for the same component as the nominated pairs for that + * media stream + * + * - If an In-Progress pair in the check list is for the same + * component as a nominated pair, the agent SHOULD cease + * retransmissions for its check if its pair priority is lower + * than the lowest priority nominated pair for that component + */ + if (check->err_code==PJ_SUCCESS && check->nominated) { - for (i=0; iclist.count; ++i) { + for (i=0; iclist.count; ++i) { - pj_ice_sess_check *c = &ice->clist.checks[i]; + pj_ice_sess_check *c = &ice->clist.checks[i]; - if (c->lcand->comp_id == check->lcand->comp_id) { + if (c->lcand->comp_id == check->lcand->comp_id) { - if (c->state < PJ_ICE_SESS_CHECK_STATE_IN_PROGRESS) { + if (c->state < PJ_ICE_SESS_CHECK_STATE_IN_PROGRESS) { - /* Just fail Frozen/Waiting check */ - LOG5((ice->obj_name, - "Check %s to be failed because state is %s", - dump_check(ice->tmp.txt, sizeof(ice->tmp.txt), - &ice->clist, c), - check_state_name[c->state])); - check_set_state(ice, c, PJ_ICE_SESS_CHECK_STATE_FAILED, - PJ_ECANCELLED); + /* Just fail Frozen/Waiting check */ + LOG5((ice->obj_name, + "Check %s to be failed because state is %s", + dump_check(ice->tmp.txt, sizeof(ice->tmp.txt), + &ice->clist, c), + check_state_name[c->state])); + check_set_state(ice, c, PJ_ICE_SESS_CHECK_STATE_FAILED, + PJ_ECANCELLED); - } else if (c->state == PJ_ICE_SESS_CHECK_STATE_IN_PROGRESS - && (PJ_ICE_CANCEL_ALL || - CMP_CHECK_PRIO(c, check) < 0)) { + } else if (c->state == PJ_ICE_SESS_CHECK_STATE_IN_PROGRESS + && (PJ_ICE_CANCEL_ALL || + CMP_CHECK_PRIO(c, check) < 0)) { - /* State is IN_PROGRESS, cancel transaction */ - if (c->tdata) { - LOG5((ice->obj_name, - "Cancelling check %s (In Progress)", - dump_check(ice->tmp.txt, sizeof(ice->tmp.txt), - &ice->clist, c))); - pj_stun_session_cancel_req(comp->stun_sess, - c->tdata, PJ_FALSE, 0); - c->tdata = NULL; - check_set_state(ice, c, PJ_ICE_SESS_CHECK_STATE_FAILED, - PJ_ECANCELLED); - } + /* State is IN_PROGRESS, cancel transaction */ + if (c->tdata) { + LOG5((ice->obj_name, + "Cancelling check %s (In Progress)", + dump_check(ice->tmp.txt, sizeof(ice->tmp.txt), + &ice->clist, c))); + pj_stun_session_cancel_req(comp->stun_sess, + c->tdata, PJ_FALSE, 0); + c->tdata = NULL; + check_set_state(ice, c, PJ_ICE_SESS_CHECK_STATE_FAILED, + PJ_ECANCELLED); + } + } + } } - } } - } - /* Still in 8.2. Updating States - * - * o Once there is at least one nominated pair in the valid list for - * every component of at least one media stream and the state of the - * check list is Running: - * - * * The agent MUST change the state of processing for its check - * list for that media stream to Completed. - * - * * The agent MUST continue to respond to any checks it may still - * receive for that media stream, and MUST perform triggered - * checks if required by the processing of Section 7.2. - * - * * The agent MAY begin transmitting media for this media stream as - * described in Section 11.1 - */ + /* Still in 8.2. Updating States + * + * o Once there is at least one nominated pair in the valid list for + * every component of at least one media stream and the state of the + * check list is Running: + * + * * The agent MUST change the state of processing for its check + * list for that media stream to Completed. + * + * * The agent MUST continue to respond to any checks it may still + * receive for that media stream, and MUST perform triggered + * checks if required by the processing of Section 7.2. + * + * * The agent MAY begin transmitting media for this media stream as + * described in Section 11.1 + */ - /* See if all components have nominated pair. If they do, then mark - * ICE processing as success, otherwise wait. - */ - for (i=0; icomp_cnt; ++i) { - if (ice->comp[i].nominated_check == NULL) - break; - } - if (i == ice->comp_cnt) { - /* All components have nominated pair */ - on_ice_complete(ice, PJ_SUCCESS); - return PJ_TRUE; - } + /* See if all components have nominated pair. If they do, then mark + * ICE processing as success, otherwise wait. + */ + for (i=0; icomp_cnt; ++i) { + if (ice->comp[i].nominated_check == NULL) + break; + } + if (i == ice->comp_cnt) { + /* All components have nominated pair */ + on_ice_complete(ice, PJ_SUCCESS); + return PJ_TRUE; + } - /* Note: this is the stuffs that we don't do in 7.1.2.2.2, since our - * ICE session only supports one media stream for now: - * - * 7.1.2.2.2. Updating Pair States - * - * 2. If there is a pair in the valid list for every component of this - * media stream (where this is the actual number of components being - * used, in cases where the number of components signaled in the SDP - * differs from offerer to answerer), the success of this check may - * unfreeze checks for other media streams. - */ + /* Note: this is the stuffs that we don't do in 7.1.2.2.2, since our + * ICE session only supports one media stream for now: + * + * 7.1.2.2.2. Updating Pair States + * + * 2. If there is a pair in the valid list for every component of this + * media stream (where this is the actual number of components being + * used, in cases where the number of components signaled in the SDP + * differs from offerer to answerer), the success of this check may + * unfreeze checks for other media streams. + */ - /* 7.1.2.3. Check List and Timer State Updates - * Regardless of whether the check was successful or failed, the - * completion of the transaction may require updating of check list and - * timer states. - * - * If all of the pairs in the check list are now either in the Failed or - * Succeeded state, and there is not a pair in the valid list for each - * component of the media stream, the state of the check list is set to - * Failed. - */ + /* 7.1.2.3. Check List and Timer State Updates + * Regardless of whether the check was successful or failed, the + * completion of the transaction may require updating of check list and + * timer states. + * + * If all of the pairs in the check list are now either in the Failed or + * Succeeded state, and there is not a pair in the valid list for each + * component of the media stream, the state of the check list is set to + * Failed. + */ - /* - * See if all checks in the checklist have completed. If we do, - * then mark ICE processing as failed. - */ - for (i=0; iclist.count; ++i) { - pj_ice_sess_check *c = &ice->clist.checks[i]; - if (c->state < PJ_ICE_SESS_CHECK_STATE_SUCCEEDED) { - break; + /* + * See if all checks in the checklist have completed. If we do, + * then mark ICE processing as failed. + */ + for (i=0; iclist.count; ++i) { + pj_ice_sess_check *c = &ice->clist.checks[i]; + if (c->state < PJ_ICE_SESS_CHECK_STATE_SUCCEEDED) { + break; + } } - } - if (i == ice->clist.count) { - /* All checks have completed, but we don't have nominated pair. - * If agent's role is controlled, check if all components have - * valid pair. If it does, this means the controlled agent has - * finished the check list and it's waiting for controlling - * agent to send checks with USE-CANDIDATE flag set. - */ - if (ice->role == PJ_ICE_SESS_ROLE_CONTROLLED) { - for (i=0; i < ice->comp_cnt; ++i) { - if (ice->comp[i].valid_check == NULL) - break; - } + if (i == ice->clist.count) { + /* All checks have completed, but we don't have nominated pair. + * If agent's role is controlled, check if all components have + * valid pair. If it does, this means the controlled agent has + * finished the check list and it's waiting for controlling + * agent to send checks with USE-CANDIDATE flag set. + */ + if (ice->role == PJ_ICE_SESS_ROLE_CONTROLLED) { + for (i=0; i < ice->comp_cnt; ++i) { + if (ice->comp[i].valid_check == NULL) + break; + } - if (i < ice->comp_cnt) { - /* This component ID doesn't have valid pair. - * Mark ICE as failed. - */ - on_ice_complete(ice, PJNATH_EICEFAILED); - return PJ_TRUE; - } else { - /* All components have a valid pair. - * We should wait until we receive nominated checks. - */ - if (ice->timer.id == TIMER_NONE && - ice->opt.controlled_agent_want_nom_timeout >= 0) - { - pj_time_val delay; + if (i < ice->comp_cnt) { + /* This component ID doesn't have valid pair. + * Mark ICE as failed. + */ + on_ice_complete(ice, PJNATH_EICEFAILED); + return PJ_TRUE; + } else { + /* All components have a valid pair. + * We should wait until we receive nominated checks. + */ + if (ice->timer.id == TIMER_NONE && + ice->opt.controlled_agent_want_nom_timeout >= 0) + { + pj_time_val delay; - delay.sec = 0; - delay.msec = ice->opt.controlled_agent_want_nom_timeout; - pj_time_val_normalize(&delay); + delay.sec = 0; + delay.msec = ice->opt.controlled_agent_want_nom_timeout; + pj_time_val_normalize(&delay); - ice->timer.id = TIMER_CONTROLLED_WAIT_NOM; - pj_timer_heap_schedule(ice->stun_cfg.timer_heap, - &ice->timer, - &delay); + ice->timer.id = TIMER_CONTROLLED_WAIT_NOM; + pj_timer_heap_schedule(ice->stun_cfg.timer_heap, + &ice->timer, + &delay); - LOG5((ice->obj_name, - "All checks have completed. Controlled agent now " - "waits for nomination from controlling agent " - "(timeout=%d msec)", - ice->opt.controlled_agent_want_nom_timeout)); - } - return PJ_FALSE; - } + LOG5((ice->obj_name, + "All checks have completed. Controlled agent now " + "waits for nomination from controlling agent " + "(timeout=%d msec)", + ice->opt.controlled_agent_want_nom_timeout)); + } + return PJ_FALSE; + } - /* Unreached */ + /* Unreached */ - } else if (ice->is_nominating) { - /* We are controlling agent and all checks have completed but - * there's at least one component without nominated pair (or - * more likely we don't have any nominated pairs at all). - */ - on_ice_complete(ice, PJNATH_EICEFAILED); - return PJ_TRUE; + } else if (ice->is_nominating) { + /* We are controlling agent and all checks have completed but + * there's at least one component without nominated pair (or + * more likely we don't have any nominated pairs at all). + */ + on_ice_complete(ice, PJNATH_EICEFAILED); + return PJ_TRUE; - } else { - /* We are controlling agent and all checks have completed. If - * we have valid list for every component, then move on to - * sending nominated check, otherwise we have failed. - */ - for (i=0; icomp_cnt; ++i) { - if (ice->comp[i].valid_check == NULL) - break; - } + } else { + /* We are controlling agent and all checks have completed. If + * we have valid list for every component, then move on to + * sending nominated check, otherwise we have failed. + */ + for (i=0; icomp_cnt; ++i) { + if (ice->comp[i].valid_check == NULL) + break; + } - if (i < ice->comp_cnt) { - /* At least one component doesn't have a valid check. Mark - * ICE as failed. - */ - on_ice_complete(ice, PJNATH_EICEFAILED); - return PJ_TRUE; - } + if (i < ice->comp_cnt) { + /* At least one component doesn't have a valid check. Mark + * ICE as failed. + */ + on_ice_complete(ice, PJNATH_EICEFAILED); + return PJ_TRUE; + } - /* Now it's time to send connectivity check with nomination - * flag set. - */ - LOG4((ice->obj_name, - "All checks have completed, starting nominated checks now")); - start_nominated_check(ice); - return PJ_FALSE; + /* Now it's time to send connectivity check with nomination + * flag set. + */ + LOG4((ice->obj_name, + "All checks have completed, starting nominated checks now")); + start_nominated_check(ice); + return PJ_FALSE; + } } - } - /* If this connectivity check has been successful, scan all components - * and see if they have a valid pair, if we are controlling and we haven't - * started our nominated check yet. - */ - if (check->err_code == PJ_SUCCESS && - ice->role==PJ_ICE_SESS_ROLE_CONTROLLING && - !ice->is_nominating && - ice->timer.id == TIMER_NONE) - { - pj_time_val delay; + /* If this connectivity check has been successful, scan all components + * and see if they have a valid pair, if we are controlling and we haven't + * started our nominated check yet. + */ + if (check->err_code == PJ_SUCCESS && + ice->role==PJ_ICE_SESS_ROLE_CONTROLLING && + !ice->is_nominating && + ice->timer.id == TIMER_NONE) + { + pj_time_val delay; - for (i=0; icomp_cnt; ++i) { - if (ice->comp[i].valid_check == NULL) - break; - } + for (i=0; icomp_cnt; ++i) { + if (ice->comp[i].valid_check == NULL) + break; + } - if (i < ice->comp_cnt) { - /* Some components still don't have valid pair, continue - * processing. - */ - return PJ_FALSE; - } + if (i < ice->comp_cnt) { + /* Some components still don't have valid pair, continue + * processing. + */ + return PJ_FALSE; + } - LOG4((ice->obj_name, - "Scheduling nominated check in %d ms", - ice->opt.nominated_check_delay)); + LOG4((ice->obj_name, + "Scheduling nominated check in %d ms", + ice->opt.nominated_check_delay)); - if (ice->timer.id != TIMER_NONE) { - pj_timer_heap_cancel(ice->stun_cfg.timer_heap, &ice->timer); - ice->timer.id = TIMER_NONE; + if (ice->timer.id != TIMER_NONE) { + pj_timer_heap_cancel(ice->stun_cfg.timer_heap, &ice->timer); + ice->timer.id = TIMER_NONE; + } + + /* All components have valid pair. Let connectivity checks run for + * a little bit more time, then start our nominated check. + */ + delay.sec = 0; + delay.msec = ice->opt.nominated_check_delay; + pj_time_val_normalize(&delay); + + ice->timer.id = TIMER_START_NOMINATED_CHECK; + pj_timer_heap_schedule(ice->stun_cfg.timer_heap, &ice->timer, &delay); + return PJ_FALSE; } - /* All components have valid pair. Let connectivity checks run for - * a little bit more time, then start our nominated check. - */ - delay.sec = 0; - delay.msec = ice->opt.nominated_check_delay; - pj_time_val_normalize(&delay); - - ice->timer.id = TIMER_START_NOMINATED_CHECK; - pj_timer_heap_schedule(ice->stun_cfg.timer_heap, &ice->timer, &delay); + /* We still have checks to perform */ return PJ_FALSE; - } - - /* We still have checks to perform */ - return PJ_FALSE; } /* Create checklist by pairing local candidates with remote candidates */ PJ_DEF(pj_status_t) pj_ice_sess_create_check_list( - pj_ice_sess *ice, - const pj_str_t *rem_ufrag, - const pj_str_t *rem_passwd, - unsigned rcand_cnt, - const pj_ice_sess_cand rcand[]) + pj_ice_sess *ice, + const pj_str_t *rem_ufrag, + const pj_str_t *rem_passwd, + unsigned rcand_cnt, + const pj_ice_sess_cand rcand[]) { - pj_ice_sess_checklist *clist; - char buf[128]; - pj_str_t username; - timer_data *td; - unsigned i, j; - unsigned highest_comp = 0; - pj_status_t status; + pj_ice_sess_checklist *clist; + char buf[128]; + pj_str_t username; + timer_data *td; + unsigned i, j; + unsigned highest_comp = 0; + pj_status_t status; - PJ_ASSERT_RETURN(ice && rem_ufrag && rem_passwd && rcand_cnt && rcand, - PJ_EINVAL); - PJ_ASSERT_RETURN(rcand_cnt + ice->rcand_cnt <= PJ_ICE_MAX_CAND, - PJ_ETOOMANY); + PJ_ASSERT_RETURN(ice && rem_ufrag && rem_passwd && rcand_cnt && rcand, + PJ_EINVAL); + PJ_ASSERT_RETURN(rcand_cnt + ice->rcand_cnt <= PJ_ICE_MAX_CAND, + PJ_ETOOMANY); - pj_mutex_lock(ice->mutex); + pj_mutex_lock(ice->mutex); - /* Save credentials */ - username.ptr = buf; + /* Save credentials */ + username.ptr = buf; - pj_strcpy(&username, rem_ufrag); - pj_strcat2(&username, ":"); - pj_strcat(&username, &ice->rx_ufrag); + pj_strcpy(&username, rem_ufrag); + if(!ice->opt.is_webrtc){ + pj_strcat2(&username, ":"); + } + pj_strcat(&username, &ice->rx_ufrag); - pj_strdup(ice->pool, &ice->tx_uname, &username); - pj_strdup(ice->pool, &ice->tx_ufrag, rem_ufrag); - pj_strdup(ice->pool, &ice->tx_pass, rem_passwd); + pj_strdup(ice->pool, &ice->tx_uname, &username); + pj_strdup(ice->pool, &ice->tx_ufrag, rem_ufrag); + pj_strdup(ice->pool, &ice->tx_pass, rem_passwd); - pj_strcpy(&username, &ice->rx_ufrag); - pj_strcat2(&username, ":"); - pj_strcat(&username, rem_ufrag); + pj_strcpy(&username, &ice->rx_ufrag); + if(!ice->opt.is_webrtc){ + pj_strcat2(&username, ":"); + } + pj_strcat(&username, rem_ufrag); - pj_strdup(ice->pool, &ice->rx_uname, &username); + pj_strdup(ice->pool, &ice->rx_uname, &username); - /* Save remote candidates */ - ice->rcand_cnt = 0; - for (i=0; ircand[ice->rcand_cnt]; + /* Save remote candidates */ + ice->rcand_cnt = 0; + for (i=0; ircand[ice->rcand_cnt]; - /* Ignore candidate which has no matching component ID */ - if (rcand[i].comp_id==0 || rcand[i].comp_id > ice->comp_cnt) { - continue; + /* Ignore candidate which has no matching component ID */ + if (rcand[i].comp_id==0 || rcand[i].comp_id > ice->comp_cnt) { + continue; + } + + if (rcand[i].comp_id > highest_comp) + highest_comp = rcand[i].comp_id; + + pj_memcpy(cn, &rcand[i], sizeof(pj_ice_sess_cand)); + pj_strdup(ice->pool, &cn->foundation, &rcand[i].foundation); + ice->rcand_cnt++; } - if (rcand[i].comp_id > highest_comp) - highest_comp = rcand[i].comp_id; + /* Generate checklist */ + clist = &ice->clist; + for (i=0; ilcand_cnt; ++i) { + for (j=0; jrcand_cnt; ++j) { - pj_memcpy(cn, &rcand[i], sizeof(pj_ice_sess_cand)); - pj_strdup(ice->pool, &cn->foundation, &rcand[i].foundation); - ice->rcand_cnt++; - } + pj_ice_sess_cand *lcand = &ice->lcand[i]; + pj_ice_sess_cand *rcand = &ice->rcand[j]; + pj_ice_sess_check *chk = &clist->checks[clist->count]; - /* Generate checklist */ - clist = &ice->clist; - for (i=0; ilcand_cnt; ++i) { - for (j=0; jrcand_cnt; ++j) { + if (clist->count >= PJ_ICE_MAX_CHECKS) { + pj_mutex_unlock(ice->mutex); + return PJ_ETOOMANY; + } - pj_ice_sess_cand *lcand = &ice->lcand[i]; - pj_ice_sess_cand *rcand = &ice->rcand[j]; - pj_ice_sess_check *chk = &clist->checks[clist->count]; + /* A local candidate is paired with a remote candidate if + * and only if the two candidates have the same component ID + * and have the same IP address version. + */ + if ((lcand->comp_id != rcand->comp_id) || + (lcand->addr.addr.sa_family != rcand->addr.addr.sa_family)) + { + continue; + } - if (clist->count >= PJ_ICE_MAX_CHECKS) { - pj_mutex_unlock(ice->mutex); - return PJ_ETOOMANY; - } - /* A local candidate is paired with a remote candidate if - * and only if the two candidates have the same component ID - * and have the same IP address version. - */ - if ((lcand->comp_id != rcand->comp_id) || - (lcand->addr.addr.sa_family != rcand->addr.addr.sa_family)) - { - continue; - } + chk->lcand = lcand; + chk->rcand = rcand; + chk->state = PJ_ICE_SESS_CHECK_STATE_FROZEN; + chk->prio = CALC_CHECK_PRIO(ice, lcand, rcand); - chk->lcand = lcand; - chk->rcand = rcand; - chk->state = PJ_ICE_SESS_CHECK_STATE_FROZEN; + clist->count++; + } + } - chk->prio = CALC_CHECK_PRIO(ice, lcand, rcand); + /* Sort checklist based on priority */ + sort_checklist(ice, clist); - clist->count++; + /* Prune the checklist */ + status = prune_checklist(ice, clist); + if (status != PJ_SUCCESS) { + pj_mutex_unlock(ice->mutex); + return status; } - } - /* Sort checklist based on priority */ - sort_checklist(ice, clist); + /* Disable our components which don't have matching component */ + for (i=highest_comp; icomp_cnt; ++i) { + if (ice->comp[i].stun_sess) { + pj_stun_session_destroy(ice->comp[i].stun_sess); + pj_bzero(&ice->comp[i], sizeof(ice->comp[i])); + } + } + ice->comp_cnt = highest_comp; - /* Prune the checklist */ - status = prune_checklist(ice, clist); - if (status != PJ_SUCCESS) { - pj_mutex_unlock(ice->mutex); - return status; - } + // 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); - /* Disable our components which don't have matching component */ - for (i=highest_comp; icomp_cnt; ++i) { - if (ice->comp[i].stun_sess) { - pj_stun_session_destroy(ice->comp[i].stun_sess); - pj_bzero(&ice->comp[i], sizeof(ice->comp[i])); + for(i = 0; i < ice->comp_cnt ; ++i){ + pj_stun_session_set_credential(ice->comp[i].stun_sess, + PJ_STUN_AUTH_WEBRTC, + &auth_cred); + } } - } - ice->comp_cnt = highest_comp; - /* Init timer entry in the checklist. Initially the timer ID is FALSE - * because timer is not running. - */ - clist->timer.id = PJ_FALSE; - td = PJ_POOL_ZALLOC_T(ice->pool, timer_data); - td->ice = ice; - td->clist = clist; - clist->timer.user_data = (void*)td; - clist->timer.cb = &periodic_timer; + /* Init timer entry in the checklist. Initially the timer ID is FALSE + * because timer is not running. + */ + clist->timer.id = PJ_FALSE; + td = PJ_POOL_ZALLOC_T(ice->pool, timer_data); + td->ice = ice; + td->clist = clist; + clist->timer.user_data = (void*)td; + clist->timer.cb = &periodic_timer; - /* Log checklist */ - dump_checklist("Checklist created:", ice, clist); + /* Log checklist */ + dump_checklist("Checklist created:", ice, clist); - pj_mutex_unlock(ice->mutex); + pj_mutex_unlock(ice->mutex); - return PJ_SUCCESS; + return PJ_SUCCESS; } /* Perform check on the specified candidate pair. */ static pj_status_t perform_check(pj_ice_sess *ice, - pj_ice_sess_checklist *clist, - unsigned check_id, - pj_bool_t nominate) + pj_ice_sess_checklist *clist, + unsigned check_id, + pj_bool_t nominate) { - pj_ice_sess_comp *comp; - pj_ice_msg_data *msg_data; - pj_ice_sess_check *check; - const pj_ice_sess_cand *lcand; - const pj_ice_sess_cand *rcand; - pj_uint32_t prio; - pj_status_t status; + pj_ice_sess_comp *comp; + pj_ice_msg_data *msg_data; + pj_ice_sess_check *check; + const pj_ice_sess_cand *lcand; + const pj_ice_sess_cand *rcand; + pj_uint32_t prio; + pj_status_t status; - check = &clist->checks[check_id]; - lcand = check->lcand; - rcand = check->rcand; - comp = find_comp(ice, lcand->comp_id); + check = &clist->checks[check_id]; + lcand = check->lcand; + rcand = check->rcand; + comp = find_comp(ice, lcand->comp_id); - LOG5((ice->obj_name, - "Sending connectivity check for check %s", - dump_check(ice->tmp.txt, sizeof(ice->tmp.txt), clist, check))); - pj_log_push_indent(); + LOG5((ice->obj_name, + "Sending connectivity check for check %s", + dump_check(ice->tmp.txt, sizeof(ice->tmp.txt), clist, check))); + pj_log_push_indent(); - /* Create request */ - status = pj_stun_session_create_req(comp->stun_sess, - PJ_STUN_BINDING_REQUEST, PJ_STUN_MAGIC, - NULL, &check->tdata); - if (status != PJ_SUCCESS) { - pjnath_perror(ice->obj_name, "Error creating STUN request", status); - pj_log_pop_indent(); - return status; - } + /* Create request */ + status = pj_stun_session_create_req(comp->stun_sess, + PJ_STUN_BINDING_REQUEST, PJ_STUN_MAGIC, + NULL, &check->tdata); + if (status != PJ_SUCCESS) { + pjnath_perror(ice->obj_name, "Error creating STUN request", status); + pj_log_pop_indent(); + return status; + } - /* Attach data to be retrieved later when STUN request transaction - * completes and on_stun_request_complete() callback is called. - */ - msg_data = PJ_POOL_ZALLOC_T(check->tdata->pool, pj_ice_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 = clist; - msg_data->data.req.ckid = check_id; + /* Attach data to be retrieved later when STUN request transaction + * completes and on_stun_request_complete() callback is called. + */ + msg_data = PJ_POOL_ZALLOC_T(check->tdata->pool, pj_ice_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 = clist; + msg_data->data.req.ckid = check_id; - /* Add PRIORITY */ + /* Add PRIORITY */ #if PJNATH_ICE_PRIO_STD - prio = CALC_CAND_PRIO(ice, PJ_ICE_CAND_TYPE_PRFLX, 65535, - lcand->comp_id); + prio = CALC_CAND_PRIO(ice, PJ_ICE_CAND_TYPE_PRFLX, 65535, + lcand->comp_id); #else - prio = CALC_CAND_PRIO(ice, PJ_ICE_CAND_TYPE_PRFLX, 0, - lcand->comp_id); + prio = CALC_CAND_PRIO(ice, PJ_ICE_CAND_TYPE_PRFLX, 0, + lcand->comp_id); #endif - pj_stun_msg_add_uint_attr(check->tdata->pool, check->tdata->msg, - PJ_STUN_ATTR_PRIORITY, prio); + pj_stun_msg_add_uint_attr(check->tdata->pool, check->tdata->msg, + PJ_STUN_ATTR_PRIORITY, prio); - /* Add USE-CANDIDATE and set this check to nominated. - * Also add ICE-CONTROLLING or ICE-CONTROLLED - */ - if (ice->role == PJ_ICE_SESS_ROLE_CONTROLLING) { - if (nominate) { - pj_stun_msg_add_empty_attr(check->tdata->pool, check->tdata->msg, - PJ_STUN_ATTR_USE_CANDIDATE); - check->nominated = PJ_TRUE; + /* Add USE-CANDIDATE and set this check to nominated. + * Also add ICE-CONTROLLING or ICE-CONTROLLED + */ + if (ice->role == PJ_ICE_SESS_ROLE_CONTROLLING) { + if (nominate) { + pj_stun_msg_add_empty_attr(check->tdata->pool, check->tdata->msg, + PJ_STUN_ATTR_USE_CANDIDATE); + check->nominated = PJ_TRUE; + } + + pj_stun_msg_add_uint64_attr(check->tdata->pool, check->tdata->msg, + PJ_STUN_ATTR_ICE_CONTROLLING, + &ice->tie_breaker); + + } else { + pj_stun_msg_add_uint64_attr(check->tdata->pool, check->tdata->msg, + PJ_STUN_ATTR_ICE_CONTROLLED, + &ice->tie_breaker); } - pj_stun_msg_add_uint64_attr(check->tdata->pool, check->tdata->msg, - PJ_STUN_ATTR_ICE_CONTROLLING, - &ice->tie_breaker); - } else { - pj_stun_msg_add_uint64_attr(check->tdata->pool, check->tdata->msg, - PJ_STUN_ATTR_ICE_CONTROLLED, - &ice->tie_breaker); - } + /* Note that USERNAME and MESSAGE-INTEGRITY will be added by the + * STUN session. + */ + /* Initiate STUN transaction to send the request */ + status = pj_stun_session_send_msg(comp->stun_sess, msg_data, PJ_FALSE, + PJ_TRUE, &rcand->addr, + sizeof(pj_sockaddr_in), check->tdata); + if (status != PJ_SUCCESS) { + check->tdata = NULL; + pjnath_perror(ice->obj_name, "Error sending STUN request", status); + pj_log_pop_indent(); + return status; + } - /* Note that USERNAME and MESSAGE-INTEGRITY will be added by the - * STUN session. - */ - - /* Initiate STUN transaction to send the request */ - status = pj_stun_session_send_msg(comp->stun_sess, msg_data, PJ_FALSE, - PJ_TRUE, &rcand->addr, - sizeof(pj_sockaddr_in), check->tdata); - if (status != PJ_SUCCESS) { - check->tdata = NULL; - pjnath_perror(ice->obj_name, "Error sending STUN request", status); + check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_IN_PROGRESS, + PJ_SUCCESS); pj_log_pop_indent(); - return status; - } - - check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_IN_PROGRESS, - PJ_SUCCESS); - pj_log_pop_indent(); - return PJ_SUCCESS; + return PJ_SUCCESS; } /* Start periodic check for the specified checklist. - * This callback is called by timer on every Ta (20msec by default) - */ +* This callback is called by timer on every Ta (20msec by default) +*/ static pj_status_t start_periodic_check(pj_timer_heap_t *th, - pj_timer_entry *te) + pj_timer_entry *te) { - timer_data *td; - pj_ice_sess *ice; - pj_ice_sess_checklist *clist; - unsigned i, start_count=0; - pj_status_t status; + timer_data *td; + pj_ice_sess *ice; + pj_ice_sess_checklist *clist; + unsigned i, start_count=0; + pj_status_t status; - td = (struct timer_data*) te->user_data; - ice = td->ice; - clist = td->clist; + td = (struct timer_data*) te->user_data; + ice = td->ice; + clist = td->clist; - if (ice->is_destroying) - return PJ_SUCCESS; + if (ice->is_destroying) + return PJ_SUCCESS; - pj_mutex_lock(ice->mutex); + pj_mutex_lock(ice->mutex); - if (ice->is_destroying) { - pj_mutex_unlock(ice->mutex); - return PJ_SUCCESS; - } + if (ice->is_destroying) { + pj_mutex_unlock(ice->mutex); + return PJ_SUCCESS; + } - /* Set timer ID to FALSE first */ - te->id = PJ_FALSE; + /* Set timer ID to FALSE first */ + te->id = PJ_FALSE; - /* Set checklist state to Running */ - clist_set_state(ice, clist, PJ_ICE_SESS_CHECKLIST_ST_RUNNING); + /* Set checklist state to Running */ + clist_set_state(ice, clist, PJ_ICE_SESS_CHECKLIST_ST_RUNNING); - LOG5((ice->obj_name, "Starting checklist periodic check")); - pj_log_push_indent(); + LOG5((ice->obj_name, "Starting checklist periodic check")); + pj_log_push_indent(); - /* Send STUN Binding request for check with highest priority on - * Waiting state. - */ - for (i=0; icount; ++i) { - pj_ice_sess_check *check = &clist->checks[i]; + /* Send STUN Binding request for check with highest priority on + * Waiting state. + */ + for (i=0; icount; ++i) { + pj_ice_sess_check *check = &clist->checks[i]; - if (check->state == PJ_ICE_SESS_CHECK_STATE_WAITING) { - status = perform_check(ice, clist, i, ice->is_nominating); - if (status != PJ_SUCCESS) { - pj_mutex_unlock(ice->mutex); - pj_log_pop_indent(); - return status; - } + if (check->state == PJ_ICE_SESS_CHECK_STATE_WAITING) { + status = perform_check(ice, clist, i, ice->is_nominating); + if (status != PJ_SUCCESS) { + pj_mutex_unlock(ice->mutex); + pj_log_pop_indent(); + return status; + } - ++start_count; - break; + ++start_count; + break; + } } - } - /* If we don't have anything in Waiting state, perform check to - * highest priority pair that is in Frozen state. - */ - if (start_count==0) { - for (i=0; icount; ++i) { - pj_ice_sess_check *check = &clist->checks[i]; + /* If we don't have anything in Waiting state, perform check to + * highest priority pair that is in Frozen state. + */ + if (start_count==0) { + for (i=0; icount; ++i) { + pj_ice_sess_check *check = &clist->checks[i]; - if (check->state == PJ_ICE_SESS_CHECK_STATE_FROZEN) { - status = perform_check(ice, clist, i, ice->is_nominating); - if (status != PJ_SUCCESS) { - pj_mutex_unlock(ice->mutex); - pj_log_pop_indent(); - return status; + if (check->state == PJ_ICE_SESS_CHECK_STATE_FROZEN) { + status = perform_check(ice, clist, i, ice->is_nominating); + if (status != PJ_SUCCESS) { + pj_mutex_unlock(ice->mutex); + pj_log_pop_indent(); + return status; + } + + ++start_count; + break; + } } - - ++start_count; - break; - } } - } - /* Cannot start check because there's no suitable candidate pair. - */ - if (start_count!=0) { - /* Schedule for next timer */ - pj_time_val timeout = {0, PJ_ICE_TA_VAL}; + /* Cannot start check because there's no suitable candidate pair. + */ + if (start_count!=0) { + /* Schedule for next timer */ + pj_time_val timeout = {0, PJ_ICE_TA_VAL}; - te->id = PJ_TRUE; - pj_time_val_normalize(&timeout); - pj_timer_heap_schedule(th, te, &timeout); - } + te->id = PJ_TRUE; + pj_time_val_normalize(&timeout); + pj_timer_heap_schedule(th, te, &timeout); + } - pj_mutex_unlock(ice->mutex); - pj_log_pop_indent(); - return PJ_SUCCESS; + pj_mutex_unlock(ice->mutex); + pj_log_pop_indent(); + return PJ_SUCCESS; } /* Start sending connectivity check with USE-CANDIDATE */ static void start_nominated_check(pj_ice_sess *ice) { - pj_time_val delay; - unsigned i; - pj_status_t status; + pj_time_val delay; + unsigned i; + pj_status_t status; - LOG4((ice->obj_name, "Starting nominated check..")); - pj_log_push_indent(); + LOG4((ice->obj_name, "Starting nominated check..")); + pj_log_push_indent(); - pj_assert(ice->is_nominating == PJ_FALSE); + pj_assert(ice->is_nominating == PJ_FALSE); - /* Stop our timer if it's active */ - if (ice->timer.id == TIMER_START_NOMINATED_CHECK) { - pj_timer_heap_cancel(ice->stun_cfg.timer_heap, &ice->timer); - ice->timer.id = TIMER_NONE; - } + /* Stop our timer if it's active */ + if (ice->timer.id == TIMER_START_NOMINATED_CHECK) { + pj_timer_heap_cancel(ice->stun_cfg.timer_heap, &ice->timer); + ice->timer.id = TIMER_NONE; + } - /* For each component, set the check state of valid check with - * highest priority to Waiting (it should have Success state now). - */ - for (i=0; icomp_cnt; ++i) { - unsigned j; - const pj_ice_sess_check *vc = ice->comp[i].valid_check; + /* For each component, set the check state of valid check with + * highest priority to Waiting (it should have Success state now). + */ + for (i=0; icomp_cnt; ++i) { + unsigned j; + const pj_ice_sess_check *vc = ice->comp[i].valid_check; - pj_assert(ice->comp[i].nominated_check == NULL); - pj_assert(vc->err_code == PJ_SUCCESS); + pj_assert(ice->comp[i].nominated_check == NULL); + pj_assert(vc->err_code == PJ_SUCCESS); - for (j=0; jclist.count; ++j) { - pj_ice_sess_check *c = &ice->clist.checks[j]; - if (c->lcand->transport_id == vc->lcand->transport_id && - c->rcand == vc->rcand) - { - pj_assert(c->err_code == PJ_SUCCESS); - c->state = PJ_ICE_SESS_CHECK_STATE_FROZEN; - check_set_state(ice, c, PJ_ICE_SESS_CHECK_STATE_WAITING, - PJ_SUCCESS); - break; - } + for (j=0; jclist.count; ++j) { + pj_ice_sess_check *c = &ice->clist.checks[j]; + if (c->lcand->transport_id == vc->lcand->transport_id && + c->rcand == vc->rcand) + { + pj_assert(c->err_code == PJ_SUCCESS); + c->state = PJ_ICE_SESS_CHECK_STATE_FROZEN; + check_set_state(ice, c, PJ_ICE_SESS_CHECK_STATE_WAITING, + PJ_SUCCESS); + break; + } + } } - } - /* And (re)start the periodic check */ - if (ice->clist.timer.id) { - pj_timer_heap_cancel(ice->stun_cfg.timer_heap, &ice->clist.timer); - ice->clist.timer.id = PJ_FALSE; - } + /* And (re)start the periodic check */ + if (ice->clist.timer.id) { + pj_timer_heap_cancel(ice->stun_cfg.timer_heap, &ice->clist.timer); + ice->clist.timer.id = PJ_FALSE; + } - ice->clist.timer.id = PJ_TRUE; - delay.sec = delay.msec = 0; - status = pj_timer_heap_schedule(ice->stun_cfg.timer_heap, - &ice->clist.timer, &delay); - if (status != PJ_SUCCESS) { - ice->clist.timer.id = PJ_FALSE; - } else { - LOG5((ice->obj_name, "Periodic timer rescheduled..")); - } + ice->clist.timer.id = PJ_TRUE; + delay.sec = delay.msec = 0; + status = pj_timer_heap_schedule(ice->stun_cfg.timer_heap, + &ice->clist.timer, &delay); + if (status != PJ_SUCCESS) { + ice->clist.timer.id = PJ_FALSE; + } else { + LOG5((ice->obj_name, "Periodic timer rescheduled..")); + } - ice->is_nominating = PJ_TRUE; - pj_log_pop_indent(); + ice->is_nominating = PJ_TRUE; + pj_log_pop_indent(); } /* Timer callback to perform periodic check */ static void periodic_timer(pj_timer_heap_t *th, - pj_timer_entry *te) + pj_timer_entry *te) { - start_periodic_check(th, te); + start_periodic_check(th, te); } /* Utility: find string in string array */ const pj_str_t *find_str(const pj_str_t *strlist[], unsigned count, - const pj_str_t *str) + const pj_str_t *str) { - unsigned i; - for (i=0; iclist.count > 0, PJ_EINVALIDOP); + /* Checklist must have been created */ + PJ_ASSERT_RETURN(ice->clist.count > 0, PJ_EINVALIDOP); - /* Lock session */ - pj_mutex_lock(ice->mutex); + /* Lock session */ + pj_mutex_lock(ice->mutex); - LOG4((ice->obj_name, "Starting ICE check..")); - pj_log_push_indent(); + LOG4((ice->obj_name, "Starting ICE check..")); + pj_log_push_indent(); - /* If we are using aggressive nomination, set the is_nominating state */ - if (ice->opt.aggressive) - ice->is_nominating = PJ_TRUE; + /* If we are using aggressive nomination, set the is_nominating state */ + if (ice->opt.aggressive) + ice->is_nominating = PJ_TRUE; - /* The agent examines the check list for the first media stream (a - * media stream is the first media stream when it is described by - * the first m-line in the SDP offer and answer). For that media - * stream, it: - * - * - Groups together all of the pairs with the same foundation, - * - * - For each group, sets the state of the pair with the lowest - * component ID to Waiting. If there is more than one such pair, - * the one with the highest priority is used. - */ + /* The agent examines the check list for the first media stream (a + * media stream is the first media stream when it is described by + * the first m-line in the SDP offer and answer). For that media + * stream, it: + * + * - Groups together all of the pairs with the same foundation, + * + * - For each group, sets the state of the pair with the lowest + * component ID to Waiting. If there is more than one such pair, + * the one with the highest priority is used. + */ - clist = &ice->clist; + clist = &ice->clist; - /* Pickup the first pair for component 1. */ - for (i=0; icount; ++i) { - if (clist->checks[i].lcand->comp_id == 1) - break; - } - if (i == clist->count) { - pj_assert(!"Unable to find checklist for component 1"); - pj_mutex_unlock(ice->mutex); - pj_log_pop_indent(); - return PJNATH_EICEINCOMPID; - } + /* Pickup the first pair for component 1. */ + for (i=0; icount; ++i) { + if (clist->checks[i].lcand->comp_id == 1) + break; + } + if (i == clist->count) { + pj_assert(!"Unable to find checklist for component 1"); + pj_mutex_unlock(ice->mutex); + pj_log_pop_indent(); + return PJNATH_EICEINCOMPID; + } - /* Set this check to WAITING only if state is frozen. It may be possible - * that this check has already been started by a trigger check - */ - if (clist->checks[i].state == PJ_ICE_SESS_CHECK_STATE_FROZEN) { - check_set_state(ice, &clist->checks[i], + /* Set this check to WAITING only if state is frozen. It may be possible + * that this check has already been started by a trigger check + */ + if (clist->checks[i].state == PJ_ICE_SESS_CHECK_STATE_FROZEN) { + check_set_state(ice, &clist->checks[i], PJ_ICE_SESS_CHECK_STATE_WAITING, PJ_SUCCESS); - } + } - cand0 = clist->checks[i].lcand; - flist[flist_cnt++] = &clist->checks[i].lcand->foundation; + cand0 = clist->checks[i].lcand; + flist[flist_cnt++] = &clist->checks[i].lcand->foundation; - /* Find all of the other pairs in that check list with the same - * component ID, but different foundations, and sets all of their - * states to Waiting as well. - */ - for (++i; icount; ++i) { - const pj_ice_sess_cand *cand1; + /* Find all of the other pairs in that check list with the same + * component ID, but different foundations, and sets all of their + * states to Waiting as well. + */ + for (++i; icount; ++i) { + const pj_ice_sess_cand *cand1; - cand1 = clist->checks[i].lcand; + cand1 = clist->checks[i].lcand; - if (cand1->comp_id==cand0->comp_id && - find_str(flist, flist_cnt, &cand1->foundation)==NULL) - { - if (clist->checks[i].state == PJ_ICE_SESS_CHECK_STATE_FROZEN) { - check_set_state(ice, &clist->checks[i], - PJ_ICE_SESS_CHECK_STATE_WAITING, PJ_SUCCESS); - } - flist[flist_cnt++] = &cand1->foundation; + if (cand1->comp_id==cand0->comp_id && + find_str(flist, flist_cnt, &cand1->foundation)==NULL) + { + if (clist->checks[i].state == PJ_ICE_SESS_CHECK_STATE_FROZEN) { + check_set_state(ice, &clist->checks[i], + PJ_ICE_SESS_CHECK_STATE_WAITING, PJ_SUCCESS); + } + flist[flist_cnt++] = &cand1->foundation; + } } - } - /* First, perform all pending triggered checks, simultaneously. */ - rcheck = ice->early_check.next; - while (rcheck != &ice->early_check) { - LOG4((ice->obj_name, - "Performing delayed triggerred check for component %d", - rcheck->comp_id)); - pj_log_push_indent(); - handle_incoming_check(ice, rcheck); - rcheck = rcheck->next; - pj_log_pop_indent(); - } - pj_list_init(&ice->early_check); + /* First, perform all pending triggered checks, simultaneously. */ + rcheck = ice->early_check.next; + while (rcheck != &ice->early_check) { + LOG4((ice->obj_name, + "Performing delayed triggerred check for component %d", + rcheck->comp_id)); + pj_log_push_indent(); + handle_incoming_check(ice, rcheck); + rcheck = rcheck->next; + pj_log_pop_indent(); + } + pj_list_init(&ice->early_check); - /* Start periodic check */ - /* We could start it immediately like below, but lets schedule timer - * instead to reduce stack usage: - * return start_periodic_check(ice->stun_cfg.timer_heap, &clist->timer); - */ - clist->timer.id = PJ_TRUE; - delay.sec = delay.msec = 0; - status = pj_timer_heap_schedule(ice->stun_cfg.timer_heap, - &clist->timer, &delay); - if (status != PJ_SUCCESS) { - clist->timer.id = PJ_FALSE; - } + /* Start periodic check */ + /* We could start it immediately like below, but lets schedule timer + * instead to reduce stack usage: + * return start_periodic_check(ice->stun_cfg.timer_heap, &clist->timer); + */ + clist->timer.id = PJ_TRUE; + delay.sec = delay.msec = 0; + status = pj_timer_heap_schedule(ice->stun_cfg.timer_heap, + &clist->timer, &delay); + if (status != PJ_SUCCESS) { + clist->timer.id = PJ_FALSE; + } - pj_mutex_unlock(ice->mutex); - pj_log_pop_indent(); - return status; + pj_mutex_unlock(ice->mutex); + pj_log_pop_indent(); + return status; } ////////////////////////////////////////////////////////////////////////////// /* Callback called by STUN session to send the STUN message. - * STUN session also doesn't have a transport, remember?! - */ +* STUN session also doesn't have a transport, remember?! +*/ static pj_status_t on_stun_send_msg(pj_stun_session *sess, - void *token, - const void *pkt, - pj_size_t pkt_size, - const pj_sockaddr_t *dst_addr, - unsigned addr_len) + void *token, + const void *pkt, + pj_size_t pkt_size, + const pj_sockaddr_t *dst_addr, + unsigned addr_len) { - stun_data *sd = (stun_data*) pj_stun_session_get_user_data(sess); - pj_ice_sess *ice = sd->ice; - pj_ice_msg_data *msg_data = (pj_ice_msg_data*) token; - - return (*ice->cb.on_tx_pkt)(ice, sd->comp_id, msg_data->transport_id, - pkt, pkt_size, dst_addr, addr_len); + stun_data *sd = (stun_data*) pj_stun_session_get_user_data(sess); + pj_ice_sess *ice = sd->ice; + pj_ice_msg_data *msg_data = (pj_ice_msg_data*) token; + + return (*ice->cb.on_tx_pkt)(ice, sd->comp_id, msg_data->transport_id, + pkt, pkt_size, dst_addr, addr_len); } /* This callback is called when outgoing STUN request completed */ static void on_stun_request_complete(pj_stun_session *stun_sess, - pj_status_t status, - void *token, - pj_stun_tx_data *tdata, - const pj_stun_msg *response, - const pj_sockaddr_t *src_addr, - unsigned src_addr_len) + pj_status_t status, + void *token, + pj_stun_tx_data *tdata, + const pj_stun_msg *response, + const pj_sockaddr_t *src_addr, + unsigned src_addr_len) { - pj_ice_msg_data *msg_data = (pj_ice_msg_data*) token; - pj_ice_sess *ice; - pj_ice_sess_check *check, *new_check; - pj_ice_sess_cand *lcand; - pj_ice_sess_checklist *clist; - pj_stun_xor_mapped_addr_attr *xaddr; - unsigned i; + pj_ice_msg_data *msg_data = (pj_ice_msg_data*) token; + pj_ice_sess *ice; + pj_ice_sess_check *check, *new_check; + 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); - PJ_UNUSED_ARG(src_addr_len); + PJ_UNUSED_ARG(stun_sess); + PJ_UNUSED_ARG(src_addr_len); - pj_assert(msg_data->has_req_data); + pj_assert(msg_data->has_req_data); - ice = msg_data->data.req.ice; - clist = msg_data->data.req.clist; - check = &clist->checks[msg_data->data.req.ckid]; - + ice = msg_data->data.req.ice; + clist = msg_data->data.req.clist; + check = &clist->checks[msg_data->data.req.ckid]; - /* Mark STUN transaction as complete */ - pj_assert(tdata == check->tdata); - check->tdata = NULL; + //FIXME: Find why WebRTC keepalive requests cause tdata mismatch + if(tdata != check->tdata){ + return; + } - pj_mutex_lock(ice->mutex); + /* Mark STUN transaction as complete */ + pj_assert(tdata == check->tdata); + check->tdata = NULL; - /* Init lcand to NULL. lcand will be found from the mapped address - * found in the response. - */ - lcand = NULL; + pj_mutex_lock(ice->mutex); - if (status != PJ_SUCCESS) { - char errmsg[PJ_ERR_MSG_SIZE]; + /* Init lcand to NULL. lcand will be found from the mapped address + * found in the response. + */ + lcand = NULL; - if (status==PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_ROLE_CONFLICT)) { + if (status != PJ_SUCCESS) { + char errmsg[PJ_ERR_MSG_SIZE]; - /* Role conclict response. - * - * 7.1.2.1. Failure Cases: - * - * If the request had contained the ICE-CONTROLLED attribute, - * the agent MUST switch to the controlling role if it has not - * already done so. If the request had contained the - * ICE-CONTROLLING attribute, the agent MUST switch to the - * controlled role if it has not already done so. Once it has - * switched, the agent MUST immediately retry the request with - * the ICE-CONTROLLING or ICE-CONTROLLED attribute reflecting - * its new role. - */ - pj_ice_sess_role new_role = PJ_ICE_SESS_ROLE_UNKNOWN; - pj_stun_msg *req = tdata->msg; + if (status==PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_ROLE_CONFLICT)) { - if (pj_stun_msg_find_attr(req, PJ_STUN_ATTR_ICE_CONTROLLING, 0)) { - new_role = PJ_ICE_SESS_ROLE_CONTROLLED; - } else if (pj_stun_msg_find_attr(req, PJ_STUN_ATTR_ICE_CONTROLLED, - 0)) { - new_role = PJ_ICE_SESS_ROLE_CONTROLLING; - } else { - pj_assert(!"We should have put CONTROLLING/CONTROLLED attr!"); - new_role = PJ_ICE_SESS_ROLE_CONTROLLED; - } + /* Role conclict response. + * + * 7.1.2.1. Failure Cases: + * + * If the request had contained the ICE-CONTROLLED attribute, + * the agent MUST switch to the controlling role if it has not + * already done so. If the request had contained the + * ICE-CONTROLLING attribute, the agent MUST switch to the + * controlled role if it has not already done so. Once it has + * switched, the agent MUST immediately retry the request with + * the ICE-CONTROLLING or ICE-CONTROLLED attribute reflecting + * its new role. + */ + pj_ice_sess_role new_role = PJ_ICE_SESS_ROLE_UNKNOWN; + pj_stun_msg *req = tdata->msg; - if (new_role != ice->role) { - LOG4((ice->obj_name, - "Changing role because of role conflict response")); - pj_ice_sess_change_role(ice, new_role); - } + if (pj_stun_msg_find_attr(req, PJ_STUN_ATTR_ICE_CONTROLLING, 0)) { + new_role = PJ_ICE_SESS_ROLE_CONTROLLED; + } else if (pj_stun_msg_find_attr(req, PJ_STUN_ATTR_ICE_CONTROLLED, + 0)) { + new_role = PJ_ICE_SESS_ROLE_CONTROLLING; + } else { + pj_assert(!"We should have put CONTROLLING/CONTROLLED attr!"); + new_role = PJ_ICE_SESS_ROLE_CONTROLLED; + } - /* Resend request */ - LOG4((ice->obj_name, "Resending check because of role conflict")); - pj_log_push_indent(); - check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_WAITING, 0); - perform_check(ice, clist, msg_data->data.req.ckid, - check->nominated || ice->is_nominating); - pj_log_pop_indent(); - pj_mutex_unlock(ice->mutex); - return; - } + if (new_role != ice->role) { + LOG4((ice->obj_name, + "Changing role because of role conflict response")); + pj_ice_sess_change_role(ice, new_role); + } - pj_strerror(status, errmsg, sizeof(errmsg)); - LOG4((ice->obj_name, - "Check %s%s: connectivity check FAILED: %s", - dump_check(ice->tmp.txt, sizeof(ice->tmp.txt), + /* Resend request */ + LOG4((ice->obj_name, "Resending check because of role conflict")); + pj_log_push_indent(); + check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_WAITING, 0); + perform_check(ice, clist, msg_data->data.req.ckid, + check->nominated || ice->is_nominating); + pj_log_pop_indent(); + pj_mutex_unlock(ice->mutex); + return; + } + + pj_strerror(status, errmsg, sizeof(errmsg)); + LOG4((ice->obj_name, + "Check %s%s: connectivity check FAILED: %s", + dump_check(ice->tmp.txt, sizeof(ice->tmp.txt), &ice->clist, check), - (check->nominated ? " (nominated)" : " (not nominated)"), - errmsg)); - pj_log_push_indent(); - check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_FAILED, status); - on_check_complete(ice, check); - pj_log_pop_indent(); - pj_mutex_unlock(ice->mutex); - return; - } + (check->nominated ? " (nominated)" : " (not nominated)"), + errmsg)); + pj_log_push_indent(); + check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_FAILED, status); + on_check_complete(ice, check); + pj_log_pop_indent(); + pj_mutex_unlock(ice->mutex); + return; + } - /* 7.1.2.1. Failure Cases - * - * The agent MUST check that the source IP address and port of the - * response equals the destination IP address and port that the Binding - * Request was sent to, and that the destination IP address and port of - * the response match the source IP address and port that the Binding - * Request was sent from. - */ - if (sockaddr_cmp(&check->rcand->addr, (const pj_sockaddr*)src_addr) != 0) { - status = PJNATH_EICEINSRCADDR; - LOG4((ice->obj_name, - "Check %s%s: connectivity check FAILED: source address mismatch", - dump_check(ice->tmp.txt, sizeof(ice->tmp.txt), + /* 7.1.2.1. Failure Cases + * + * The agent MUST check that the source IP address and port of the + * response equals the destination IP address and port that the Binding + * Request was sent to, and that the destination IP address and port of + * the response match the source IP address and port that the Binding + * Request was sent from. + */ + if (sockaddr_cmp(&check->rcand->addr, (const pj_sockaddr*)src_addr) != 0) { + status = PJNATH_EICEINSRCADDR; + LOG4((ice->obj_name, + "Check %s%s: connectivity check FAILED: source address mismatch", + dump_check(ice->tmp.txt, sizeof(ice->tmp.txt), &ice->clist, check), - (check->nominated ? " (nominated)" : " (not nominated)"))); - pj_log_push_indent(); - check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_FAILED, status); - on_check_complete(ice, check); - pj_log_pop_indent(); - pj_mutex_unlock(ice->mutex); - return; - } + (check->nominated ? " (nominated)" : " (not nominated)"))); + pj_log_push_indent(); + check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_FAILED, status); + on_check_complete(ice, check); + pj_log_pop_indent(); + pj_mutex_unlock(ice->mutex); + return; + } - /* 7.1.2.2. Success Cases - * - * A check is considered to be a success if all of the following are - * true: - * - * o the STUN transaction generated a success response - * - * o the source IP address and port of the response equals the - * destination IP address and port that the Binding Request was sent - * to - * - * o the destination IP address and port of the response match the - * source IP address and port that the Binding Request was sent from - */ + /* 7.1.2.2. Success Cases + * + * A check is considered to be a success if all of the following are + * true: + * + * o the STUN transaction generated a success response + * + * o the source IP address and port of the response equals the + * destination IP address and port that the Binding Request was sent + * to + * + * o the destination IP address and port of the response match the + * source IP address and port that the Binding Request was sent from + */ - LOG4((ice->obj_name, - "Check %s%s: connectivity check SUCCESS", - dump_check(ice->tmp.txt, sizeof(ice->tmp.txt), - &ice->clist, check), - (check->nominated ? " (nominated)" : " (not nominated)"))); + LOG4((ice->obj_name, + "Check %s%s: connectivity check SUCCESS", + dump_check(ice->tmp.txt, sizeof(ice->tmp.txt), + &ice->clist, check), + (check->nominated ? " (nominated)" : " (not nominated)"))); - /* 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, + /* 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); + /* 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; - } + 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) { - /* Match */ - lcand = &ice->lcand[i]; - break; + /* Find local candidate that matches the XOR-MAPPED-ADDRESS */ + pj_assert(lcand == NULL); + for (i=0; ilcand_cnt; ++i) { + 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; + } } - } - /* 7.1.2.2.1. Discovering Peer Reflexive Candidates - * If the transport address returned in XOR-MAPPED-ADDRESS does not match - * any of the local candidates that the agent knows about, the mapped - * address represents a new candidate - a peer reflexive candidate. - */ - if (lcand == NULL) { - unsigned cand_id; - pj_str_t foundation; + /* 7.1.2.2.1. Discovering Peer Reflexive Candidates + * If the transport address returned in XOR-MAPPED-ADDRESS does not match + * any of the local candidates that the agent knows about, the mapped + * address represents a new candidate - a peer reflexive candidate. + */ + if (lcand == NULL) { + unsigned cand_id; + pj_str_t foundation; - pj_ice_calc_foundation(ice->pool, &foundation, PJ_ICE_CAND_TYPE_PRFLX, - &check->lcand->base_addr); + pj_ice_calc_foundation(ice->pool, &foundation, PJ_ICE_CAND_TYPE_PRFLX, + &check->lcand->base_addr); - /* Still in 7.1.2.2.1. Discovering Peer Reflexive Candidates - * Its priority is set equal to the value of the PRIORITY attribute - * in the Binding Request. - * - * I think the priority calculated by add_cand() should be the same - * as the one calculated in perform_check(), so there's no need to - * get the priority from the PRIORITY attribute. - */ + /* Still in 7.1.2.2.1. Discovering Peer Reflexive Candidates + * Its priority is set equal to the value of the PRIORITY attribute + * in the Binding Request. + * + * I think the priority calculated by add_cand() should be the same + * as the one calculated in perform_check(), so there's no need to + * get the priority from the PRIORITY attribute. + */ - /* Add new peer reflexive candidate */ - status = pj_ice_sess_add_cand(ice, check->lcand->comp_id, - msg_data->transport_id, - PJ_ICE_CAND_TYPE_PRFLX, - 65535, &foundation, - &xaddr->sockaddr, - &check->lcand->base_addr, - &check->lcand->base_addr, - sizeof(pj_sockaddr_in), &cand_id); - if (status != PJ_SUCCESS) { - check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_FAILED, - status); - on_check_complete(ice, check); - pj_mutex_unlock(ice->mutex); - return; + /* Add new peer reflexive candidate */ + status = pj_ice_sess_add_cand(ice, check->lcand->comp_id, + msg_data->transport_id, + PJ_ICE_CAND_TYPE_PRFLX, + 65535, &foundation, + xaddr ? &xaddr->sockaddr : &addr->sockaddr, + &check->lcand->base_addr, + &check->lcand->base_addr, + sizeof(pj_sockaddr_in), &cand_id); + if (status != PJ_SUCCESS) { + check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_FAILED, + status); + on_check_complete(ice, check); + pj_mutex_unlock(ice->mutex); + return; + } + + /* Update local candidate */ + lcand = &ice->lcand[cand_id]; + } - /* Update local candidate */ - lcand = &ice->lcand[cand_id]; + /* 7.1.2.2.3. Constructing a Valid Pair + * Next, the agent constructs a candidate pair whose local candidate + * equals the mapped address of the response, and whose remote candidate + * equals the destination address to which the request was sent. + */ - } + /* Add pair to valid list, if it's not there, otherwise just update + * nominated flag + */ + for (i=0; ivalid_list.count; ++i) { + if (ice->valid_list.checks[i].lcand == lcand && + ice->valid_list.checks[i].rcand == check->rcand) + break; + } - /* 7.1.2.2.3. Constructing a Valid Pair - * Next, the agent constructs a candidate pair whose local candidate - * equals the mapped address of the response, and whose remote candidate - * equals the destination address to which the request was sent. - */ + if (i==ice->valid_list.count) { + pj_assert(ice->valid_list.count < PJ_ICE_MAX_CHECKS); + new_check = &ice->valid_list.checks[ice->valid_list.count++]; + new_check->lcand = lcand; + new_check->rcand = check->rcand; + new_check->prio = CALC_CHECK_PRIO(ice, lcand, check->rcand); + new_check->state = PJ_ICE_SESS_CHECK_STATE_SUCCEEDED; + new_check->nominated = check->nominated; + new_check->err_code = PJ_SUCCESS; + } else { + new_check = &ice->valid_list.checks[i]; + ice->valid_list.checks[i].nominated = check->nominated; + } - /* Add pair to valid list, if it's not there, otherwise just update - * nominated flag - */ - for (i=0; ivalid_list.count; ++i) { - if (ice->valid_list.checks[i].lcand == lcand && - ice->valid_list.checks[i].rcand == check->rcand) - break; - } + /* Update valid check and nominated check for the component */ + update_comp_check(ice, new_check->lcand->comp_id, new_check); - if (i==ice->valid_list.count) { - pj_assert(ice->valid_list.count < PJ_ICE_MAX_CHECKS); - new_check = &ice->valid_list.checks[ice->valid_list.count++]; - new_check->lcand = lcand; - new_check->rcand = check->rcand; - new_check->prio = CALC_CHECK_PRIO(ice, lcand, check->rcand); - new_check->state = PJ_ICE_SESS_CHECK_STATE_SUCCEEDED; - new_check->nominated = check->nominated; - new_check->err_code = PJ_SUCCESS; - } else { - new_check = &ice->valid_list.checks[i]; - ice->valid_list.checks[i].nominated = check->nominated; - } + /* Sort valid_list (must do so after update_comp_check(), otherwise + * new_check will point to something else (#953) + */ + sort_checklist(ice, &ice->valid_list); - /* Update valid check and nominated check for the component */ - update_comp_check(ice, new_check->lcand->comp_id, new_check); + /* 7.1.2.2.2. Updating Pair States + * + * The agent sets the state of the pair that generated the check to + * Succeeded. The success of this check might also cause the state of + * other checks to change as well. + */ + check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_SUCCEEDED, + PJ_SUCCESS); - /* Sort valid_list (must do so after update_comp_check(), otherwise - * new_check will point to something else (#953) - */ - sort_checklist(ice, &ice->valid_list); + /* Perform 7.1.2.2.2. Updating Pair States. + * This may terminate ICE processing. + */ + if (on_check_complete(ice, check)) { + /* ICE complete! */ + pj_mutex_unlock(ice->mutex); + return; + } - /* 7.1.2.2.2. Updating Pair States - * - * The agent sets the state of the pair that generated the check to - * Succeeded. The success of this check might also cause the state of - * other checks to change as well. - */ - check_set_state(ice, check, PJ_ICE_SESS_CHECK_STATE_SUCCEEDED, - PJ_SUCCESS); - - /* Perform 7.1.2.2.2. Updating Pair States. - * This may terminate ICE processing. - */ - if (on_check_complete(ice, check)) { - /* ICE complete! */ pj_mutex_unlock(ice->mutex); - return; - } - - pj_mutex_unlock(ice->mutex); } /* This callback is called by the STUN session associated with a candidate - * when it receives incoming request. - */ +* when it receives incoming request. +*/ static pj_status_t on_stun_rx_request(pj_stun_session *sess, - const pj_uint8_t *pkt, - unsigned pkt_len, - const pj_stun_rx_data *rdata, - void *token, - const pj_sockaddr_t *src_addr, - unsigned src_addr_len) + const pj_uint8_t *pkt, + unsigned pkt_len, + const pj_stun_rx_data *rdata, + void *token, + const pj_sockaddr_t *src_addr, + unsigned src_addr_len) { - stun_data *sd; - const pj_stun_msg *msg = rdata->msg; - pj_ice_msg_data *msg_data; - pj_ice_sess *ice; - pj_stun_priority_attr *prio_attr; - pj_stun_use_candidate_attr *uc_attr; - pj_stun_uint64_attr *role_attr; - pj_stun_tx_data *tdata; - pj_ice_rx_check *rcheck, tmp_rcheck; - pj_status_t status; + stun_data *sd; + const pj_stun_msg *msg = rdata->msg; + pj_ice_msg_data *msg_data; + pj_ice_sess *ice; + pj_stun_priority_attr *prio_attr; + pj_stun_use_candidate_attr *uc_attr; + pj_stun_uint64_attr *role_attr; + pj_stun_tx_data *tdata; + pj_ice_rx_check *rcheck, tmp_rcheck; + pj_status_t status; - PJ_UNUSED_ARG(pkt); - PJ_UNUSED_ARG(pkt_len); - - /* Reject any requests except Binding request */ - if (msg->hdr.type != PJ_STUN_BINDING_REQUEST) { - pj_stun_session_respond(sess, rdata, PJ_STUN_SC_BAD_REQUEST, - NULL, token, PJ_TRUE, - src_addr, src_addr_len); - return PJ_SUCCESS; - } + PJ_UNUSED_ARG(pkt); + PJ_UNUSED_ARG(pkt_len); + /* Reject any requests except Binding request */ + if (msg->hdr.type != PJ_STUN_BINDING_REQUEST) { + pj_stun_session_respond(sess, rdata, PJ_STUN_SC_BAD_REQUEST, + NULL, token, PJ_TRUE, + src_addr, src_addr_len); + return PJ_SUCCESS; + } - sd = (stun_data*) pj_stun_session_get_user_data(sess); - ice = sd->ice; - pj_mutex_lock(ice->mutex); + sd = (stun_data*) pj_stun_session_get_user_data(sess); + ice = sd->ice; - /* - * Note: - * Be aware that when STUN request is received, we might not get - * SDP answer yet, so we might not have remote candidates and - * checklist yet. This case will be handled after we send - * a response. - */ + pj_mutex_lock(ice->mutex); - /* Get PRIORITY attribute */ - prio_attr = (pj_stun_priority_attr*) - pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_PRIORITY, 0); - if (prio_attr == NULL) { - LOG5((ice->obj_name, "Received Binding request with no PRIORITY")); - pj_mutex_unlock(ice->mutex); - return PJ_SUCCESS; - } + /* + * Note: + * Be aware that when STUN request is received, we might not get + * SDP answer yet, so we might not have remote candidates and + * checklist yet. This case will be handled after we send + * a response. + */ - /* Get USE-CANDIDATE attribute */ - uc_attr = (pj_stun_use_candidate_attr*) - pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_USE_CANDIDATE, 0); + /* Get PRIORITY attribute */ + prio_attr = (pj_stun_priority_attr*) + pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_PRIORITY, 0); + 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; + } + /* Get USE-CANDIDATE attribute */ + uc_attr = (pj_stun_use_candidate_attr*) + pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_USE_CANDIDATE, 0); - /* Get ICE-CONTROLLING or ICE-CONTROLLED */ - role_attr = (pj_stun_uint64_attr*) - pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_ICE_CONTROLLING, 0); - if (role_attr == NULL) { + + /* Get ICE-CONTROLLING or ICE-CONTROLLED */ role_attr = (pj_stun_uint64_attr*) - pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_ICE_CONTROLLED, 0); - } + pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_ICE_CONTROLLING, 0); + if (role_attr == NULL) { + role_attr = (pj_stun_uint64_attr*) + pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_ICE_CONTROLLED, 0); + } - /* Handle the case when request comes before answer is received. - * We need to put credential in the response, and since we haven't - * got the response, copy the username from the request. - */ - if (ice->rcand_cnt == 0) { - pj_stun_string_attr *uname_attr; + /* Handle the case when request comes before answer is received. + * We need to put credential in the response, and since we haven't + * got the response, copy the username from the request. + */ + if (ice->rcand_cnt == 0) { + pj_stun_string_attr *uname_attr; - uname_attr = (pj_stun_string_attr*) - pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_USERNAME, 0); - pj_assert(uname_attr != NULL); - pj_strdup(ice->pool, &ice->rx_uname, &uname_attr->value); - } + uname_attr = (pj_stun_string_attr*) + pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_USERNAME, 0); + pj_assert(uname_attr != NULL); + pj_strdup(ice->pool, &ice->rx_uname, &uname_attr->value); + } - /* 7.2.1.1. Detecting and Repairing Role Conflicts - */ - if (ice->role == PJ_ICE_SESS_ROLE_CONTROLLING && - role_attr && role_attr->hdr.type == PJ_STUN_ATTR_ICE_CONTROLLING) - { - if (pj_cmp_timestamp(&ice->tie_breaker, &role_attr->value) < 0) { - /* Switch role to controlled */ - LOG4((ice->obj_name, - "Changing role because of ICE-CONTROLLING attribute")); - pj_ice_sess_change_role(ice, PJ_ICE_SESS_ROLE_CONTROLLED); - } else { - /* Generate 487 response */ - pj_stun_session_respond(sess, rdata, PJ_STUN_SC_ROLE_CONFLICT, - NULL, token, PJ_TRUE, - src_addr, src_addr_len); - pj_mutex_unlock(ice->mutex); - return PJ_SUCCESS; + /* 7.2.1.1. Detecting and Repairing Role Conflicts + */ + if (ice->role == PJ_ICE_SESS_ROLE_CONTROLLING && + role_attr && role_attr->hdr.type == PJ_STUN_ATTR_ICE_CONTROLLING) + { + if (pj_cmp_timestamp(&ice->tie_breaker, &role_attr->value) < 0) { + /* Switch role to controlled */ + LOG4((ice->obj_name, + "Changing role because of ICE-CONTROLLING attribute")); + pj_ice_sess_change_role(ice, PJ_ICE_SESS_ROLE_CONTROLLED); + } else { + /* Generate 487 response */ + pj_stun_session_respond(sess, rdata, PJ_STUN_SC_ROLE_CONFLICT, + NULL, token, PJ_TRUE, + src_addr, src_addr_len); + pj_mutex_unlock(ice->mutex); + return PJ_SUCCESS; + } + + } else if (ice->role == PJ_ICE_SESS_ROLE_CONTROLLED && + role_attr && role_attr->hdr.type == PJ_STUN_ATTR_ICE_CONTROLLED) + { + if (pj_cmp_timestamp(&ice->tie_breaker, &role_attr->value) < 0) { + /* Generate 487 response */ + pj_stun_session_respond(sess, rdata, PJ_STUN_SC_ROLE_CONFLICT, + NULL, token, PJ_TRUE, + src_addr, src_addr_len); + pj_mutex_unlock(ice->mutex); + return PJ_SUCCESS; + } else { + /* Switch role to controlled */ + LOG4((ice->obj_name, + "Changing role because of ICE-CONTROLLED attribute")); + pj_ice_sess_change_role(ice, PJ_ICE_SESS_ROLE_CONTROLLING); + } } - } else if (ice->role == PJ_ICE_SESS_ROLE_CONTROLLED && - role_attr && role_attr->hdr.type == PJ_STUN_ATTR_ICE_CONTROLLED) - { - if (pj_cmp_timestamp(&ice->tie_breaker, &role_attr->value) < 0) { - /* Generate 487 response */ - pj_stun_session_respond(sess, rdata, PJ_STUN_SC_ROLE_CONFLICT, - NULL, token, PJ_TRUE, - src_addr, src_addr_len); - pj_mutex_unlock(ice->mutex); - return PJ_SUCCESS; - } else { - /* Switch role to controlled */ - LOG4((ice->obj_name, - "Changing role because of ICE-CONTROLLED attribute")); - pj_ice_sess_change_role(ice, PJ_ICE_SESS_ROLE_CONTROLLING); + /* + * First send response to this request + */ + status = pj_stun_session_create_res(sess, rdata, 0, NULL, &tdata); + if (status != PJ_SUCCESS) { + pj_mutex_unlock(ice->mutex); + return status; } - } - /* - * First send response to this request - */ - status = pj_stun_session_create_res(sess, rdata, 0, NULL, &tdata); - if (status != PJ_SUCCESS) { - pj_mutex_unlock(ice->mutex); - return status; - } + /* Add XOR-MAPPED-ADDRESS attribute */ + status = pj_stun_msg_add_sockaddr_attr(tdata->pool, tdata->msg, + PJ_STUN_ATTR_XOR_MAPPED_ADDR, + PJ_TRUE, src_addr, src_addr_len); - /* Add XOR-MAPPED-ADDRESS attribute */ - status = pj_stun_msg_add_sockaddr_attr(tdata->pool, tdata->msg, - 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; - msg_data->has_req_data = PJ_FALSE; + /* 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; + msg_data->has_req_data = PJ_FALSE; - /* Send the response */ - status = pj_stun_session_send_msg(sess, msg_data, PJ_TRUE, PJ_TRUE, - src_addr, src_addr_len, tdata); + /* Send the response */ + status = pj_stun_session_send_msg(sess, msg_data, PJ_TRUE, PJ_TRUE, + src_addr, src_addr_len, tdata); - /* - * Handling early check. - * - * It's possible that we receive this request before we receive SDP - * answer. In this case, we can't perform trigger check since we - * don't have checklist yet, so just save this check in a pending - * triggered check array to be acted upon later. - */ - if (ice->rcand_cnt == 0) { - rcheck = PJ_POOL_ZALLOC_T(ice->pool, pj_ice_rx_check); - } else { - rcheck = &tmp_rcheck; - } + /* + * Handling early check. + * + * It's possible that we receive this request before we receive SDP + * answer. In this case, we can't perform trigger check since we + * don't have checklist yet, so just save this check in a pending + * triggered check array to be acted upon later. + */ + if (ice->rcand_cnt == 0) { + rcheck = PJ_POOL_ZALLOC_T(ice->pool, pj_ice_rx_check); + } else { + rcheck = &tmp_rcheck; + } - /* Init rcheck */ - rcheck->comp_id = sd->comp_id; - rcheck->transport_id = ((pj_ice_msg_data*)token)->transport_id; - 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; - rcheck->role_attr = role_attr; + /* Init rcheck */ + rcheck->comp_id = sd->comp_id; + rcheck->transport_id = ((pj_ice_msg_data*)token)->transport_id; + rcheck->src_addr_len = src_addr_len; + pj_memcpy(&rcheck->src_addr, src_addr, src_addr_len); + rcheck->use_candidate = (uc_attr != NULL); + if(prio_attr){ + rcheck->priority = prio_attr->value; + } + rcheck->role_attr = role_attr; - if (ice->rcand_cnt == 0) { - /* We don't have answer yet, so keep this request for later */ - LOG4((ice->obj_name, "Received an early check for comp %d", - rcheck->comp_id)); - pj_list_push_back(&ice->early_check, rcheck); - } else { - /* Handle this check */ - handle_incoming_check(ice, rcheck); - } + if (ice->rcand_cnt == 0) { + /* We don't have answer yet, so keep this request for later */ + LOG4((ice->obj_name, "Received an early check for comp %d", + rcheck->comp_id)); + pj_list_push_back(&ice->early_check, rcheck); + } else { + /* Handle this check */ + handle_incoming_check(ice, rcheck); + } - pj_mutex_unlock(ice->mutex); - return PJ_SUCCESS; + pj_mutex_unlock(ice->mutex); + return PJ_SUCCESS; } /* Handle incoming Binding request and perform triggered check. - * This function may be called by on_stun_rx_request(), or when - * SDP answer is received and we have received early checks. - */ +* This function may be called by on_stun_rx_request(), or when +* SDP answer is received and we have received early checks. +*/ static void handle_incoming_check(pj_ice_sess *ice, - const pj_ice_rx_check *rcheck) + const pj_ice_rx_check *rcheck) { - pj_ice_sess_comp *comp; - pj_ice_sess_cand *lcand = NULL; - pj_ice_sess_cand *rcand; - unsigned i; + pj_ice_sess_comp *comp; + pj_ice_sess_cand *lcand = NULL; + pj_ice_sess_cand *rcand; + unsigned i; - comp = find_comp(ice, rcheck->comp_id); + comp = find_comp(ice, rcheck->comp_id); - /* Find remote candidate based on the source transport address of - * the request. - */ - for (i=0; ircand_cnt; ++i) { - if (sockaddr_cmp(&rcheck->src_addr, &ice->rcand[i].addr)==0) - break; - } - - /* 7.2.1.3. Learning Peer Reflexive Candidates - * If the source transport address of the request does not match any - * existing remote candidates, it represents a new peer reflexive remote - * candidate. - */ - if (i == ice->rcand_cnt) { - if (ice->rcand_cnt >= PJ_ICE_MAX_CAND) { - LOG4((ice->obj_name, - "Unable to add new peer reflexive candidate: too many " - "candidates already (%d)", PJ_ICE_MAX_CAND)); - return; + /* Find remote candidate based on the source transport address of + * the request. + */ + for (i=0; ircand_cnt; ++i) { + if (sockaddr_cmp(&rcheck->src_addr, &ice->rcand[i].addr)==0) + break; } - rcand = &ice->rcand[ice->rcand_cnt++]; - rcand->comp_id = (pj_uint8_t)rcheck->comp_id; - rcand->type = PJ_ICE_CAND_TYPE_PRFLX; - rcand->prio = rcheck->priority; - pj_memcpy(&rcand->addr, &rcheck->src_addr, rcheck->src_addr_len); + /* 7.2.1.3. Learning Peer Reflexive Candidates + * If the source transport address of the request does not match any + * existing remote candidates, it represents a new peer reflexive remote + * candidate. + */ + if (i == ice->rcand_cnt) { + if (ice->rcand_cnt >= PJ_ICE_MAX_CAND) { + LOG4((ice->obj_name, + "Unable to add new peer reflexive candidate: too many " + "candidates already (%d)", PJ_ICE_MAX_CAND)); + return; + } - /* Foundation is random, unique from other foundation */ - rcand->foundation.ptr = (char*) pj_pool_alloc(ice->pool, 36); - rcand->foundation.slen = pj_ansi_snprintf(rcand->foundation.ptr, 36, - "f%p", - rcand->foundation.ptr); + rcand = &ice->rcand[ice->rcand_cnt++]; + rcand->comp_id = (pj_uint8_t)rcheck->comp_id; + rcand->type = PJ_ICE_CAND_TYPE_PRFLX; + rcand->prio = rcheck->priority; + pj_memcpy(&rcand->addr, &rcheck->src_addr, rcheck->src_addr_len); - LOG4((ice->obj_name, - "Added new remote candidate from the request: %s:%d", - pj_inet_ntoa(rcand->addr.ipv4.sin_addr), - (int)pj_ntohs(rcand->addr.ipv4.sin_port))); + /* Foundation is random, unique from other foundation */ + rcand->foundation.ptr = (char*) pj_pool_alloc(ice->pool, 36); + rcand->foundation.slen = pj_ansi_snprintf(rcand->foundation.ptr, 36, + "f%p", + rcand->foundation.ptr); - } else { - /* Remote candidate found */ - rcand = &ice->rcand[i]; - } + LOG4((ice->obj_name, + "Added new remote candidate from the request: %s:%d", + pj_inet_ntoa(rcand->addr.ipv4.sin_addr), + (int)pj_ntohs(rcand->addr.ipv4.sin_port))); + } else { + /* Remote candidate found */ + rcand = &ice->rcand[i]; + } + #if 0 - /* Find again the local candidate by matching the base address - * with the local candidates in the checklist. Checks may have - * been pruned before, so it's possible that if we use the lcand - * as it is, we wouldn't be able to find the check in the checklist - * and we will end up creating a new check unnecessarily. - */ - for (i=0; iclist.count; ++i) { - pj_ice_sess_check *c = &ice->clist.checks[i]; - if (/*c->lcand == lcand ||*/ - sockaddr_cmp(&c->lcand->base_addr, &lcand->base_addr)==0) - { - lcand = c->lcand; - break; + /* Find again the local candidate by matching the base address + * with the local candidates in the checklist. Checks may have + * been pruned before, so it's possible that if we use the lcand + * as it is, we wouldn't be able to find the check in the checklist + * and we will end up creating a new check unnecessarily. + */ + for (i=0; iclist.count; ++i) { + pj_ice_sess_check *c = &ice->clist.checks[i]; + if (/*c->lcand == lcand ||*/ + sockaddr_cmp(&c->lcand->base_addr, &lcand->base_addr)==0) + { + lcand = c->lcand; + break; + } } - } #else - /* Just get candidate with the highest priority and same transport ID - * for the specified component ID in the checklist. - */ - for (i=0; iclist.count; ++i) { - pj_ice_sess_check *c = &ice->clist.checks[i]; - if (c->lcand->comp_id == rcheck->comp_id && - c->lcand->transport_id == rcheck->transport_id) - { - lcand = c->lcand; - break; + /* Just get candidate with the highest priority and same transport ID + * for the specified component ID in the checklist. + */ + for (i=0; iclist.count; ++i) { + pj_ice_sess_check *c = &ice->clist.checks[i]; + if (c->lcand->comp_id == rcheck->comp_id && + c->lcand->transport_id == rcheck->transport_id) + { + lcand = c->lcand; + break; + } } - } - if (lcand == NULL) { - /* Should not happen, but just in case remote is sending a - * Binding request for a component which it doesn't have. - */ - LOG4((ice->obj_name, - "Received Binding request but no local candidate is found!")); - return; - } + if (lcand == NULL) { + /* Should not happen, but just in case remote is sending a + * Binding request for a component which it doesn't have. + */ + LOG4((ice->obj_name, + "Received Binding request but no local candidate is found!")); + return; + } #endif - /* - * Create candidate pair for this request. - */ + /* + * Create candidate pair for this request. + */ - /* - * 7.2.1.4. Triggered Checks - * - * Now that we have local and remote candidate, check if we already - * have this pair in our checklist. - */ - for (i=0; iclist.count; ++i) { - pj_ice_sess_check *c = &ice->clist.checks[i]; - if (c->lcand == lcand && c->rcand == rcand) - break; - } + /* + * 7.2.1.4. Triggered Checks + * + * Now that we have local and remote candidate, check if we already + * have this pair in our checklist. + */ + for (i=0; iclist.count; ++i) { + pj_ice_sess_check *c = &ice->clist.checks[i]; + if (c->lcand == lcand && c->rcand == rcand) + break; + } - /* If the pair is already on the check list: - * - If the state of that pair is Waiting or Frozen, its state is - * changed to In-Progress and a check for that pair is performed - * immediately. This is called a triggered check. - * - * - If the state of that pair is In-Progress, the agent SHOULD - * generate an immediate retransmit of the Binding Request for the - * check in progress. This is to facilitate rapid completion of - * ICE when both agents are behind NAT. - * - * - If the state of that pair is Failed or Succeeded, no triggered - * check is sent. - */ - if (i != ice->clist.count) { - pj_ice_sess_check *c = &ice->clist.checks[i]; + /* If the pair is already on the check list: + * - If the state of that pair is Waiting or Frozen, its state is + * changed to In-Progress and a check for that pair is performed + * immediately. This is called a triggered check. + * + * - If the state of that pair is In-Progress, the agent SHOULD + * generate an immediate retransmit of the Binding Request for the + * check in progress. This is to facilitate rapid completion of + * ICE when both agents are behind NAT. + * + * - If the state of that pair is Failed or Succeeded, no triggered + * check is sent. + */ + if (i != ice->clist.count) { + pj_ice_sess_check *c = &ice->clist.checks[i]; - /* If USE-CANDIDATE is present, set nominated flag - * Note: DO NOT overwrite nominated flag if one is already set. - */ - c->nominated = ((rcheck->use_candidate) || c->nominated); + /* If USE-CANDIDATE is present, set nominated flag + * Note: DO NOT overwrite nominated flag if one is already set. + */ + c->nominated = ((rcheck->use_candidate) || c->nominated); - if (c->state == PJ_ICE_SESS_CHECK_STATE_FROZEN || - c->state == PJ_ICE_SESS_CHECK_STATE_WAITING) - { - /* See if we shall nominate this check */ - pj_bool_t nominate = (c->nominated || ice->is_nominating); + if (c->state == PJ_ICE_SESS_CHECK_STATE_FROZEN || + c->state == PJ_ICE_SESS_CHECK_STATE_WAITING) + { + /* See if we shall nominate this check */ + pj_bool_t nominate = (c->nominated || ice->is_nominating); - LOG5((ice->obj_name, "Performing triggered check for check %d",i)); - pj_log_push_indent(); - perform_check(ice, &ice->clist, i, nominate); - pj_log_pop_indent(); + LOG5((ice->obj_name, "Performing triggered check for check %d",i)); + pj_log_push_indent(); + perform_check(ice, &ice->clist, i, nominate); + pj_log_pop_indent(); - } else if (c->state == PJ_ICE_SESS_CHECK_STATE_IN_PROGRESS) { - /* Should retransmit immediately - */ - LOG5((ice->obj_name, "Triggered check for check %d not performed " - "because it's in progress. Retransmitting", i)); - pj_log_push_indent(); - pj_stun_session_retransmit_req(comp->stun_sess, c->tdata); - pj_log_pop_indent(); + } else if (c->state == PJ_ICE_SESS_CHECK_STATE_IN_PROGRESS) { + /* Should retransmit immediately + */ + LOG5((ice->obj_name, "Triggered check for check %d not performed " + "because it's in progress. Retransmitting", i)); + pj_log_push_indent(); + pj_stun_session_retransmit_req(comp->stun_sess, c->tdata); + pj_log_pop_indent(); - } else if (c->state == PJ_ICE_SESS_CHECK_STATE_SUCCEEDED) { - /* Check complete for this component. - * Note this may end ICE process. - */ - pj_bool_t complete; - unsigned j; + } else if (c->state == PJ_ICE_SESS_CHECK_STATE_SUCCEEDED) { + /* Check complete for this component. + * Note this may end ICE process. + */ + pj_bool_t complete; + unsigned j; - /* 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. - */ - if (rcheck->use_candidate) { - for (j=0; jvalid_list.count; ++j) { - pj_ice_sess_check *vc = &ice->valid_list.checks[j]; - if (vc->lcand->transport_id == c->lcand->transport_id && - vc->rcand == c->rcand) - { - /* Set nominated flag */ - vc->nominated = PJ_TRUE; + // WebRTC STUN refreshness + if(ice->opt.is_webrtc){ + pj_ice_sess_check *_c; + pj_ice_sess_comp *_comp; - /* Update valid check and nominated check for the component */ - update_comp_check(ice, vc->lcand->comp_id, vc); + if ((_comp = &ice->comp[0]) && ((_c = _comp->nominated_check) || (_c = _comp->valid_check)) && _c->rcand){ + pj_status_t status; + pj_ice_msg_data* msg_data; - LOG5((ice->obj_name, "Valid check %s is nominated", - dump_check(ice->tmp.txt, sizeof(ice->tmp.txt), - &ice->valid_list, vc))); - } + 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, &_c->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. + */ + if (rcheck->use_candidate) { + for (j=0; jvalid_list.count; ++j) { + pj_ice_sess_check *vc = &ice->valid_list.checks[j]; + if (vc->lcand->transport_id == c->lcand->transport_id && + vc->rcand == c->rcand) + { + /* Set nominated flag */ + vc->nominated = PJ_TRUE; + + /* Update valid check and nominated check for the component */ + update_comp_check(ice, vc->lcand->comp_id, vc); + + LOG5((ice->obj_name, "Valid check %s is nominated", + dump_check(ice->tmp.txt, sizeof(ice->tmp.txt), + &ice->valid_list, vc))); + } + } + } + + LOG5((ice->obj_name, "Triggered check for check %d not performed " + "because it's completed", i)); + pj_log_push_indent(); + complete = on_check_complete(ice, c); + pj_log_pop_indent(); + if (complete) { + return; + } } - } - LOG5((ice->obj_name, "Triggered check for check %d not performed " - "because it's completed", i)); - pj_log_push_indent(); - complete = on_check_complete(ice, c); - pj_log_pop_indent(); - if (complete) { - return; - } } + /* 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 + * - A triggered check for that pair is performed immediately. + */ + /* Note: only do this if we don't have too many checks in checklist */ + else if (ice->clist.count < PJ_ICE_MAX_CHECKS) { - } - /* 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 - * - A triggered check for that pair is performed immediately. - */ - /* Note: only do this if we don't have too many checks in checklist */ - else if (ice->clist.count < PJ_ICE_MAX_CHECKS) { + pj_ice_sess_check *c = &ice->clist.checks[ice->clist.count]; + pj_bool_t nominate; - pj_ice_sess_check *c = &ice->clist.checks[ice->clist.count]; - pj_bool_t nominate; + c->lcand = lcand; + c->rcand = rcand; + c->prio = CALC_CHECK_PRIO(ice, lcand, rcand); + c->state = PJ_ICE_SESS_CHECK_STATE_WAITING; + c->nominated = rcheck->use_candidate; + c->err_code = PJ_SUCCESS; - c->lcand = lcand; - c->rcand = rcand; - c->prio = CALC_CHECK_PRIO(ice, lcand, rcand); - c->state = PJ_ICE_SESS_CHECK_STATE_WAITING; - c->nominated = rcheck->use_candidate; - c->err_code = PJ_SUCCESS; + nominate = (c->nominated || ice->is_nominating); - nominate = (c->nominated || ice->is_nominating); + LOG4((ice->obj_name, "New triggered check added: %d", + ice->clist.count)); + pj_log_push_indent(); + perform_check(ice, &ice->clist, ice->clist.count++, nominate); + pj_log_pop_indent(); - LOG4((ice->obj_name, "New triggered check added: %d", - ice->clist.count)); - pj_log_push_indent(); - perform_check(ice, &ice->clist, ice->clist.count++, nominate); - pj_log_pop_indent(); - - } else { - LOG4((ice->obj_name, "Error: unable to perform triggered check: " - "TOO MANY CHECKS IN CHECKLIST!")); - } + } else { + LOG4((ice->obj_name, "Error: unable to perform triggered check: " + "TOO MANY CHECKS IN CHECKLIST!")); + } } static pj_status_t on_stun_rx_indication(pj_stun_session *sess, - const pj_uint8_t *pkt, - unsigned pkt_len, - const pj_stun_msg *msg, - void *token, - const pj_sockaddr_t *src_addr, - unsigned src_addr_len) + const pj_uint8_t *pkt, + unsigned pkt_len, + const pj_stun_msg *msg, + void *token, + const pj_sockaddr_t *src_addr, + unsigned src_addr_len) { - struct stun_data *sd; + struct stun_data *sd; - PJ_UNUSED_ARG(sess); - PJ_UNUSED_ARG(pkt); - PJ_UNUSED_ARG(pkt_len); - PJ_UNUSED_ARG(msg); - PJ_UNUSED_ARG(token); - PJ_UNUSED_ARG(src_addr); - PJ_UNUSED_ARG(src_addr_len); + PJ_UNUSED_ARG(sess); + PJ_UNUSED_ARG(pkt); + PJ_UNUSED_ARG(pkt_len); + PJ_UNUSED_ARG(msg); + PJ_UNUSED_ARG(token); + PJ_UNUSED_ARG(src_addr); + PJ_UNUSED_ARG(src_addr_len); - sd = (struct stun_data*) pj_stun_session_get_user_data(sess); + sd = (struct stun_data*) pj_stun_session_get_user_data(sess); - pj_log_push_indent(); + pj_log_push_indent(); - if (msg->hdr.type == PJ_STUN_BINDING_INDICATION) { - LOG5((sd->ice->obj_name, "Received Binding Indication keep-alive " - "for component %d", sd->comp_id)); - } else { - LOG4((sd->ice->obj_name, "Received unexpected %s indication " - "for component %d", pj_stun_get_method_name(msg->hdr.type), - sd->comp_id)); - } + if (msg->hdr.type == PJ_STUN_BINDING_INDICATION) { + LOG5((sd->ice->obj_name, "Received Binding Indication keep-alive " + "for component %d", sd->comp_id)); + } else { + LOG4((sd->ice->obj_name, "Received unexpected %s indication " + "for component %d", pj_stun_get_method_name(msg->hdr.type), + sd->comp_id)); + } - pj_log_pop_indent(); + pj_log_pop_indent(); - return PJ_SUCCESS; + return PJ_SUCCESS; } PJ_DEF(pj_status_t) pj_ice_sess_send_data(pj_ice_sess *ice, - unsigned comp_id, - const void *data, - pj_size_t data_len) + unsigned comp_id, + const void *data, + pj_size_t data_len) { - pj_status_t status = PJ_SUCCESS; - pj_ice_sess_comp *comp; - pj_ice_sess_cand *cand; - pj_uint8_t transport_id; - pj_sockaddr addr; + pj_status_t status = PJ_SUCCESS; + pj_ice_sess_comp *comp; + pj_ice_sess_cand *cand; + pj_uint8_t transport_id; + pj_sockaddr addr; - PJ_ASSERT_RETURN(ice && comp_id, PJ_EINVAL); - - /* It is possible that comp_cnt is less than comp_id, when remote - * doesn't support all the components that we have. - */ - if (comp_id > ice->comp_cnt) { - return PJNATH_EICEINCOMPID; - } + PJ_ASSERT_RETURN(ice && comp_id, PJ_EINVAL); - pj_mutex_lock(ice->mutex); + /* It is possible that comp_cnt is less than comp_id, when remote + * doesn't support all the components that we have. + */ + if (comp_id > ice->comp_cnt) { + return PJNATH_EICEINCOMPID; + } - comp = find_comp(ice, comp_id); - if (comp == NULL) { - status = PJNATH_EICEINCOMPID; - pj_mutex_unlock(ice->mutex); - goto on_return; - } + pj_mutex_lock(ice->mutex); - if (comp->valid_check == NULL) { - status = PJNATH_EICEINPROGRESS; - pj_mutex_unlock(ice->mutex); - goto on_return; - } + comp = find_comp(ice, comp_id); + if (comp == NULL) { + status = PJNATH_EICEINCOMPID; + pj_mutex_unlock(ice->mutex); + goto on_return; + } - cand = comp->valid_check->lcand; - transport_id = cand->transport_id; - pj_sockaddr_cp(&addr, &comp->valid_check->rcand->addr); + if (comp->valid_check == NULL) { + status = PJNATH_EICEINPROGRESS; + pj_mutex_unlock(ice->mutex); + goto on_return; + } - /* Release the mutex now to avoid deadlock (see ticket #1451). */ - pj_mutex_unlock(ice->mutex); + cand = comp->valid_check->lcand; + transport_id = cand->transport_id; + pj_sockaddr_cp(&addr, &comp->valid_check->rcand->addr); - status = (*ice->cb.on_tx_pkt)(ice, comp_id, transport_id, - data, data_len, - &addr, - sizeof(pj_sockaddr_in)); + /* Release the mutex now to avoid deadlock (see ticket #1451). */ + pj_mutex_unlock(ice->mutex); + status = (*ice->cb.on_tx_pkt)(ice, comp_id, transport_id, + data, data_len, + &addr, + sizeof(pj_sockaddr_in)); + on_return: - return status; + return status; } PJ_DEF(pj_status_t) pj_ice_sess_on_rx_pkt(pj_ice_sess *ice, - unsigned comp_id, - unsigned transport_id, - void *pkt, - pj_size_t pkt_size, - const pj_sockaddr_t *src_addr, - int src_addr_len) + unsigned comp_id, + unsigned transport_id, + void *pkt, + pj_size_t pkt_size, + const pj_sockaddr_t *src_addr, + int src_addr_len) { - pj_status_t status = PJ_SUCCESS; - pj_ice_sess_comp *comp; - pj_ice_msg_data *msg_data = NULL; - unsigned i; + pj_status_t status = PJ_SUCCESS; + pj_ice_sess_comp *comp; + pj_ice_msg_data *msg_data = NULL; + unsigned i; - PJ_ASSERT_RETURN(ice, PJ_EINVAL); + PJ_ASSERT_RETURN(ice, PJ_EINVAL); - pj_mutex_lock(ice->mutex); + pj_mutex_lock(ice->mutex); - comp = find_comp(ice, comp_id); - if (comp == NULL) { - pj_mutex_unlock(ice->mutex); - return PJNATH_EICEINCOMPID; - } + comp = find_comp(ice, comp_id); + if (comp == NULL) { + pj_mutex_unlock(ice->mutex); + return PJNATH_EICEINCOMPID; + } - /* Find transport */ - for (i=0; itp_data); ++i) { - if (ice->tp_data[i].transport_id == transport_id) { - msg_data = &ice->tp_data[i]; - break; + /* Find transport */ + for (i=0; itp_data); ++i) { + if (ice->tp_data[i].transport_id == transport_id) { + msg_data = &ice->tp_data[i]; + break; + } } - } - if (msg_data == NULL) { - pj_assert(!"Invalid transport ID"); - pj_mutex_unlock(ice->mutex); - return PJ_EINVAL; - } + if (msg_data == NULL) { + pj_assert(!"Invalid transport ID"); + pj_mutex_unlock(ice->mutex); + return PJ_EINVAL; + } - /* Don't check fingerprint. We only need to distinguish STUN and non-STUN - * packets. We don't need to verify the STUN packet too rigorously, that - * will be done by the user. - */ - status = pj_stun_msg_check((const pj_uint8_t*)pkt, pkt_size, - PJ_STUN_IS_DATAGRAM | - PJ_STUN_NO_FINGERPRINT_CHECK); - if (status == PJ_SUCCESS) { - status = pj_stun_session_on_rx_pkt(comp->stun_sess, pkt, pkt_size, - PJ_STUN_IS_DATAGRAM, msg_data, - NULL, src_addr, src_addr_len); - if (status != PJ_SUCCESS) { - pj_strerror(status, ice->tmp.errmsg, sizeof(ice->tmp.errmsg)); - LOG4((ice->obj_name, "Error processing incoming message: %s", - ice->tmp.errmsg)); + /* Don't check fingerprint. We only need to distinguish STUN and non-STUN + * packets. We don't need to verify the STUN packet too rigorously, that + * will be done by the user. + */ + status = pj_stun_msg_check((const pj_uint8_t*)pkt, pkt_size, + PJ_STUN_IS_DATAGRAM | + PJ_STUN_NO_FINGERPRINT_CHECK); + if (status == PJ_SUCCESS) { + status = pj_stun_session_on_rx_pkt(comp->stun_sess, pkt, pkt_size, + PJ_STUN_IS_DATAGRAM, msg_data, + NULL, src_addr, src_addr_len); + if (status != PJ_SUCCESS) { + pj_strerror(status, ice->tmp.errmsg, sizeof(ice->tmp.errmsg)); + LOG4((ice->obj_name, "Error processing incoming message: %s", + ice->tmp.errmsg)); + } + pj_mutex_unlock(ice->mutex); + } else { + /* Not a STUN packet. Call application's callback instead, but release + * the mutex now or otherwise we may get deadlock. + */ + pj_mutex_unlock(ice->mutex); + + (*ice->cb.on_rx_data)(ice, comp_id, transport_id, pkt, pkt_size, + src_addr, src_addr_len); + status = PJ_SUCCESS; } - pj_mutex_unlock(ice->mutex); - } else { - /* Not a STUN packet. Call application's callback instead, but release - * the mutex now or otherwise we may get deadlock. - */ - pj_mutex_unlock(ice->mutex); - (*ice->cb.on_rx_data)(ice, comp_id, transport_id, pkt, pkt_size, - src_addr, src_addr_len); - status = PJ_SUCCESS; - } - - return status; + return status; } Index: res/pjproject/pjnath/src/pjnath/turn_sock.c =================================================================== --- res/pjproject/pjnath/src/pjnath/turn_sock.c (revision 373380) +++ 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 373380) +++ 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 373380) +++ 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 373380) +++ 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 373380) +++ 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 373380) +++ res/res_rtp_asterisk.c (working copy) @@ -40,12 +40,6 @@ #include #include -#ifdef HAVE_OPENSSL_SRTP -#include -#include -#include -#endif - /* Asterisk discourages the use of bzero in favor of memset, in fact if you try to use bzero it will tell you to use memset. As a result bzero has to be undefined * here since it is used internally by pjlib. The only other option would be to modify pjlib... which won't happen. */ #undef bzero @@ -91,7 +85,18 @@ #define RTCP_PT_SDES 202 #define RTCP_PT_BYE 203 #define RTCP_PT_APP 204 +#define RTCP_PT_RTPFB 205 +#define RTCP_PT_PSFB 206 +#define RTCP_PT_PSFB_PLI 1 /* rfc 4585: Picture Loss Indication (PLI) */ +#define RTCP_PT_PSFB_SLI 2 /* rfc 4585: Slice Loss Indication (SLI) */ +#define RTCP_PT_PSFB_RPSI 3 /* rfc 4585: Reference Picture Selection Indication (RPSI) */ +#define RTCP_PT_PSFB_FIR 4 /* rfc 5104: Full Intra Request (FIR) Command*/ +#define RTCP_PT_PSFB_AFB 15 /* rfc 4585: Application layer FB (AFB) message */ + +#define RTCP_PT_RTPFB_NACK 1 /* RFC 4585 */ +#define RTCP_PT_RTPFB_TMMBN 4 /* RFC 5104 */ + #define RTP_MTU 1200 #define DEFAULT_DTMF_TIMEOUT (150 * (8000 / 1000)) /*!< samples */ @@ -100,10 +105,6 @@ #define DEFAULT_LEARNING_MIN_SEQUENTIAL 4 -#define SRTP_MASTER_KEY_LEN 16 -#define SRTP_MASTER_SALT_LEN 14 -#define SRTP_MASTER_LEN (SRTP_MASTER_KEY_LEN + SRTP_MASTER_SALT_LEN) - enum strict_rtp_state { STRICT_RTP_OPEN = 0, /*! No RTP packets should be dropped, all sources accepted */ STRICT_RTP_LEARN, /*! Accept next packet as source */ @@ -114,8 +115,6 @@ #define DEFAULT_ICESUPPORT 1 extern struct ast_srtp_res *res_srtp; -extern struct ast_srtp_policy_res *res_srtp_policy; - static int dtmftimeout = DEFAULT_DTMF_TIMEOUT; static int rtpstart = DEFAULT_RTP_START; /*!< First port for RTP sessions (set in rtp.conf) */ @@ -265,21 +264,6 @@ struct ao2_container *local_candidates; /*!< The local ICE candidates */ struct ao2_container *remote_candidates; /*!< The remote ICE candidates */ - -#ifdef HAVE_OPENSSL_SRTP - SSL_CTX *ssl_ctx; /*!< SSL context */ - SSL *ssl; /*!< SSL session */ - BIO *read_bio; /*!< Memory buffer for reading */ - BIO *write_bio; /*!< Memory buffer for writing */ - enum ast_rtp_dtls_setup dtls_setup; /*!< Current setup state */ - enum ast_srtp_suite suite; /*!< SRTP crypto suite */ - char local_fingerprint[160]; /*!< Fingerprint of our certificate */ - unsigned char remote_fingerprint[EVP_MAX_MD_SIZE]; /*!< Fingerprint of the peer certificate */ - enum ast_rtp_dtls_connection connection; /*!< Whether this is a new or existing connection */ - unsigned int dtls_failure:1; /*!< Failure occurred during DTLS negotiation */ - unsigned int rekey; /*!< Interval at which to renegotiate and rekey */ - int rekeyid; /*!< Scheduled item id for rekeying */ -#endif }; /*! @@ -385,13 +369,8 @@ static void ast_rtp_stop(struct ast_rtp_instance *instance); static int ast_rtp_qos_set(struct ast_rtp_instance *instance, int tos, int cos, const char* desc); static int ast_rtp_sendcng(struct ast_rtp_instance *instance, int level); +static int ast_rtcpfb(struct ast_rtp_instance *instance, void* data, unsigned int len); -#ifdef HAVE_OPENSSL_SRTP -static int ast_rtp_activate(struct ast_rtp_instance *instance); -#endif - -static int __rtp_sendto(struct ast_rtp_instance *instance, void *buf, size_t size, int flags, struct ast_sockaddr *sa, int rtcp, int *ice, int use_srtp); - /*! \brief Destructor for locally created ICE candidates */ static void ast_rtp_ice_candidate_destroy(void *obj) { @@ -444,6 +423,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); @@ -495,6 +475,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; @@ -531,11 +512,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); @@ -690,305 +691,6 @@ .ice_lite = ast_rtp_ice_lite, }; -#ifdef HAVE_OPENSSL_SRTP -static void dtls_info_callback(const SSL *ssl, int where, int ret) -{ - struct ast_rtp *rtp = SSL_get_ex_data(ssl, 0); - - /* We only care about alerts */ - if (!(where & SSL_CB_ALERT)) { - return; - } - - rtp->dtls_failure = 1; -} - -static int ast_rtp_dtls_set_configuration(struct ast_rtp_instance *instance, const struct ast_rtp_dtls_cfg *dtls_cfg) -{ - struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); - - if (!dtls_cfg->enabled) { - return 0; - } - - if (!ast_rtp_engine_srtp_is_registered()) { - return -1; - } - - if (!(rtp->ssl_ctx = SSL_CTX_new(DTLSv1_method()))) { - return -1; - } - - SSL_CTX_set_verify(rtp->ssl_ctx, dtls_cfg->verify ? SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT : SSL_VERIFY_NONE, NULL); - - if (dtls_cfg->suite == AST_AES_CM_128_HMAC_SHA1_80) { - SSL_CTX_set_tlsext_use_srtp(rtp->ssl_ctx, "SRTP_AES128_CM_SHA1_80"); - } else if (dtls_cfg->suite == AST_AES_CM_128_HMAC_SHA1_32) { - SSL_CTX_set_tlsext_use_srtp(rtp->ssl_ctx, "SRTP_AES128_CM_SHA1_32"); - } else { - ast_log(LOG_ERROR, "Unsupported suite specified for DTLS-SRTP on RTP instance '%p'\n", instance); - goto error; - } - - if (!ast_strlen_zero(dtls_cfg->certfile)) { - char *private = ast_strlen_zero(dtls_cfg->pvtfile) ? dtls_cfg->certfile : dtls_cfg->pvtfile; - BIO *certbio; - X509 *cert; - unsigned int size, i; - unsigned char fingerprint[EVP_MAX_MD_SIZE]; - char *local_fingerprint = rtp->local_fingerprint; - - if (!SSL_CTX_use_certificate_file(rtp->ssl_ctx, dtls_cfg->certfile, SSL_FILETYPE_PEM)) { - ast_log(LOG_ERROR, "Specified certificate file '%s' for RTP instance '%p' could not be used\n", - dtls_cfg->certfile, instance); - goto error; - } - - if (!SSL_CTX_use_PrivateKey_file(rtp->ssl_ctx, private, SSL_FILETYPE_PEM) || - !SSL_CTX_check_private_key(rtp->ssl_ctx)) { - ast_log(LOG_ERROR, "Specified private key file '%s' for RTP instance '%p' could not be used\n", - private, instance); - goto error; - } - - if (!(certbio = BIO_new(BIO_s_file()))) { - ast_log(LOG_ERROR, "Failed to allocate memory for certificate fingerprinting on RTP instance '%p'\n", - instance); - goto error; - } - - if (!BIO_read_filename(certbio, dtls_cfg->certfile) || - !(cert = PEM_read_bio_X509(certbio, NULL, 0, NULL)) || - !X509_digest(cert, EVP_sha1(), fingerprint, &size) || - !size) { - ast_log(LOG_ERROR, "Could not produce fingerprint from certificate '%s' for RTP instance '%p'\n", - dtls_cfg->certfile, instance); - BIO_free_all(certbio); - goto error; - } - - for (i = 0; i < size; i++) { - sprintf(local_fingerprint, "%.2X:", fingerprint[i]); - local_fingerprint += 3; - } - - *(local_fingerprint-1) = 0; - - BIO_free_all(certbio); - } - - if (!ast_strlen_zero(dtls_cfg->cipher)) { - if (!SSL_CTX_set_cipher_list(rtp->ssl_ctx, dtls_cfg->cipher)) { - ast_log(LOG_ERROR, "Invalid cipher specified in cipher list '%s' for RTP instance '%p'\n", - dtls_cfg->cipher, instance); - goto error; - } - } - - if (!ast_strlen_zero(dtls_cfg->cafile) || !ast_strlen_zero(dtls_cfg->capath)) { - if (!SSL_CTX_load_verify_locations(rtp->ssl_ctx, S_OR(dtls_cfg->cafile, NULL), S_OR(dtls_cfg->capath, NULL))) { - ast_log(LOG_ERROR, "Invalid certificate authority file '%s' or path '%s' specified for RTP instance '%p'\n", - S_OR(dtls_cfg->cafile, ""), S_OR(dtls_cfg->capath, ""), instance); - goto error; - } - } - - rtp->rekey = dtls_cfg->rekey; - rtp->dtls_setup = dtls_cfg->default_setup; - rtp->suite = dtls_cfg->suite; - - if (!(rtp->ssl = SSL_new(rtp->ssl_ctx))) { - ast_log(LOG_ERROR, "Failed to allocate memory for SSL context on RTP instance '%p'\n", - instance); - goto error; - } - - SSL_set_ex_data(rtp->ssl, 0, rtp); - SSL_set_info_callback(rtp->ssl, dtls_info_callback); - - if (!(rtp->read_bio = BIO_new(BIO_s_mem()))) { - ast_log(LOG_ERROR, "Failed to allocate memory for inbound SSL traffic on RTP instance '%p'\n", - instance); - goto error; - } - BIO_set_mem_eof_return(rtp->read_bio, -1); - - if (!(rtp->write_bio = BIO_new(BIO_s_mem()))) { - ast_log(LOG_ERROR, "Failed to allocate memory for outbound SSL traffic on RTP instance '%p'\n", - instance); - goto error; - } - BIO_set_mem_eof_return(rtp->write_bio, -1); - - SSL_set_bio(rtp->ssl, rtp->read_bio, rtp->write_bio); - - if (rtp->dtls_setup == AST_RTP_DTLS_SETUP_PASSIVE) { - SSL_set_accept_state(rtp->ssl); - } else { - SSL_set_connect_state(rtp->ssl); - } - - rtp->connection = AST_RTP_DTLS_CONNECTION_NEW; - - return 0; - -error: - if (rtp->read_bio) { - BIO_free(rtp->read_bio); - rtp->read_bio = NULL; - } - - if (rtp->write_bio) { - BIO_free(rtp->write_bio); - rtp->write_bio = NULL; - } - - if (rtp->ssl) { - SSL_free(rtp->ssl); - rtp->ssl = NULL; - } - - SSL_CTX_free(rtp->ssl_ctx); - rtp->ssl_ctx = NULL; - - return -1; -} - -static int ast_rtp_dtls_active(struct ast_rtp_instance *instance) -{ - struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); - - return !rtp->ssl_ctx ? 0 : 1; -} - -static void ast_rtp_dtls_stop(struct ast_rtp_instance *instance) -{ - struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); - - if (rtp->ssl_ctx) { - SSL_CTX_free(rtp->ssl_ctx); - rtp->ssl_ctx = NULL; - } - - if (rtp->ssl) { - SSL_free(rtp->ssl); - rtp->ssl = NULL; - } -} - -static void ast_rtp_dtls_reset(struct ast_rtp_instance *instance) -{ - struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); - - /* If the SSL session is not yet finalized don't bother resetting */ - if (!SSL_is_init_finished(rtp->ssl)) { - return; - } - - SSL_shutdown(rtp->ssl); - rtp->connection = AST_RTP_DTLS_CONNECTION_NEW; -} - -static enum ast_rtp_dtls_connection ast_rtp_dtls_get_connection(struct ast_rtp_instance *instance) -{ - struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); - - return rtp->connection; -} - -static enum ast_rtp_dtls_setup ast_rtp_dtls_get_setup(struct ast_rtp_instance *instance) -{ - struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); - - return rtp->dtls_setup; -} - -static void ast_rtp_dtls_set_setup(struct ast_rtp_instance *instance, enum ast_rtp_dtls_setup setup) -{ - struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); - enum ast_rtp_dtls_setup old = rtp->dtls_setup; - - switch (setup) { - case AST_RTP_DTLS_SETUP_ACTIVE: - rtp->dtls_setup = AST_RTP_DTLS_SETUP_PASSIVE; - break; - case AST_RTP_DTLS_SETUP_PASSIVE: - rtp->dtls_setup = AST_RTP_DTLS_SETUP_ACTIVE; - break; - case AST_RTP_DTLS_SETUP_ACTPASS: - /* We can't respond to an actpass setup with actpass ourselves... so respond with active, as we can initiate connections */ - if (rtp->dtls_setup == AST_RTP_DTLS_SETUP_ACTPASS) { - rtp->dtls_setup = AST_RTP_DTLS_SETUP_ACTIVE; - } - break; - case AST_RTP_DTLS_SETUP_HOLDCONN: - rtp->dtls_setup = AST_RTP_DTLS_SETUP_HOLDCONN; - break; - default: - /* This should never occur... if it does exit early as we don't know what state things are in */ - return; - } - - /* If the setup state did not change we go on as if nothing happened */ - if (old == rtp->dtls_setup) { - return; - } - - /* If they don't want us to establish a connection wait until later */ - if (rtp->dtls_setup == AST_RTP_DTLS_SETUP_HOLDCONN) { - return; - } - - if (rtp->dtls_setup == AST_RTP_DTLS_SETUP_ACTIVE) { - SSL_set_connect_state(rtp->ssl); - } else if (rtp->dtls_setup == AST_RTP_DTLS_SETUP_PASSIVE) { - SSL_set_accept_state(rtp->ssl); - } else { - return; - } -} - -static void ast_rtp_dtls_set_fingerprint(struct ast_rtp_instance *instance, enum ast_rtp_dtls_hash hash, const char *fingerprint) -{ - char *tmp = ast_strdupa(fingerprint), *value; - int pos = 0; - struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); - - if (hash != AST_RTP_DTLS_HASH_SHA1) { - return; - } - - while ((value = strsep(&tmp, ":")) && (pos != (EVP_MAX_MD_SIZE - 1))) { - sscanf(value, "%02x", (unsigned int*)&rtp->remote_fingerprint[pos++]); - } -} - -static const char *ast_rtp_dtls_get_fingerprint(struct ast_rtp_instance *instance, enum ast_rtp_dtls_hash hash) -{ - struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); - - if (hash != AST_RTP_DTLS_HASH_SHA1) { - return NULL; - } - - return rtp->local_fingerprint; -} - -/* DTLS RTP Engine interface declaration */ -static struct ast_rtp_engine_dtls ast_rtp_dtls = { - .set_configuration = ast_rtp_dtls_set_configuration, - .active = ast_rtp_dtls_active, - .stop = ast_rtp_dtls_stop, - .reset = ast_rtp_dtls_reset, - .get_connection = ast_rtp_dtls_get_connection, - .get_setup = ast_rtp_dtls_get_setup, - .set_setup = ast_rtp_dtls_set_setup, - .set_fingerprint = ast_rtp_dtls_set_fingerprint, - .get_fingerprint = ast_rtp_dtls_get_fingerprint, -}; - -#endif - /* RTP Engine Declaration */ static struct ast_rtp_engine asterisk_rtp_engine = { .name = "asterisk", @@ -1016,11 +718,8 @@ .stop = ast_rtp_stop, .qos = ast_rtp_qos_set, .sendcng = ast_rtp_sendcng, + .rtcpfb = ast_rtcpfb, .ice = &ast_rtp_ice, -#ifdef HAVE_OPENSSL_SRTP - .dtls = &ast_rtp_dtls, - .activate = ast_rtp_activate, -#endif }; static void ast_rtp_on_ice_rx_data(pj_ice_sess *ice, unsigned comp_id, unsigned transport_id, void *pkt, pj_size_t size, const pj_sockaddr_t *src_addr, unsigned src_addr_len) @@ -1036,19 +735,14 @@ { struct ast_rtp *rtp = ice->user_data; pj_status_t status = PJ_EINVALIDOP; - pj_ssize_t _size = (pj_ssize_t)size; if (transport_id == TRANSPORT_SOCKET_RTP) { /* Traffic is destined to go right out the RTP socket we already have */ - status = pj_sock_sendto(rtp->s, pkt, &_size, 0, dst_addr, dst_addr_len); - /* sendto on a connectionless socket should send all the data, or none at all */ - ast_assert(_size == size || status != PJ_SUCCESS); + status = pj_sock_sendto(rtp->s, pkt, (pj_ssize_t*)&size, 0, dst_addr, dst_addr_len); } else if (transport_id == TRANSPORT_SOCKET_RTCP) { /* Traffic is destined to go right out the RTCP socket we already have */ if (rtp->rtcp) { - status = pj_sock_sendto(rtp->rtcp->s, pkt, &_size, 0, dst_addr, dst_addr_len); - /* sendto on a connectionless socket should send all the data, or none at all */ - ast_assert(_size == size || status != PJ_SUCCESS); + status = pj_sock_sendto(rtp->rtcp->s, pkt, (pj_ssize_t*)&size, 0, dst_addr, dst_addr_len); } else { status = PJ_SUCCESS; } @@ -1210,228 +904,28 @@ return 1; } -#ifdef HAVE_OPENSSL_SRTP -static void dtls_srtp_check_pending(struct ast_rtp_instance *instance, struct ast_rtp *rtp) -{ - size_t pending = BIO_ctrl_pending(rtp->write_bio); - - if (pending > 0) { - char outgoing[pending]; - size_t out; - struct ast_sockaddr remote_address = { {0, } }; - int ice; - - ast_rtp_instance_get_remote_address(instance, &remote_address); - - /* If we do not yet know an address to send this to defer it until we do */ - if (ast_sockaddr_isnull(&remote_address)) { - return; - } - - out = BIO_read(rtp->write_bio, outgoing, sizeof(outgoing)); - - __rtp_sendto(instance, outgoing, out, 0, &remote_address, 0, &ice, 0); - } -} - -static int dtls_srtp_renegotiate(const void *data) -{ - struct ast_rtp_instance *instance = (struct ast_rtp_instance *)data; - struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); - - SSL_renegotiate(rtp->ssl); - SSL_do_handshake(rtp->ssl); - dtls_srtp_check_pending(instance, rtp); - - rtp->rekeyid = -1; - ao2_ref(instance, -1); - - return 0; -} - -static int dtls_srtp_setup(struct ast_rtp *rtp, struct ast_srtp *srtp, struct ast_rtp_instance *instance) -{ - unsigned char material[SRTP_MASTER_LEN * 2]; - unsigned char *local_key, *local_salt, *remote_key, *remote_salt; - struct ast_srtp_policy *local_policy, *remote_policy = NULL; - struct ast_rtp_instance_stats stats = { 0, }; - - /* If a fingerprint is present in the SDP make sure that the peer certificate matches it */ - if (SSL_CTX_get_verify_mode(rtp->ssl_ctx) != SSL_VERIFY_NONE) { - X509 *certificate; - - if (!(certificate = SSL_get_peer_certificate(rtp->ssl))) { - ast_log(LOG_WARNING, "No certificate was provided by the peer on RTP instance '%p'\n", instance); - return -1; - } - - /* If a fingerprint is present in the SDP make sure that the peer certificate matches it */ - if (rtp->remote_fingerprint[0]) { - unsigned char fingerprint[EVP_MAX_MD_SIZE]; - unsigned int size; - - if (!X509_digest(certificate, EVP_sha1(), fingerprint, &size) || - !size || - memcmp(fingerprint, rtp->remote_fingerprint, size)) { - X509_free(certificate); - ast_log(LOG_WARNING, "Fingerprint provided by remote party does not match that of peer certificate on RTP instance '%p'\n", - instance); - return -1; - } - } - - X509_free(certificate); - } - - /* Ensure that certificate verification was successful */ - if (SSL_get_verify_result(rtp->ssl) != X509_V_OK) { - ast_log(LOG_WARNING, "Peer certificate on RTP instance '%p' failed verification test\n", - instance); - return -1; - } - - /* Produce key information and set up SRTP */ - if (!SSL_export_keying_material(rtp->ssl, material, SRTP_MASTER_LEN * 2, "EXTRACTOR-dtls_srtp", 19, NULL, 0, 0)) { - ast_log(LOG_WARNING, "Unable to extract SRTP keying material from DTLS-SRTP negotiation on RTP instance '%p'\n", - instance); - return -1; - } - - /* Whether we are acting as a server or client determines where the keys/salts are */ - if (rtp->dtls_setup == AST_RTP_DTLS_SETUP_ACTIVE) { - local_key = material; - remote_key = local_key + SRTP_MASTER_KEY_LEN; - local_salt = remote_key + SRTP_MASTER_KEY_LEN; - remote_salt = local_salt + SRTP_MASTER_SALT_LEN; - } else { - remote_key = material; - local_key = remote_key + SRTP_MASTER_KEY_LEN; - remote_salt = local_key + SRTP_MASTER_KEY_LEN; - local_salt = remote_salt + SRTP_MASTER_SALT_LEN; - } - - if (!(local_policy = res_srtp_policy->alloc())) { - return -1; - } - - if (res_srtp_policy->set_master_key(local_policy, local_key, SRTP_MASTER_KEY_LEN, local_salt, SRTP_MASTER_SALT_LEN) < 0) { - ast_log(LOG_WARNING, "Could not set key/salt information on local policy of '%p' when setting up DTLS-SRTP\n", rtp); - goto error; - } - - if (res_srtp_policy->set_suite(local_policy, rtp->suite)) { - ast_log(LOG_WARNING, "Could not set suite to '%d' on local policy of '%p' when setting up DTLS-SRTP\n", rtp->suite, rtp); - goto error; - } - - if (ast_rtp_instance_get_stats(instance, &stats, AST_RTP_INSTANCE_STAT_LOCAL_SSRC)) { - goto error; - } - - res_srtp_policy->set_ssrc(local_policy, stats.local_ssrc, 0); - - if (!(remote_policy = res_srtp_policy->alloc())) { - goto error; - } - - if (res_srtp_policy->set_master_key(remote_policy, remote_key, SRTP_MASTER_KEY_LEN, remote_salt, SRTP_MASTER_SALT_LEN) < 0) { - ast_log(LOG_WARNING, "Could not set key/salt information on remote policy of '%p' when setting up DTLS-SRTP\n", rtp); - goto error; - } - - if (res_srtp_policy->set_suite(remote_policy, rtp->suite)) { - ast_log(LOG_WARNING, "Could not set suite to '%d' on remote policy of '%p' when setting up DTLS-SRTP\n", rtp->suite, rtp); - goto error; - } - - res_srtp_policy->set_ssrc(remote_policy, 0, 1); - - if (ast_rtp_instance_add_srtp_policy(instance, remote_policy, local_policy)) { - ast_log(LOG_WARNING, "Could not set policies when setting up DTLS-SRTP on '%p'\n", rtp); - goto error; - } - - if (rtp->rekey) { - ao2_ref(instance, +1); - if ((rtp->rekeyid = ast_sched_add(rtp->sched, rtp->rekey * 1000, dtls_srtp_renegotiate, instance)) < 0) { - ao2_ref(instance, -1); - goto error; - } - } - - return 0; - -error: - res_srtp_policy->destroy(local_policy); - - if (remote_policy) { - res_srtp_policy->destroy(remote_policy); - } - - return -1; -} -#endif - static int __rtp_recvfrom(struct ast_rtp_instance *instance, void *buf, size_t size, int flags, struct ast_sockaddr *sa, int rtcp) { int len; struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); struct ast_srtp *srtp = ast_rtp_instance_get_srtp(instance); + const unsigned char* buf_ptr = (const unsigned char*)buf; + int is_muxed_rtcp = 0; + if(!rtcp && size >= 2 && (buf_ptr[1] & 0x80)){ + // RFC 5761 + switch((buf_ptr[1] & 0x7F)){ + case 64: case 65: + case 72: case 73: case 74: case 75: case 76: + case 77: case 78: + case 79: is_muxed_rtcp = 1; break; + } + } + if ((len = ast_recvfrom(rtcp ? rtp->rtcp->s : rtp->s, buf, size, flags, sa)) < 0) { return len; } -#ifdef HAVE_OPENSSL_SRTP - if (!rtcp) { - char *in = buf; - - dtls_srtp_check_pending(instance, rtp); - - /* If this is an SSL packet pass it to OpenSSL for processing */ - if ((*in >= 20) && (*in <= 64)) { - int res = 0; - - /* If no SSL session actually exists terminate things */ - if (!rtp->ssl) { - ast_log(LOG_ERROR, "Received SSL traffic on RTP instance '%p' without an SSL session\n", - instance); - return -1; - } - - /* If we don't yet know if we are active or passive and we receive a packet... we are obviously passive */ - if (rtp->dtls_setup == AST_RTP_DTLS_SETUP_ACTPASS) { - rtp->dtls_setup = AST_RTP_DTLS_SETUP_PASSIVE; - SSL_set_accept_state(rtp->ssl); - } - - dtls_srtp_check_pending(instance, rtp); - - BIO_write(rtp->read_bio, buf, len); - - len = SSL_read(rtp->ssl, buf, len); - - dtls_srtp_check_pending(instance, rtp); - - if (rtp->dtls_failure) { - ast_log(LOG_ERROR, "DTLS failure occurred on RTP instance '%p', terminating\n", - instance); - return -1; - } - - if (SSL_is_init_finished(rtp->ssl)) { - /* Any further connections will be existing since this is now established */ - rtp->connection = AST_RTP_DTLS_CONNECTION_EXISTING; - - /* Use the keying material to set up key/salt information */ - res = dtls_srtp_setup(rtp, srtp, instance); - } - - return res; - } - } -#endif - if (rtp->ice) { pj_str_t combined = pj_str(ast_sockaddr_stringify(sa)); pj_sockaddr address; @@ -1444,7 +938,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)); @@ -1458,7 +952,7 @@ rtp->passthrough = 0; } - if (res_srtp && srtp && res_srtp->unprotect(srtp, buf, &len, rtcp) < 0) { + if (res_srtp && srtp && res_srtp->unprotect(srtp, buf, &len, (rtcp || is_muxed_rtcp)) < 0) { return -1; } @@ -1475,7 +969,7 @@ return __rtp_recvfrom(instance, buf, size, flags, sa, 0); } -static int __rtp_sendto(struct ast_rtp_instance *instance, void *buf, size_t size, int flags, struct ast_sockaddr *sa, int rtcp, int *ice, int use_srtp) +static int __rtp_sendto(struct ast_rtp_instance *instance, void *buf, size_t size, int flags, struct ast_sockaddr *sa, int rtcp, int *ice) { int len = size; void *temp = buf; @@ -1484,7 +978,7 @@ *ice = 0; - if (use_srtp && res_srtp && srtp && res_srtp->protect(srtp, &temp, &len, rtcp) < 0) { + if (res_srtp && srtp && res_srtp->protect(srtp, &temp, &len, rtcp) < 0) { return -1; } @@ -1502,12 +996,12 @@ static int rtcp_sendto(struct ast_rtp_instance *instance, void *buf, size_t size, int flags, struct ast_sockaddr *sa, int *ice) { - return __rtp_sendto(instance, buf, size, flags, sa, 1, ice, 1); + return __rtp_sendto(instance, buf, size, flags, sa, 1, ice); } static int rtp_sendto(struct ast_rtp_instance *instance, void *buf, size_t size, int flags, struct ast_sockaddr *sa, int *ice) { - return __rtp_sendto(instance, buf, size, flags, sa, 0, ice, 1); + return __rtp_sendto(instance, buf, size, flags, sa, 0, ice); } static int rtp_get_rate(struct ast_format *format) @@ -1771,10 +1265,6 @@ /* Record any information we may need */ rtp->sched = sched; -#ifdef HAVE_OPENSSL_SRTP - rtp->rekeyid = -1; -#endif - return 0; } @@ -1837,18 +1327,6 @@ ao2_ref(rtp->remote_candidates, -1); } -#ifdef HAVE_OPENSSL_SRTP - /* Destroy the SSL context if present */ - if (rtp->ssl_ctx) { - SSL_CTX_free(rtp->ssl_ctx); - } - - /* Destroy the SSL session if present */ - if (rtp->ssl) { - SSL_free(rtp->ssl); - } -#endif - /* Destroy synchronization items */ ast_mutex_destroy(&rtp->lock); ast_cond_destroy(&rtp->cond); @@ -2143,7 +1621,7 @@ } /*! \brief Send RTCP recipient's report */ -static int ast_rtcp_write_rr(struct ast_rtp_instance *instance) +static int ast_rtcp_write_rr(struct ast_rtp_instance *instance, void* xdata, unsigned int xdata_len) { struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); int res; @@ -2216,12 +1694,21 @@ /*! \note Insert SDES here. Probably should make SDES text equal to mimetypes[code].type (not subtype 'cos it can change mid call, and SDES can't) */ - rtcpheader[len/4] = htonl((2 << 30) | (1 << 24) | (RTCP_PT_SDES << 16) | 2); - rtcpheader[(len/4)+1] = htonl(rtp->ssrc); /* Our SSRC */ - rtcpheader[(len/4)+2] = htonl(0x01 << 24); /* Empty for the moment */ + rtcpheader[(len>>2)] = htonl((2 << 30) | (1 << 24) | (RTCP_PT_SDES << 16) | 2); + rtcpheader[(len>>2) + 1] = htonl(rtp->ssrc); /* Our SSRC */ + rtcpheader[(len>>2) + 2] = htonl(0x01 << 24); /* Empty for the moment */ len += 12; ast_sockaddr_copy(&remote_address, &rtp->rtcp->them); + if(xdata && xdata_len){ + if ((len + xdata_len) > sizeof(bdata)) { + ast_log(LOG_ERROR, "RTCP RR transmission cannot append: %d\n", xdata_len); + } + else { + memcpy(&rtcpheader[(len>>2)], xdata, xdata_len); + len += xdata_len; + } + } res = rtcp_sendto(instance, (unsigned int *)rtcpheader, len, 0, &remote_address, &ice); @@ -2402,7 +1889,7 @@ if (rtp->txcount > rtp->rtcp->lastsrtxcount) { res = ast_rtcp_write_sr(instance); } else { - res = ast_rtcp_write_rr(instance); + res = ast_rtcp_write_rr(instance, 0, 0); } if (!res) { @@ -2500,7 +1987,7 @@ int hdrlen = 12, res, ice; unsigned char *rtpheader = (unsigned char *)(frame->data.ptr - hdrlen); - put_unaligned_uint32(rtpheader, htonl((2 << 30) | (codec << 16) | (rtp->seqno) | (mark << 23))); + put_unaligned_uint32(rtpheader, htonl((2 << 30) | (codec << 16) | (/*rtp->seqno*/frame->seqno) | (mark << 23))); // FIXME: use frame seqno to reflect pkt loss put_unaligned_uint32(rtpheader + 4, htonl(rtp->lastts)); put_unaligned_uint32(rtpheader + 8, htonl(rtp->ssrc)); @@ -2531,7 +2018,7 @@ } } } - + update_address_with_ice_candidate(rtp, COMPONENT_RTP, &remote_address); if (rtp_debug_test_addr(&remote_address)) { @@ -3141,9 +2628,25 @@ } switch (pt) { + case RTCP_PT_RTPFB: + case RTCP_PT_PSFB: + { + unsigned int* pkt = &rtcpheader[i - 2]; + unsigned int len = (((ntohl(pkt[0]) & 0xFFFF) + 1) << 2); + + rtp->f.frametype = AST_FRAME_CONTROL; + rtp->f.subclass.integer = AST_CONTROL_RTCPFB; + rtp->f.mallocd = 0; + rtp->f.datalen = len; + rtp->f.data.ptr = (void*)&pkt[0]; + rtp->f.offset = 0; + f = &rtp->f; + break; + } + case RTCP_PT_SR: gettimeofday(&rtp->rtcp->rxlsr,NULL); /* To be able to populate the dlsr */ - rtp->rtcp->spc = ntohl(rtcpheader[i+3]); + rtp->rtcp->spc = ntohl(rtcpheader[i + 3]); rtp->rtcp->soc = ntohl(rtcpheader[i + 4]); rtp->rtcp->themrxlsr = ((ntohl(rtcpheader[i]) & 0x0000ffff) << 16) | ((ntohl(rtcpheader[i + 1]) & 0xffff0000) >> 16); /* Going to LSR in RR*/ @@ -3433,6 +2936,7 @@ struct ast_rtp_payload_type payload; struct ast_sockaddr remote_address = { {0,} }; struct frame_list frames; + int is_muxed_rtcp = 0; /* If this is actually RTCP let's hop on over and handle it */ if (rtcp) { @@ -3568,6 +3072,21 @@ timestamp = ntohl(rtpheader[1]); ssrc = ntohl(rtpheader[2]); + + /* check if it's RTCP muxed with RTP */ + switch((payloadtype)){ + case 64: case 65: + case 72: case 73: case 74: case 75: case 76: + case 77: case 78: + case 79: is_muxed_rtcp = 1; break; + } + if(is_muxed_rtcp){ + if (rtp->rtcp) { + return ast_rtcp_read(instance); + } + return &ast_null_frame; + } + AST_LIST_HEAD_INIT_NOLOCK(&frames); /* Force a marker bit and change SSRC if the SSRC changes */ if (rtp->rxssrc && rtp->rxssrc != ssrc) { @@ -4071,10 +3590,6 @@ struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); struct ast_sockaddr addr = { {0,} }; -#ifdef HAVE_OPENSSL_SRTP - AST_SCHED_DEL_UNREF(rtp->sched, rtp->rekeyid, ao2_ref(instance, -1)); -#endif - if (rtp->rtcp && rtp->rtcp->schedid > 0) { if (!ast_sched_del(rtp->sched, rtp->rtcp->schedid)) { /* successfully cancelled scheduler entry. */ @@ -4154,22 +3669,57 @@ return res; } -#ifdef HAVE_OPENSSL_SRTP -static int ast_rtp_activate(struct ast_rtp_instance *instance) +/*! \brief forward RTCP-FB message received from remote peer */ +static int ast_rtcpfb(struct ast_rtp_instance *instance, void* data, unsigned int len) { + unsigned int pkt_len, u1, fci, pkt_type, *pkt; + int forward = 0; struct ast_rtp *rtp = ast_rtp_instance_get_data(instance); + pkt = (unsigned int*)data; + u1 = ntohl(pkt[0]); + pkt_type = ((u1 >> 16) & 0xFF); + pkt_len = (((u1 & 0xFFFF) + 1) << 2); + fci = (pkt[0] & 0x1F); - if (!rtp->ssl) { - return 0; + if(pkt_len != len){ + ast_log(LOG_ERROR, "Invalid packet length %u<>%u\n", len, pkt_len); + return -1; } - SSL_do_handshake(rtp->ssl); - - dtls_srtp_check_pending(instance, rtp); - - return 0; + if(pkt_type == RTCP_PT_RTPFB){ + switch(fci){ + case RTCP_PT_RTPFB_NACK: + { + if(len >= 12){ + pkt[1] = htonl(rtp->ssrc); /* Sender SSRC */ + pkt[2] = htonl(rtp->themssrc); /* Media SSRC */ + } + forward = 1; + break; + } + } + } + else if(pkt_type == RTCP_PT_PSFB){ + switch(fci){ + case RTCP_PT_PSFB_FIR: + case RTCP_PT_PSFB_PLI: + { + if(len >= 12){ + pkt[1] = htonl(rtp->ssrc); /* Sender SSRC */ + pkt[2] = htonl(rtp->themssrc); /* Media SSRC */ + } + if(fci == RTCP_PT_PSFB_FIR){ + if(len >= 20){ + pkt[3] = htonl(rtp->themssrc); /* FIR-1 Media SSRC */ + } + } + forward = 1; + break; + } + } + } + return forward ? ast_rtcp_write_rr(instance, data, len) : 0; } -#endif static char *rtp_do_debug_ip(struct ast_cli_args *a) {