diff --git a/branches/2.0/doubango/tinyNET/src/ice/tnet_ice_candidate.c b/branches/2.0/doubango/tinyNET/src/ice/tnet_ice_candidate.c index fffb64d7..6dba738f 100644 --- a/branches/2.0/doubango/tinyNET/src/ice/tnet_ice_candidate.c +++ b/branches/2.0/doubango/tinyNET/src/ice/tnet_ice_candidate.c @@ -1,23 +1,23 @@ /* -* Copyright (C) 2012-2015 Mamadou DIOP -* Copyright (C) 2012-2015 Doubango Telecom . -* -* This file is part of Open Source Doubango Framework. -* -* DOUBANGO 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 3 of the License, or -* (at your option) any later version. -* -* DOUBANGO 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 DOUBANGO. -* -*/ + * Copyright (C) 2012-2015 Mamadou DIOP + * Copyright (C) 2012-2015 Doubango Telecom . + * + * This file is part of Open Source Doubango Framework. + * + * DOUBANGO 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 3 of the License, or + * (at your option) any later version. + * + * DOUBANGO 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 DOUBANGO. + * + */ #include "tnet_ice_candidate.h" #include "tnet_ice_utils.h" #include "tnet_utils.h" @@ -34,15 +34,15 @@ #include static int _tnet_ice_candidate_tostring( - uint8_t* foundation, - uint32_t comp_id, - const char* transport_str, - uint32_t priority, - const char* connection_addr, - tnet_port_t port, - const char* cand_type_str, - const tsk_params_L_t *extension_att_list, - char** output); + uint8_t* foundation, + uint32_t comp_id, + const char* transport_str, + uint32_t priority, + const char* connection_addr, + tnet_port_t port, + const char* cand_type_str, + const tsk_params_L_t *extension_att_list, + char** output); static const char* _tnet_ice_candidate_get_foundation(tnet_ice_cand_type_t type); static tnet_stun_pkt_t * _tnet_ice_candidate_stun_create_bind_request(tnet_ice_candidate_t* self, const char* username, const char* password); static tsk_bool_t _tnet_ice_candidate_stun_transac_id_equals(const tnet_stun_transac_id_t id1, const tnet_stun_transac_id_t id2); @@ -53,608 +53,608 @@ static tnet_ice_cand_type_t _tnet_ice_candtype_get_transport_type(const char* ca static tsk_object_t* tnet_ice_candidate_ctor(tsk_object_t * self, va_list * app) { - tnet_ice_candidate_t *candidate = self; - if (candidate){ - candidate->extension_att_list = tsk_list_create(); - } - return self; + tnet_ice_candidate_t *candidate = self; + if (candidate){ + candidate->extension_att_list = tsk_list_create(); + } + return self; } static tsk_object_t* tnet_ice_candidate_dtor(tsk_object_t * self) { - tnet_ice_candidate_t *candidate = self; - if (candidate){ - TSK_SAFE_FREE(candidate->transport_str); - TSK_SAFE_FREE(candidate->cand_type_str); - TSK_OBJECT_SAFE_FREE(candidate->extension_att_list); - TSK_OBJECT_SAFE_FREE(candidate->socket); - - - TSK_SAFE_FREE(candidate->stun.nonce); - TSK_SAFE_FREE(candidate->stun.realm); - TSK_SAFE_FREE(candidate->stun.srflx_addr); - - TSK_SAFE_FREE(candidate->turn.relay_addr); - TSK_OBJECT_SAFE_FREE(candidate->turn.ss); - - TSK_SAFE_FREE(candidate->ufrag); - TSK_SAFE_FREE(candidate->pwd); - - TSK_SAFE_FREE(candidate->tostring); - } - return self; + tnet_ice_candidate_t *candidate = self; + if (candidate){ + TSK_SAFE_FREE(candidate->transport_str); + TSK_SAFE_FREE(candidate->cand_type_str); + TSK_OBJECT_SAFE_FREE(candidate->extension_att_list); + TSK_OBJECT_SAFE_FREE(candidate->socket); + + + TSK_SAFE_FREE(candidate->stun.nonce); + TSK_SAFE_FREE(candidate->stun.realm); + TSK_SAFE_FREE(candidate->stun.srflx_addr); + + TSK_SAFE_FREE(candidate->turn.relay_addr); + TSK_OBJECT_SAFE_FREE(candidate->turn.ss); + + TSK_SAFE_FREE(candidate->ufrag); + TSK_SAFE_FREE(candidate->pwd); + + TSK_SAFE_FREE(candidate->tostring); + } + return self; } static int tnet_ice_candidate_cmp(const tsk_object_t *_s1, const tsk_object_t *_s2) { - const tnet_ice_candidate_t *c1 = _s1; - const tnet_ice_candidate_t *c2 = _s2; - - if (c1 && c2){ - return (int)(c1->priority - c2->priority); - } - else if (!c1 && !c2) return 0; - else return -1; + const tnet_ice_candidate_t *c1 = _s1; + const tnet_ice_candidate_t *c2 = _s2; + + if (c1 && c2){ + return (int)(c1->priority - c2->priority); + } + else if (!c1 && !c2) return 0; + else return -1; } static const tsk_object_def_t tnet_ice_candidate_def_s = { - sizeof(tnet_ice_candidate_t), - tnet_ice_candidate_ctor, - tnet_ice_candidate_dtor, - tnet_ice_candidate_cmp, + sizeof(tnet_ice_candidate_t), + tnet_ice_candidate_ctor, + tnet_ice_candidate_dtor, + tnet_ice_candidate_cmp, }; tnet_ice_candidate_t* tnet_ice_candidate_create(tnet_ice_cand_type_t type_e, tnet_socket_t* socket, tsk_bool_t is_ice_jingle, tsk_bool_t is_rtp, tsk_bool_t is_video, const char* ufrag, const char* pwd, const char *foundation) { - tnet_ice_candidate_t* candidate; - - if (!(candidate = tsk_object_new(&tnet_ice_candidate_def_s))){ - TSK_DEBUG_ERROR("Failed to create candidate"); - return tsk_null; - } - - candidate->type_e = type_e; - candidate->socket = tsk_object_ref(socket); - candidate->local_pref = 0xFFFF; - candidate->is_ice_jingle = is_ice_jingle; - candidate->is_rtp = is_rtp; - candidate->is_video = is_video; - candidate->comp_id = is_rtp ? TNET_ICE_CANDIDATE_COMPID_RTP : TNET_ICE_CANDIDATE_COMPID_RTCP; - if (foundation){ - memcpy(candidate->foundation, foundation, TSK_MIN(tsk_strlen(foundation), TNET_ICE_CANDIDATE_FOUND_SIZE_PREF)); - } - else{ - tnet_ice_utils_compute_foundation((char*)candidate->foundation, TSK_MIN(sizeof(candidate->foundation), TNET_ICE_CANDIDATE_FOUND_SIZE_PREF)); - } - candidate->priority = tnet_ice_utils_get_priority(candidate->type_e, candidate->local_pref, candidate->is_rtp); - if (candidate->socket){ - memcpy(candidate->connection_addr, candidate->socket->ip, sizeof(candidate->socket->ip)); - candidate->port = candidate->socket->port; - candidate->transport_e = socket->type; - } - tnet_ice_candidate_set_credential(candidate, ufrag, pwd); - - return candidate; + tnet_ice_candidate_t* candidate; + + if (!(candidate = tsk_object_new(&tnet_ice_candidate_def_s))){ + TSK_DEBUG_ERROR("Failed to create candidate"); + return tsk_null; + } + + candidate->type_e = type_e; + candidate->socket = tsk_object_ref(socket); + candidate->local_pref = 0xFFFF; + candidate->is_ice_jingle = is_ice_jingle; + candidate->is_rtp = is_rtp; + candidate->is_video = is_video; + candidate->comp_id = is_rtp ? TNET_ICE_CANDIDATE_COMPID_RTP : TNET_ICE_CANDIDATE_COMPID_RTCP; + if (foundation){ + memcpy(candidate->foundation, foundation, TSK_MIN(tsk_strlen(foundation), TNET_ICE_CANDIDATE_FOUND_SIZE_PREF)); + } + else{ + tnet_ice_utils_compute_foundation((char*)candidate->foundation, TSK_MIN(sizeof(candidate->foundation), TNET_ICE_CANDIDATE_FOUND_SIZE_PREF)); + } + candidate->priority = tnet_ice_utils_get_priority(candidate->type_e, candidate->local_pref, candidate->is_rtp); + if (candidate->socket){ + memcpy(candidate->connection_addr, candidate->socket->ip, sizeof(candidate->socket->ip)); + candidate->port = candidate->socket->port; + candidate->transport_e = socket->type; + } + tnet_ice_candidate_set_credential(candidate, ufrag, pwd); + + return candidate; } // @param str e.g. "1 1 udp 1 192.168.196.1 57806 typ host name video_rtcp network_name {0C0137CC-DB78-46B6-9B6C-7E097FFA79FE} username StFEVThMK2DHThkv password qkhKUDr4WqKRwZTo generation 0" tnet_ice_candidate_t* tnet_ice_candidate_parse(const char* str) { - char *v, *copy, *saveptr; - int32_t k; - tnet_ice_candidate_t* candidate; - - if (tsk_strnullORempty(str)){ - TSK_DEBUG_ERROR("Invalid parameter"); - return tsk_null; - } - - if (!(candidate = tsk_object_new(&tnet_ice_candidate_def_s))){ - TSK_DEBUG_ERROR("Failed to create candidate"); - return tsk_null; - } - - k = 0; - copy = tsk_strdup(str); - v = tsk_strtok_r(copy, " ", &saveptr); - - while (v){ - switch (k){ - case 0: - { - memcpy(candidate->foundation, v, TSK_MIN(tsk_strlen(v), sizeof(candidate->foundation))); - break; - } - case 1: - { - candidate->comp_id = atoi(v); - break; - } - case 2: - { - candidate->transport_str = tsk_strdup(v); - break; - } - case 3: - { - candidate->priority = atoi(v); - break; - } - case 4: - { - memcpy(candidate->connection_addr, v, TSK_MIN(tsk_strlen(v), sizeof(candidate->connection_addr))); - break; - } - case 5: - { - tnet_family_t family; - candidate->port = atoi(v); - family = tnet_get_family(candidate->connection_addr, candidate->port); - candidate->transport_e = _tnet_ice_candidate_get_transport_type((family == AF_INET6), candidate->transport_str); - break; - } - case 6: - { - v = tsk_strtok_r(tsk_null, " ", &saveptr); - tsk_strupdate(&candidate->cand_type_str, v); - candidate->type_e = _tnet_ice_candtype_get_transport_type(v); - break; - } - default: - { - const char* name = v; - const char* value = (v = tsk_strtok_r(tsk_null, " ", &saveptr)); - tsk_param_t* param = tsk_param_create(name, value); - if (param){ - tsk_list_push_back_data(candidate->extension_att_list, (void**)¶m); - } - break; - } - } - - ++k; - v = tsk_strtok_r(tsk_null, " ", &saveptr); - } - - if (k < 6){ - TSK_DEBUG_ERROR("Failed to parse: %s", str); - TSK_OBJECT_SAFE_FREE(candidate); - } - TSK_FREE(copy); - - return candidate; + char *v, *copy, *saveptr; + int32_t k; + tnet_ice_candidate_t* candidate; + + if (tsk_strnullORempty(str)){ + TSK_DEBUG_ERROR("Invalid parameter"); + return tsk_null; + } + + if (!(candidate = tsk_object_new(&tnet_ice_candidate_def_s))){ + TSK_DEBUG_ERROR("Failed to create candidate"); + return tsk_null; + } + + k = 0; + copy = tsk_strdup(str); + v = tsk_strtok_r(copy, " ", &saveptr); + + while (v){ + switch (k){ + case 0: + { + memcpy(candidate->foundation, v, TSK_MIN(tsk_strlen(v), sizeof(candidate->foundation))); + break; + } + case 1: + { + candidate->comp_id = atoi(v); + break; + } + case 2: + { + candidate->transport_str = tsk_strdup(v); + break; + } + case 3: + { + candidate->priority = atoi(v); + break; + } + case 4: + { + memcpy(candidate->connection_addr, v, TSK_MIN(tsk_strlen(v), sizeof(candidate->connection_addr))); + break; + } + case 5: + { + tnet_family_t family; + candidate->port = atoi(v); + family = tnet_get_family(candidate->connection_addr, candidate->port); + candidate->transport_e = _tnet_ice_candidate_get_transport_type((family == AF_INET6), candidate->transport_str); + break; + } + case 6: + { + v = tsk_strtok_r(tsk_null, " ", &saveptr); + tsk_strupdate(&candidate->cand_type_str, v); + candidate->type_e = _tnet_ice_candtype_get_transport_type(v); + break; + } + default: + { + const char* name = v; + const char* value = (v = tsk_strtok_r(tsk_null, " ", &saveptr)); + tsk_param_t* param = tsk_param_create(name, value); + if (param){ + tsk_list_push_back_data(candidate->extension_att_list, (void**)¶m); + } + break; + } + } + + ++k; + v = tsk_strtok_r(tsk_null, " ", &saveptr); + } + + if (k < 6){ + TSK_DEBUG_ERROR("Failed to parse: %s", str); + TSK_OBJECT_SAFE_FREE(candidate); + } + TSK_FREE(copy); + + return candidate; } int tnet_ice_candidate_set_credential(tnet_ice_candidate_t* self, const char* ufrag, const char* pwd) { - if (!self){ - TSK_DEBUG_ERROR("Invalid parameter"); - return -1; - } - - tsk_strupdate(&self->ufrag, ufrag); - tsk_strupdate(&self->pwd, pwd); - - return 0; + if (!self){ + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + + tsk_strupdate(&self->ufrag, ufrag); + tsk_strupdate(&self->pwd, pwd); + + return 0; } int tnet_ice_candidate_set_rflx_addr(tnet_ice_candidate_t* self, const char* addr, tnet_port_t port) { - if (!self || !addr || !port){ - TSK_DEBUG_ERROR("Invalid argument"); - return -1; - } - memset(self->connection_addr, 0, sizeof(self->connection_addr)); - memcpy(self->connection_addr, addr, TSK_MIN(tsk_strlen(addr), sizeof(self->connection_addr))); - self->port = port; - return 0; + if (!self || !addr || !port){ + TSK_DEBUG_ERROR("Invalid argument"); + return -1; + } + memset(self->connection_addr, 0, sizeof(self->connection_addr)); + memcpy(self->connection_addr, addr, TSK_MIN(tsk_strlen(addr), sizeof(self->connection_addr))); + self->port = port; + return 0; } const char* tnet_ice_candidate_get_att_value(const tnet_ice_candidate_t* self, const char* att_name) { - if (!self || !att_name){ - TSK_DEBUG_ERROR("Invalid parameter"); - return tsk_null; - } - return tsk_params_get_param_value(self->extension_att_list, att_name); + if (!self || !att_name){ + TSK_DEBUG_ERROR("Invalid parameter"); + return tsk_null; + } + return tsk_params_get_param_value(self->extension_att_list, att_name); } int tnet_ice_candidate_set_local_pref(tnet_ice_candidate_t* self, uint16_t local_pref) { - if (!self){ - TSK_DEBUG_ERROR("Invalid parameter"); - return -1; - } - self->local_pref = local_pref; - self->priority = tnet_ice_utils_get_priority(self->type_e, self->local_pref, self->is_rtp); - return 0; + if (!self){ + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + self->local_pref = local_pref; + self->priority = tnet_ice_utils_get_priority(self->type_e, self->local_pref, self->is_rtp); + return 0; } const char* tnet_ice_candidate_tostring(tnet_ice_candidate_t* self) { - const char* _transport_str; - char __str[255]; // always allocated: bad idea :( - - if (!self){ - TSK_DEBUG_ERROR("Invalid argument"); - return tsk_null; - } - - if (self->type_e == tnet_ice_cand_type_relay && self->turn.ss) { - enum tnet_turn_transport_e e_req_transport = tnet_turn_transport_udp; - tnet_turn_session_get_req_transport(self->turn.ss, &e_req_transport); - _transport_str = (e_req_transport == tnet_turn_transport_tcp) ? "tcp" : "udp"; - } - else { - _transport_str = self->transport_str ? self->transport_str : _tnet_ice_candidate_get_transport_str(self->transport_e); - if (self->is_ice_jingle){ - tsk_size_t i, s = tsk_strlen(_transport_str); - memset(__str, 0, sizeof(__str)); - for (i = 0; i < s && i < sizeof(__str) / sizeof(__str[0]); ++i){ - __str[i] = tolower(_transport_str[i]); - } - _transport_str = &__str[0]; - } - } - - _tnet_ice_candidate_tostring( - self->foundation, - self->comp_id, - _transport_str, - self->priority, - (tsk_strnullORempty(self->connection_addr) && self->socket) ? self->socket->ip : self->connection_addr, - (self->port <= 0 && self->socket) ? self->socket->port : self->port, - self->cand_type_str ? self->cand_type_str : _tnet_ice_candidate_get_candtype_str(self->type_e), - self->extension_att_list, - &self->tostring); - - /* and : convey transport addresses related to the - candidate, useful for diagnostics and other purposes. - and MUST be present for server reflexive, peer - reflexive, and relayed candidates. */ - switch (self->type_e){ - case tnet_ice_cand_type_srflx: - case tnet_ice_cand_type_prflx: - case tnet_ice_cand_type_relay: - { - if (self->socket){ // when called from the browser(IE, Safari, Opera or Firefox) webrtc4all - tsk_strcat_2(&self->tostring, " raddr %s rport %d", self->socket->ip, self->socket->port); - } - break; - } - default: - { - break; - } - } - - // To ease debugging - if (self->socket) { - tsk_strcat_2(&self->tostring, " tr %s", _tnet_ice_candidate_get_transport_str(self->socket->type)); - if (self->type_e == tnet_ice_cand_type_relay && self->turn.ss) { - tnet_socket_t* turn_sock = tsk_null; - if (tnet_turn_session_get_socket_local(self->turn.ss, &turn_sock) == 0 && turn_sock) { - tsk_strcat_2(&self->tostring, " fd %d", turn_sock->fd); - } - TSK_OBJECT_SAFE_FREE(turn_sock); - } - else { - tsk_strcat_2(&self->tostring, " fd %d", self->socket->fd); - } - } - - // WebRTC (Chrome) specific - if (self->is_ice_jingle) { - if (!tsk_params_have_param(self->extension_att_list, "name")){ - tsk_strcat_2(&self->tostring, " name %s", self->is_rtp ? (self->is_video ? "video_rtp" : "rtp") : (self->is_video ? "video_rtcp" : "rtcp")); - } - if (!tsk_params_have_param(self->extension_att_list, "username")){ - tsk_strcat_2(&self->tostring, " username %s", self->ufrag); - } - if (!tsk_params_have_param(self->extension_att_list, "password")){ - tsk_strcat_2(&self->tostring, " password %s", self->pwd); - } - if (!tsk_params_have_param(self->extension_att_list, "network_name")){ - tsk_strcat_2(&self->tostring, " network_name %s", "{9EBBE687-CCE6-42D3-87F5-B57BB30DEE23}"); - } - if (!tsk_params_have_param(self->extension_att_list, "generation")){ - tsk_strcat_2(&self->tostring, " generation %s", "0"); - } - } - - return self->tostring; + const char* _transport_str; + char __str[255]; // always allocated: bad idea :( + + if (!self){ + TSK_DEBUG_ERROR("Invalid argument"); + return tsk_null; + } + + if (self->type_e == tnet_ice_cand_type_relay && self->turn.ss) { + enum tnet_turn_transport_e e_req_transport = tnet_turn_transport_udp; + tnet_turn_session_get_req_transport(self->turn.ss, &e_req_transport); + _transport_str = (e_req_transport == tnet_turn_transport_tcp) ? "tcp" : "udp"; + } + else { + _transport_str = self->transport_str ? self->transport_str : _tnet_ice_candidate_get_transport_str(self->transport_e); + if (self->is_ice_jingle){ + tsk_size_t i, s = tsk_strlen(_transport_str); + memset(__str, 0, sizeof(__str)); + for (i = 0; i < s && i < sizeof(__str) / sizeof(__str[0]); ++i){ + __str[i] = tolower(_transport_str[i]); + } + _transport_str = &__str[0]; + } + } + + _tnet_ice_candidate_tostring( + self->foundation, + self->comp_id, + _transport_str, + self->priority, + (tsk_strnullORempty(self->connection_addr) && self->socket) ? self->socket->ip : self->connection_addr, + (self->port <= 0 && self->socket) ? self->socket->port : self->port, + self->cand_type_str ? self->cand_type_str : _tnet_ice_candidate_get_candtype_str(self->type_e), + self->extension_att_list, + &self->tostring); + + /* and : convey transport addresses related to the + candidate, useful for diagnostics and other purposes. + and MUST be present for server reflexive, peer + reflexive, and relayed candidates. */ + switch (self->type_e){ + case tnet_ice_cand_type_srflx: + case tnet_ice_cand_type_prflx: + case tnet_ice_cand_type_relay: + { + if (self->socket){ // when called from the browser(IE, Safari, Opera or Firefox) webrtc4all + tsk_strcat_2(&self->tostring, " raddr %s rport %d", self->socket->ip, self->socket->port); + } + break; + } + default: + { + break; + } + } + + // To ease debugging + if (self->socket) { + tsk_strcat_2(&self->tostring, " tr %s", _tnet_ice_candidate_get_transport_str(self->socket->type)); + if (self->type_e == tnet_ice_cand_type_relay && self->turn.ss) { + tnet_socket_t* turn_sock = tsk_null; + if (tnet_turn_session_get_socket_local(self->turn.ss, &turn_sock) == 0 && turn_sock) { + tsk_strcat_2(&self->tostring, " fd %d", turn_sock->fd); + } + TSK_OBJECT_SAFE_FREE(turn_sock); + } + else { + tsk_strcat_2(&self->tostring, " fd %d", self->socket->fd); + } + } + + // WebRTC (Chrome) specific + if (self->is_ice_jingle) { + if (!tsk_params_have_param(self->extension_att_list, "name")){ + tsk_strcat_2(&self->tostring, " name %s", self->is_rtp ? (self->is_video ? "video_rtp" : "rtp") : (self->is_video ? "video_rtcp" : "rtcp")); + } + if (!tsk_params_have_param(self->extension_att_list, "username")){ + tsk_strcat_2(&self->tostring, " username %s", self->ufrag); + } + if (!tsk_params_have_param(self->extension_att_list, "password")){ + tsk_strcat_2(&self->tostring, " password %s", self->pwd); + } + if (!tsk_params_have_param(self->extension_att_list, "network_name")){ + tsk_strcat_2(&self->tostring, " network_name %s", "{9EBBE687-CCE6-42D3-87F5-B57BB30DEE23}"); + } + if (!tsk_params_have_param(self->extension_att_list, "generation")){ + tsk_strcat_2(&self->tostring, " generation %s", "0"); + } + } + + return self->tostring; } int tnet_ice_candidate_send_stun_bind_request(tnet_ice_candidate_t* self, const struct sockaddr_storage* server_addr, const char* username, const char* password) { - tnet_stun_pkt_t *request = tsk_null; - tsk_buffer_t *buffer = tsk_null; - int ret, sendBytes; - - if (!self || !server_addr || !TNET_SOCKET_IS_VALID(self->socket)){ - TSK_DEBUG_ERROR("Invalid parameter"); - return -1; - } - - request = _tnet_ice_candidate_stun_create_bind_request(self, username, password); - if (!request){ - TSK_DEBUG_ERROR("Failed to create STUN request"); - ret = -2; - goto bail; - } - - if ((ret = tnet_stun_pkt_write_with_padding_2(request, &buffer))){ - TSK_DEBUG_ERROR("Failed to serialize STUN request"); - goto bail; - } - - sendBytes = tnet_sockfd_sendto(self->socket->fd, (const struct sockaddr*)server_addr, buffer->data, buffer->size);// return number of sent bytes - if (sendBytes == buffer->size){ - memcpy(self->stun.transac_id, request->transac_id, sizeof(tnet_stun_transac_id_t)); - ret = 0; - } - else{ - TSK_DEBUG_ERROR("Only %d bytes sent", sendBytes); - ret = -4; - goto bail; - } - + tnet_stun_pkt_t *request = tsk_null; + tsk_buffer_t *buffer = tsk_null; + int ret, sendBytes; + + if (!self || !server_addr || !TNET_SOCKET_IS_VALID(self->socket)){ + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + + request = _tnet_ice_candidate_stun_create_bind_request(self, username, password); + if (!request){ + TSK_DEBUG_ERROR("Failed to create STUN request"); + ret = -2; + goto bail; + } + + if ((ret = tnet_stun_pkt_write_with_padding_2(request, &buffer))){ + TSK_DEBUG_ERROR("Failed to serialize STUN request"); + goto bail; + } + + sendBytes = tnet_sockfd_sendto(self->socket->fd, (const struct sockaddr*)server_addr, buffer->data, buffer->size);// return number of sent bytes + if (sendBytes == buffer->size){ + memcpy(self->stun.transac_id, request->transac_id, sizeof(tnet_stun_transac_id_t)); + ret = 0; + } + else{ + TSK_DEBUG_ERROR("Only %d bytes sent", sendBytes); + ret = -4; + goto bail; + } + bail: - TSK_OBJECT_SAFE_FREE(request); - TSK_OBJECT_SAFE_FREE(buffer); - - return 0; + TSK_OBJECT_SAFE_FREE(request); + TSK_OBJECT_SAFE_FREE(buffer); + + return 0; } int tnet_ice_candidate_process_stun_response(tnet_ice_candidate_t* self, const tnet_stun_pkt_resp_t* response, tnet_fd_t fd) { - int ret = 0; - - if (!self || !response){ - TSK_DEBUG_ERROR("Inavlid parameter"); - return -1; - } - - //if(!(_tnet_ice_candidate_stun_transac_id_equals(response->transac_id, self->stun.transac_id))){ - // TSK_DEBUG_ERROR("Transaction id mismatch"); - // return -2; - //} - - if (TNET_STUN_PKT_RESP_IS_ERROR(response)) { - uint16_t u_code; - if ((ret = tnet_stun_pkt_get_errorcode(response, &u_code))) { - return ret; - } - if (u_code == kStunErrCodeUnauthorized || u_code == kStunErrCodeStaleNonce) { - const tnet_stun_attr_vdata_t* pc_attr; - if (u_code == kStunErrCodeUnauthorized) { - // Make sure this is not an authentication failure (#2 401) - // Do not send another req to avoid endless messages - if ((tnet_stun_pkt_attr_exists(response, tnet_stun_attr_type_message_integrity))) { // already has a MESSAGE-INTEGRITY? - TSK_DEBUG_ERROR("TURN authentication failed"); - return -3; - } - } - if ((ret = tnet_stun_pkt_attr_find_first(response, tnet_stun_attr_type_nonce, (const tnet_stun_attr_t**)&pc_attr)) == 0 && pc_attr) { - tsk_strupdate(&self->stun.nonce, pc_attr->p_data_ptr); - } - if ((ret = tnet_stun_pkt_attr_find_first(response, tnet_stun_attr_type_realm, (const tnet_stun_attr_t**)&pc_attr)) == 0 && pc_attr) { - tsk_strupdate(&self->stun.realm, pc_attr->p_data_ptr); - } - return 0; - } - else { - TSK_DEBUG_ERROR("STUN error: %hu", u_code); - return -4; - } - } - else if (TNET_STUN_PKT_RESP_IS_SUCCESS(response)) { - const tnet_stun_attr_address_t* pc_attr_addr; - if (((ret = tnet_stun_pkt_attr_find_first(response, tnet_stun_attr_type_xor_mapped_address, (const tnet_stun_attr_t**)&pc_attr_addr)) == 0 && pc_attr_addr) - || ((ret = tnet_stun_pkt_attr_find_first(response, tnet_stun_attr_type_mapped_address, (const tnet_stun_attr_t**)&pc_attr_addr)) == 0 && pc_attr_addr)) { - tnet_ip_t ip; - if ((ret = tnet_stun_utils_inet_ntop((pc_attr_addr->e_family == tnet_stun_address_family_ipv6), &pc_attr_addr->address, &ip))) { - return ret; - } - tsk_strupdate(&self->stun.srflx_addr, ip); - self->stun.srflx_port = pc_attr_addr->u_port; - } - } - - return ret; + int ret = 0; + + if (!self || !response){ + TSK_DEBUG_ERROR("Inavlid parameter"); + return -1; + } + + //if(!(_tnet_ice_candidate_stun_transac_id_equals(response->transac_id, self->stun.transac_id))){ + // TSK_DEBUG_ERROR("Transaction id mismatch"); + // return -2; + //} + + if (TNET_STUN_PKT_RESP_IS_ERROR(response)) { + uint16_t u_code; + if ((ret = tnet_stun_pkt_get_errorcode(response, &u_code))) { + return ret; + } + if (u_code == kStunErrCodeUnauthorized || u_code == kStunErrCodeStaleNonce) { + const tnet_stun_attr_vdata_t* pc_attr; + if (u_code == kStunErrCodeUnauthorized) { + // Make sure this is not an authentication failure (#2 401) + // Do not send another req to avoid endless messages + if ((tnet_stun_pkt_attr_exists(response, tnet_stun_attr_type_message_integrity))) { // already has a MESSAGE-INTEGRITY? + TSK_DEBUG_ERROR("TURN authentication failed"); + return -3; + } + } + if ((ret = tnet_stun_pkt_attr_find_first(response, tnet_stun_attr_type_nonce, (const tnet_stun_attr_t**)&pc_attr)) == 0 && pc_attr) { + tsk_strupdate(&self->stun.nonce, pc_attr->p_data_ptr); + } + if ((ret = tnet_stun_pkt_attr_find_first(response, tnet_stun_attr_type_realm, (const tnet_stun_attr_t**)&pc_attr)) == 0 && pc_attr) { + tsk_strupdate(&self->stun.realm, pc_attr->p_data_ptr); + } + return 0; + } + else { + TSK_DEBUG_ERROR("STUN error: %hu", u_code); + return -4; + } + } + else if (TNET_STUN_PKT_RESP_IS_SUCCESS(response)) { + const tnet_stun_attr_address_t* pc_attr_addr; + if (((ret = tnet_stun_pkt_attr_find_first(response, tnet_stun_attr_type_xor_mapped_address, (const tnet_stun_attr_t**)&pc_attr_addr)) == 0 && pc_attr_addr) + || ((ret = tnet_stun_pkt_attr_find_first(response, tnet_stun_attr_type_mapped_address, (const tnet_stun_attr_t**)&pc_attr_addr)) == 0 && pc_attr_addr)) { + tnet_ip_t ip; + if ((ret = tnet_stun_utils_inet_ntop((pc_attr_addr->e_family == tnet_stun_address_family_ipv6), &pc_attr_addr->address, &ip))) { + return ret; + } + tsk_strupdate(&self->stun.srflx_addr, ip); + self->stun.srflx_port = pc_attr_addr->u_port; + } + } + + return ret; } const tnet_ice_candidate_t* tnet_ice_candidate_find_by_fd(tnet_ice_candidates_L_t* candidates, tnet_fd_t fd) { - if (candidates) { - const tsk_list_item_t *item; - const tnet_ice_candidate_t* candidate; - - tsk_list_lock(candidates); - tsk_list_foreach(item, candidates) { - if (!(candidate = item->data)) { - continue; - } - if (candidate->socket && (candidate->socket->fd == fd)) { - tsk_list_unlock(candidates); - return candidate; - } - } - } - - return tsk_null; + if (candidates) { + const tsk_list_item_t *item; + const tnet_ice_candidate_t* candidate; + + tsk_list_lock(candidates); + tsk_list_foreach(item, candidates) { + if (!(candidate = item->data)) { + continue; + } + if (candidate->socket && (candidate->socket->fd == fd)) { + tsk_list_unlock(candidates); + return candidate; + } + } + } + + return tsk_null; } const char* tnet_ice_candidate_get_ufrag(const tnet_ice_candidate_t* self) { - if (self){ - return self->ufrag ? self->ufrag : tnet_ice_candidate_get_att_value(self, "username"); - } - return tsk_null; + if (self){ + return self->ufrag ? self->ufrag : tnet_ice_candidate_get_att_value(self, "username"); + } + return tsk_null; } const char* tnet_ice_candidate_get_pwd(const tnet_ice_candidate_t* self) { - if (self){ - return self->pwd ? self->pwd : tnet_ice_candidate_get_att_value(self, "password"); - } - return tsk_null; + if (self){ + return self->pwd ? self->pwd : tnet_ice_candidate_get_att_value(self, "password"); + } + return tsk_null; } static int _tnet_ice_candidate_tostring( - uint8_t* foundation, - uint32_t comp_id, - const char* transport_str, - uint32_t priority, - const char* connection_addr, - tnet_port_t port, - const char* cand_type_str, - const tsk_params_L_t *extension_att_list, - char** output) + uint8_t* foundation, + uint32_t comp_id, + const char* transport_str, + uint32_t priority, + const char* connection_addr, + tnet_port_t port, + const char* cand_type_str, + const tsk_params_L_t *extension_att_list, + char** output) { - if (!output) { - TSK_DEBUG_ERROR("Invalid argument"); - return -1; - } - tsk_sprintf(output, "%s %d %s %d %s %d typ %s", - foundation, - comp_id, - transport_str, - priority, - connection_addr, - port, - cand_type_str); - - if (extension_att_list) { - const tsk_list_item_t *item; - tsk_list_foreach(item, extension_att_list){ - tsk_strcat_2(output, " %s %s", TSK_PARAM(item->data)->name, TSK_PARAM(item->data)->value); - } - } - return 0; + if (!output) { + TSK_DEBUG_ERROR("Invalid argument"); + return -1; + } + tsk_sprintf(output, "%s %d %s %d %s %d typ %s", + foundation, + comp_id, + transport_str, + priority, + connection_addr, + port, + cand_type_str); + + if (extension_att_list) { + const tsk_list_item_t *item; + tsk_list_foreach(item, extension_att_list){ + tsk_strcat_2(output, " %s %s", TSK_PARAM(item->data)->name, TSK_PARAM(item->data)->value); + } + } + return 0; } static const char* _tnet_ice_candidate_get_foundation(tnet_ice_cand_type_t type) { - switch (type){ - case tnet_ice_cand_type_host: return TNET_ICE_CANDIDATE_FOUNDATION_HOST; - case tnet_ice_cand_type_srflx: return TNET_ICE_CANDIDATE_FOUNDATION_SRFLX; - case tnet_ice_cand_type_prflx: return TNET_ICE_CANDIDATE_FOUNDATION_PRFLX; - case tnet_ice_cand_type_relay: default: return TNET_ICE_CANDIDATE_FOUNDATION_RELAY; - } + switch (type){ + case tnet_ice_cand_type_host: return TNET_ICE_CANDIDATE_FOUNDATION_HOST; + case tnet_ice_cand_type_srflx: return TNET_ICE_CANDIDATE_FOUNDATION_SRFLX; + case tnet_ice_cand_type_prflx: return TNET_ICE_CANDIDATE_FOUNDATION_PRFLX; + case tnet_ice_cand_type_relay: default: return TNET_ICE_CANDIDATE_FOUNDATION_RELAY; + } } static tsk_bool_t _tnet_ice_candidate_stun_transac_id_equals(const tnet_stun_transac_id_t id1, const tnet_stun_transac_id_t id2) { - tsk_size_t i; - static const tsk_size_t size = sizeof(tnet_stun_transac_id_t); - for (i = 0; i < size; i++){ - if (id1[i] != id2[i]){ - return tsk_false; - } - } - return tsk_true; + tsk_size_t i; + static const tsk_size_t size = sizeof(tnet_stun_transac_id_t); + for (i = 0; i < size; i++){ + if (id1[i] != id2[i]){ + return tsk_false; + } + } + return tsk_true; } static tnet_stun_pkt_t * _tnet_ice_candidate_stun_create_bind_request(tnet_ice_candidate_t* self, const char* username, const char* password) { - tnet_stun_pkt_t *request = tsk_null; - int ret; - - if (!self) { - TSK_DEBUG_ERROR("Invalid parameter"); - return tsk_null; - } - - if ((ret = tnet_stun_pkt_create_empty(tnet_stun_pkt_type_binding_request, &request))) { - TSK_DEBUG_ERROR("Failed to create STUN Bind request"); - goto bail; - } - // add attributes - request->opt.dontfrag = 0; - ret = tnet_stun_pkt_attrs_add(request, - TNET_STUN_PKT_ATTR_ADD_SOFTWARE_ZT(kStunSoftware), - TNET_STUN_PKT_ATTR_ADD_NULL()); - if (ret) { - goto bail; - } - if (username && self->stun.realm && self->stun.nonce) { - if ((ret = tnet_stun_pkt_auth_prepare(request, username, password, self->stun.realm, self->stun.nonce))) { - goto bail; - } - } - + tnet_stun_pkt_t *request = tsk_null; + int ret; + + if (!self) { + TSK_DEBUG_ERROR("Invalid parameter"); + return tsk_null; + } + + if ((ret = tnet_stun_pkt_create_empty(tnet_stun_pkt_type_binding_request, &request))) { + TSK_DEBUG_ERROR("Failed to create STUN Bind request"); + goto bail; + } + // add attributes + request->opt.dontfrag = 0; + ret = tnet_stun_pkt_attrs_add(request, + TNET_STUN_PKT_ATTR_ADD_SOFTWARE_ZT(kStunSoftware), + TNET_STUN_PKT_ATTR_ADD_NULL()); + if (ret) { + goto bail; + } + if (username && self->stun.realm && self->stun.nonce) { + if ((ret = tnet_stun_pkt_auth_prepare(request, username, password, self->stun.realm, self->stun.nonce))) { + goto bail; + } + } + bail: - if (ret) { - TSK_OBJECT_SAFE_FREE(request); - } - return request; + if (ret) { + TSK_OBJECT_SAFE_FREE(request); + } + return request; } static const char* _tnet_ice_candidate_get_transport_str(tnet_socket_type_t transport_e) { #define TRANSPORT_GET(STR) \ - if(TNET_SOCKET_TYPE_IS_##STR(transport_e)){ \ - return TNET_ICE_CANDIDATE_TRANSPORT_##STR; \ - } - TRANSPORT_GET(UDP); - TRANSPORT_GET(TCP); - TRANSPORT_GET(TLS); - TRANSPORT_GET(SCTP); - TRANSPORT_GET(WS); - TRANSPORT_GET(WSS); - return "UNKNOWN"; - +if(TNET_SOCKET_TYPE_IS_##STR(transport_e)){ \ +return TNET_ICE_CANDIDATE_TRANSPORT_##STR; \ +} + TRANSPORT_GET(UDP); + TRANSPORT_GET(TCP); + TRANSPORT_GET(TLS); + TRANSPORT_GET(SCTP); + TRANSPORT_GET(WS); + TRANSPORT_GET(WSS); + return "UNKNOWN"; + #undef TRANSPORT_GET } static tnet_socket_type_t _tnet_ice_candidate_get_transport_type(tsk_bool_t ipv6, const char* transport_str) { #define TRANSPORT_GET(STR, str) \ - if(tsk_striequals(TNET_ICE_CANDIDATE_TRANSPORT_##STR, transport_str)){ \ - return tnet_socket_type_##str##_ipv4; \ - } - - TRANSPORT_GET(UDP, udp); - TRANSPORT_GET(TCP, tcp); - TRANSPORT_GET(TLS, tls); - TRANSPORT_GET(SCTP, sctp); - TRANSPORT_GET(WS, ws); - TRANSPORT_GET(WSS, wss); - return tnet_socket_type_invalid; - +if(tsk_striequals(TNET_ICE_CANDIDATE_TRANSPORT_##STR, transport_str)){ \ +return tnet_socket_type_##str##_ipv4; \ +} + + TRANSPORT_GET(UDP, udp); + TRANSPORT_GET(TCP, tcp); + TRANSPORT_GET(TLS, tls); + TRANSPORT_GET(SCTP, sctp); + TRANSPORT_GET(WS, ws); + TRANSPORT_GET(WSS, wss); + return tnet_socket_type_invalid; + #undef TRANSPORT_GET } static const char* _tnet_ice_candidate_get_candtype_str(tnet_ice_cand_type_t candtype_e) { - switch (candtype_e){ - case tnet_ice_cand_type_unknown: - default: return "unknown"; - case tnet_ice_cand_type_host: return TNET_ICE_CANDIDATE_TYPE_HOST; - case tnet_ice_cand_type_srflx: return TNET_ICE_CANDIDATE_TYPE_SRFLX; - case tnet_ice_cand_type_prflx: return TNET_ICE_CANDIDATE_TYPE_PRFLX; - case tnet_ice_cand_type_relay: return TNET_ICE_CANDIDATE_TYPE_RELAY; - } + switch (candtype_e){ + case tnet_ice_cand_type_unknown: + default: return "unknown"; + case tnet_ice_cand_type_host: return TNET_ICE_CANDIDATE_TYPE_HOST; + case tnet_ice_cand_type_srflx: return TNET_ICE_CANDIDATE_TYPE_SRFLX; + case tnet_ice_cand_type_prflx: return TNET_ICE_CANDIDATE_TYPE_PRFLX; + case tnet_ice_cand_type_relay: return TNET_ICE_CANDIDATE_TYPE_RELAY; + } } static tnet_ice_cand_type_t _tnet_ice_candtype_get_transport_type(const char* candtype_str) { - if (tsk_striequals(TNET_ICE_CANDIDATE_TYPE_HOST, candtype_str)){ - return tnet_ice_cand_type_host; - } - else if (tsk_striequals(TNET_ICE_CANDIDATE_TYPE_SRFLX, candtype_str)){ - return tnet_ice_cand_type_srflx; - } - else if (tsk_striequals(TNET_ICE_CANDIDATE_TYPE_PRFLX, candtype_str)){ - return tnet_ice_cand_type_prflx; - } - else if (tsk_striequals(TNET_ICE_CANDIDATE_TYPE_RELAY, candtype_str)){ - return tnet_ice_cand_type_relay; - } - else{ - return tnet_ice_cand_type_unknown; - } + if (tsk_striequals(TNET_ICE_CANDIDATE_TYPE_HOST, candtype_str)){ + return tnet_ice_cand_type_host; + } + else if (tsk_striequals(TNET_ICE_CANDIDATE_TYPE_SRFLX, candtype_str)){ + return tnet_ice_cand_type_srflx; + } + else if (tsk_striequals(TNET_ICE_CANDIDATE_TYPE_PRFLX, candtype_str)){ + return tnet_ice_cand_type_prflx; + } + else if (tsk_striequals(TNET_ICE_CANDIDATE_TYPE_RELAY, candtype_str)){ + return tnet_ice_cand_type_relay; + } + else{ + return tnet_ice_cand_type_unknown; + } } diff --git a/branches/2.0/doubango/tinyNET/src/ice/tnet_ice_ctx.c b/branches/2.0/doubango/tinyNET/src/ice/tnet_ice_ctx.c index d1c8f677..62462d4e 100644 --- a/branches/2.0/doubango/tinyNET/src/ice/tnet_ice_ctx.c +++ b/branches/2.0/doubango/tinyNET/src/ice/tnet_ice_ctx.c @@ -1,23 +1,23 @@ /* -* Copyright (C) 2012-2015 Mamadou DIOP -* Copyright (C) 2012-2015 Doubango Telecom . -* -* This file is part of Open Source Doubango Framework. -* -* DOUBANGO 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 3 of the License, or -* (at your option) any later version. -* -* DOUBANGO 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 DOUBANGO. -* -*/ + * Copyright (C) 2012-2015 Mamadou DIOP + * Copyright (C) 2012-2015 Doubango Telecom . + * + * This file is part of Open Source Doubango Framework. + * + * DOUBANGO 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 3 of the License, or + * (at your option) any later version. + * + * DOUBANGO 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 DOUBANGO. + * + */ /**@file tnet_ice_ctx.c * @brief Interactive Connectivity Establishment (ICE) implementation as per RFC 5245 @@ -58,14 +58,14 @@ #endif /**@ingroup tnet_nat_group -* Estimate of the round-trip time (RTT) in millisecond. -*/ + * Estimate of the round-trip time (RTT) in millisecond. + */ #define kIceDefaultRTO 500 /**@ingroup tnet_nat_group -* Number of retransmission for UDP retransmission in millisecond. -* 7.2.1. Sending over UDP -Rc SHOULD be configurable and SHOULD have a default of 7. -*/ + * Number of retransmission for UDP retransmission in millisecond. + * 7.2.1. Sending over UDP + Rc SHOULD be configurable and SHOULD have a default of 7. + */ #define kIceDefaultRC 4 //7 #define kIceDefaultTurnEnabled 0 // Relay candidates @@ -85,10 +85,10 @@ static const char* foundation_default = tsk_null; typedef enum tnet_ice_server_proto_e { - tnet_ice_server_proto_none = 0x00, - tnet_ice_server_proto_stun = (0x01 << 0), - tnet_ice_server_proto_turn = (0x01 << 1), - tnet_ice_server_proto_all = 0xFF + tnet_ice_server_proto_none = 0x00, + tnet_ice_server_proto_stun = (0x01 << 0), + tnet_ice_server_proto_turn = (0x01 << 1), + tnet_ice_server_proto_all = 0xFF } tnet_ice_server_proto_t; @@ -119,10 +119,10 @@ static int _tnet_ice_ctx_fsm_Any_2_Terminated_X_AnyNotStarted(va_list *app); // static int _tnet_ice_ctx_servers_clear(struct tnet_ice_ctx_s* self); static int _tnet_ice_ctx_server_add(struct tnet_ice_ctx_s* self, enum tnet_ice_server_proto_e e_proto, -enum tnet_socket_type_e e_transport, - const char* str_server_addr, uint16_t u_server_port, - const char* str_software, - const char* str_username, const char* str_password); + enum tnet_socket_type_e e_transport, + const char* str_server_addr, uint16_t u_server_port, + const char* str_software, + const char* str_username, const char* str_password); static int _tnet_ice_ctx_server_remove(struct tnet_ice_ctx_s* self, enum tnet_ice_server_proto_e e_proto, enum tnet_socket_type_e e_transport, const char* str_server_addr, uint16_t u_server_port); static const struct tnet_ice_server_s* _tnet_ice_ctx_server_find(struct tnet_ice_ctx_s* self, enum tnet_ice_server_proto_e e_proto, enum tnet_socket_type_e e_transport, const char* str_server_addr, uint16_t u_server_port); static tsk_bool_t _tnet_ice_ctx_server_exists(struct tnet_ice_ctx_s* self, enum tnet_ice_server_proto_e e_proto, enum tnet_socket_type_e e_transport, const char* str_server_addr, uint16_t u_server_port); @@ -135,216 +135,216 @@ static int _tnet_ice_ctx_turn_callback(const struct tnet_turn_session_event_xs * typedef struct tnet_ice_server_s { - TSK_DECLARE_OBJECT; - - enum tnet_socket_type_e e_transport; - tnet_ice_server_proto_t e_proto; - char* str_server_addr; - uint16_t u_server_port; - struct sockaddr_storage obj_server_addr; - char* str_software; - char* str_username; - char* str_password; - int rto; + TSK_DECLARE_OBJECT; + + enum tnet_socket_type_e e_transport; + tnet_ice_server_proto_t e_proto; + char* str_server_addr; + uint16_t u_server_port; + struct sockaddr_storage obj_server_addr; + char* str_software; + char* str_username; + char* str_password; + int rto; } tnet_ice_server_t; static tsk_object_t* tnet_ice_server_ctor(tsk_object_t * self, va_list * app) { - tnet_ice_server_t *ice_server = self; - if (ice_server) { - } - return self; + tnet_ice_server_t *ice_server = self; + if (ice_server) { + } + return self; } static tsk_object_t* tnet_ice_server_dtor(tsk_object_t * self) { - tnet_ice_server_t *ice_server = self; - if (ice_server) { - TSK_FREE(ice_server->str_server_addr); - TSK_FREE(ice_server->str_software); - TSK_FREE(ice_server->str_username); - TSK_FREE(ice_server->str_password); - - TSK_DEBUG_INFO("*** ICE server destroyed ***"); - } - return self; + tnet_ice_server_t *ice_server = self; + if (ice_server) { + TSK_FREE(ice_server->str_server_addr); + TSK_FREE(ice_server->str_software); + TSK_FREE(ice_server->str_username); + TSK_FREE(ice_server->str_password); + + TSK_DEBUG_INFO("*** ICE server destroyed ***"); + } + return self; } static const tsk_object_def_t tnet_ice_server_def_s = { - sizeof(tnet_ice_server_t), - tnet_ice_server_ctor, - tnet_ice_server_dtor, - tsk_null, + sizeof(tnet_ice_server_t), + tnet_ice_server_ctor, + tnet_ice_server_dtor, + tsk_null, }; static tnet_ice_server_t* tnet_ice_server_create( -enum tnet_ice_server_proto_e e_proto, -enum tnet_socket_type_e e_transport, - const char* str_server_addr, uint16_t u_server_port, - const char* str_software, - const char* str_username, const char* str_password) + enum tnet_ice_server_proto_e e_proto, + enum tnet_socket_type_e e_transport, + const char* str_server_addr, uint16_t u_server_port, + const char* str_software, + const char* str_username, const char* str_password) { - tnet_ice_server_t *ice_server; - struct sockaddr_storage obj_server_addr; - - if (tsk_strnullORempty(str_server_addr) || !u_server_port) { - TSK_DEBUG_ERROR("Invalid parameter"); - return tsk_null; - } - - if (tnet_sockaddr_init(str_server_addr, u_server_port, e_transport, &obj_server_addr) != 0) { - TSK_DEBUG_ERROR("Invalid server address (host=%s, port=%d, transport=%d)", str_server_addr, u_server_port, e_transport); - return tsk_null; - } - - if ((ice_server = tsk_object_new(&tnet_ice_server_def_s))) { - ice_server->e_proto = e_proto; - ice_server->e_transport = e_transport; - tsk_strupdate(&ice_server->str_server_addr, str_server_addr); - ice_server->u_server_port = u_server_port; - tsk_strupdate(&ice_server->str_software, str_software); - tsk_strupdate(&ice_server->str_username, str_username); - tsk_strupdate(&ice_server->str_password, str_password); - memcpy(&ice_server->obj_server_addr, &obj_server_addr, sizeof(struct sockaddr_storage)); - } - return ice_server; + tnet_ice_server_t *ice_server; + struct sockaddr_storage obj_server_addr; + + if (tsk_strnullORempty(str_server_addr) || !u_server_port) { + TSK_DEBUG_ERROR("Invalid parameter"); + return tsk_null; + } + + if (tnet_sockaddr_init(str_server_addr, u_server_port, e_transport, &obj_server_addr) != 0) { + TSK_DEBUG_ERROR("Invalid server address (host=%s, port=%d, transport=%d)", str_server_addr, u_server_port, e_transport); + return tsk_null; + } + + if ((ice_server = tsk_object_new(&tnet_ice_server_def_s))) { + ice_server->e_proto = e_proto; + ice_server->e_transport = e_transport; + tsk_strupdate(&ice_server->str_server_addr, str_server_addr); + ice_server->u_server_port = u_server_port; + tsk_strupdate(&ice_server->str_software, str_software); + tsk_strupdate(&ice_server->str_username, str_username); + tsk_strupdate(&ice_server->str_password, str_password); + memcpy(&ice_server->obj_server_addr, &obj_server_addr, sizeof(struct sockaddr_storage)); + } + return ice_server; } typedef struct tnet_ice_ctx_s { - TSK_DECLARE_RUNNABLE; - - tsk_bool_t is_started; - tsk_bool_t is_active; - tsk_bool_t is_sync_mode; - tsk_bool_t is_silent_mode; - tnet_ice_callback_f callback; - const void* userdata; - tsk_bool_t use_ipv6; - tsk_bool_t use_rtcp; - tsk_bool_t use_rtcpmux; - tsk_bool_t is_video; - tsk_bool_t is_building_pairs; - tsk_bool_t unicast; - tsk_bool_t anycast; - tsk_bool_t multicast; - - tsk_bool_t is_connchecking; - tsk_bool_t is_controlling; - tsk_bool_t is_ice_jingle; - tsk_bool_t is_turn_enabled; - tsk_bool_t is_stun_enabled; - uint64_t tie_breaker; - uint64_t concheck_timeout; - - const void* rtp_callback_data; - tnet_ice_rtp_callback_f rtp_callback; - - tnet_ice_servers_L_t *servers; - - char* ufrag; - char* pwd; - - tsk_timer_manager_handle_t* h_timer_mgr; - - tsk_fsm_t* fsm; - - tsk_condwait_handle_t* condwait_pairs; - tnet_ice_candidates_L_t* candidates_local; - tnet_ice_candidates_L_t* candidates_remote; - tnet_ice_pairs_L_t* candidates_pairs; - tsk_bool_t have_nominated_offer; - tsk_bool_t have_nominated_answer; - tsk_bool_t have_nominated_symetric; /**< Whether symetic RTP has been negotiated */ - - uint16_t RTO; /**< Estimate of the round-trip time (RTT) in millisecond */ - uint16_t Rc; /**< Number of retransmissions for UDP in millisecond */ - - struct { - char* path_priv; - char* path_pub; - char* path_ca; - tsk_bool_t verify; - } ssl; - - struct { - tsk_condwait_handle_t* condwait; - struct tnet_turn_session_s* ss_nominated_rtp; - tnet_turn_peer_id_t peer_id_rtp; - struct tnet_turn_session_s* ss_nominated_rtcp; - tnet_turn_peer_id_t peer_id_rtcp; - } turn; - - TSK_DECLARE_SAFEOBJ; + TSK_DECLARE_RUNNABLE; + + tsk_bool_t is_started; + tsk_bool_t is_active; + tsk_bool_t is_sync_mode; + tsk_bool_t is_silent_mode; + tnet_ice_callback_f callback; + const void* userdata; + tsk_bool_t use_ipv6; + tsk_bool_t use_rtcp; + tsk_bool_t use_rtcpmux; + tsk_bool_t is_video; + tsk_bool_t is_building_pairs; + tsk_bool_t unicast; + tsk_bool_t anycast; + tsk_bool_t multicast; + + tsk_bool_t is_connchecking; + tsk_bool_t is_controlling; + tsk_bool_t is_ice_jingle; + tsk_bool_t is_turn_enabled; + tsk_bool_t is_stun_enabled; + uint64_t tie_breaker; + uint64_t concheck_timeout; + + const void* rtp_callback_data; + tnet_ice_rtp_callback_f rtp_callback; + + tnet_ice_servers_L_t *servers; + + char* ufrag; + char* pwd; + + tsk_timer_manager_handle_t* h_timer_mgr; + + tsk_fsm_t* fsm; + + tsk_condwait_handle_t* condwait_pairs; + tnet_ice_candidates_L_t* candidates_local; + tnet_ice_candidates_L_t* candidates_remote; + tnet_ice_pairs_L_t* candidates_pairs; + tsk_bool_t have_nominated_offer; + tsk_bool_t have_nominated_answer; + tsk_bool_t have_nominated_symetric; /**< Whether symetic RTP has been negotiated */ + + uint16_t RTO; /**< Estimate of the round-trip time (RTT) in millisecond */ + uint16_t Rc; /**< Number of retransmissions for UDP in millisecond */ + + struct { + char* path_priv; + char* path_pub; + char* path_ca; + tsk_bool_t verify; + } ssl; + + struct { + tsk_condwait_handle_t* condwait; + struct tnet_turn_session_s* ss_nominated_rtp; + tnet_turn_peer_id_t peer_id_rtp; + struct tnet_turn_session_s* ss_nominated_rtcp; + tnet_turn_peer_id_t peer_id_rtcp; + } turn; + + TSK_DECLARE_SAFEOBJ; } tnet_ice_ctx_t; typedef struct tnet_ice_action_s { - TSK_DECLARE_OBJECT; - - tsk_fsm_action_id id; + TSK_DECLARE_OBJECT; + + tsk_fsm_action_id id; } tnet_ice_action_t; typedef enum _fsm_state_e { - _fsm_state_Started, - _fsm_state_GatheringHostCandidates, - _fsm_state_GatheringHostCandidatesDone, - _fsm_state_GatheringReflexiveCandidates, - _fsm_state_GatheringReflexiveCandidatesDone, - _fsm_state_GatheringRelayCandidates, - _fsm_state_GatheringRelayCandidatesDone, - _fsm_state_GatheringCompleted, - _fsm_state_ConnChecking, - _fsm_state_ConnCheckingCompleted, - _fsm_state_Terminated + _fsm_state_Started, + _fsm_state_GatheringHostCandidates, + _fsm_state_GatheringHostCandidatesDone, + _fsm_state_GatheringReflexiveCandidates, + _fsm_state_GatheringReflexiveCandidatesDone, + _fsm_state_GatheringRelayCandidates, + _fsm_state_GatheringRelayCandidatesDone, + _fsm_state_GatheringCompleted, + _fsm_state_ConnChecking, + _fsm_state_ConnCheckingCompleted, + _fsm_state_Terminated } _fsm_state_t; typedef enum _fsm_action_e { - _fsm_action_Success, - _fsm_action_Failure, - _fsm_action_GatherHostCandidates, - _fsm_action_GatherReflexiveCandidates, - _fsm_action_GatherRelayCandidates, - _fsm_action_GatheringComplet, - _fsm_action_ConnCheck, - _fsm_action_Cancel, - _fsm_action_Error, + _fsm_action_Success, + _fsm_action_Failure, + _fsm_action_GatherHostCandidates, + _fsm_action_GatherReflexiveCandidates, + _fsm_action_GatherRelayCandidates, + _fsm_action_GatheringComplet, + _fsm_action_ConnCheck, + _fsm_action_Cancel, + _fsm_action_Error, } _fsm_action_t; static tsk_object_t* tnet_ice_action_ctor(tsk_object_t * self, va_list * app) { - tnet_ice_action_t *action = self; - if (action){ - } - return self; + tnet_ice_action_t *action = self; + if (action){ + } + return self; } static tsk_object_t* tnet_ice_action_dtor(tsk_object_t * self) { - tnet_ice_action_t *action = self; - if (action){ - } - return self; + tnet_ice_action_t *action = self; + if (action){ + } + return self; } static const tsk_object_def_t tnet_ice_action_def_s = { - sizeof(tnet_ice_action_t), - tnet_ice_action_ctor, - tnet_ice_action_dtor, - tsk_null, + sizeof(tnet_ice_action_t), + tnet_ice_action_ctor, + tnet_ice_action_dtor, + tsk_null, }; static tnet_ice_action_t* tnet_ice_action_create(tsk_fsm_action_id id) { - tnet_ice_action_t *action = tsk_object_new(&tnet_ice_action_def_s); - if (action){ - action->id = id; - } - return action; + tnet_ice_action_t *action = tsk_object_new(&tnet_ice_action_def_s); + if (action){ + action->id = id; + } + return action; } @@ -352,697 +352,710 @@ static tnet_ice_action_t* tnet_ice_action_create(tsk_fsm_action_id id) static tsk_object_t* tnet_ice_ctx_ctor(tsk_object_t * self, va_list * app) { - tnet_ice_ctx_t *ctx = self; - if (ctx){ - tsk_safeobj_init(ctx); - - if (!(ctx->h_timer_mgr = tsk_timer_manager_create())){ - TSK_DEBUG_ERROR("Failed to create timer manager"); - return tsk_null; - } - if (!(ctx->fsm = tsk_fsm_create(_fsm_state_Started, _fsm_state_Terminated))){ - TSK_DEBUG_ERROR("Failed to create state machine"); - return tsk_null; - } - if (!(ctx->candidates_local = tsk_list_create())){ - TSK_DEBUG_ERROR("Failed to create candidates list"); - return tsk_null; - } - if (!(ctx->candidates_remote = tsk_list_create())){ - TSK_DEBUG_ERROR("Failed to create candidates list"); - return tsk_null; - } - if (!(ctx->candidates_pairs = tsk_list_create())){ - TSK_DEBUG_ERROR("Failed to create candidates list"); - return tsk_null; - } - - // Create condwait for pairs - if (!(ctx->condwait_pairs = tsk_condwait_create())) { - TSK_DEBUG_ERROR("Failed to create condwait for pairs"); - return tsk_null; - } - - // Create list objects to hold the servers - if (!(ctx->servers = tsk_list_create())){ - TSK_DEBUG_ERROR("Failed to create server list"); - return tsk_null; - } - - tsk_runnable_set_important(TSK_RUNNABLE(self), tsk_false); - - /* 7.2.1. Sending over UDP - In fixed-line access links, a value of 500 ms is RECOMMENDED. - */ - ctx->RTO = kIceDefaultRTO; - - /* 7.2.1. Sending over UDP - Rc SHOULD be configurable and SHOULD have a default of 7. - */ - ctx->Rc = kIceDefaultRC; - - ctx->tie_breaker = ((tsk_time_now() << 32) ^ tsk_time_now()); - ctx->is_ice_jingle = tsk_false; - ctx->is_stun_enabled = kIceDefaultStunEnabled; - ctx->is_turn_enabled = kIceDefaultTurnEnabled; - - ctx->concheck_timeout = LONG_MAX; - } - return self; + tnet_ice_ctx_t *ctx = self; + if (ctx){ + tsk_safeobj_init(ctx); + + if (!(ctx->h_timer_mgr = tsk_timer_manager_create())){ + TSK_DEBUG_ERROR("Failed to create timer manager"); + return tsk_null; + } + if (!(ctx->fsm = tsk_fsm_create(_fsm_state_Started, _fsm_state_Terminated))){ + TSK_DEBUG_ERROR("Failed to create state machine"); + return tsk_null; + } + if (!(ctx->candidates_local = tsk_list_create())){ + TSK_DEBUG_ERROR("Failed to create candidates list"); + return tsk_null; + } + if (!(ctx->candidates_remote = tsk_list_create())){ + TSK_DEBUG_ERROR("Failed to create candidates list"); + return tsk_null; + } + if (!(ctx->candidates_pairs = tsk_list_create())){ + TSK_DEBUG_ERROR("Failed to create candidates list"); + return tsk_null; + } + + // Create condwait for pairs + if (!(ctx->condwait_pairs = tsk_condwait_create())) { + TSK_DEBUG_ERROR("Failed to create condwait for pairs"); + return tsk_null; + } + + // Create list objects to hold the servers + if (!(ctx->servers = tsk_list_create())){ + TSK_DEBUG_ERROR("Failed to create server list"); + return tsk_null; + } + + tsk_runnable_set_important(TSK_RUNNABLE(self), tsk_false); + + /* 7.2.1. Sending over UDP + In fixed-line access links, a value of 500 ms is RECOMMENDED. + */ + ctx->RTO = kIceDefaultRTO; + + /* 7.2.1. Sending over UDP + Rc SHOULD be configurable and SHOULD have a default of 7. + */ + ctx->Rc = kIceDefaultRC; + + ctx->tie_breaker = ((tsk_time_now() << 32) ^ tsk_time_now()); + ctx->is_ice_jingle = tsk_false; + ctx->is_stun_enabled = kIceDefaultStunEnabled; + ctx->is_turn_enabled = kIceDefaultTurnEnabled; + + ctx->concheck_timeout = LONG_MAX; + } + return self; } static tsk_object_t* tnet_ice_ctx_dtor(tsk_object_t * self) { - tnet_ice_ctx_t *ctx = self; - if (ctx){ - tnet_ice_ctx_stop(ctx); - if (ctx->h_timer_mgr){ - tsk_timer_manager_destroy(&ctx->h_timer_mgr); - } - - TSK_OBJECT_SAFE_FREE(ctx->fsm); - TSK_OBJECT_SAFE_FREE(ctx->candidates_local); - TSK_OBJECT_SAFE_FREE(ctx->candidates_remote); - TSK_OBJECT_SAFE_FREE(ctx->candidates_pairs); - - TSK_OBJECT_SAFE_FREE(ctx->turn.ss_nominated_rtp); - TSK_OBJECT_SAFE_FREE(ctx->turn.ss_nominated_rtcp); - if (ctx->turn.condwait) { - tsk_condwait_destroy(&ctx->turn.condwait); - } - if (ctx->condwait_pairs) { - tsk_condwait_destroy(&ctx->condwait_pairs); - } - TSK_OBJECT_SAFE_FREE(ctx->servers); - - TSK_FREE(ctx->ssl.path_priv); - TSK_FREE(ctx->ssl.path_pub); - TSK_FREE(ctx->ssl.path_ca); - - tsk_safeobj_deinit(ctx); - } - TSK_DEBUG_INFO("*** ICE context destroyed ***"); - return self; + tnet_ice_ctx_t *ctx = self; + if (ctx){ + tnet_ice_ctx_stop(ctx); + if (ctx->h_timer_mgr){ + tsk_timer_manager_destroy(&ctx->h_timer_mgr); + } + + TSK_OBJECT_SAFE_FREE(ctx->fsm); + TSK_OBJECT_SAFE_FREE(ctx->candidates_local); + TSK_OBJECT_SAFE_FREE(ctx->candidates_remote); + TSK_OBJECT_SAFE_FREE(ctx->candidates_pairs); + + TSK_OBJECT_SAFE_FREE(ctx->turn.ss_nominated_rtp); + TSK_OBJECT_SAFE_FREE(ctx->turn.ss_nominated_rtcp); + if (ctx->turn.condwait) { + tsk_condwait_destroy(&ctx->turn.condwait); + } + if (ctx->condwait_pairs) { + tsk_condwait_destroy(&ctx->condwait_pairs); + } + TSK_OBJECT_SAFE_FREE(ctx->servers); + + TSK_FREE(ctx->ssl.path_priv); + TSK_FREE(ctx->ssl.path_pub); + TSK_FREE(ctx->ssl.path_ca); + + tsk_safeobj_deinit(ctx); + } + TSK_DEBUG_INFO("*** ICE context destroyed ***"); + return self; } static const tsk_object_def_t tnet_ice_ctx_def_s = { - sizeof(tnet_ice_ctx_t), - tnet_ice_ctx_ctor, - tnet_ice_ctx_dtor, - tsk_null, + sizeof(tnet_ice_ctx_t), + tnet_ice_ctx_ctor, + tnet_ice_ctx_dtor, + tsk_null, }; tnet_ice_ctx_t* tnet_ice_ctx_create(tsk_bool_t is_ice_jingle, tsk_bool_t use_ipv6, tsk_bool_t use_rtcp, tsk_bool_t is_video, tnet_ice_callback_f callback, const void* userdata) { - tnet_ice_ctx_t* ctx; - - if (!(ctx = tsk_object_new(&tnet_ice_ctx_def_s))){ - TSK_DEBUG_ERROR("Failed to create ICE context object"); - return tsk_null; - } - - ctx->is_ice_jingle = is_ice_jingle; - ctx->use_ipv6 = use_ipv6; - ctx->use_rtcp = use_rtcp; - ctx->is_video = is_video; - ctx->callback = callback; - ctx->userdata = userdata; - ctx->unicast = tsk_true; - ctx->anycast = tsk_false; - ctx->multicast = tsk_false; - - tnet_ice_utils_set_ufrag(&ctx->ufrag); - tnet_ice_utils_set_pwd(&ctx->pwd); - - ctx->fsm->debug = TNET_ICE_DEBUG_STATE_MACHINE; - tsk_fsm_set_callback_terminated(ctx->fsm, TSK_FSM_ONTERMINATED_F(_tnet_ice_ctx_fsm_OnTerminated), (const void*)ctx); - tsk_fsm_set(ctx->fsm, - // (Started) -> (GatherHostCandidates) -> (GatheringHostCandidates) - TSK_FSM_ADD_ALWAYS(_fsm_state_Started, _fsm_action_GatherHostCandidates, _fsm_state_GatheringHostCandidates, _tnet_ice_ctx_fsm_Started_2_GatheringHostCandidates_X_GatherHostCandidates, "ICE_Started_2_GatheringHostCandidates_X_GatherHostCandidates"), - // (GatheringHostCandidates) -> (Success) -> (GatheringHostCandidatesDone) - TSK_FSM_ADD_ALWAYS(_fsm_state_GatheringHostCandidates, _fsm_action_Success, _fsm_state_GatheringHostCandidatesDone, _tnet_ice_ctx_fsm_GatheringHostCandidates_2_GatheringHostCandidatesDone_X_Success, "ICE_GatheringHostCandidates_2_GatheringHostCandidatesDone_X_Success"), - // (GatheringHostCandidates) -> (Failure) -> (Terminated) - TSK_FSM_ADD_ALWAYS(_fsm_state_GatheringHostCandidates, _fsm_action_Failure, _fsm_state_Terminated, _tnet_ice_ctx_fsm_GatheringHostCandidates_2_Terminated_X_Failure, "ICE_GatheringHostCandidates_2_Terminated_X_Failure"), - - // (GatheringHostCandidatesDone) -> (GatherReflexiveCandidates) -> (GatheringReflexiveCandidates) - TSK_FSM_ADD_ALWAYS(_fsm_state_GatheringHostCandidatesDone, _fsm_action_GatherReflexiveCandidates, _fsm_state_GatheringReflexiveCandidates, _tnet_ice_ctx_fsm_GatheringHostCandidatesDone_2_GatheringReflexiveCandidates_X_GatherReflexiveCandidates, "ICE_GatheringHostCandidatesDone_2_GatheringReflexiveCandidates_X_GatherReflexiveCandidates"), - // (GatheringReflexiveCandidates) -> (Success) -> GatheringReflexiveCandidatesDone - TSK_FSM_ADD_ALWAYS(_fsm_state_GatheringReflexiveCandidates, _fsm_action_Success, _fsm_state_GatheringReflexiveCandidatesDone, _tnet_ice_ctx_fsm_GatheringReflexiveCandidates_2_GatheringReflexiveCandidatesDone_X_Success, "ICE_fsm_GatheringReflexiveCandidates_2_GatheringReflexiveCandidatesDone_X_Success"), - // (GatheringReflexiveCandidates) -> (Failure) -> Terminated - TSK_FSM_ADD_ALWAYS(_fsm_state_GatheringReflexiveCandidates, _fsm_action_Failure, _fsm_state_Terminated, _tnet_ice_ctx_fsm_GatheringReflexiveCandidates_2_Terminated_X_Failure, "ICE_GatheringReflexiveCandidates_2_Terminated_X_Failure"), - - // (GatheringReflexiveCandidatesDone) -> (GatherRelayCandidates) -> (GatheringRelayCandidates) - TSK_FSM_ADD_ALWAYS(_fsm_state_GatheringReflexiveCandidatesDone, _fsm_action_GatherRelayCandidates, _fsm_state_GatheringRelayCandidates, _tnet_ice_ctx_fsm_GatheringReflexiveCandidatesDone_2_GatheringRelayCandidates_X_GatherRelayCandidates, "ICE_GatheringReflexiveCandidatesDone_2_GatheringRelayCandidates_X_GatherRelayCandidates"), - // (GatheringHostCandidatesDone) -> (GatherRelayCandidates) -> (GatheringRelayCandidates) - TSK_FSM_ADD_ALWAYS(_fsm_state_GatheringHostCandidatesDone, _fsm_action_GatherRelayCandidates, _fsm_state_GatheringRelayCandidates, _tnet_ice_ctx_fsm_GatheringReflexiveCandidatesDone_2_GatheringRelayCandidates_X_GatherRelayCandidates, "ICE_GatheringHostCandidatesDone_2_GatheringRelayCandidates_X_GatherRelayCandidates"), - // (GatheringRelayCandidates) -> (Success) -> GatheringRelayCandidatesDone - TSK_FSM_ADD_ALWAYS(_fsm_state_GatheringRelayCandidates, _fsm_action_Success, _fsm_state_GatheringRelayCandidatesDone, _tnet_ice_ctx_fsm_GatheringRelayCandidates_2_GatheringRelayCandidatesDone_X_Success, "ICE_fsm_GatheringRelayCandidates_2_GatheringRelayCandidatesDone_X_Success"), - // (GatheringRelayCandidates) -> (Failure) -> Terminated - TSK_FSM_ADD_ALWAYS(_fsm_state_GatheringRelayCandidates, _fsm_action_Failure, _fsm_state_Terminated, _tnet_ice_ctx_fsm_GatheringRelayCandidates_2_Terminated_X_Failure, "ICE_GatheringRelayCandidates_2_Terminated_X_Failure"), - - // (GatheringComplet) -> (ConnCheck) -> ConnChecking - TSK_FSM_ADD_ALWAYS(_fsm_state_GatheringCompleted, _fsm_action_ConnCheck, _fsm_state_ConnChecking, _tnet_ice_ctx_fsm_GatheringCompleted_2_ConnChecking_X_ConnCheck, "ICE_GatheringCompleted_2_ConnChecking_X_ConnCheck"), - // (ConnChecking) -> (Success) -> ConnCheckingCompleted - TSK_FSM_ADD_ALWAYS(_fsm_state_ConnChecking, _fsm_action_Success, _fsm_state_ConnCheckingCompleted, _tnet_ice_ctx_fsm_ConnChecking_2_ConnCheckingCompleted_X_Success, "ICE_ConnChecking_2_ConnCheckingCompleted_X_Success"), - // (ConnChecking) -> (Failure) -> Terminated - TSK_FSM_ADD_ALWAYS(_fsm_state_ConnChecking, _fsm_action_Failure, _fsm_state_Terminated, _tnet_ice_ctx_fsm_ConnChecking_2_Terminated_X_Failure, "ICE_ConnChecking_2_Terminated_X_Failure"), - - // (Any) -> (GatheringComplet) -> GatheringCompleted - TSK_FSM_ADD_ALWAYS(tsk_fsm_state_any, _fsm_action_GatheringComplet, _fsm_state_GatheringCompleted, _tnet_ice_ctx_fsm_Any_2_GatheringCompleted_X_GatheringComplet, "ICE_Any_2_GatheringCompleted_X_GatheringComplet"), - // (Any) -> (Cancel) -> Started - TSK_FSM_ADD_ALWAYS(tsk_fsm_state_any, _fsm_action_Cancel, _fsm_state_Started, _tnet_ice_ctx_fsm_Any_2_Started_X_Cancel, "ICE_Any_2_Started_X_Cancel"), - // (Any) -> (AnyNotStarted) -> Terminated - TSK_FSM_ADD(tsk_fsm_state_any, tsk_fsm_action_any, _tnet_ice_ctx_fsm_cond_NotStarted, _fsm_state_Terminated, _tnet_ice_ctx_fsm_Any_2_Terminated_X_AnyNotStarted, "ICE_fsm_Any_2_Terminated_X_AnyNotStarted") - ); - - return ctx; + tnet_ice_ctx_t* ctx; + + if (!(ctx = tsk_object_new(&tnet_ice_ctx_def_s))){ + TSK_DEBUG_ERROR("Failed to create ICE context object"); + return tsk_null; + } + + ctx->is_ice_jingle = is_ice_jingle; + ctx->use_ipv6 = use_ipv6; + ctx->use_rtcp = use_rtcp; + ctx->is_video = is_video; + ctx->callback = callback; + ctx->userdata = userdata; + ctx->unicast = tsk_true; + ctx->anycast = tsk_false; + ctx->multicast = tsk_false; + + tnet_ice_utils_set_ufrag(&ctx->ufrag); + tnet_ice_utils_set_pwd(&ctx->pwd); + + ctx->fsm->debug = TNET_ICE_DEBUG_STATE_MACHINE; + tsk_fsm_set_callback_terminated(ctx->fsm, TSK_FSM_ONTERMINATED_F(_tnet_ice_ctx_fsm_OnTerminated), (const void*)ctx); + tsk_fsm_set(ctx->fsm, + // (Started) -> (GatherHostCandidates) -> (GatheringHostCandidates) + TSK_FSM_ADD_ALWAYS(_fsm_state_Started, _fsm_action_GatherHostCandidates, _fsm_state_GatheringHostCandidates, _tnet_ice_ctx_fsm_Started_2_GatheringHostCandidates_X_GatherHostCandidates, "ICE_Started_2_GatheringHostCandidates_X_GatherHostCandidates"), + // (GatheringHostCandidates) -> (Success) -> (GatheringHostCandidatesDone) + TSK_FSM_ADD_ALWAYS(_fsm_state_GatheringHostCandidates, _fsm_action_Success, _fsm_state_GatheringHostCandidatesDone, _tnet_ice_ctx_fsm_GatheringHostCandidates_2_GatheringHostCandidatesDone_X_Success, "ICE_GatheringHostCandidates_2_GatheringHostCandidatesDone_X_Success"), + // (GatheringHostCandidates) -> (Failure) -> (Terminated) + TSK_FSM_ADD_ALWAYS(_fsm_state_GatheringHostCandidates, _fsm_action_Failure, _fsm_state_Terminated, _tnet_ice_ctx_fsm_GatheringHostCandidates_2_Terminated_X_Failure, "ICE_GatheringHostCandidates_2_Terminated_X_Failure"), + + // (GatheringHostCandidatesDone) -> (GatherReflexiveCandidates) -> (GatheringReflexiveCandidates) + TSK_FSM_ADD_ALWAYS(_fsm_state_GatheringHostCandidatesDone, _fsm_action_GatherReflexiveCandidates, _fsm_state_GatheringReflexiveCandidates, _tnet_ice_ctx_fsm_GatheringHostCandidatesDone_2_GatheringReflexiveCandidates_X_GatherReflexiveCandidates, "ICE_GatheringHostCandidatesDone_2_GatheringReflexiveCandidates_X_GatherReflexiveCandidates"), + // (GatheringReflexiveCandidates) -> (Success) -> GatheringReflexiveCandidatesDone + TSK_FSM_ADD_ALWAYS(_fsm_state_GatheringReflexiveCandidates, _fsm_action_Success, _fsm_state_GatheringReflexiveCandidatesDone, _tnet_ice_ctx_fsm_GatheringReflexiveCandidates_2_GatheringReflexiveCandidatesDone_X_Success, "ICE_fsm_GatheringReflexiveCandidates_2_GatheringReflexiveCandidatesDone_X_Success"), + // (GatheringReflexiveCandidates) -> (Failure) -> Terminated + TSK_FSM_ADD_ALWAYS(_fsm_state_GatheringReflexiveCandidates, _fsm_action_Failure, _fsm_state_Terminated, _tnet_ice_ctx_fsm_GatheringReflexiveCandidates_2_Terminated_X_Failure, "ICE_GatheringReflexiveCandidates_2_Terminated_X_Failure"), + + // (GatheringReflexiveCandidatesDone) -> (GatherRelayCandidates) -> (GatheringRelayCandidates) + TSK_FSM_ADD_ALWAYS(_fsm_state_GatheringReflexiveCandidatesDone, _fsm_action_GatherRelayCandidates, _fsm_state_GatheringRelayCandidates, _tnet_ice_ctx_fsm_GatheringReflexiveCandidatesDone_2_GatheringRelayCandidates_X_GatherRelayCandidates, "ICE_GatheringReflexiveCandidatesDone_2_GatheringRelayCandidates_X_GatherRelayCandidates"), + // (GatheringHostCandidatesDone) -> (GatherRelayCandidates) -> (GatheringRelayCandidates) + TSK_FSM_ADD_ALWAYS(_fsm_state_GatheringHostCandidatesDone, _fsm_action_GatherRelayCandidates, _fsm_state_GatheringRelayCandidates, _tnet_ice_ctx_fsm_GatheringReflexiveCandidatesDone_2_GatheringRelayCandidates_X_GatherRelayCandidates, "ICE_GatheringHostCandidatesDone_2_GatheringRelayCandidates_X_GatherRelayCandidates"), + // (GatheringRelayCandidates) -> (Success) -> GatheringRelayCandidatesDone + TSK_FSM_ADD_ALWAYS(_fsm_state_GatheringRelayCandidates, _fsm_action_Success, _fsm_state_GatheringRelayCandidatesDone, _tnet_ice_ctx_fsm_GatheringRelayCandidates_2_GatheringRelayCandidatesDone_X_Success, "ICE_fsm_GatheringRelayCandidates_2_GatheringRelayCandidatesDone_X_Success"), + // (GatheringRelayCandidates) -> (Failure) -> Terminated + TSK_FSM_ADD_ALWAYS(_fsm_state_GatheringRelayCandidates, _fsm_action_Failure, _fsm_state_Terminated, _tnet_ice_ctx_fsm_GatheringRelayCandidates_2_Terminated_X_Failure, "ICE_GatheringRelayCandidates_2_Terminated_X_Failure"), + + // (GatheringComplet) -> (ConnCheck) -> ConnChecking + TSK_FSM_ADD_ALWAYS(_fsm_state_GatheringCompleted, _fsm_action_ConnCheck, _fsm_state_ConnChecking, _tnet_ice_ctx_fsm_GatheringCompleted_2_ConnChecking_X_ConnCheck, "ICE_GatheringCompleted_2_ConnChecking_X_ConnCheck"), + // (ConnChecking) -> (Success) -> ConnCheckingCompleted + TSK_FSM_ADD_ALWAYS(_fsm_state_ConnChecking, _fsm_action_Success, _fsm_state_ConnCheckingCompleted, _tnet_ice_ctx_fsm_ConnChecking_2_ConnCheckingCompleted_X_Success, "ICE_ConnChecking_2_ConnCheckingCompleted_X_Success"), + // (ConnChecking) -> (Failure) -> Terminated + TSK_FSM_ADD_ALWAYS(_fsm_state_ConnChecking, _fsm_action_Failure, _fsm_state_Terminated, _tnet_ice_ctx_fsm_ConnChecking_2_Terminated_X_Failure, "ICE_ConnChecking_2_Terminated_X_Failure"), + + // (Any) -> (GatheringComplet) -> GatheringCompleted + TSK_FSM_ADD_ALWAYS(tsk_fsm_state_any, _fsm_action_GatheringComplet, _fsm_state_GatheringCompleted, _tnet_ice_ctx_fsm_Any_2_GatheringCompleted_X_GatheringComplet, "ICE_Any_2_GatheringCompleted_X_GatheringComplet"), + // (Any) -> (Cancel) -> Started + TSK_FSM_ADD_ALWAYS(tsk_fsm_state_any, _fsm_action_Cancel, _fsm_state_Started, _tnet_ice_ctx_fsm_Any_2_Started_X_Cancel, "ICE_Any_2_Started_X_Cancel"), + // (Any) -> (AnyNotStarted) -> Terminated + TSK_FSM_ADD(tsk_fsm_state_any, tsk_fsm_action_any, _tnet_ice_ctx_fsm_cond_NotStarted, _fsm_state_Terminated, _tnet_ice_ctx_fsm_Any_2_Terminated_X_AnyNotStarted, "ICE_fsm_Any_2_Terminated_X_AnyNotStarted") + ); + + return ctx; } int tnet_ice_ctx_set_userdata(tnet_ice_ctx_t* self, const void* userdata) { - if (!self){ - TSK_DEBUG_ERROR("Invalid parameter"); - return -1; - } - self->userdata = userdata; - return 0; + if (!self){ + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + self->userdata = userdata; + return 0; } // @deprecated: use "tnet_ice_ctx_add_server()" int tnet_ice_ctx_set_stun( - tnet_ice_ctx_t* self, - const char* server_addr, - uint16_t server_port, - const char* software, - const char* username, - const char* password) + tnet_ice_ctx_t* self, + const char* server_addr, + uint16_t server_port, + const char* software, + const char* username, + const char* password) { - _tnet_ice_ctx_servers_clear(self); - return tnet_ice_ctx_add_server( - self, - "udp", - server_addr, - server_port, - (!tsk_strnullORempty(username) && !tsk_strnullORempty(password)), /* use_turn*/ - tsk_true, /* use_stun*/ - username, - password); + _tnet_ice_ctx_servers_clear(self); + return tnet_ice_ctx_add_server( + self, + "udp", + server_addr, + server_port, + (!tsk_strnullORempty(username) && !tsk_strnullORempty(password)), /* use_turn*/ + tsk_true, /* use_stun*/ + username, + password); } int tnet_ice_ctx_add_server( -struct tnet_ice_ctx_s* self, - const char* transport_proto, // "udp", "tcp", "tls", "ws", "wss" - const char* server_addr, - uint16_t server_port, - tsk_bool_t use_turn, - tsk_bool_t use_stun, - const char* username, - const char* password) + struct tnet_ice_ctx_s* self, + const char* transport_proto, // "udp", "tcp", "tls", "ws", "wss" + const char* server_addr, + uint16_t server_port, + tsk_bool_t use_turn, + tsk_bool_t use_stun, + const char* username, + const char* password) { - tnet_socket_type_t socket_type; - tnet_ice_server_proto_t e_proto = tnet_ice_server_proto_none; - if (!self || tsk_strnullORempty(server_addr) || !server_port) { - TSK_DEBUG_ERROR("Invalid parameter"); - return -1; - } - if (!use_turn && !use_stun) { - TSK_DEBUG_ERROR("'use_stun' or 'use_turn' must be true"); - return -1; - } - if (use_stun) { - e_proto |= tnet_ice_server_proto_stun; - } - if (use_turn) { - e_proto |= tnet_ice_server_proto_turn; - } - - if (tsk_striequals(transport_proto, "udp")) { - socket_type = self->use_ipv6 ? tnet_socket_type_udp_ipv6 : tnet_socket_type_udp_ipv4; - } - else if (tsk_striequals(transport_proto, "tcp")) { - socket_type = self->use_ipv6 ? tnet_socket_type_tcp_ipv6 : tnet_socket_type_tcp_ipv4; - } - else if (tsk_striequals(transport_proto, "tls")) { - socket_type = self->use_ipv6 ? tnet_socket_type_tls_ipv6 : tnet_socket_type_tls_ipv4; - } - else if (tsk_striequals(transport_proto, "ws")) { - socket_type = self->use_ipv6 ? tnet_socket_type_ws_ipv6 : tnet_socket_type_ws_ipv4; - } - else if (tsk_striequals(transport_proto, "wss")) { - socket_type = self->use_ipv6 ? tnet_socket_type_wss_ipv6 : tnet_socket_type_wss_ipv4; - } - else { - TSK_DEBUG_ERROR("'%s' not a valid transport proto", transport_proto); - return -1; - } - return _tnet_ice_ctx_server_add(self, e_proto, - socket_type, server_addr, server_port, - kStunSoftware, - username, password); + tnet_socket_type_t socket_type; + tnet_ice_server_proto_t e_proto = tnet_ice_server_proto_none; + if (!self || tsk_strnullORempty(server_addr) || !server_port) { + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + if (!use_turn && !use_stun) { + TSK_DEBUG_ERROR("'use_stun' or 'use_turn' must be true"); + return -1; + } + if (use_stun) { + e_proto |= tnet_ice_server_proto_stun; + } + if (use_turn) { + e_proto |= tnet_ice_server_proto_turn; + } + + if (tsk_striequals(transport_proto, "udp")) { + socket_type = self->use_ipv6 ? tnet_socket_type_udp_ipv6 : tnet_socket_type_udp_ipv4; + } + else if (tsk_striequals(transport_proto, "tcp")) { + socket_type = self->use_ipv6 ? tnet_socket_type_tcp_ipv6 : tnet_socket_type_tcp_ipv4; + } + else if (tsk_striequals(transport_proto, "tls")) { + socket_type = self->use_ipv6 ? tnet_socket_type_tls_ipv6 : tnet_socket_type_tls_ipv4; + } + else if (tsk_striequals(transport_proto, "ws")) { + socket_type = self->use_ipv6 ? tnet_socket_type_ws_ipv6 : tnet_socket_type_ws_ipv4; + } + else if (tsk_striequals(transport_proto, "wss")) { + socket_type = self->use_ipv6 ? tnet_socket_type_wss_ipv6 : tnet_socket_type_wss_ipv4; + } + else { + TSK_DEBUG_ERROR("'%s' not a valid transport proto", transport_proto); + return -1; + } + return _tnet_ice_ctx_server_add(self, e_proto, + socket_type, server_addr, server_port, + kStunSoftware, + username, password); } int tnet_ice_ctx_set_sync_mode(tnet_ice_ctx_t* self, tsk_bool_t sync_mode) { - if (!self){ - TSK_DEBUG_ERROR("Invalid parameter"); - return -1; - } - self->is_sync_mode = sync_mode; - return 0; + if (!self){ + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + self->is_sync_mode = sync_mode; + return 0; } int tnet_ice_ctx_set_silent_mode(struct tnet_ice_ctx_s* self, tsk_bool_t silent_mode) { - if (!self){ - TSK_DEBUG_ERROR("Invalid parameter"); - return -1; - } - self->is_silent_mode = silent_mode; - return 0; + if (!self){ + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + self->is_silent_mode = silent_mode; + return 0; } // Whether to gather reflexive candidates int tnet_ice_ctx_set_stun_enabled(struct tnet_ice_ctx_s* self, tsk_bool_t stun_enabled) { - if (!self){ - TSK_DEBUG_ERROR("Invalid parameter"); - return -1; - } - self->is_stun_enabled = stun_enabled; - return 0; + if (!self){ + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + self->is_stun_enabled = stun_enabled; + return 0; } // Whether to gather relay candidates int tnet_ice_ctx_set_turn_enabled(struct tnet_ice_ctx_s* self, tsk_bool_t turn_enabled) { - if (!self){ - TSK_DEBUG_ERROR("Invalid parameter"); - return -1; - } - self->is_turn_enabled = turn_enabled; - return 0; + if (!self){ + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + self->is_turn_enabled = turn_enabled; + return 0; } int tnet_ice_ctx_start(tnet_ice_ctx_t* self) { - int ret; - tsk_bool_t timer_mgr_started = tsk_false; - tsk_bool_t runnable_started = tsk_false; - const char* err = tsk_null; - - if (!self){ - TSK_DEBUG_ERROR("Invalid parameter"); - return -1; - } - - tsk_safeobj_lock(self); - - if (self->is_started){ - ret = 0; - if (!self->is_active){ - TSK_DEBUG_INFO("ICE restart"); - ret = _tnet_ice_ctx_restart(self); - } - TSK_DEBUG_INFO("ICE already started"); - tsk_safeobj_unlock(self); - return ret; - } - - /* === Timer manager === */ - if ((ret = tsk_timer_manager_start(self->h_timer_mgr))){ - err = "Failed to start timer manager"; - TSK_DEBUG_ERROR("%s", err); - goto bail; - } - timer_mgr_started = tsk_true; - - /* === Runnable === */ - TSK_RUNNABLE(self)->run = _tnet_ice_ctx_run; - if ((ret = tsk_runnable_start(TSK_RUNNABLE(self), tnet_ice_event_def_t))){ - err = "Failed to start runnable"; - TSK_DEBUG_ERROR("%s", err); - goto bail; - } - runnable_started = tsk_true; - - self->is_started = tsk_true; // needed by FSM -> "Must" be before fsm_ast() - self->is_active = tsk_true; - - if ((ret = _tnet_ice_ctx_fsm_act(self, _fsm_action_GatherHostCandidates))){ - err = "FSM execution failed"; - TSK_DEBUG_ERROR("%s", err); - goto bail; - } - + int ret; + tsk_bool_t timer_mgr_started = tsk_false; + tsk_bool_t runnable_started = tsk_false; + const char* err = tsk_null; + + if (!self){ + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + + tsk_safeobj_lock(self); + + TSK_DEBUG_INFO("tnet_ice_ctx_start"); + + if (self->is_started){ + ret = 0; + if (!self->is_active){ + TSK_DEBUG_INFO("ICE restart"); + ret = _tnet_ice_ctx_restart(self); + } + TSK_DEBUG_INFO("ICE already started"); + tsk_safeobj_unlock(self); + return ret; + } + + /* === Timer manager === */ + if ((ret = tsk_timer_manager_start(self->h_timer_mgr))){ + err = "Failed to start timer manager"; + TSK_DEBUG_ERROR("%s", err); + goto bail; + } + timer_mgr_started = tsk_true; + + /* === Runnable === */ + TSK_RUNNABLE(self)->run = _tnet_ice_ctx_run; + if ((ret = tsk_runnable_start(TSK_RUNNABLE(self), tnet_ice_event_def_t))){ + err = "Failed to start runnable"; + TSK_DEBUG_ERROR("%s", err); + goto bail; + } + runnable_started = tsk_true; + + self->is_started = tsk_true; // needed by FSM -> "Must" be before fsm_ast() + self->is_active = tsk_true; + + if ((ret = _tnet_ice_ctx_fsm_act(self, _fsm_action_GatherHostCandidates))){ + err = "FSM execution failed"; + TSK_DEBUG_ERROR("%s", err); + goto bail; + } + bail: - tsk_safeobj_unlock(self); - - if (ret){ - _tnet_ice_ctx_signal_async(self, tnet_ice_event_type_start_failed, err); - if (timer_mgr_started){ - tsk_timer_manager_stop(self->h_timer_mgr); - } - if (runnable_started){ - tsk_runnable_stop(TSK_RUNNABLE(self)); - } - self->is_started = tsk_false; - self->is_active = tsk_false; - } - return ret; + tsk_safeobj_unlock(self); + + if (ret){ + _tnet_ice_ctx_signal_async(self, tnet_ice_event_type_start_failed, err); + if (timer_mgr_started){ + tsk_timer_manager_stop(self->h_timer_mgr); + } + if (runnable_started){ + tsk_runnable_stop(TSK_RUNNABLE(self)); + } + self->is_started = tsk_false; + self->is_active = tsk_false; + } + return ret; } // register callback to call when we receive early RTP packets while negotaiating ICE pairs int tnet_ice_ctx_rtp_callback(tnet_ice_ctx_t* self, tnet_ice_rtp_callback_f rtp_callback, const void* rtp_callback_data) { - if (!self){ - TSK_DEBUG_ERROR("Invalid parameter"); - return -1; - } - - self->rtp_callback_data = rtp_callback_data; - self->rtp_callback = rtp_callback; - return 0; + if (!self){ + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + + self->rtp_callback_data = rtp_callback_data; + self->rtp_callback = rtp_callback; + return 0; } // timeout (millis): <=0 to disable int tnet_ice_ctx_set_concheck_timeout(tnet_ice_ctx_t* self, int64_t timeout) { - if (!self){ - TSK_DEBUG_ERROR("Invalid parameter"); - return -1; - } + if (!self){ + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + + self->concheck_timeout = (timeout <= 0 ? LONG_MAX : timeout); + + return 0; +} - self->concheck_timeout = (timeout <= 0 ? LONG_MAX : timeout); - - return 0; +// @param candidates (candidate \r\n)+ +int tnet_ice_ctx_set_remote_candidates_2(struct tnet_ice_ctx_s* self, const char* candidates, const char* ufrag, const char* pwd, tsk_bool_t is_controlling, tsk_bool_t is_ice_jingle, tsk_bool_t use_rtcpmux) +{ + int ret = 0; + char *v, *copy, *saveptr; + tsk_size_t size, idx = 0; + tsk_bool_t exists; + tnet_ice_candidate_t* candidate; + tsk_strings_L_t *added_candidates = tsk_null; + if (!self) { + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + + self->is_controlling = is_controlling; + self->is_ice_jingle = is_ice_jingle; + tnet_ice_ctx_set_rtcpmux(self, use_rtcpmux); + + if (tsk_strnullORempty(candidates)) { + // remote party is ICE-lite or doesn't support ICE + return tnet_ice_ctx_cancel(self); + } + + TSK_DEBUG_INFO("tnet_ice_ctx_set_remote_candidates(ufrag=%s, pwd=%s, is_controlling=%d, is_ice_jingle=%d, use_rtcpmux=%d)", + ufrag, pwd, is_controlling, is_ice_jingle, use_rtcpmux); + + tsk_list_lock(self->candidates_pairs); + if (!TSK_LIST_IS_EMPTY(self->candidates_pairs)) { + TSK_DEBUG_WARN("Adding Remote ICE candidates after pairs building"); + } + tsk_list_unlock(self->candidates_pairs); + + // active if remote is full-ICE + // in all case we are always full-ICE + // self->is_active = tsk_true; + + tsk_list_lock(self->candidates_remote); + + // clear old candidates + tsk_list_clear_items(self->candidates_remote); + + copy = tsk_strdup(candidates); + size = (tsk_size_t)tsk_strlen(copy); + do { + v = tsk_strtok_r(©[idx], "\r\n", &saveptr); + idx += tsk_strlen(v) + 2; + if (v && (candidate = tnet_ice_candidate_parse(v))) { + exists = tsk_false; + if (!added_candidates) { + added_candidates = tsk_list_create(); + } + if (ufrag && pwd) { + tnet_ice_candidate_set_credential(candidate, ufrag, pwd); + } + if (added_candidates) { + tsk_string_t* str_cand = tsk_string_create(tnet_ice_candidate_tostring(candidate)); + if (str_cand) { + if ((exists = !!tsk_list_find_object_by_data(added_candidates, str_cand))) { + TSK_DEBUG_INFO("Remote candidate [[%s]] is duplicated ...skipping", str_cand->value); + } + else { + tsk_list_push_back_data(added_candidates, (void**)&str_cand); + } + TSK_OBJECT_SAFE_FREE(str_cand); + } + } + if (!exists) { + tsk_list_push_descending_data(self->candidates_remote, (void**)&candidate); + } + TSK_OBJECT_SAFE_FREE(candidate); + } + } while (v && (idx < size)); + + tsk_list_unlock(self->candidates_remote); + + TSK_FREE(copy); + TSK_OBJECT_SAFE_FREE(added_candidates); + + if (!tnet_ice_ctx_is_connected(self) && tnet_ice_ctx_got_local_candidates(self) && !TSK_LIST_IS_EMPTY(self->candidates_remote)) { + ret = _tnet_ice_ctx_fsm_act(self, _fsm_action_ConnCheck); + } + return ret; } // @param candidates (candidate \r\n)+ int tnet_ice_ctx_set_remote_candidates(tnet_ice_ctx_t* self, const char* candidates, const char* ufrag, const char* pwd, tsk_bool_t is_controlling, tsk_bool_t is_ice_jingle) { - int ret = 0; - char *v, *copy, *saveptr; - tsk_size_t size, idx = 0; - tsk_bool_t exists; - tnet_ice_candidate_t* candidate; - tsk_strings_L_t *added_candidates = tsk_null; - if (!self) { - TSK_DEBUG_ERROR("Invalid parameter"); - return -1; - } - - self->is_controlling = is_controlling; - self->is_ice_jingle = is_ice_jingle; - - if (tsk_strnullORempty(candidates)) { - // remote party is ICE-lite or doesn't support ICE - return tnet_ice_ctx_cancel(self); - } - - TSK_DEBUG_INFO("tnet_ice_ctx_set_remote_candidates"); - - tsk_list_lock(self->candidates_pairs); - if (!TSK_LIST_IS_EMPTY(self->candidates_pairs)) { - TSK_DEBUG_WARN("Adding Remote ICE candidates after pairs building"); - } - tsk_list_unlock(self->candidates_pairs); - - // active if remote is full-ICE - // in all case we are always full-ICE - // self->is_active = tsk_true; - - tsk_list_lock(self->candidates_remote); - - // clear old candidates - tsk_list_clear_items(self->candidates_remote); - - copy = tsk_strdup(candidates); - size = (tsk_size_t)tsk_strlen(copy); - do { - v = tsk_strtok_r(©[idx], "\r\n", &saveptr); - idx += tsk_strlen(v) + 2; - if (v && (candidate = tnet_ice_candidate_parse(v))) { - exists = tsk_false; - if (!added_candidates) { - added_candidates = tsk_list_create(); - } - if (ufrag && pwd) { - tnet_ice_candidate_set_credential(candidate, ufrag, pwd); - } - if (added_candidates) { - tsk_string_t* str_cand = tsk_string_create(tnet_ice_candidate_tostring(candidate)); - if (str_cand) { - if ((exists = !!tsk_list_find_object_by_data(added_candidates, str_cand))) { - TSK_DEBUG_INFO("Remote candidate [[%s]] is duplicated ...skipping", str_cand->value); - } - else { - tsk_list_push_back_data(added_candidates, (void**)&str_cand); - } - TSK_OBJECT_SAFE_FREE(str_cand); - } - } - if (!exists) { - tsk_list_push_descending_data(self->candidates_remote, (void**)&candidate); - } - TSK_OBJECT_SAFE_FREE(candidate); - } - } while (v && (idx < size)); - - tsk_list_unlock(self->candidates_remote); - - TSK_FREE(copy); - TSK_OBJECT_SAFE_FREE(added_candidates); - - if (!tnet_ice_ctx_is_connected(self) && tnet_ice_ctx_got_local_candidates(self) && !TSK_LIST_IS_EMPTY(self->candidates_remote)) { - ret = _tnet_ice_ctx_fsm_act(self, _fsm_action_ConnCheck); - } - return ret; + return tnet_ice_ctx_set_remote_candidates_2(self, candidates, ufrag, pwd, is_controlling, is_ice_jingle, self->use_rtcpmux); } int tnet_ice_ctx_set_rtcpmux(tnet_ice_ctx_t* self, tsk_bool_t use_rtcpmux) { - if (!self) { - TSK_DEBUG_ERROR("Invalid parameter"); - return -1; - } - self->use_rtcpmux = use_rtcpmux; - return 0; + if (!self) { + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + if (self->is_connchecking && self->use_rtcpmux != use_rtcpmux) { + TSK_DEBUG_WARN("use_rtcpmux changed(%d->%d) while connchecking", self->use_rtcpmux, use_rtcpmux); + } + self->use_rtcpmux = use_rtcpmux; + return 0; } int tnet_ice_ctx_set_ssl_certs(struct tnet_ice_ctx_s* self, const char* path_priv, const char* path_pub, const char* path_ca, tsk_bool_t verify) { - if (!self) { - TSK_DEBUG_ERROR("Invalid parameter"); - return -1; - } - tsk_strupdate(&self->ssl.path_priv, path_priv); - tsk_strupdate(&self->ssl.path_pub, path_pub); - tsk_strupdate(&self->ssl.path_ca, path_ca); - self->ssl.verify = verify; - return 0; + if (!self) { + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + tsk_strupdate(&self->ssl.path_priv, path_priv); + tsk_strupdate(&self->ssl.path_pub, path_pub); + tsk_strupdate(&self->ssl.path_ca, path_ca); + self->ssl.verify = verify; + return 0; } tsk_size_t tnet_ice_ctx_count_local_candidates(const tnet_ice_ctx_t* self) { - if (!self){ - TSK_DEBUG_ERROR("Invalid parameter"); - return 0; - } - return tsk_list_count(self->candidates_local, tsk_null, tsk_null); + if (!self){ + TSK_DEBUG_ERROR("Invalid parameter"); + return 0; + } + return tsk_list_count(self->candidates_local, tsk_null, tsk_null); } tsk_bool_t tnet_ice_ctx_got_local_candidates(const tnet_ice_ctx_t* self) { - tsk_fsm_state_id curr_state; - if (!self){ - TSK_DEBUG_ERROR("Invalid parameter"); - return tsk_false; - } - if (!self->is_started){ - return tsk_false; - } - - curr_state = tsk_fsm_get_current_state(self->fsm); - - return (curr_state >= _fsm_state_GatheringCompleted && curr_state < _fsm_state_Terminated); + tsk_fsm_state_id curr_state; + if (!self){ + TSK_DEBUG_ERROR("Invalid parameter"); + return tsk_false; + } + if (!self->is_started){ + return tsk_false; + } + + curr_state = tsk_fsm_get_current_state(self->fsm); + + return (curr_state >= _fsm_state_GatheringCompleted && curr_state < _fsm_state_Terminated); } const tnet_ice_candidate_t* tnet_ice_ctx_get_local_candidate_at(const tnet_ice_ctx_t* self, tsk_size_t index) { - const tsk_list_item_t *item; - tsk_size_t pos = 0; - if (!self){ - TSK_DEBUG_ERROR("Invalid parameter"); - return tsk_null; - } - - tsk_list_foreach(item, self->candidates_local){ - if (pos++ == index){ - return (const tnet_ice_candidate_t*)item->data; - } - } - return tsk_null; + const tsk_list_item_t *item; + tsk_size_t pos = 0; + if (!self){ + TSK_DEBUG_ERROR("Invalid parameter"); + return tsk_null; + } + + tsk_list_foreach(item, self->candidates_local){ + if (pos++ == index){ + return (const tnet_ice_candidate_t*)item->data; + } + } + return tsk_null; } tsk_bool_t tnet_ice_ctx_is_started(const tnet_ice_ctx_t* self) { - return (self && self->is_started); + return (self && self->is_started); } // says if ICE is enabled // doesn't say if the connection has been negotiated (see is_connecte()) tsk_bool_t tnet_ice_ctx_is_active(const tnet_ice_ctx_t* self) { - return (self && self->is_started && self->is_active); + return (self && self->is_started && self->is_active); } tsk_bool_t tnet_ice_ctx_is_turn_rtp_active(const struct tnet_ice_ctx_s* self) { - tsk_bool_t b_active; - return tnet_ice_ctx_is_active(self) - && self->turn.ss_nominated_rtp - && tnet_turn_session_is_active(self->turn.ss_nominated_rtp, self->turn.peer_id_rtp, &b_active) == 0 - && b_active; + tsk_bool_t b_active; + return tnet_ice_ctx_is_active(self) + && self->turn.ss_nominated_rtp + && tnet_turn_session_is_active(self->turn.ss_nominated_rtp, self->turn.peer_id_rtp, &b_active) == 0 + && b_active; } tsk_bool_t tnet_ice_ctx_is_turn_rtcp_active(const struct tnet_ice_ctx_s* self) { - if (self->use_rtcpmux) { - return tnet_ice_ctx_is_turn_rtp_active(self); - } - else { - tsk_bool_t b_active; - return tnet_ice_ctx_is_active(self) - && self->turn.ss_nominated_rtcp - && tnet_turn_session_is_active(self->turn.ss_nominated_rtcp, self->turn.peer_id_rtcp, &b_active) == 0 - && b_active; - } + if (self->use_rtcpmux) { + return tnet_ice_ctx_is_turn_rtp_active(self); + } + else { + tsk_bool_t b_active; + return tnet_ice_ctx_is_active(self) + && self->turn.ss_nominated_rtcp + && tnet_turn_session_is_active(self->turn.ss_nominated_rtcp, self->turn.peer_id_rtcp, &b_active) == 0 + && b_active; + } } // says if media can start in both direction tsk_bool_t tnet_ice_ctx_is_connected(const tnet_ice_ctx_t* self) { - return (self && self->have_nominated_symetric); + return (self && self->have_nominated_symetric); } tsk_bool_t tnet_ice_ctx_is_can_send(const tnet_ice_ctx_t* self) { - return (self && self->have_nominated_offer); + return (self && self->have_nominated_offer); } tsk_bool_t tnet_ice_ctx_is_can_recv(const tnet_ice_ctx_t* self) { - return (self && self->have_nominated_answer); + return (self && self->have_nominated_answer); } tsk_bool_t tnet_ice_ctx_use_ipv6(const tnet_ice_ctx_t* self) { - return (self && self->use_ipv6); + return (self && self->use_ipv6); } tsk_bool_t tnet_ice_ctx_use_rtcp(const tnet_ice_ctx_t* self) { - return (self && self->use_rtcp); + return (self && self->use_rtcp); } int tnet_ice_ctx_get_nominated_symetric_candidates(const tnet_ice_ctx_t* self, uint32_t comp_id, - const tnet_ice_candidate_t** candidate_offer, - const tnet_ice_candidate_t** candidate_answer_src, - const tnet_ice_candidate_t** candidate_answer_dest) + const tnet_ice_candidate_t** candidate_offer, + const tnet_ice_candidate_t** candidate_answer_src, + const tnet_ice_candidate_t** candidate_answer_dest) { - if (!self){ - TSK_DEBUG_ERROR("Invalid parameter"); - return -1; - } - return tnet_ice_pairs_get_nominated_symetric_candidates(self->candidates_pairs, comp_id, candidate_offer, candidate_answer_src, candidate_answer_dest); + if (!self){ + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + return tnet_ice_pairs_get_nominated_symetric_candidates(self->candidates_pairs, comp_id, candidate_offer, candidate_answer_src, candidate_answer_dest); } int tnet_ice_ctx_recv_stun_message(tnet_ice_ctx_t* self, const void* data, tsk_size_t size, tnet_fd_t local_fd, const struct sockaddr_storage* remote_addr, tsk_bool_t *role_conflict) { - static const tnet_ice_pair_t* kNullPair = tsk_null; // means seach for the pair using local_fd and remote_addr - return _tnet_ice_ctx_recv_stun_message_for_pair(self, kNullPair, data, size, local_fd, remote_addr, role_conflict); + static const tnet_ice_pair_t* kNullPair = tsk_null; // means seach for the pair using local_fd and remote_addr + return _tnet_ice_ctx_recv_stun_message_for_pair(self, kNullPair, data, size, local_fd, remote_addr, role_conflict); } int tnet_ice_ctx_send_turn_rtp(struct tnet_ice_ctx_s* self, const void* data, tsk_size_t size) { - return _tnet_ice_ctx_send_turn_raw(self, self->turn.ss_nominated_rtp, self->turn.peer_id_rtp, data, size); + return _tnet_ice_ctx_send_turn_raw(self, self->turn.ss_nominated_rtp, self->turn.peer_id_rtp, data, size); } int tnet_ice_ctx_send_turn_rtcp(struct tnet_ice_ctx_s* self, const void* data, tsk_size_t size) { - return self->use_rtcpmux - ? tnet_ice_ctx_send_turn_rtp(self, data, size) - : _tnet_ice_ctx_send_turn_raw(self, self->turn.ss_nominated_rtcp, self->turn.peer_id_rtcp, data, size); + return self->use_rtcpmux + ? tnet_ice_ctx_send_turn_rtp(self, data, size) + : _tnet_ice_ctx_send_turn_raw(self, self->turn.ss_nominated_rtcp, self->turn.peer_id_rtcp, data, size); } const char* tnet_ice_ctx_get_ufrag(const struct tnet_ice_ctx_s* self) { - return (self && self->ufrag) ? self->ufrag : tsk_null; + return (self && self->ufrag) ? self->ufrag : tsk_null; } const char* tnet_ice_ctx_get_pwd(const struct tnet_ice_ctx_s* self) { - return (self && self->pwd) ? self->pwd : tsk_null; + return (self && self->pwd) ? self->pwd : tsk_null; } // cancels the ICE processing without stopping the process int tnet_ice_ctx_cancel(tnet_ice_ctx_t* self) { - int ret; - - if (!self) { - TSK_DEBUG_ERROR("Invalid parameter"); - return -1; - } - - tsk_safeobj_lock(self); - if (tsk_fsm_get_current_state(self->fsm) == _fsm_state_Started) { - // Do nothing if already in the "started" state - ret = 0; - goto bail; - } - - self->is_active = tsk_false; - self->have_nominated_symetric = tsk_false; - self->have_nominated_answer = tsk_false; - self->have_nominated_offer = tsk_false; - tsk_condwait_broadcast(self->condwait_pairs); - if (self->turn.condwait) { - ret = tsk_condwait_broadcast(self->turn.condwait); - } - ret = _tnet_ice_ctx_fsm_act(self, _fsm_action_Cancel); - + int ret; + + if (!self) { + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + + tsk_safeobj_lock(self); + if (tsk_fsm_get_current_state(self->fsm) == _fsm_state_Started) { + // Do nothing if already in the "started" state + ret = 0; + goto bail; + } + + self->is_active = tsk_false; + self->have_nominated_symetric = tsk_false; + self->have_nominated_answer = tsk_false; + self->have_nominated_offer = tsk_false; + tsk_condwait_broadcast(self->condwait_pairs); + if (self->turn.condwait) { + ret = tsk_condwait_broadcast(self->turn.condwait); + } + ret = _tnet_ice_ctx_fsm_act(self, _fsm_action_Cancel); + bail: - tsk_safeobj_unlock(self); - return ret; + tsk_safeobj_unlock(self); + return ret; } int tnet_ice_ctx_stop(tnet_ice_ctx_t* self) { - int ret; - - if (!self) { - TSK_DEBUG_ERROR("Invalid parameter"); - return -1; - } - - tsk_safeobj_lock(self); - if (!self->is_started) { - ret = 0; - goto bail; - } - - self->is_started = tsk_false; - tsk_condwait_broadcast(self->condwait_pairs); - if (self->turn.condwait) { - ret = tsk_condwait_broadcast(self->turn.condwait); - } - ret = tsk_timer_manager_stop(self->h_timer_mgr); - ret = tsk_runnable_stop(TSK_RUNNABLE(self)); - + int ret; + + if (!self) { + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + + tsk_safeobj_lock(self); + if (!self->is_started) { + ret = 0; + goto bail; + } + + self->is_started = tsk_false; + tsk_condwait_broadcast(self->condwait_pairs); + if (self->turn.condwait) { + ret = tsk_condwait_broadcast(self->turn.condwait); + } + ret = tsk_timer_manager_stop(self->h_timer_mgr); + ret = tsk_runnable_stop(TSK_RUNNABLE(self)); + bail: - tsk_safeobj_unlock(self); - return ret; + tsk_safeobj_unlock(self); + return ret; } //-------------------------------------------------------- @@ -1052,1072 +1065,1072 @@ bail: // Started -> (GatherHostCandidates) -> (GatheringHostCandidates) static int _tnet_ice_ctx_fsm_Started_2_GatheringHostCandidates_X_GatherHostCandidates(va_list *app) { - int ret = 0; - tnet_ice_ctx_t* self; - tnet_addresses_L_t* addresses; - const tsk_list_item_t *item; - const tnet_address_t* address; - tnet_ice_candidate_t* candidate; - tnet_socket_t* socket_rtp = tsk_null; - tnet_socket_t* socket_rtcp = tsk_null; - tnet_socket_type_t socket_type; - uint16_t local_pref, curr_local_pref; - tnet_ip_t best_local_ip; - tsk_bool_t check_best_local_ip; - static const tsk_bool_t dnsserver = tsk_false; - static const long if_index_any = -1; // any interface - static const char* destination = "doubango.org"; - - self = va_arg(*app, tnet_ice_ctx_t *); - socket_type = self->use_ipv6 ? tnet_socket_type_udp_ipv6 : tnet_socket_type_udp_ipv4; - - addresses = tnet_get_addresses((self->use_ipv6 ? AF_INET6 : AF_INET), self->unicast, self->anycast, self->multicast, dnsserver, if_index_any); - if (!addresses || TSK_LIST_IS_EMPTY(addresses)){ - TSK_DEBUG_ERROR("Failed to get addresses"); - ret = -1; - goto bail; - } - - - check_best_local_ip = (tnet_getbestsource(destination, 5060, socket_type, &best_local_ip) == 0); - curr_local_pref = local_pref = check_best_local_ip ? 0xFFFE : 0xFFFF; - - // lock-list - tsk_list_lock(self->candidates_local); - // clear-list - tsk_list_clear_items(self->candidates_local); - - tsk_list_foreach(item, addresses){ - if (!(address = item->data)){ - continue; - } - - // Skip loopback address to avoid problems :) - if ((address->family == AF_INET && tsk_striequals(address->ip, "127.0.0.1")) || (address->family == AF_INET6 && tsk_striequals(address->ip, "::1"))){ - continue; - } - - // host candidates - ret = tnet_ice_utils_create_sockets(socket_type, - address->ip, &socket_rtp, - self->use_rtcp ? &socket_rtcp : tsk_null); - if (ret == 0){ - const char* foundation_rtp = foundation_default; - tsk_list_lock(self->candidates_local); - if (socket_rtp){ - if ((candidate = tnet_ice_candidate_create(tnet_ice_cand_type_host, socket_rtp, self->is_ice_jingle, tsk_true, self->is_video, self->ufrag, self->pwd, foundation_default))){ - foundation_rtp = (const char*)candidate->foundation; - if (check_best_local_ip && (candidate->socket && (tsk_striequals(candidate->socket->ip, best_local_ip)))){ - curr_local_pref = 0xFFFF; - check_best_local_ip = tsk_false; - tnet_ice_candidate_set_local_pref(candidate, curr_local_pref); - tsk_list_push_front_data(self->candidates_local, (void**)&candidate); - } - else{ - curr_local_pref = local_pref--; - tnet_ice_candidate_set_local_pref(candidate, curr_local_pref); - tsk_list_push_back_data(self->candidates_local, (void**)&candidate); - } - } - } - if (socket_rtcp){ - if ((candidate = tnet_ice_candidate_create(tnet_ice_cand_type_host, socket_rtcp, self->is_ice_jingle, tsk_false, self->is_video, self->ufrag, self->pwd, foundation_rtp))){ - tnet_ice_candidate_set_local_pref(candidate, curr_local_pref); - tsk_list_push_back_data(self->candidates_local, (void**)&candidate); - } - } - tsk_list_unlock(self->candidates_local); - } - - TSK_OBJECT_SAFE_FREE(socket_rtp); - TSK_OBJECT_SAFE_FREE(socket_rtcp); - - // break if no longer running - if (!self->is_started){ - break; - } - - TSK_DEBUG_INFO("local ip address = %s", address->ip); - } - - // unlock-list - tsk_list_unlock(self->candidates_local); - + int ret = 0; + tnet_ice_ctx_t* self; + tnet_addresses_L_t* addresses; + const tsk_list_item_t *item; + const tnet_address_t* address; + tnet_ice_candidate_t* candidate; + tnet_socket_t* socket_rtp = tsk_null; + tnet_socket_t* socket_rtcp = tsk_null; + tnet_socket_type_t socket_type; + uint16_t local_pref, curr_local_pref; + tnet_ip_t best_local_ip; + tsk_bool_t check_best_local_ip; + static const tsk_bool_t dnsserver = tsk_false; + static const long if_index_any = -1; // any interface + static const char* destination = "doubango.org"; + + self = va_arg(*app, tnet_ice_ctx_t *); + socket_type = self->use_ipv6 ? tnet_socket_type_udp_ipv6 : tnet_socket_type_udp_ipv4; + + addresses = tnet_get_addresses((self->use_ipv6 ? AF_INET6 : AF_INET), self->unicast, self->anycast, self->multicast, dnsserver, if_index_any); + if (!addresses || TSK_LIST_IS_EMPTY(addresses)){ + TSK_DEBUG_ERROR("Failed to get addresses"); + ret = -1; + goto bail; + } + + + check_best_local_ip = (tnet_getbestsource(destination, 5060, socket_type, &best_local_ip) == 0); + curr_local_pref = local_pref = check_best_local_ip ? 0xFFFE : 0xFFFF; + + // lock-list + tsk_list_lock(self->candidates_local); + // clear-list + tsk_list_clear_items(self->candidates_local); + + tsk_list_foreach(item, addresses){ + if (!(address = item->data)){ + continue; + } + + // Skip loopback address to avoid problems :) + if ((address->family == AF_INET && tsk_striequals(address->ip, "127.0.0.1")) || (address->family == AF_INET6 && tsk_striequals(address->ip, "::1"))){ + continue; + } + + // host candidates + ret = tnet_ice_utils_create_sockets(socket_type, + address->ip, &socket_rtp, + self->use_rtcp ? &socket_rtcp : tsk_null); + if (ret == 0){ + const char* foundation_rtp = foundation_default; + tsk_list_lock(self->candidates_local); + if (socket_rtp){ + if ((candidate = tnet_ice_candidate_create(tnet_ice_cand_type_host, socket_rtp, self->is_ice_jingle, tsk_true, self->is_video, self->ufrag, self->pwd, foundation_default))){ + foundation_rtp = (const char*)candidate->foundation; + if (check_best_local_ip && (candidate->socket && (tsk_striequals(candidate->socket->ip, best_local_ip)))){ + curr_local_pref = 0xFFFF; + check_best_local_ip = tsk_false; + tnet_ice_candidate_set_local_pref(candidate, curr_local_pref); + tsk_list_push_front_data(self->candidates_local, (void**)&candidate); + } + else{ + curr_local_pref = local_pref--; + tnet_ice_candidate_set_local_pref(candidate, curr_local_pref); + tsk_list_push_back_data(self->candidates_local, (void**)&candidate); + } + } + } + if (socket_rtcp){ + if ((candidate = tnet_ice_candidate_create(tnet_ice_cand_type_host, socket_rtcp, self->is_ice_jingle, tsk_false, self->is_video, self->ufrag, self->pwd, foundation_rtp))){ + tnet_ice_candidate_set_local_pref(candidate, curr_local_pref); + tsk_list_push_back_data(self->candidates_local, (void**)&candidate); + } + } + tsk_list_unlock(self->candidates_local); + } + + TSK_OBJECT_SAFE_FREE(socket_rtp); + TSK_OBJECT_SAFE_FREE(socket_rtcp); + + // break if no longer running + if (!self->is_started){ + break; + } + + TSK_DEBUG_INFO("local ip address = %s", address->ip); + } + + // unlock-list + tsk_list_unlock(self->candidates_local); + bail: - if (self->is_started){ - if (ret == 0 && !TSK_LIST_IS_EMPTY(self->candidates_local)){ - ret = _tnet_ice_ctx_fsm_act(self, _fsm_action_Success); - } - else{ - ret = _tnet_ice_ctx_fsm_act(self, _fsm_action_Failure); - } - } - - TSK_OBJECT_SAFE_FREE(addresses); - return ret; + if (self->is_started){ + if (ret == 0 && !TSK_LIST_IS_EMPTY(self->candidates_local)){ + ret = _tnet_ice_ctx_fsm_act(self, _fsm_action_Success); + } + else{ + ret = _tnet_ice_ctx_fsm_act(self, _fsm_action_Failure); + } + } + + TSK_OBJECT_SAFE_FREE(addresses); + return ret; } // GatheringHostCandidates -> (Success) -> (GatheringHostCandidatesDone) static int _tnet_ice_ctx_fsm_GatheringHostCandidates_2_GatheringHostCandidatesDone_X_Success(va_list *app) { - int ret; - tnet_ice_ctx_t* self; - - self = va_arg(*app, tnet_ice_ctx_t *); - - ret = _tnet_ice_ctx_signal_async(self, tnet_ice_event_type_gathering_host_candidates_succeed, "Gathering host candidates succeed"); - if (ret == 0) { - if (self->is_stun_enabled && _tnet_ice_ctx_servers_count_by_proto(self, tnet_ice_server_proto_stun) > 0) { - TSK_DEBUG_INFO("ICE-STUN enabled and we have STUN servers"); - ret = _tnet_ice_ctx_fsm_act(self, _fsm_action_GatherReflexiveCandidates); - } - else { - if (self->is_turn_enabled && _tnet_ice_ctx_servers_count_by_proto(self, tnet_ice_server_proto_turn) > 0) { - TSK_DEBUG_INFO("ICE-TURN enabled and we have STUN servers"); - ret = _tnet_ice_ctx_fsm_act(self, _fsm_action_GatherRelayCandidates); - } - else { - TSK_DEBUG_INFO("Do not gather reflexive/relayed candidates because ICE-STUN/TURN is disabled or no server defined"); - ret = _tnet_ice_ctx_fsm_act(self, _fsm_action_GatheringComplet); - } - } - } - - return ret; + int ret; + tnet_ice_ctx_t* self; + + self = va_arg(*app, tnet_ice_ctx_t *); + + ret = _tnet_ice_ctx_signal_async(self, tnet_ice_event_type_gathering_host_candidates_succeed, "Gathering host candidates succeed"); + if (ret == 0) { + if (self->is_stun_enabled && _tnet_ice_ctx_servers_count_by_proto(self, tnet_ice_server_proto_stun) > 0) { + TSK_DEBUG_INFO("ICE-STUN enabled and we have STUN servers"); + ret = _tnet_ice_ctx_fsm_act(self, _fsm_action_GatherReflexiveCandidates); + } + else { + if (self->is_turn_enabled && _tnet_ice_ctx_servers_count_by_proto(self, tnet_ice_server_proto_turn) > 0) { + TSK_DEBUG_INFO("ICE-TURN enabled and we have STUN servers"); + ret = _tnet_ice_ctx_fsm_act(self, _fsm_action_GatherRelayCandidates); + } + else { + TSK_DEBUG_INFO("Do not gather reflexive/relayed candidates because ICE-STUN/TURN is disabled or no server defined"); + ret = _tnet_ice_ctx_fsm_act(self, _fsm_action_GatheringComplet); + } + } + } + + return ret; } // GatheringHostCandidates -> (Failure) -> (Terminated) static int _tnet_ice_ctx_fsm_GatheringHostCandidates_2_Terminated_X_Failure(va_list *app) { - tnet_ice_ctx_t* self; - - self = va_arg(*app, tnet_ice_ctx_t *); - return _tnet_ice_ctx_signal_async(self, tnet_ice_event_type_gathering_host_candidates_failed, "Gathering host candidates failed"); + tnet_ice_ctx_t* self; + + self = va_arg(*app, tnet_ice_ctx_t *); + return _tnet_ice_ctx_signal_async(self, tnet_ice_event_type_gathering_host_candidates_failed, "Gathering host candidates failed"); } // GatheringHostCandidatesDone -> (GatherReflexiveCandidate) -> GatheringReflexiveCandidates static int _tnet_ice_ctx_fsm_GatheringHostCandidatesDone_2_GatheringReflexiveCandidates_X_GatherReflexiveCandidates(va_list *app) { - /* RFC 5389 - 7.2.1. Sending over UDP - STUN indications are not retransmitted; thus, indication transactions over UDP - are not reliable. - */ - int ret = 0; - tnet_ice_servers_L_t* ice_servers = tsk_null; - tnet_ice_server_t* ice_server; - tnet_ice_ctx_t* self; - uint16_t i, k, rc; - struct timeval tv; - tnet_stun_pkt_resp_t *response = tsk_null; - const tsk_list_item_t *item, *item_server; - tnet_ice_candidate_t* candidate; - tnet_fd_t fds[kIceCandidatesCountMax] = { TNET_INVALID_FD }; // -1, then zeros - tnet_fd_t fds_skipped[kIceCandidatesCountMax] = { TNET_INVALID_FD }; // -1, then zeros - uint16_t fds_count = 0; - tnet_fd_t fd_max = -1; - fd_set set; - tsk_size_t srflx_addr_count_added = 0, srflx_addr_count_skipped = 0, host_addr_count = 0; - long tv_sec, tv_usec; //very important to save these values as timeval could be modified by select() - happens on iOS - - - self = va_arg(*app, tnet_ice_ctx_t *); - - // Get ICE servers to use to gather reflexive candidates - ice_servers = _tnet_ice_ctx_servers_copy(self, tnet_ice_server_proto_stun); - if (!ice_servers || TSK_LIST_IS_EMPTY(ice_servers)) { // not expected to be null or empty because we checked the number of such servers before calling this transition - TSK_DEBUG_WARN("No valid STUN server could be used to gather reflexive candidates"); - goto bail; - } - - // set all default values to -1 - // = {{ -1 }} will only set the first element - for (i = 0; i < sizeof(fds) / sizeof(fds[0]); ++i) { - fds[i] = TNET_INVALID_FD; - } - for (i = 0; i < sizeof(fds_skipped) / sizeof(fds_skipped[0]); ++i) { - fds_skipped[i] = TNET_INVALID_FD; - } - - rc = self->Rc; - tv.tv_sec = tv_sec = 0; - tv.tv_usec = tv_usec = 0; - - // load fds for both rtp and rtcp sockets - tsk_list_foreach(item, self->candidates_local) { - if (!(candidate = item->data)) { - continue; - } - - ++host_addr_count; - if ((fds_count < sizeof(fds) / sizeof(fds[0])) && candidate->socket) { - fds[fds_count++] = candidate->socket->fd; - if (candidate->socket->fd > fd_max) { - fd_max = candidate->socket->fd; - } - } - } - - - /* RFC 5389 - 7.2.1. Sending over UDP - A client SHOULD retransmit a STUN request message starting with an - interval of RTO ("Retransmission TimeOut"), doubling after each - retransmission. - - e.g. 0 ms, 500 ms, 1500 ms, 3500 ms, 7500ms, 15500 ms, and 31500 ms - */ - for (i = 0; (i < rc && self->is_started && ((srflx_addr_count_added + srflx_addr_count_skipped) < host_addr_count)); ++i) { - // Try gathering the reflexive candidate for each server - tsk_list_foreach(item_server, ice_servers) { - if (!self->is_started) { - break; - } - if (!(ice_server = item_server->data)) { - continue; // must never happen - } - if (i == 0) { - ice_server->rto = 0; - } - else if (i == 1) { - ice_server->rto = self->RTO; - } - // else // ice_server->rto <<= 1; - tv_sec = ice_server->rto / 1000; - tv_usec = (ice_server->rto % 1000) * 1000; - if (tv_usec >= 1000000) { // > 1000000 is invalid and produce EINVAL when passed to select(iOS) - tv_usec -= 1000000; - tv_sec++; - } - // restore values for new select - tv.tv_sec = tv_sec; + /* RFC 5389 - 7.2.1. Sending over UDP + STUN indications are not retransmitted; thus, indication transactions over UDP + are not reliable. + */ + int ret = 0; + tnet_ice_servers_L_t* ice_servers = tsk_null; + tnet_ice_server_t* ice_server; + tnet_ice_ctx_t* self; + uint16_t i, k, rc; + struct timeval tv; + tnet_stun_pkt_resp_t *response = tsk_null; + const tsk_list_item_t *item, *item_server; + tnet_ice_candidate_t* candidate; + tnet_fd_t fds[kIceCandidatesCountMax] = { TNET_INVALID_FD }; // -1, then zeros + tnet_fd_t fds_skipped[kIceCandidatesCountMax] = { TNET_INVALID_FD }; // -1, then zeros + uint16_t fds_count = 0; + tnet_fd_t fd_max = -1; + fd_set set; + tsk_size_t srflx_addr_count_added = 0, srflx_addr_count_skipped = 0, host_addr_count = 0; + long tv_sec, tv_usec; //very important to save these values as timeval could be modified by select() - happens on iOS - + + self = va_arg(*app, tnet_ice_ctx_t *); + + // Get ICE servers to use to gather reflexive candidates + ice_servers = _tnet_ice_ctx_servers_copy(self, tnet_ice_server_proto_stun); + if (!ice_servers || TSK_LIST_IS_EMPTY(ice_servers)) { // not expected to be null or empty because we checked the number of such servers before calling this transition + TSK_DEBUG_WARN("No valid STUN server could be used to gather reflexive candidates"); + goto bail; + } + + // set all default values to -1 + // = {{ -1 }} will only set the first element + for (i = 0; i < sizeof(fds) / sizeof(fds[0]); ++i) { + fds[i] = TNET_INVALID_FD; + } + for (i = 0; i < sizeof(fds_skipped) / sizeof(fds_skipped[0]); ++i) { + fds_skipped[i] = TNET_INVALID_FD; + } + + rc = self->Rc; + tv.tv_sec = tv_sec = 0; + tv.tv_usec = tv_usec = 0; + + // load fds for both rtp and rtcp sockets + tsk_list_foreach(item, self->candidates_local) { + if (!(candidate = item->data)) { + continue; + } + + ++host_addr_count; + if ((fds_count < sizeof(fds) / sizeof(fds[0])) && candidate->socket) { + fds[fds_count++] = candidate->socket->fd; + if (candidate->socket->fd > fd_max) { + fd_max = candidate->socket->fd; + } + } + } + + + /* RFC 5389 - 7.2.1. Sending over UDP + A client SHOULD retransmit a STUN request message starting with an + interval of RTO ("Retransmission TimeOut"), doubling after each + retransmission. + + e.g. 0 ms, 500 ms, 1500 ms, 3500 ms, 7500ms, 15500 ms, and 31500 ms + */ + for (i = 0; (i < rc && self->is_started && ((srflx_addr_count_added + srflx_addr_count_skipped) < host_addr_count)); ++i) { + // Try gathering the reflexive candidate for each server + tsk_list_foreach(item_server, ice_servers) { + if (!self->is_started) { + break; + } + if (!(ice_server = item_server->data)) { + continue; // must never happen + } + if (i == 0) { + ice_server->rto = 0; + } + else if (i == 1) { + ice_server->rto = self->RTO; + } + // else // ice_server->rto <<= 1; + tv_sec = ice_server->rto / 1000; + tv_usec = (ice_server->rto % 1000) * 1000; + if (tv_usec >= 1000000) { // > 1000000 is invalid and produce EINVAL when passed to select(iOS) + tv_usec -= 1000000; + tv_sec++; + } + // restore values for new select + tv.tv_sec = tv_sec; #if TNET_UNDER_APPLE tv.tv_usec = (__darwin_suseconds_t)tv_usec; #else - tv.tv_usec = tv_usec; + tv.tv_usec = tv_usec; #endif - - TSK_DEBUG_INFO("ICE reflexive candidates gathering ...srv_addr=%s,srv_port=%u,tv_sec=%lu,tv_usec=%lu,rto=%d", ice_server->str_server_addr, ice_server->u_server_port, tv_sec, tv_usec, ice_server->rto); - - FD_ZERO(&set); - for (k = 0; k < fds_count; ++k) { - FD_SET(fds[k], &set); - } - - // sends STUN binding requets - tsk_list_foreach(item, self->candidates_local){ - if (!(candidate = (tnet_ice_candidate_t*)item->data)) { - continue; - } - if (candidate->socket && tsk_strnullORempty(candidate->stun.srflx_addr)) { - ret = tnet_ice_candidate_send_stun_bind_request(candidate, &ice_server->obj_server_addr, ice_server->str_username, ice_server->str_password); - } - } - - if ((ret = select(fd_max + 1, &set, NULL, NULL, &tv)) < 0) { - TSK_DEBUG_ERROR("select() failed with error code = %d", tnet_geterrno()); - goto bail; - } - else if (ret == 0) { - // timeout - TSK_DEBUG_INFO("STUN request timedout at %d, rc = %d, rto=%d", i, rc - 1, ice_server->rto); - ice_server->rto <<= 1; - continue; - } - else if (ret > 0) { - // there is data to read - for (k = 0; k < fds_count; ++k) { - tnet_fd_t fd = fds[k]; - if (FD_ISSET(fd, &set)) { - unsigned int len = 0; - void* data = 0; - const tnet_ice_candidate_t* candidate_curr; - - // Check how many bytes are pending - if ((ret = tnet_ioctlt(fd, FIONREAD, &len)) < 0) { - TSK_DEBUG_ERROR("tnet_ioctlt() failed"); - continue; - } - - if (len == 0) { - TSK_DEBUG_INFO("tnet_ioctlt() retured zero bytes"); - continue; - } - - // Receive pending data - data = tsk_calloc(len, sizeof(uint8_t)); - if ((ret = tnet_sockfd_recv(fd, data, len, 0)) < 0) { - TSK_FREE(data); - - TSK_DEBUG_ERROR("Recving STUN dgrams failed with error code:%d", tnet_geterrno()); - continue; - } - - // Parse the incoming response - if ((ret = tnet_stun_pkt_read(data, (tsk_size_t)ret, &response))) { - TSK_FREE(data); - continue; - } - TSK_FREE(data); - if (response) { - ret = 0; - if ((candidate_curr = tnet_ice_candidate_find_by_fd(self->candidates_local, fd))) { - if (tsk_strnullORempty(candidate_curr->stun.srflx_addr)) { // "srflx" candidate? - ret = tnet_ice_candidate_process_stun_response((tnet_ice_candidate_t*)candidate_curr, response, fd); - if (!tsk_strnullORempty(candidate_curr->stun.srflx_addr)) { // ...and now (after processing the response)...is it "srflx" candidate? - if (tsk_striequals(candidate_curr->connection_addr, candidate_curr->stun.srflx_addr) && candidate_curr->port == candidate_curr->stun.srflx_port) { - tsk_size_t j; - tsk_bool_t already_skipped = tsk_false; - /* refc 5245- 4.1.3. Eliminating Redundant Candidates - - Next, the agent eliminates redundant candidates. A candidate is - redundant if its transport address equals another candidate, and its - base equals the base of that other candidate. Note that two - candidates can have the same transport address yet have different - bases, and these would not be considered redundant. Frequently, a - server reflexive candidate and a host candidate will be redundant - when the agent is not behind a NAT. The agent SHOULD eliminate the - redundant candidate with the lower priority. */ - for (j = 0; (fds_skipped[j] != TNET_INVALID_FD && j < (sizeof(fds_skipped) / sizeof(fds_skipped[0]))); ++j) { - if (fds_skipped[j] == fd) { - already_skipped = tsk_true; - break; - } - } - - if (!already_skipped) { - ++srflx_addr_count_skipped; - fds_skipped[j] = fd; - } - TSK_DEBUG_INFO("Skipping redundant candidate address=%s and port=%d, fd=%d, already_skipped(%u)=%s", - candidate_curr->stun.srflx_addr, - candidate_curr->stun.srflx_port, - fd, - (unsigned)j, already_skipped ? "yes" : "no"); - } - else { - char* foundation = tsk_strdup(TNET_ICE_CANDIDATE_TYPE_SRFLX); - tnet_ice_candidate_t* new_cand; - tsk_strcat(&foundation, (const char*)candidate_curr->foundation); - new_cand = tnet_ice_candidate_create(tnet_ice_cand_type_srflx, candidate_curr->socket, candidate_curr->is_ice_jingle, candidate_curr->is_rtp, self->is_video, self->ufrag, self->pwd, foundation); - TSK_FREE(foundation); - if (new_cand) { - ++srflx_addr_count_added; - tsk_list_lock(self->candidates_local); - tnet_ice_candidate_set_rflx_addr(new_cand, candidate_curr->stun.srflx_addr, candidate_curr->stun.srflx_port); - tsk_list_push_descending_data(self->candidates_local, (void**)&new_cand); - tsk_list_unlock(self->candidates_local); - } - } - } - } - } - } - TSK_OBJECT_SAFE_FREE(response); - } - } - } - else { - continue; - } - } // tsk_list_foreach (item, ice_servers)... - } // for (i = 0; (i < rc.... - + + TSK_DEBUG_INFO("ICE reflexive candidates gathering ...srv_addr=%s,srv_port=%u,tv_sec=%lu,tv_usec=%lu,rto=%d", ice_server->str_server_addr, ice_server->u_server_port, tv_sec, tv_usec, ice_server->rto); + + FD_ZERO(&set); + for (k = 0; k < fds_count; ++k) { + FD_SET(fds[k], &set); + } + + // sends STUN binding requets + tsk_list_foreach(item, self->candidates_local){ + if (!(candidate = (tnet_ice_candidate_t*)item->data)) { + continue; + } + if (candidate->socket && tsk_strnullORempty(candidate->stun.srflx_addr)) { + ret = tnet_ice_candidate_send_stun_bind_request(candidate, &ice_server->obj_server_addr, ice_server->str_username, ice_server->str_password); + } + } + + if ((ret = select(fd_max + 1, &set, NULL, NULL, &tv)) < 0) { + TSK_DEBUG_ERROR("select() failed with error code = %d", tnet_geterrno()); + goto bail; + } + else if (ret == 0) { + // timeout + TSK_DEBUG_INFO("STUN request timedout at %d, rc = %d, rto=%d", i, rc - 1, ice_server->rto); + ice_server->rto <<= 1; + continue; + } + else if (ret > 0) { + // there is data to read + for (k = 0; k < fds_count; ++k) { + tnet_fd_t fd = fds[k]; + if (FD_ISSET(fd, &set)) { + unsigned int len = 0; + void* data = 0; + const tnet_ice_candidate_t* candidate_curr; + + // Check how many bytes are pending + if ((ret = tnet_ioctlt(fd, FIONREAD, &len)) < 0) { + TSK_DEBUG_ERROR("tnet_ioctlt() failed"); + continue; + } + + if (len == 0) { + TSK_DEBUG_INFO("tnet_ioctlt() retured zero bytes"); + continue; + } + + // Receive pending data + data = tsk_calloc(len, sizeof(uint8_t)); + if ((ret = tnet_sockfd_recv(fd, data, len, 0)) < 0) { + TSK_FREE(data); + + TSK_DEBUG_ERROR("Recving STUN dgrams failed with error code:%d", tnet_geterrno()); + continue; + } + + // Parse the incoming response + if ((ret = tnet_stun_pkt_read(data, (tsk_size_t)ret, &response))) { + TSK_FREE(data); + continue; + } + TSK_FREE(data); + if (response) { + ret = 0; + if ((candidate_curr = tnet_ice_candidate_find_by_fd(self->candidates_local, fd))) { + if (tsk_strnullORempty(candidate_curr->stun.srflx_addr)) { // "srflx" candidate? + ret = tnet_ice_candidate_process_stun_response((tnet_ice_candidate_t*)candidate_curr, response, fd); + if (!tsk_strnullORempty(candidate_curr->stun.srflx_addr)) { // ...and now (after processing the response)...is it "srflx" candidate? + if (tsk_striequals(candidate_curr->connection_addr, candidate_curr->stun.srflx_addr) && candidate_curr->port == candidate_curr->stun.srflx_port) { + tsk_size_t j; + tsk_bool_t already_skipped = tsk_false; + /* refc 5245- 4.1.3. Eliminating Redundant Candidates + + Next, the agent eliminates redundant candidates. A candidate is + redundant if its transport address equals another candidate, and its + base equals the base of that other candidate. Note that two + candidates can have the same transport address yet have different + bases, and these would not be considered redundant. Frequently, a + server reflexive candidate and a host candidate will be redundant + when the agent is not behind a NAT. The agent SHOULD eliminate the + redundant candidate with the lower priority. */ + for (j = 0; (fds_skipped[j] != TNET_INVALID_FD && j < (sizeof(fds_skipped) / sizeof(fds_skipped[0]))); ++j) { + if (fds_skipped[j] == fd) { + already_skipped = tsk_true; + break; + } + } + + if (!already_skipped) { + ++srflx_addr_count_skipped; + fds_skipped[j] = fd; + } + TSK_DEBUG_INFO("Skipping redundant candidate address=%s and port=%d, fd=%d, already_skipped(%u)=%s", + candidate_curr->stun.srflx_addr, + candidate_curr->stun.srflx_port, + fd, + (unsigned)j, already_skipped ? "yes" : "no"); + } + else { + char* foundation = tsk_strdup(TNET_ICE_CANDIDATE_TYPE_SRFLX); + tnet_ice_candidate_t* new_cand; + tsk_strcat(&foundation, (const char*)candidate_curr->foundation); + new_cand = tnet_ice_candidate_create(tnet_ice_cand_type_srflx, candidate_curr->socket, candidate_curr->is_ice_jingle, candidate_curr->is_rtp, self->is_video, self->ufrag, self->pwd, foundation); + TSK_FREE(foundation); + if (new_cand) { + ++srflx_addr_count_added; + tsk_list_lock(self->candidates_local); + tnet_ice_candidate_set_rflx_addr(new_cand, candidate_curr->stun.srflx_addr, candidate_curr->stun.srflx_port); + tsk_list_push_descending_data(self->candidates_local, (void**)&new_cand); + tsk_list_unlock(self->candidates_local); + } + } + } + } + } + } + TSK_OBJECT_SAFE_FREE(response); + } + } + } + else { + continue; + } + } // tsk_list_foreach (item, ice_servers)... + } // for (i = 0; (i < rc.... + bail: - TSK_DEBUG_INFO("srflx_addr_count_added=%u, srflx_addr_count_skipped=%u", (unsigned)srflx_addr_count_added, (unsigned)srflx_addr_count_skipped); - if ((srflx_addr_count_added + srflx_addr_count_skipped) > 0) ret = 0; // Hack the returned value if we have at least one success (happens when timeouts) - if (self->is_started) { - if (ret == 0) { - ret = _tnet_ice_ctx_fsm_act(self, _fsm_action_Success); - } - else{ - ret = _tnet_ice_ctx_fsm_act(self, _fsm_action_Failure); - } - } - - tsk_list_foreach(item, self->candidates_local) { - if (!(candidate = (tnet_ice_candidate_t*)item->data)) { - continue; - } - TSK_DEBUG_INFO("Candidate: %s", tnet_ice_candidate_tostring(candidate)); - } - TSK_OBJECT_SAFE_FREE(ice_servers); - return ret; + TSK_DEBUG_INFO("srflx_addr_count_added=%u, srflx_addr_count_skipped=%u", (unsigned)srflx_addr_count_added, (unsigned)srflx_addr_count_skipped); + if ((srflx_addr_count_added + srflx_addr_count_skipped) > 0) ret = 0; // Hack the returned value if we have at least one success (happens when timeouts) + if (self->is_started) { + if (ret == 0) { + ret = _tnet_ice_ctx_fsm_act(self, _fsm_action_Success); + } + else{ + ret = _tnet_ice_ctx_fsm_act(self, _fsm_action_Failure); + } + } + + tsk_list_foreach(item, self->candidates_local) { + if (!(candidate = (tnet_ice_candidate_t*)item->data)) { + continue; + } + TSK_DEBUG_INFO("Candidate: %s", tnet_ice_candidate_tostring(candidate)); + } + TSK_OBJECT_SAFE_FREE(ice_servers); + return ret; } // GatheringReflexiveCandidates -> (Success) -> GatheringReflexiveCandidatesDone static int _tnet_ice_ctx_fsm_GatheringReflexiveCandidates_2_GatheringReflexiveCandidatesDone_X_Success(va_list *app) { - tnet_ice_ctx_t* self; - - self = va_arg(*app, tnet_ice_ctx_t *); - - if (self->is_started) { - int ret = _tnet_ice_ctx_signal_async(self, tnet_ice_event_type_gathering_reflexive_candidates_succeed, "Gathering reflexive candidates succeed"); - if (ret == 0) { - enum _fsm_action_e action_next = _fsm_action_GatheringComplet; - if (self->is_turn_enabled) { - if (_tnet_ice_ctx_servers_count_by_proto(self, tnet_ice_server_proto_turn) == 0) { - TSK_DEBUG_WARN("TURN is enabled but no TURN server could be found"); - } - else { - action_next = _fsm_action_GatherRelayCandidates; - } - } - ret = _tnet_ice_ctx_fsm_act(self, action_next); - } - return ret; - } - else { - return -1; - } + tnet_ice_ctx_t* self; + + self = va_arg(*app, tnet_ice_ctx_t *); + + if (self->is_started) { + int ret = _tnet_ice_ctx_signal_async(self, tnet_ice_event_type_gathering_reflexive_candidates_succeed, "Gathering reflexive candidates succeed"); + if (ret == 0) { + enum _fsm_action_e action_next = _fsm_action_GatheringComplet; + if (self->is_turn_enabled) { + if (_tnet_ice_ctx_servers_count_by_proto(self, tnet_ice_server_proto_turn) == 0) { + TSK_DEBUG_WARN("TURN is enabled but no TURN server could be found"); + } + else { + action_next = _fsm_action_GatherRelayCandidates; + } + } + ret = _tnet_ice_ctx_fsm_act(self, action_next); + } + return ret; + } + else { + return -1; + } } // GatheringReflexiveCandidates -> (Failure) -> Terminated static int _tnet_ice_ctx_fsm_GatheringReflexiveCandidates_2_Terminated_X_Failure(va_list *app) { - tnet_ice_ctx_t* self = va_arg(*app, tnet_ice_ctx_t *); - return _tnet_ice_ctx_signal_async(self, tnet_ice_event_type_gathering_reflexive_candidates_failed, "Gathering reflexive candidates failed"); + tnet_ice_ctx_t* self = va_arg(*app, tnet_ice_ctx_t *); + return _tnet_ice_ctx_signal_async(self, tnet_ice_event_type_gathering_reflexive_candidates_failed, "Gathering reflexive candidates failed"); } // GatheringReflexiveCandidatesDone -> (GatherRelayCandidates) -> GatheringRelayCandidates static int _tnet_ice_ctx_fsm_GatheringReflexiveCandidatesDone_2_GatheringRelayCandidates_X_GatherRelayCandidates(va_list *app) { - tnet_ice_ctx_t* self = va_arg(*app, tnet_ice_ctx_t *); - int ret = 0; - tsk_list_item_t *item, *item_server = tsk_null; - tnet_ice_candidate_t* candidate; - uint16_t i, rto, rc; - tsk_size_t relay_addr_count_ok = 0, relay_addr_count_nok = 0, relay_addr_count_added = 0, host_addr_count = 0; - uint64_t u_t0, u_t1; - enum tnet_stun_state_e e_tunrn_state; - tnet_ice_servers_L_t* ice_servers = tsk_null; - tnet_ice_server_t* ice_server; - tnet_ice_candidates_L_t* candidates_local_copy = tsk_null;; - - // Create TURN condwait handle if not already done - if (!self->turn.condwait && !(self->turn.condwait = tsk_condwait_create())) { - TSK_DEBUG_ERROR("Failed to create TURN condwait handle"); - ret = -2; - goto bail; - } - - // Copy local ICE candidates - tsk_list_lock(self->candidates_local); - candidates_local_copy = tsk_list_clone(self->candidates_local); - tsk_list_unlock(self->candidates_local); - - // Take reference to the TURN servers - ice_servers = _tnet_ice_ctx_servers_copy(self, tnet_ice_server_proto_turn); - if (!ice_servers || TSK_LIST_IS_EMPTY(ice_servers)) { - TSK_DEBUG_WARN("TURN enabled but no server could be found"); // should never happen...but who knows? - goto bail; - } + tnet_ice_ctx_t* self = va_arg(*app, tnet_ice_ctx_t *); + int ret = 0; + tsk_list_item_t *item, *item_server = tsk_null; + tnet_ice_candidate_t* candidate; + uint16_t i, rto, rc; + tsk_size_t relay_addr_count_ok = 0, relay_addr_count_nok = 0, relay_addr_count_added = 0, host_addr_count = 0; + uint64_t u_t0, u_t1; + enum tnet_stun_state_e e_tunrn_state; + tnet_ice_servers_L_t* ice_servers = tsk_null; + tnet_ice_server_t* ice_server; + tnet_ice_candidates_L_t* candidates_local_copy = tsk_null;; + + // Create TURN condwait handle if not already done + if (!self->turn.condwait && !(self->turn.condwait = tsk_condwait_create())) { + TSK_DEBUG_ERROR("Failed to create TURN condwait handle"); + ret = -2; + goto bail; + } + + // Copy local ICE candidates + tsk_list_lock(self->candidates_local); + candidates_local_copy = tsk_list_clone(self->candidates_local); + tsk_list_unlock(self->candidates_local); + + // Take reference to the TURN servers + ice_servers = _tnet_ice_ctx_servers_copy(self, tnet_ice_server_proto_turn); + if (!ice_servers || TSK_LIST_IS_EMPTY(ice_servers)) { + TSK_DEBUG_WARN("TURN enabled but no server could be found"); // should never happen...but who knows? + goto bail; + } next_server: - if (!self->is_started) { - goto bail; - } - relay_addr_count_ok = 0, relay_addr_count_nok = 0, relay_addr_count_added = 0, host_addr_count = 0; - if (!item_server) { - item_server = ice_servers->head; - } - else { - item_server = item_server->next; - } - if (!item_server) { - TSK_DEBUG_INFO("We have reached the end of TURN servers"); - goto bail; - } - ice_server = (tnet_ice_server_t*)item_server->data; - - // Create TURN sessions for each local host candidate - tsk_list_foreach(item, candidates_local_copy) { - if (!(candidate = item->data)) { - continue; - } - TSK_DEBUG_INFO("Gathering relay candidate: local addr=%s=%d, TURN server=%s:%d", candidate->connection_addr, candidate->port, ice_server->str_server_addr, ice_server->u_server_port); - - // Destroy previvious TURN session (if exist) - TSK_OBJECT_SAFE_FREE(candidate->turn.ss); - if (candidate->type_e == tnet_ice_cand_type_host && candidate->socket) { // do not create TURN session for reflexive candidates - // create the TURN session - // FIXME: For now we support UDP relaying only (like Chrome): more info at https://groups.google.com/forum/#!topic/turn-server-project-rfc5766-turn-server/vR_2OAV9a_w - // This is not an issue even if both peers requires TCP/TLS connection to the TURN server. UDP relaying will be local to the servers. - // - static enum tnet_turn_transport_e __e_req_transport = tnet_turn_transport_udp; // We should create two TURN sessions: #1 UDP relay + #1 TCP relay - if ((ret = tnet_turn_session_create_4(candidate->socket, __e_req_transport, ice_server->str_server_addr, ice_server->u_server_port, ice_server->e_transport, &candidate->turn.ss))) { - continue; - } - // set TURN callback - if ((ret = tnet_turn_session_set_callback(candidate->turn.ss, _tnet_ice_ctx_turn_callback, self))) { - continue; - } - // set SSL certificates - if ((ret = tnet_turn_session_set_ssl_certs(candidate->turn.ss, self->ssl.path_priv, self->ssl.path_pub, self->ssl.path_ca, self->ssl.verify))) { - continue; - } - // set TURN credentials - if ((ret = tnet_turn_session_set_cred(candidate->turn.ss, ice_server->str_username, ice_server->str_password))) { - continue; - } - // prepare() - if ((ret = tnet_turn_session_prepare(candidate->turn.ss))) { - continue; - } - // start() - if ((ret = tnet_turn_session_start(candidate->turn.ss))) { - continue; - } - // allocate() - if ((ret = tnet_turn_session_allocate(candidate->turn.ss))) { - continue; - } - ++host_addr_count; - } - } // tsk_list_foreach(item, self->candidates_local) { - - rto = self->RTO; - rc = self->Rc; - - for (i = 0; (i < rc && self->is_started && ((relay_addr_count_ok + relay_addr_count_nok) < host_addr_count));) { - if (!self->is_started || !self->is_active) { - TSK_DEBUG_INFO("ICE context stopped/cancelled while gathering TURN candidates"); - goto bail; - } - - u_t0 = tsk_time_now(); - tsk_condwait_timedwait(self->turn.condwait, rto); - u_t1 = tsk_time_now(); - if ((u_t1 - u_t0) >= rto) { - // timedwait() -> timedout - rto <<= 1; - ++i; - } - - // count the number of TURN sessions with alloc() = ok/nok and ignore ones without response - relay_addr_count_ok = 0; - tsk_list_foreach(item, candidates_local_copy) { - if (!(candidate = item->data) || !candidate->turn.ss) { - continue; - } - if ((ret = tnet_turn_session_get_state_alloc(candidate->turn.ss, &e_tunrn_state))) { - goto bail; - } - if (e_tunrn_state == tnet_stun_state_ok) { - ++relay_addr_count_ok; - } - else if (e_tunrn_state == tnet_stun_state_nok) { - TSK_OBJECT_SAFE_FREE(candidate->turn.ss); // delete the session - ++relay_addr_count_nok; - } - } - } - - // add/delete TURN candidates - tsk_list_foreach(item, candidates_local_copy) { - if (!(candidate = item->data) || !candidate->turn.ss) { - continue; - } - if ((ret = tnet_turn_session_get_state_alloc(candidate->turn.ss, &e_tunrn_state))) { - goto bail; - } - if (e_tunrn_state == tnet_stun_state_ok) { - static tsk_bool_t __b_ipv6; - char* foundation = tsk_null; - char* relay_addr = tsk_null; - tnet_port_t relay_port; - tnet_ice_candidate_t* new_cand = tsk_null; - struct tnet_socket_s* p_lcl_sock = tsk_null; - - if ((ret = tnet_turn_session_get_relayed_addr(candidate->turn.ss, &relay_addr, &relay_port, &__b_ipv6))) { - goto bail; - } - if (tsk_striequals(candidate->connection_addr, relay_addr) && candidate->port == relay_port) { - TSK_DEBUG_INFO("Skipping redundant candidate address=%s and port=%d", relay_addr, relay_port); - TSK_FREE(relay_addr); - continue; - } - if ((ret = tnet_turn_session_get_socket_local(candidate->turn.ss, &p_lcl_sock))) { - goto bail; - } - tsk_strcat_2(&foundation, "%s%s", TNET_ICE_CANDIDATE_TYPE_RELAY, (const char*)candidate->foundation); - new_cand = tnet_ice_candidate_create(tnet_ice_cand_type_relay, p_lcl_sock, candidate->is_ice_jingle, candidate->is_rtp, self->is_video, self->ufrag, self->pwd, foundation); - TSK_FREE(foundation); - TSK_OBJECT_SAFE_FREE(p_lcl_sock); - if (new_cand) { - tsk_list_lock(self->candidates_local); - new_cand->turn.ss = candidate->turn.ss, candidate->turn.ss = tsk_null; - new_cand->turn.relay_addr = relay_addr, relay_addr = tsk_null; - new_cand->turn.relay_port = relay_port; - tnet_ice_candidate_set_rflx_addr(new_cand, new_cand->turn.relay_addr, new_cand->turn.relay_port); - tsk_list_push_descending_data(self->candidates_local, (void**)&new_cand); - tsk_list_unlock(self->candidates_local); - ++relay_addr_count_added; - } - TSK_FREE(relay_addr); - } - else { - TSK_OBJECT_SAFE_FREE(candidate->turn.ss); - } - } - - // Try next TURN server - if (self->is_started && item_server && relay_addr_count_added == 0) { - goto next_server; - } - + if (!self->is_started) { + goto bail; + } + relay_addr_count_ok = 0, relay_addr_count_nok = 0, relay_addr_count_added = 0, host_addr_count = 0; + if (!item_server) { + item_server = ice_servers->head; + } + else { + item_server = item_server->next; + } + if (!item_server) { + TSK_DEBUG_INFO("We have reached the end of TURN servers"); + goto bail; + } + ice_server = (tnet_ice_server_t*)item_server->data; + + // Create TURN sessions for each local host candidate + tsk_list_foreach(item, candidates_local_copy) { + if (!(candidate = item->data)) { + continue; + } + TSK_DEBUG_INFO("Gathering relay candidate: local addr=%s=%d, TURN server=%s:%d", candidate->connection_addr, candidate->port, ice_server->str_server_addr, ice_server->u_server_port); + + // Destroy previvious TURN session (if exist) + TSK_OBJECT_SAFE_FREE(candidate->turn.ss); + if (candidate->type_e == tnet_ice_cand_type_host && candidate->socket) { // do not create TURN session for reflexive candidates + // create the TURN session + // FIXME: For now we support UDP relaying only (like Chrome): more info at https://groups.google.com/forum/#!topic/turn-server-project-rfc5766-turn-server/vR_2OAV9a_w + // This is not an issue even if both peers requires TCP/TLS connection to the TURN server. UDP relaying will be local to the servers. + // + static enum tnet_turn_transport_e __e_req_transport = tnet_turn_transport_udp; // We should create two TURN sessions: #1 UDP relay + #1 TCP relay + if ((ret = tnet_turn_session_create_4(candidate->socket, __e_req_transport, ice_server->str_server_addr, ice_server->u_server_port, ice_server->e_transport, &candidate->turn.ss))) { + continue; + } + // set TURN callback + if ((ret = tnet_turn_session_set_callback(candidate->turn.ss, _tnet_ice_ctx_turn_callback, self))) { + continue; + } + // set SSL certificates + if ((ret = tnet_turn_session_set_ssl_certs(candidate->turn.ss, self->ssl.path_priv, self->ssl.path_pub, self->ssl.path_ca, self->ssl.verify))) { + continue; + } + // set TURN credentials + if ((ret = tnet_turn_session_set_cred(candidate->turn.ss, ice_server->str_username, ice_server->str_password))) { + continue; + } + // prepare() + if ((ret = tnet_turn_session_prepare(candidate->turn.ss))) { + continue; + } + // start() + if ((ret = tnet_turn_session_start(candidate->turn.ss))) { + continue; + } + // allocate() + if ((ret = tnet_turn_session_allocate(candidate->turn.ss))) { + continue; + } + ++host_addr_count; + } + } // tsk_list_foreach(item, self->candidates_local) { + + rto = self->RTO; + rc = self->Rc; + + for (i = 0; (i < rc && self->is_started && ((relay_addr_count_ok + relay_addr_count_nok) < host_addr_count));) { + if (!self->is_started || !self->is_active) { + TSK_DEBUG_INFO("ICE context stopped/cancelled while gathering TURN candidates"); + goto bail; + } + + u_t0 = tsk_time_now(); + tsk_condwait_timedwait(self->turn.condwait, rto); + u_t1 = tsk_time_now(); + if ((u_t1 - u_t0) >= rto) { + // timedwait() -> timedout + rto <<= 1; + ++i; + } + + // count the number of TURN sessions with alloc() = ok/nok and ignore ones without response + relay_addr_count_ok = 0; + tsk_list_foreach(item, candidates_local_copy) { + if (!(candidate = item->data) || !candidate->turn.ss) { + continue; + } + if ((ret = tnet_turn_session_get_state_alloc(candidate->turn.ss, &e_tunrn_state))) { + goto bail; + } + if (e_tunrn_state == tnet_stun_state_ok) { + ++relay_addr_count_ok; + } + else if (e_tunrn_state == tnet_stun_state_nok) { + TSK_OBJECT_SAFE_FREE(candidate->turn.ss); // delete the session + ++relay_addr_count_nok; + } + } + } + + // add/delete TURN candidates + tsk_list_foreach(item, candidates_local_copy) { + if (!(candidate = item->data) || !candidate->turn.ss) { + continue; + } + if ((ret = tnet_turn_session_get_state_alloc(candidate->turn.ss, &e_tunrn_state))) { + goto bail; + } + if (e_tunrn_state == tnet_stun_state_ok) { + static tsk_bool_t __b_ipv6; + char* foundation = tsk_null; + char* relay_addr = tsk_null; + tnet_port_t relay_port; + tnet_ice_candidate_t* new_cand = tsk_null; + struct tnet_socket_s* p_lcl_sock = tsk_null; + + if ((ret = tnet_turn_session_get_relayed_addr(candidate->turn.ss, &relay_addr, &relay_port, &__b_ipv6))) { + goto bail; + } + if (tsk_striequals(candidate->connection_addr, relay_addr) && candidate->port == relay_port) { + TSK_DEBUG_INFO("Skipping redundant candidate address=%s and port=%d", relay_addr, relay_port); + TSK_FREE(relay_addr); + continue; + } + if ((ret = tnet_turn_session_get_socket_local(candidate->turn.ss, &p_lcl_sock))) { + goto bail; + } + tsk_strcat_2(&foundation, "%s%s", TNET_ICE_CANDIDATE_TYPE_RELAY, (const char*)candidate->foundation); + new_cand = tnet_ice_candidate_create(tnet_ice_cand_type_relay, p_lcl_sock, candidate->is_ice_jingle, candidate->is_rtp, self->is_video, self->ufrag, self->pwd, foundation); + TSK_FREE(foundation); + TSK_OBJECT_SAFE_FREE(p_lcl_sock); + if (new_cand) { + tsk_list_lock(self->candidates_local); + new_cand->turn.ss = candidate->turn.ss, candidate->turn.ss = tsk_null; + new_cand->turn.relay_addr = relay_addr, relay_addr = tsk_null; + new_cand->turn.relay_port = relay_port; + tnet_ice_candidate_set_rflx_addr(new_cand, new_cand->turn.relay_addr, new_cand->turn.relay_port); + tsk_list_push_descending_data(self->candidates_local, (void**)&new_cand); + tsk_list_unlock(self->candidates_local); + ++relay_addr_count_added; + } + TSK_FREE(relay_addr); + } + else { + TSK_OBJECT_SAFE_FREE(candidate->turn.ss); + } + } + + // Try next TURN server + if (self->is_started && item_server && relay_addr_count_added == 0) { + goto next_server; + } + bail: - if (self->is_started) { - if (ret == 0) { - ret = _tnet_ice_ctx_fsm_act(self, _fsm_action_Success); - } - else { - ret = _tnet_ice_ctx_fsm_act(self, _fsm_action_Failure); - } - } - TSK_OBJECT_SAFE_FREE(ice_servers); - TSK_OBJECT_SAFE_FREE(candidates_local_copy); - return ret; + if (self->is_started) { + if (ret == 0) { + ret = _tnet_ice_ctx_fsm_act(self, _fsm_action_Success); + } + else { + ret = _tnet_ice_ctx_fsm_act(self, _fsm_action_Failure); + } + } + TSK_OBJECT_SAFE_FREE(ice_servers); + TSK_OBJECT_SAFE_FREE(candidates_local_copy); + return ret; } // GatheringRelayCandidates -> (Success) -> GatheringRelayCandidatesDone static int _tnet_ice_ctx_fsm_GatheringRelayCandidates_2_GatheringRelayCandidatesDone_X_Success(va_list *app) { - tnet_ice_ctx_t* self = va_arg(*app, tnet_ice_ctx_t *); - if (self->is_started) { - // Relay candidates are the last ones -> gathering is competed - return _tnet_ice_ctx_fsm_act(self, _fsm_action_GatheringComplet); - } - else { - return -1; - } + tnet_ice_ctx_t* self = va_arg(*app, tnet_ice_ctx_t *); + if (self->is_started) { + // Relay candidates are the last ones -> gathering is competed + return _tnet_ice_ctx_fsm_act(self, _fsm_action_GatheringComplet); + } + else { + return -1; + } } // GatheringReflexiveCandidates -> (Failure) -> Terminated static int _tnet_ice_ctx_fsm_GatheringRelayCandidates_2_Terminated_X_Failure(va_list *app) { - tnet_ice_ctx_t* self = va_arg(*app, tnet_ice_ctx_t *); - return _tnet_ice_ctx_signal_async(self, tnet_ice_event_type_gathering_relay_candidates_failed, "Gathering relay candidates failed"); + tnet_ice_ctx_t* self = va_arg(*app, tnet_ice_ctx_t *); + return _tnet_ice_ctx_signal_async(self, tnet_ice_event_type_gathering_relay_candidates_failed, "Gathering relay candidates failed"); } // Any -> (Cancel) -> Started static int _tnet_ice_ctx_fsm_Any_2_Started_X_Cancel(va_list *app) { - tnet_ice_ctx_t* self; - self = va_arg(*app, tnet_ice_ctx_t *); - - tsk_list_lock(self->candidates_remote); - tsk_list_clear_items(self->candidates_remote); - tsk_list_unlock(self->candidates_remote); - - tsk_list_lock(self->candidates_pairs); - tsk_list_clear_items(self->candidates_pairs); - tsk_list_unlock(self->candidates_pairs); - - TSK_OBJECT_SAFE_FREE(self->turn.ss_nominated_rtp); - TSK_OBJECT_SAFE_FREE(self->turn.ss_nominated_rtcp); - - // Do not clear local candidates because then will be used as fallback if the remote peer is an ICE-lite - // These candidates will be cleared before the next local gathering - // tsk_list_lock(self->candidates_local); - // tsk_list_clear_items(self->candidates_local); - // tsk_list_unlock(self->candidates_local); - - // restore "is_cancelled" until next cancel - // set "is_active" to false to allow ICE re-start - // self->is_cancelled = tsk_false; - // self->is_active = tsk_false; - - // alert user - _tnet_ice_ctx_signal_async(self, tnet_ice_event_type_cancelled, "Cancelled"); - - return 0; - + tnet_ice_ctx_t* self; + self = va_arg(*app, tnet_ice_ctx_t *); + + tsk_list_lock(self->candidates_remote); + tsk_list_clear_items(self->candidates_remote); + tsk_list_unlock(self->candidates_remote); + + tsk_list_lock(self->candidates_pairs); + tsk_list_clear_items(self->candidates_pairs); + tsk_list_unlock(self->candidates_pairs); + + TSK_OBJECT_SAFE_FREE(self->turn.ss_nominated_rtp); + TSK_OBJECT_SAFE_FREE(self->turn.ss_nominated_rtcp); + + // Do not clear local candidates because then will be used as fallback if the remote peer is an ICE-lite + // These candidates will be cleared before the next local gathering + // tsk_list_lock(self->candidates_local); + // tsk_list_clear_items(self->candidates_local); + // tsk_list_unlock(self->candidates_local); + + // restore "is_cancelled" until next cancel + // set "is_active" to false to allow ICE re-start + // self->is_cancelled = tsk_false; + // self->is_active = tsk_false; + + // alert user + _tnet_ice_ctx_signal_async(self, tnet_ice_event_type_cancelled, "Cancelled"); + + return 0; + } // Any -> (GatheringComplet) -> GatheringCompleted static int _tnet_ice_ctx_fsm_Any_2_GatheringCompleted_X_GatheringComplet(va_list *app) { - int ret = 0; - tnet_ice_ctx_t* self; - tsk_bool_t has_remote_candidates; - - self = va_arg(*app, tnet_ice_ctx_t *); - - // alert user - _tnet_ice_ctx_signal_async(self, tnet_ice_event_type_gathering_completed, "Gathering candidates completed"); - - if (self->is_started){ - tsk_list_lock(self->candidates_remote); - has_remote_candidates = !TSK_LIST_IS_EMPTY(self->candidates_remote); - tsk_list_unlock(self->candidates_remote); - - if (has_remote_candidates){ - ret = _tnet_ice_ctx_fsm_act(self, _fsm_action_ConnCheck); - } - } - else{ - return -1; - } - - return ret; + int ret = 0; + tnet_ice_ctx_t* self; + tsk_bool_t has_remote_candidates; + + self = va_arg(*app, tnet_ice_ctx_t *); + + // alert user + _tnet_ice_ctx_signal_async(self, tnet_ice_event_type_gathering_completed, "Gathering candidates completed"); + + if (self->is_started){ + tsk_list_lock(self->candidates_remote); + has_remote_candidates = !TSK_LIST_IS_EMPTY(self->candidates_remote); + tsk_list_unlock(self->candidates_remote); + + if (has_remote_candidates){ + ret = _tnet_ice_ctx_fsm_act(self, _fsm_action_ConnCheck); + } + } + else{ + return -1; + } + + return ret; } // GatheringComplet -> (ConnCheck) -> ConnChecking static int _tnet_ice_ctx_fsm_GatheringCompleted_2_ConnChecking_X_ConnCheck(va_list *app) { - // Implements: - // 5.8. Scheduling Checks + // Implements: + // 5.8. Scheduling Checks #if !defined(FD_SETSIZE) #define FD_SETSIZE 64 #endif - int ret, err; - const tsk_list_item_t *item; - tnet_ice_ctx_t* self; - tnet_fd_t fds[FD_SETSIZE] = { -1 }; - tnet_fd_t fds_turn[FD_SETSIZE] = { -1 }; - uint16_t fds_count = 0, fds_turn_count = 0, k; - tnet_fd_t fd_max = -1; - fd_set set; - const tnet_ice_pair_t *pair; - struct timeval tv; - static const long rto = 160; // milliseconds - struct sockaddr_storage remote_addr; - uint64_t time_start, time_curr, time_end, concheck_timeout; - tsk_bool_t role_conflict, restart_conneck, check_rtcp, isset, got_hosts; - void* recvfrom_buff_ptr = tsk_null; - tsk_size_t recvfrom_buff_size = 0, tries_count = 0, tries_count_min = kIceConnCheckMinTriesMin; - enum tnet_stun_state_e e_state; - - self = va_arg(*app, tnet_ice_ctx_t *); - - self->is_connchecking = tsk_true; - - // "tries_count" and "tries_count_min" - // The connection checks to to the "relay", "prflx", "srflx" and "host" candidates are sent at the same time. - // Because the requests are sent at the same time it's possible to have success check for "relay" (or "srflx") candidates before the "host" candidates. - // "tries_count_min" is the minimum (if success check is not for "host" candidates) tries before giving up. - // The pairs are already sorted ("host"->"srflx"->"prflx", "relay") to make sure to choose the best candidates when there are more than one success conncheck. - + int ret, err; + const tsk_list_item_t *item; + tnet_ice_ctx_t* self; + tnet_fd_t fds[FD_SETSIZE] = { -1 }; + tnet_fd_t fds_turn[FD_SETSIZE] = { -1 }; + uint16_t fds_count = 0, fds_turn_count = 0, k; + tnet_fd_t fd_max = -1; + fd_set set; + const tnet_ice_pair_t *pair; + struct timeval tv; + static const long rto = 160; // milliseconds + struct sockaddr_storage remote_addr; + uint64_t time_start, time_curr, time_end, concheck_timeout; + tsk_bool_t role_conflict, restart_conneck, check_rtcp, isset, got_hosts; + void* recvfrom_buff_ptr = tsk_null; + tsk_size_t recvfrom_buff_size = 0, tries_count = 0, tries_count_min = kIceConnCheckMinTriesMin; + enum tnet_stun_state_e e_state; + + self = va_arg(*app, tnet_ice_ctx_t *); + + self->is_connchecking = tsk_true; + + // "tries_count" and "tries_count_min" + // The connection checks to to the "relay", "prflx", "srflx" and "host" candidates are sent at the same time. + // Because the requests are sent at the same time it's possible to have success check for "relay" (or "srflx") candidates before the "host" candidates. + // "tries_count_min" is the minimum (if success check is not for "host" candidates) tries before giving up. + // The pairs are already sorted ("host"->"srflx"->"prflx", "relay") to make sure to choose the best candidates when there are more than one success conncheck. + start_conneck: - role_conflict = tsk_false; - restart_conneck = tsk_false; - - tsk_list_lock(self->candidates_pairs); - tsk_list_clear_items(self->candidates_pairs); - tsk_list_unlock(self->candidates_pairs); - - TSK_OBJECT_SAFE_FREE(self->turn.ss_nominated_rtp); - TSK_OBJECT_SAFE_FREE(self->turn.ss_nominated_rtcp); - - if ((ret = _tnet_ice_ctx_build_pairs(self, self->candidates_local, self->candidates_remote, self->candidates_pairs, self->is_controlling, self->tie_breaker, self->is_ice_jingle, self->use_rtcpmux))) { - TSK_DEBUG_ERROR("_tnet_ice_ctx_build_pairs() failed"); - goto bail; - } - + role_conflict = tsk_false; + restart_conneck = tsk_false; + + tsk_list_lock(self->candidates_pairs); + tsk_list_clear_items(self->candidates_pairs); + tsk_list_unlock(self->candidates_pairs); + + TSK_OBJECT_SAFE_FREE(self->turn.ss_nominated_rtp); + TSK_OBJECT_SAFE_FREE(self->turn.ss_nominated_rtcp); + + if ((ret = _tnet_ice_ctx_build_pairs(self, self->candidates_local, self->candidates_remote, self->candidates_pairs, self->is_controlling, self->tie_breaker, self->is_ice_jingle, self->use_rtcpmux))) { + TSK_DEBUG_ERROR("_tnet_ice_ctx_build_pairs() failed"); + goto bail; + } + #define _FD_ISSET(_fds, _fds_count, _fd, _isset) { uint16_t __i; *_isset = 0; for (__i = 0; __i < _fds_count; ++__i) { if (_fds[__i] == _fd) { *_isset = 1; break; } } } - - // load fds for both rtp and rtcp sockets / create TURN permissions - tsk_list_lock(self->candidates_pairs); - tsk_list_foreach(item, self->candidates_pairs){ - if (!(pair = item->data) || !pair->candidate_offer || !pair->candidate_offer->socket){ - continue; - } - - if ((fds_count < sizeof(fds) / sizeof(fds[0])) && pair->candidate_offer->socket) { - if (pair->candidate_offer->turn.ss && (ret = tnet_turn_session_get_state_createperm(pair->candidate_offer->turn.ss, pair->turn_peer_id, &e_state)) == 0) { - if (e_state == tnet_stun_state_none) { - ret = tnet_turn_session_createpermission(((tnet_ice_pair_t *)pair)->candidate_offer->turn.ss, pair->candidate_answer->connection_addr, pair->candidate_answer->port, &((tnet_ice_pair_t *)pair)->turn_peer_id); - if (ret) { - continue; - // goto bail; - } - } - fds_turn[fds_turn_count++] = pair->candidate_offer->socket->fd; - // When TURN is active the socket (host) is pulled in the TURN session and any incoming data will be forwarded to us. - // Do not add fd to the set - continue; - } - _FD_ISSET(fds, fds_count, pair->candidate_offer->socket->fd, &isset); // not in the set -> to avoid doubloon - if (!isset) { - _FD_ISSET(fds_turn, fds_turn_count, pair->candidate_offer->socket->fd, &isset); // not already managed by a TURN session - if (!isset) { - fds[fds_count++] = pair->candidate_offer->socket->fd; - if (pair->candidate_offer->socket->fd > fd_max) { - fd_max = pair->candidate_offer->socket->fd; - } - } - } - } - } - tsk_list_unlock(self->candidates_pairs); - - concheck_timeout = self->concheck_timeout; - time_start = time_curr = tsk_time_now(); - time_end = (time_start + concheck_timeout); - tries_count_min = fds_turn_count > 0 ? kIceConnCheckMinTriesMax : kIceConnCheckMinTriesMin; - - while (self->is_started && self->is_active && (time_curr < time_end) && !self->have_nominated_symetric) { - tv.tv_sec = 0; - tv.tv_usec = (rto * 1000); - - FD_ZERO(&set); - for (k = 0; k < fds_count; ++k) { - FD_SET(fds[k], &set); - } - - // set new current time here to avoid "continue" skips - // ignore already ellapsed time if new timeout value is defined - time_curr = tsk_time_now(); - if (self->concheck_timeout != concheck_timeout) { - concheck_timeout = self->concheck_timeout; - time_start = time_curr; - time_end = (time_start + concheck_timeout); - } - - // Send ConnCheck requests - // the pairs are already sorted by priority (from high to low) - if (!self->have_nominated_symetric) { - tsk_list_foreach(item, self->candidates_pairs) { - if (!(pair = item->data) || !pair->candidate_offer || !pair->candidate_offer->socket) { - continue; - } - switch (pair->state_offer) { - case tnet_ice_pair_state_failed: - case tnet_ice_pair_state_succeed: - continue; - default: break; - } - - ret = tnet_ice_pair_send_conncheck((tnet_ice_pair_t *)pair); - } - } - - if (fds_count == 0) { - tsk_thread_sleep(10); - goto check_nomination; - } - - if ((ret = select(fd_max + 1, &set, NULL, NULL, &tv)) < 0) { - TNET_PRINT_LAST_ERROR("select() failed"); - goto bail; - } - else if (ret == 0) { - // timeout - // TSK_DEBUG_INFO("STUN request timedout"); - goto check_nomination; //!\ continue == possible endless loop - } - else if (ret > 0) { - // there is data to read - for (k = 0; k < fds_count; ++k) { - tnet_fd_t fd = fds[k]; - unsigned int len = 0; - tsk_size_t read = 0; - - if (!FD_ISSET(fd, &set)) { - continue; - } - - // Check how many bytes are pending - if ((ret = tnet_ioctlt(fd, FIONREAD, &len)) < 0) { - continue; - } - - if (len == 0){ - // TSK_DEBUG_INFO("tnet_ioctlt() returent zero bytes"); - continue; - } - - // Receive pending data - if (recvfrom_buff_size < len){ - if (!(recvfrom_buff_ptr = tsk_realloc(recvfrom_buff_ptr, len))){ - recvfrom_buff_size = 0; - goto bail; - } - recvfrom_buff_size = len; - } - - // receive all messages - while (self->is_started && self->is_active && read < len && ret == 0) { - if ((ret = tnet_sockfd_recvfrom(fd, recvfrom_buff_ptr, recvfrom_buff_size, 0, (struct sockaddr *)&remote_addr)) < 0) { + + // load fds for both rtp and rtcp sockets / create TURN permissions + tsk_list_lock(self->candidates_pairs); + tsk_list_foreach(item, self->candidates_pairs){ + if (!(pair = item->data) || !pair->candidate_offer || !pair->candidate_offer->socket){ + continue; + } + + if ((fds_count < sizeof(fds) / sizeof(fds[0])) && pair->candidate_offer->socket) { + if (pair->candidate_offer->turn.ss && (ret = tnet_turn_session_get_state_createperm(pair->candidate_offer->turn.ss, pair->turn_peer_id, &e_state)) == 0) { + if (e_state == tnet_stun_state_none) { + ret = tnet_turn_session_createpermission(((tnet_ice_pair_t *)pair)->candidate_offer->turn.ss, pair->candidate_answer->connection_addr, pair->candidate_answer->port, &((tnet_ice_pair_t *)pair)->turn_peer_id); + if (ret) { + continue; + // goto bail; + } + } + fds_turn[fds_turn_count++] = pair->candidate_offer->socket->fd; + // When TURN is active the socket (host) is pulled in the TURN session and any incoming data will be forwarded to us. + // Do not add fd to the set + continue; + } + _FD_ISSET(fds, fds_count, pair->candidate_offer->socket->fd, &isset); // not in the set -> to avoid doubloon + if (!isset) { + _FD_ISSET(fds_turn, fds_turn_count, pair->candidate_offer->socket->fd, &isset); // not already managed by a TURN session + if (!isset) { + fds[fds_count++] = pair->candidate_offer->socket->fd; + if (pair->candidate_offer->socket->fd > fd_max) { + fd_max = pair->candidate_offer->socket->fd; + } + } + } + } + } + tsk_list_unlock(self->candidates_pairs); + + concheck_timeout = self->concheck_timeout; + time_start = time_curr = tsk_time_now(); + time_end = (time_start + concheck_timeout); + tries_count_min = fds_turn_count > 0 ? kIceConnCheckMinTriesMax : kIceConnCheckMinTriesMin; + + while (self->is_started && self->is_active && (time_curr < time_end) && !self->have_nominated_symetric) { + tv.tv_sec = 0; + tv.tv_usec = (rto * 1000); + + FD_ZERO(&set); + for (k = 0; k < fds_count; ++k) { + FD_SET(fds[k], &set); + } + + // set new current time here to avoid "continue" skips + // ignore already ellapsed time if new timeout value is defined + time_curr = tsk_time_now(); + if (self->concheck_timeout != concheck_timeout) { + concheck_timeout = self->concheck_timeout; + time_start = time_curr; + time_end = (time_start + concheck_timeout); + } + + // Send ConnCheck requests + // the pairs are already sorted by priority (from high to low) + if (!self->have_nominated_symetric) { + tsk_list_foreach(item, self->candidates_pairs) { + if (!(pair = item->data) || !pair->candidate_offer || !pair->candidate_offer->socket) { + continue; + } + switch (pair->state_offer) { + case tnet_ice_pair_state_failed: + case tnet_ice_pair_state_succeed: + continue; + default: break; + } + + ret = tnet_ice_pair_send_conncheck((tnet_ice_pair_t *)pair); + } + } + + if (fds_count == 0) { + tsk_thread_sleep(10); + goto check_nomination; + } + + if ((ret = select(fd_max + 1, &set, NULL, NULL, &tv)) < 0) { + TNET_PRINT_LAST_ERROR("select() failed"); + goto bail; + } + else if (ret == 0) { + // timeout + // TSK_DEBUG_INFO("STUN request timedout"); + goto check_nomination; //!\ continue == possible endless loop + } + else if (ret > 0) { + // there is data to read + for (k = 0; k < fds_count; ++k) { + tnet_fd_t fd = fds[k]; + unsigned int len = 0; + tsk_size_t read = 0; + + if (!FD_ISSET(fd, &set)) { + continue; + } + + // Check how many bytes are pending + if ((ret = tnet_ioctlt(fd, FIONREAD, &len)) < 0) { + continue; + } + + if (len == 0){ + // TSK_DEBUG_INFO("tnet_ioctlt() returent zero bytes"); + continue; + } + + // Receive pending data + if (recvfrom_buff_size < len){ + if (!(recvfrom_buff_ptr = tsk_realloc(recvfrom_buff_ptr, len))){ + recvfrom_buff_size = 0; + goto bail; + } + recvfrom_buff_size = len; + } + + // receive all messages + while (self->is_started && self->is_active && read < len && ret == 0) { + if ((ret = tnet_sockfd_recvfrom(fd, recvfrom_buff_ptr, recvfrom_buff_size, 0, (struct sockaddr *)&remote_addr)) < 0) { err = tnet_geterrno(); - /* "EAGAIN" means no data to read. We must trust "EAGAIN" instead of "read" because pending data could be removed by the system + /* "EAGAIN" means no data to read. We must trust "EAGAIN" instead of "read" because pending data could be removed by the system */ /* "WSAECONNRESET" The virtual circuit was reset by the remote side executing a hard or abortive close. The application should close the socket as it is no longer usable. On a UDP-datagram socket, this error would indicate that a previous send operation resulted in an ICMP "Port Unreachable" message. */ - if (err == TNET_ERROR_EAGAIN || err == TNET_ERROR_CONNRESET) { + if (err == TNET_ERROR_EAGAIN || err == TNET_ERROR_CONNRESET) { // TODO: remove "fd" from the list if "E_CONNRESET" - len = 0; - continue; - } - - TNET_PRINT_LAST_ERROR("Receiving STUN dgrams failed with errno=%d", err); - goto bail; - } - - read += ret; - - // recv() STUN message (request / response) - ret = tnet_ice_ctx_recv_stun_message(self, recvfrom_buff_ptr, (tsk_size_t)ret, fd, &remote_addr, &role_conflict); - if (ret == 0 && role_conflict) { - // A change in roles will require to recompute pair priorities - restart_conneck = tsk_true; - // do not break the loop -> read/process all pending STUN messages - } - } - } - } - - check_nomination: - // check whether we need to re-start connection checking - if (restart_conneck) { - goto start_conneck; - } - - check_rtcp = (self->use_rtcp && !self->use_rtcpmux); - if (!self->have_nominated_offer) { - self->have_nominated_offer = tnet_ice_pairs_have_nominated_offer(self->candidates_pairs, check_rtcp); - } - if (!self->have_nominated_answer) { - self->have_nominated_answer = tnet_ice_pairs_have_nominated_answer(self->candidates_pairs, check_rtcp); - } - if (self->have_nominated_offer && self->have_nominated_answer) { - self->have_nominated_symetric = tnet_ice_pairs_have_nominated_symetric_2(self->candidates_pairs, check_rtcp, &got_hosts); - self->have_nominated_symetric &= (got_hosts || ((tries_count++) >= tries_count_min)); - } - } // while (self->is_started... - - // "ret" could be "<>0" if last function used was "select()", "recvfrom()", "ioctlt()"...this is why we set the value to #0. - // if there was an error then, we'll jump to "bail:" and next code is skipped - ret = 0; - + len = 0; + continue; + } + + TNET_PRINT_LAST_ERROR("Receiving STUN dgrams failed with errno=%d", err); + goto bail; + } + + read += ret; + + // recv() STUN message (request / response) + ret = tnet_ice_ctx_recv_stun_message(self, recvfrom_buff_ptr, (tsk_size_t)ret, fd, &remote_addr, &role_conflict); + if (ret == 0 && role_conflict) { + // A change in roles will require to recompute pair priorities + restart_conneck = tsk_true; + // do not break the loop -> read/process all pending STUN messages + } + } + } + } + + check_nomination: + // check whether we need to re-start connection checking + if (restart_conneck) { + goto start_conneck; + } + + check_rtcp = (self->use_rtcp && !self->use_rtcpmux); + if (!self->have_nominated_offer) { + self->have_nominated_offer = tnet_ice_pairs_have_nominated_offer(self->candidates_pairs, check_rtcp); + } + if (!self->have_nominated_answer) { + self->have_nominated_answer = tnet_ice_pairs_have_nominated_answer(self->candidates_pairs, check_rtcp); + } + if (self->have_nominated_offer && self->have_nominated_answer) { + self->have_nominated_symetric = tnet_ice_pairs_have_nominated_symetric_2(self->candidates_pairs, check_rtcp, &got_hosts); + self->have_nominated_symetric &= (got_hosts || ((tries_count++) >= tries_count_min)); + } + } // while (self->is_started... + + // "ret" could be "<>0" if last function used was "select()", "recvfrom()", "ioctlt()"...this is why we set the value to #0. + // if there was an error then, we'll jump to "bail:" and next code is skipped + ret = 0; + bail: - // move to the next state depending on the conncheck result - if (self->is_started) { - if (ret == 0 && self->have_nominated_symetric) { - ret = _tnet_ice_ctx_fsm_act(self, _fsm_action_Success); - } - else { - if (time_curr >= time_end) { - TSK_DEBUG_ERROR("ConnCheck timedout, have_nominated_symetric=%s, have_nominated_answer=%s, have_nominated_offer=%s", - self->have_nominated_symetric ? "yes" : "false", - self->have_nominated_answer ? "yes" : "false", - self->have_nominated_offer ? "yes" : "false"); - } - ret = _tnet_ice_ctx_fsm_act(self, _fsm_action_Failure); - } - } - - TSK_FREE(recvfrom_buff_ptr); - - self->is_connchecking = tsk_false; - - return ret; + // move to the next state depending on the conncheck result + if (self->is_started) { + if (ret == 0 && self->have_nominated_symetric) { + ret = _tnet_ice_ctx_fsm_act(self, _fsm_action_Success); + } + else { + if (time_curr >= time_end) { + TSK_DEBUG_ERROR("ConnCheck timedout, have_nominated_symetric=%s, have_nominated_answer=%s, have_nominated_offer=%s", + self->have_nominated_symetric ? "yes" : "false", + self->have_nominated_answer ? "yes" : "false", + self->have_nominated_offer ? "yes" : "false"); + } + ret = _tnet_ice_ctx_fsm_act(self, _fsm_action_Failure); + } + } + + TSK_FREE(recvfrom_buff_ptr); + + self->is_connchecking = tsk_false; + + return ret; } // ConnChecking -> (Success) -> ConnCheckingCompleted static int _tnet_ice_ctx_fsm_ConnChecking_2_ConnCheckingCompleted_X_Success(va_list *app) { - tnet_ice_ctx_t* self = va_arg(*app, tnet_ice_ctx_t *); - const tnet_ice_pair_t *pair_offer, *pair_answer_src, *pair_answer_dest; - const tsk_list_item_t *item; - const tnet_ice_pair_t *pair; - const tnet_ice_candidate_t *candidate; - tsk_list_t* sessions = tsk_list_create(); // for lock-free TURN sessions destroying - int ret; - - // When destroying TURN sessions the transport is locked by shutdown() - // This function locks "self->candidates_pairs" - // TURN callback locks "self->candidates_pairs" - // TURN callback locks the transport - // => We must not lock the candidates when destroying the TURN session - // Test with WES8 if you want to reproduce the issue - - TSK_OBJECT_SAFE_FREE(self->turn.ss_nominated_rtp); - TSK_OBJECT_SAFE_FREE(self->turn.ss_nominated_rtcp); - - tsk_list_lock(self->candidates_pairs); - - // take a reference to the negotiated TURN sessions - ret = tnet_ice_pairs_get_nominated_symetric_pairs(self->candidates_pairs, TNET_ICE_CANDIDATE_COMPID_RTP, &pair_offer, &pair_answer_src, &pair_answer_dest); - if (ret == 0) { - if (pair_offer && pair_offer->candidate_offer && pair_offer->candidate_offer->type_e == tnet_ice_cand_type_relay && pair_offer->candidate_offer->turn.ss) { - self->turn.ss_nominated_rtp = tsk_object_ref(pair_offer->candidate_offer->turn.ss); - self->turn.peer_id_rtp = pair_offer->turn_peer_id; - TSK_DEBUG_INFO("ICE: nominated TURN peer id [RTP] = %ld", self->turn.peer_id_rtp); - } - TSK_DEBUG_INFO("ICE: nominated symetric RTP pairs: offer:%llu, answer-src:%llu, answser-dest:%llu", - pair_offer ? pair_offer->id : 0, pair_answer_src ? pair_answer_src->id : 0, pair_answer_dest ? pair_answer_dest->id : 0); - } - if (ret == 0 && pair_offer) { ((tnet_ice_pair_t *)pair_offer)->is_nominated = tsk_true; } // "is_nominated" is used do decide whether to include "USE-CANDIDATE" attribute when aggressive mode is disabled - - ret = tnet_ice_pairs_get_nominated_symetric_pairs(self->candidates_pairs, TNET_ICE_CANDIDATE_COMPID_RTCP, &pair_offer, &pair_answer_src, &pair_answer_dest); - if (ret == 0) { - if (pair_offer && pair_offer->candidate_offer && pair_offer->candidate_offer->type_e == tnet_ice_cand_type_relay && pair_offer->candidate_offer->turn.ss) { - self->turn.ss_nominated_rtcp = tsk_object_ref(pair_offer->candidate_offer->turn.ss); - self->turn.peer_id_rtcp = pair_offer->turn_peer_id; - TSK_DEBUG_INFO("ICE: nominated TURN peer id [RTCP] = %ld", self->turn.peer_id_rtp); - } - TSK_DEBUG_INFO("ICE: nominated symetric RTCP(use:%d, mux:%d) pairs: offer:%llu, answer-src:%llu, answser-dest:%llu", - self->use_rtcp ? 1 : 0, self->use_rtcpmux ? 1 : 0, - pair_offer ? pair_offer->id : 0, pair_answer_src ? pair_answer_src->id : 0, pair_answer_dest ? pair_answer_dest->id : 0); - } - if (ret == 0 && pair_offer) { ((tnet_ice_pair_t *)pair_offer)->is_nominated = tsk_true; } // "is_nominated" is used do decide whether to include "USE-CANDIDATE" attribute when aggressive mode is disabled - - // collect all useless TURN sessions (pairs) - tsk_list_foreach(item, self->candidates_pairs) { - if (!(pair = item->data) || !pair->candidate_offer || !pair->candidate_offer->turn.ss) { - continue; - } - if (pair->candidate_offer->turn.ss != self->turn.ss_nominated_rtp && pair->candidate_offer->turn.ss != self->turn.ss_nominated_rtcp) { - tsk_list_push_back_data(sessions, (void**)&pair->candidate_offer->turn.ss); - TSK_OBJECT_SAFE_FREE(pair->candidate_offer->turn.ss); - } - } - - tsk_list_unlock(self->candidates_pairs); - - // collect all useless TURN sessions (local candidates) - tsk_list_lock(self->candidates_local); - tsk_list_foreach(item, self->candidates_local) { - if (!(candidate = item->data) || !candidate->turn.ss) { - continue; - } - if (candidate->turn.ss != self->turn.ss_nominated_rtp && candidate->turn.ss != self->turn.ss_nominated_rtcp) { - tsk_list_push_back_data(sessions, (void**)&candidate->turn.ss); - TSK_OBJECT_SAFE_FREE(((tnet_ice_candidate_t*)candidate)->turn.ss); - } - } - tsk_list_unlock(self->candidates_local); - - // collect all useless TURN sessions (remote candidates) - tsk_list_lock(self->candidates_remote); - tsk_list_foreach(item, self->candidates_remote) { - if (!(candidate = item->data) || !candidate->turn.ss) { - continue; - } - if (candidate->turn.ss != self->turn.ss_nominated_rtp && candidate->turn.ss != self->turn.ss_nominated_rtcp) { - tsk_list_push_back_data(sessions, (void**)&candidate->turn.ss); - TSK_OBJECT_SAFE_FREE(((tnet_ice_candidate_t*)candidate)->turn.ss); - } - } - tsk_list_unlock(self->candidates_remote); - - // lock-free destruction - TSK_OBJECT_SAFE_FREE(sessions); - - return _tnet_ice_ctx_signal_async(self, tnet_ice_event_type_conncheck_succeed, "ConnCheck succeed"); + tnet_ice_ctx_t* self = va_arg(*app, tnet_ice_ctx_t *); + const tnet_ice_pair_t *pair_offer, *pair_answer_src, *pair_answer_dest; + const tsk_list_item_t *item; + const tnet_ice_pair_t *pair; + const tnet_ice_candidate_t *candidate; + tsk_list_t* sessions = tsk_list_create(); // for lock-free TURN sessions destroying + int ret; + + // When destroying TURN sessions the transport is locked by shutdown() + // This function locks "self->candidates_pairs" + // TURN callback locks "self->candidates_pairs" + // TURN callback locks the transport + // => We must not lock the candidates when destroying the TURN session + // Test with WES8 if you want to reproduce the issue + + TSK_OBJECT_SAFE_FREE(self->turn.ss_nominated_rtp); + TSK_OBJECT_SAFE_FREE(self->turn.ss_nominated_rtcp); + + tsk_list_lock(self->candidates_pairs); + + // take a reference to the negotiated TURN sessions + ret = tnet_ice_pairs_get_nominated_symetric_pairs(self->candidates_pairs, TNET_ICE_CANDIDATE_COMPID_RTP, &pair_offer, &pair_answer_src, &pair_answer_dest); + if (ret == 0) { + if (pair_offer && pair_offer->candidate_offer && pair_offer->candidate_offer->type_e == tnet_ice_cand_type_relay && pair_offer->candidate_offer->turn.ss) { + self->turn.ss_nominated_rtp = tsk_object_ref(pair_offer->candidate_offer->turn.ss); + self->turn.peer_id_rtp = pair_offer->turn_peer_id; + TSK_DEBUG_INFO("ICE: nominated TURN peer id [RTP] = %ld", self->turn.peer_id_rtp); + } + TSK_DEBUG_INFO("ICE: nominated symetric RTP pairs: offer:%llu, answer-src:%llu, answser-dest:%llu", + pair_offer ? pair_offer->id : 0, pair_answer_src ? pair_answer_src->id : 0, pair_answer_dest ? pair_answer_dest->id : 0); + } + if (ret == 0 && pair_offer) { ((tnet_ice_pair_t *)pair_offer)->is_nominated = tsk_true; } // "is_nominated" is used do decide whether to include "USE-CANDIDATE" attribute when aggressive mode is disabled + + ret = tnet_ice_pairs_get_nominated_symetric_pairs(self->candidates_pairs, TNET_ICE_CANDIDATE_COMPID_RTCP, &pair_offer, &pair_answer_src, &pair_answer_dest); + if (ret == 0) { + if (pair_offer && pair_offer->candidate_offer && pair_offer->candidate_offer->type_e == tnet_ice_cand_type_relay && pair_offer->candidate_offer->turn.ss) { + self->turn.ss_nominated_rtcp = tsk_object_ref(pair_offer->candidate_offer->turn.ss); + self->turn.peer_id_rtcp = pair_offer->turn_peer_id; + TSK_DEBUG_INFO("ICE: nominated TURN peer id [RTCP] = %ld", self->turn.peer_id_rtp); + } + TSK_DEBUG_INFO("ICE: nominated symetric RTCP(use:%d, mux:%d) pairs: offer:%llu, answer-src:%llu, answser-dest:%llu", + self->use_rtcp ? 1 : 0, self->use_rtcpmux ? 1 : 0, + pair_offer ? pair_offer->id : 0, pair_answer_src ? pair_answer_src->id : 0, pair_answer_dest ? pair_answer_dest->id : 0); + } + if (ret == 0 && pair_offer) { ((tnet_ice_pair_t *)pair_offer)->is_nominated = tsk_true; } // "is_nominated" is used do decide whether to include "USE-CANDIDATE" attribute when aggressive mode is disabled + + // collect all useless TURN sessions (pairs) + tsk_list_foreach(item, self->candidates_pairs) { + if (!(pair = item->data) || !pair->candidate_offer || !pair->candidate_offer->turn.ss) { + continue; + } + if (pair->candidate_offer->turn.ss != self->turn.ss_nominated_rtp && pair->candidate_offer->turn.ss != self->turn.ss_nominated_rtcp) { + tsk_list_push_back_data(sessions, (void**)&pair->candidate_offer->turn.ss); + TSK_OBJECT_SAFE_FREE(pair->candidate_offer->turn.ss); + } + } + + tsk_list_unlock(self->candidates_pairs); + + // collect all useless TURN sessions (local candidates) + tsk_list_lock(self->candidates_local); + tsk_list_foreach(item, self->candidates_local) { + if (!(candidate = item->data) || !candidate->turn.ss) { + continue; + } + if (candidate->turn.ss != self->turn.ss_nominated_rtp && candidate->turn.ss != self->turn.ss_nominated_rtcp) { + tsk_list_push_back_data(sessions, (void**)&candidate->turn.ss); + TSK_OBJECT_SAFE_FREE(((tnet_ice_candidate_t*)candidate)->turn.ss); + } + } + tsk_list_unlock(self->candidates_local); + + // collect all useless TURN sessions (remote candidates) + tsk_list_lock(self->candidates_remote); + tsk_list_foreach(item, self->candidates_remote) { + if (!(candidate = item->data) || !candidate->turn.ss) { + continue; + } + if (candidate->turn.ss != self->turn.ss_nominated_rtp && candidate->turn.ss != self->turn.ss_nominated_rtcp) { + tsk_list_push_back_data(sessions, (void**)&candidate->turn.ss); + TSK_OBJECT_SAFE_FREE(((tnet_ice_candidate_t*)candidate)->turn.ss); + } + } + tsk_list_unlock(self->candidates_remote); + + // lock-free destruction + TSK_OBJECT_SAFE_FREE(sessions); + + return _tnet_ice_ctx_signal_async(self, tnet_ice_event_type_conncheck_succeed, "ConnCheck succeed"); } // ConnChecking -> (Failure) ->Terminated static int _tnet_ice_ctx_fsm_ConnChecking_2_Terminated_X_Failure(va_list *app) { - tnet_ice_ctx_t* self = va_arg(*app, tnet_ice_ctx_t *); - return _tnet_ice_ctx_signal_async(self, tnet_ice_event_type_conncheck_failed, "ConnCheck failed"); + tnet_ice_ctx_t* self = va_arg(*app, tnet_ice_ctx_t *); + return _tnet_ice_ctx_signal_async(self, tnet_ice_event_type_conncheck_failed, "ConnCheck failed"); } // Any (AnyNotStarted) -> Terminated static int _tnet_ice_ctx_fsm_Any_2_Terminated_X_AnyNotStarted(va_list *app) { - return 0; + return 0; } //++++++++++++++++++++++++++++++++++++++++++++++++++++++++ @@ -2126,682 +2139,682 @@ static int _tnet_ice_ctx_fsm_Any_2_Terminated_X_AnyNotStarted(va_list *app) static int _tnet_ice_ctx_fsm_OnTerminated(tnet_ice_ctx_t* self) { - TSK_DEBUG_INFO("=== ICE CTX SM Terminated ==="); - - if (!self){ - TSK_DEBUG_ERROR("Invalid parameter."); - return -1; - } - - // still started but no longer active - self->is_active = tsk_false; - - return 0; + TSK_DEBUG_INFO("=== ICE CTX SM Terminated ==="); + + if (!self){ + TSK_DEBUG_ERROR("Invalid parameter."); + return -1; + } + + // still started but no longer active + self->is_active = tsk_false; + + return 0; } static tsk_bool_t _tnet_ice_ctx_fsm_cond_NotStarted(tnet_ice_ctx_t* self, const void* _any) { - return (!self || !self->is_started); + return (!self || !self->is_started); } static int _tnet_ice_ctx_restart(tnet_ice_ctx_t* self) { - int ret = 0; - if (!self) { - TSK_DEBUG_ERROR("Invalid parameter"); - return -1; - } - - ret = tsk_fsm_set_current_state(self->fsm, _fsm_state_Started); - ret = _tnet_ice_ctx_fsm_act(self, _fsm_action_GatherHostCandidates); - - self->is_active = (ret == 0); - return ret; + int ret = 0; + if (!self) { + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + + ret = tsk_fsm_set_current_state(self->fsm, _fsm_state_Started); + ret = _tnet_ice_ctx_fsm_act(self, _fsm_action_GatherHostCandidates); + + self->is_active = (ret == 0); + return ret; } static int _tnet_ice_ctx_recv_stun_message_for_pair(tnet_ice_ctx_t* self, const tnet_ice_pair_t* pair, const void* data, tsk_size_t size, tnet_fd_t local_fd, const struct sockaddr_storage* remote_addr, tsk_bool_t *role_conflict) { - tnet_stun_pkt_t* message; - int ret = 0; - if (!self || !role_conflict || !data || !size || local_fd < 0 || !remote_addr) { - TSK_DEBUG_ERROR("Invalid parameter"); - return -1; - } - - *role_conflict = tsk_false; - - if (!TNET_STUN_BUFF_IS_STUN2(((uint8_t*)data), size)) { - if (self->rtp_callback) { - return self->rtp_callback(self->rtp_callback_data, data, size, local_fd, remote_addr); - } - TSK_DEBUG_INFO("Not STUN message"); - return 0; - } - - if (!self->is_active) { - TSK_DEBUG_INFO("ICE context not active yet"); - return 0; - } - - if ((ret = tnet_stun_pkt_read(data, size, &message)) == 0 && message) { - if (message->e_type == tnet_stun_pkt_type_binding_request) { - tsk_bool_t is_local_conncheck_started; - if (self->is_building_pairs) { - TSK_DEBUG_INFO("Incoming STUN binding request while building new ICE pairs... wait for %d milliseconds max", kIcePairsBuildingTimeMax); - tsk_condwait_timedwait(self->condwait_pairs, kIcePairsBuildingTimeMax); - if (self->is_building_pairs) { - TSK_DEBUG_WARN("%d milliseconds ellapsed and still building pairs", kIcePairsBuildingTimeMax); - } - if (!self->is_active) { - TSK_DEBUG_WARN("ICE context deactivated while waiting for ICE pairs to finish building"); - TSK_OBJECT_SAFE_FREE(message); - return 0; - } - } - is_local_conncheck_started = !TSK_LIST_IS_EMPTY(self->candidates_pairs); // if empty means local conncheck haven't started - if (!pair && is_local_conncheck_started) { - pair = tnet_ice_pairs_find_by_fd_and_addr(self->candidates_pairs, local_fd, remote_addr); - } - if (!pair && !self->have_nominated_symetric && is_local_conncheck_started){ // pair not found and we're still negotiating - // rfc 5245 - 7.1.3.2.1. Discovering Peer Reflexive Candidates - tnet_ice_pair_t* pair_peer = tnet_ice_pair_prflx_create(self->candidates_pairs, local_fd, remote_addr); - if (pair_peer) { - pair = pair_peer; // save memory address - tsk_list_push_descending_data(self->candidates_pairs, (void**)&pair_peer); - TSK_OBJECT_SAFE_FREE(pair_peer); - } - } - if (pair) { - short resp_code = 0; - char* resp_phrase = tsk_null; - // authenticate the request - tnet_ice_pair_auth_conncheck(pair, message, data, size, &resp_code, &resp_phrase); - if (resp_code > 0 && resp_phrase){ - if (resp_code >= 200 && resp_code <= 299){ - // Before sending the success response check that there are no role conflict - if (self->is_controlling){ // I'm ICE-CONTROLLING - const tnet_stun_attr_vdata_t* stun_att_ice_controlling; - if ((ret = tnet_stun_pkt_attr_find_first(message, tnet_stun_attr_type_ice_controlling, (const tnet_stun_attr_t**)&stun_att_ice_controlling)) == 0 && stun_att_ice_controlling){ - TSK_DEBUG_WARN("Role conflicts (SEND)"); - if (self->tie_breaker >= *((uint64_t*)stun_att_ice_controlling->p_data_ptr)) { - resp_code = kStunErrCodeIceConflict; - tsk_strupdate(&resp_phrase, "Role conflicts"); - } - else { - // switch to "controlled" role - self->is_controlling = tsk_false; - *role_conflict = tsk_true; - } - } - else; - } - else { // I'm ICE-CONTROLLED - const tnet_stun_attr_vdata_t* stun_att_ice_controlled; - if ((ret = tnet_stun_pkt_attr_find_first(message, tnet_stun_attr_type_ice_controlled, (const tnet_stun_attr_t**)&stun_att_ice_controlled)) == 0 && stun_att_ice_controlled) { - TSK_DEBUG_WARN("Role conflicts (SEND)"); - if (self->tie_breaker >= *((uint64_t*)stun_att_ice_controlled->p_data_ptr)) { - self->is_controlling = tsk_true; - *role_conflict = tsk_true; - } - else { - resp_code = kStunErrCodeIceConflict; - tsk_strupdate(&resp_phrase, "Role conflicts"); - } - } - } - } - ret = tnet_ice_pair_send_response((tnet_ice_pair_t *)pair, message, resp_code, resp_phrase, remote_addr); - // "keepalive": also send STUN-BINDING if we receive one in the nominated pair and conneck is finished - //!\ IMPORTANT: chrome requires this - //!\ We also need to continue sending connection checks as we don't really know if the remote party has finished checking - if ((self->is_ice_jingle || pair->is_nominated) && self->have_nominated_symetric) { - ret = tnet_ice_pair_send_conncheck((tnet_ice_pair_t *)pair); // "keepalive" - } - } - TSK_FREE(resp_phrase); - } - else { // if(pair == null) - if (!is_local_conncheck_started) { - TSK_DEBUG_INFO("ICE local conncheck haven't started yet"); - } - else { - TSK_DEBUG_ERROR("Cannot find ICE pair with local fd = %d", local_fd); - } - } - } - else if (TNET_STUN_PKT_IS_RESP(message)) { - if (pair || (pair = tnet_ice_pairs_find_by_response(self->candidates_pairs, message))) { - ret = tnet_ice_pair_recv_response(((tnet_ice_pair_t*)pair), message, self->is_connchecking); + tnet_stun_pkt_t* message; + int ret = 0; + if (!self || !role_conflict || !data || !size || local_fd < 0 || !remote_addr) { + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + + *role_conflict = tsk_false; + + if (!TNET_STUN_BUFF_IS_STUN2(((uint8_t*)data), size)) { + if (self->rtp_callback) { + return self->rtp_callback(self->rtp_callback_data, data, size, local_fd, remote_addr); + } + TSK_DEBUG_INFO("Not STUN message"); + return 0; + } + + if (!self->is_active) { + TSK_DEBUG_INFO("ICE context not active yet"); + return 0; + } + + if ((ret = tnet_stun_pkt_read(data, size, &message)) == 0 && message) { + if (message->e_type == tnet_stun_pkt_type_binding_request) { + tsk_bool_t is_local_conncheck_started; + if (self->is_building_pairs) { + TSK_DEBUG_INFO("Incoming STUN binding request while building new ICE pairs... wait for %d milliseconds max", kIcePairsBuildingTimeMax); + tsk_condwait_timedwait(self->condwait_pairs, kIcePairsBuildingTimeMax); + if (self->is_building_pairs) { + TSK_DEBUG_WARN("%d milliseconds ellapsed and still building pairs", kIcePairsBuildingTimeMax); + } + if (!self->is_active) { + TSK_DEBUG_WARN("ICE context deactivated while waiting for ICE pairs to finish building"); + TSK_OBJECT_SAFE_FREE(message); + return 0; + } + } + is_local_conncheck_started = !TSK_LIST_IS_EMPTY(self->candidates_pairs); // if empty means local conncheck haven't started + if (!pair && is_local_conncheck_started) { + pair = tnet_ice_pairs_find_by_fd_and_addr(self->candidates_pairs, local_fd, remote_addr); + } + if (!pair && !self->have_nominated_symetric && is_local_conncheck_started){ // pair not found and we're still negotiating + // rfc 5245 - 7.1.3.2.1. Discovering Peer Reflexive Candidates + tnet_ice_pair_t* pair_peer = tnet_ice_pair_prflx_create(self->candidates_pairs, local_fd, remote_addr); + if (pair_peer) { + pair = pair_peer; // save memory address + tsk_list_push_descending_data(self->candidates_pairs, (void**)&pair_peer); + TSK_OBJECT_SAFE_FREE(pair_peer); + } + } + if (pair) { + short resp_code = 0; + char* resp_phrase = tsk_null; + // authenticate the request + tnet_ice_pair_auth_conncheck(pair, message, data, size, &resp_code, &resp_phrase); + if (resp_code > 0 && resp_phrase){ + if (resp_code >= 200 && resp_code <= 299){ + // Before sending the success response check that there are no role conflict + if (self->is_controlling){ // I'm ICE-CONTROLLING + const tnet_stun_attr_vdata_t* stun_att_ice_controlling; + if ((ret = tnet_stun_pkt_attr_find_first(message, tnet_stun_attr_type_ice_controlling, (const tnet_stun_attr_t**)&stun_att_ice_controlling)) == 0 && stun_att_ice_controlling){ + TSK_DEBUG_WARN("Role conflicts (SEND)"); + if (self->tie_breaker >= *((uint64_t*)stun_att_ice_controlling->p_data_ptr)) { + resp_code = kStunErrCodeIceConflict; + tsk_strupdate(&resp_phrase, "Role conflicts"); + } + else { + // switch to "controlled" role + self->is_controlling = tsk_false; + *role_conflict = tsk_true; + } + } + else; + } + else { // I'm ICE-CONTROLLED + const tnet_stun_attr_vdata_t* stun_att_ice_controlled; + if ((ret = tnet_stun_pkt_attr_find_first(message, tnet_stun_attr_type_ice_controlled, (const tnet_stun_attr_t**)&stun_att_ice_controlled)) == 0 && stun_att_ice_controlled) { + TSK_DEBUG_WARN("Role conflicts (SEND)"); + if (self->tie_breaker >= *((uint64_t*)stun_att_ice_controlled->p_data_ptr)) { + self->is_controlling = tsk_true; + *role_conflict = tsk_true; + } + else { + resp_code = kStunErrCodeIceConflict; + tsk_strupdate(&resp_phrase, "Role conflicts"); + } + } + } + } + ret = tnet_ice_pair_send_response((tnet_ice_pair_t *)pair, message, resp_code, resp_phrase, remote_addr); + // "keepalive": also send STUN-BINDING if we receive one in the nominated pair and conneck is finished + //!\ IMPORTANT: chrome requires this + //!\ We also need to continue sending connection checks as we don't really know if the remote party has finished checking + if ((self->is_ice_jingle || pair->is_nominated) && self->have_nominated_symetric) { + ret = tnet_ice_pair_send_conncheck((tnet_ice_pair_t *)pair); // "keepalive" + } + } + TSK_FREE(resp_phrase); + } + else { // if(pair == null) + if (!is_local_conncheck_started) { + TSK_DEBUG_INFO("ICE local conncheck haven't started yet"); + } + else { + TSK_DEBUG_ERROR("Cannot find ICE pair with local fd = %d", local_fd); + } + } + } + else if (TNET_STUN_PKT_IS_RESP(message)) { + if (pair || (pair = tnet_ice_pairs_find_by_response(self->candidates_pairs, message))) { + ret = tnet_ice_pair_recv_response(((tnet_ice_pair_t*)pair), message, self->is_connchecking); #if 0 - if (TNET_STUN_PKT_RESP_IS_ERROR(message)) { - uint16_t u_code; - if ((ret = tnet_stun_pkt_get_errorcode(message, &u_code)) == 0 && u_code == kStunErrCodeIceConflict) { - // If this code is called this means that we have lower tie-breaker and we must toggle our role - TSK_DEBUG_WARN("Role conflicts (RECV)"); - self->is_controlling = !self->is_controlling; - *role_conflict = tsk_true; - } - } + if (TNET_STUN_PKT_RESP_IS_ERROR(message)) { + uint16_t u_code; + if ((ret = tnet_stun_pkt_get_errorcode(message, &u_code)) == 0 && u_code == kStunErrCodeIceConflict) { + // If this code is called this means that we have lower tie-breaker and we must toggle our role + TSK_DEBUG_WARN("Role conflicts (RECV)"); + self->is_controlling = !self->is_controlling; + *role_conflict = tsk_true; + } + } #endif - } - } - } - TSK_OBJECT_SAFE_FREE(message); - - return ret; + } + } + } + TSK_OBJECT_SAFE_FREE(message); + + return ret; } static int _tnet_ice_ctx_send_turn_raw(struct tnet_ice_ctx_s* self, struct tnet_turn_session_s* turn_ss, tnet_turn_peer_id_t turn_peer_id, const void* data, tsk_size_t size) { - if (!self || !turn_ss || !data || !size){ - TSK_DEBUG_ERROR("Invalid parameter"); - return -1; - } - // (self); - return tnet_turn_session_send_data(turn_ss, turn_peer_id, data, (uint16_t)size); + if (!self || !turn_ss || !data || !size){ + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + // (self); + return tnet_turn_session_send_data(turn_ss, turn_peer_id, data, (uint16_t)size); } // build pairs as per RFC 5245 section "5.7.1. Forming Candidate Pairs" static int _tnet_ice_ctx_build_pairs(struct tnet_ice_ctx_s* self, tnet_ice_candidates_L_t* local_candidates, tnet_ice_candidates_L_t* remote_candidates, tnet_ice_pairs_L_t* result_pairs, tsk_bool_t is_controlling, uint64_t tie_breaker, tsk_bool_t is_ice_jingle, tsk_bool_t is_rtcpmuxed) { - const tsk_list_item_t *item_local, *item_remote; - const tnet_ice_candidate_t *cand_local, *cand_remote; - tnet_ice_pair_t *pair; - enum tnet_turn_transport_e e_req_transport; - tnet_family_t addr_family_local, addr_family_remote; - - if (!self || TSK_LIST_IS_EMPTY(local_candidates) || TSK_LIST_IS_EMPTY(remote_candidates) || !result_pairs) { - TSK_DEBUG_ERROR("Invalid parameter"); - return -1; - } - - self->is_building_pairs = tsk_true; - TSK_DEBUG_INFO("ICE: begin building pairs"); - - tsk_list_clear_items(result_pairs); - - tsk_list_lock(local_candidates); - tsk_list_lock(remote_candidates); - tsk_list_lock(result_pairs); - - tsk_list_foreach(item_local, local_candidates) { - if (!(cand_local = item_local->data)) { - continue; - } - if (is_rtcpmuxed && cand_local->comp_id == TNET_ICE_CANDIDATE_COMPID_RTCP) { - continue; - } + const tsk_list_item_t *item_local, *item_remote; + const tnet_ice_candidate_t *cand_local, *cand_remote; + tnet_ice_pair_t *pair; + enum tnet_turn_transport_e e_req_transport; + tnet_family_t addr_family_local, addr_family_remote; + + if (!self || TSK_LIST_IS_EMPTY(local_candidates) || TSK_LIST_IS_EMPTY(remote_candidates) || !result_pairs) { + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + + self->is_building_pairs = tsk_true; + TSK_DEBUG_INFO("ICE: begin building pairs(is_rtcpmuxed=%d)", is_rtcpmuxed); + + tsk_list_clear_items(result_pairs); + + tsk_list_lock(local_candidates); + tsk_list_lock(remote_candidates); + tsk_list_lock(result_pairs); + + tsk_list_foreach(item_local, local_candidates) { + if (!(cand_local = item_local->data)) { + continue; + } + if (is_rtcpmuxed && cand_local->comp_id == TNET_ICE_CANDIDATE_COMPID_RTCP) { + continue; + } #if 0 // TURN:FORCE - if (cand_local->type_e != tnet_ice_cand_type_relay) { - continue; - } + if (cand_local->type_e != tnet_ice_cand_type_relay) { + continue; + } #endif - - tsk_list_foreach(item_remote, remote_candidates) { - if (!(cand_remote = item_remote->data)) { - continue; - } - // Hack for Chrome bug (candidate with port=zero) to avoid printing errors. - if (cand_remote->port == 0) { - TSK_DEBUG_INFO("Skipping remote ICE candidate with port = 0"); - continue; - } - - // CompIds(1=RTP, 2=RTCP) must match - if ((cand_remote->comp_id != cand_local->comp_id)){ - continue; - } - // IP versions must match. Cannot use IPv4 socket to send/recv to IPv6 address. - if (cand_local->socket) { - addr_family_local = TNET_SOCKET_TYPE_IS_IPV4(cand_local->socket->type) ? AF_INET : AF_INET6; - addr_family_remote = tnet_get_family(cand_remote->connection_addr, cand_remote->port); - if (addr_family_local != addr_family_remote) { - TSK_DEBUG_INFO("Address family mismatch:%d<->%d", addr_family_local, addr_family_remote); - continue; - } - } - if (cand_local->turn.ss) { - if (tnet_turn_session_get_req_transport(cand_local->turn.ss, &e_req_transport) != 0) { - continue; - } - if (e_req_transport == tnet_turn_transport_udp && !TNET_SOCKET_TYPE_IS_DGRAM(cand_remote->transport_e)) { - continue; - } - if (e_req_transport == tnet_turn_transport_tcp && !TNET_SOCKET_TYPE_IS_STREAM(cand_remote->transport_e)) { - continue; - } - } - else { - if (cand_remote->transport_e != cand_local->transport_e) { - continue; - } - } - - if ((pair = tnet_ice_pair_create(cand_local, cand_remote, is_controlling, tie_breaker, is_ice_jingle))) { - TSK_DEBUG_INFO("ICE Pair(%llu, %llu): [%s %u %u %s %d] -> [%s %u %u %s %d]", - pair->id, - pair->priority, - - cand_local->foundation, - cand_local->priority, - cand_local->comp_id, - cand_local->connection_addr, - cand_local->port, - - cand_remote->foundation, - cand_remote->priority, - cand_remote->comp_id, - cand_remote->connection_addr, - cand_remote->port); - tsk_list_push_descending_data(result_pairs, (void**)&pair); - } - } - } + + tsk_list_foreach(item_remote, remote_candidates) { + if (!(cand_remote = item_remote->data)) { + continue; + } + // Hack for Chrome bug (candidate with port=zero) to avoid printing errors. + if (cand_remote->port == 0) { + TSK_DEBUG_INFO("Skipping remote ICE candidate with port = 0"); + continue; + } + + // CompIds(1=RTP, 2=RTCP) must match + if ((cand_remote->comp_id != cand_local->comp_id)){ + continue; + } + // IP versions must match. Cannot use IPv4 socket to send/recv to IPv6 address. + if (cand_local->socket) { + addr_family_local = TNET_SOCKET_TYPE_IS_IPV4(cand_local->socket->type) ? AF_INET : AF_INET6; + addr_family_remote = tnet_get_family(cand_remote->connection_addr, cand_remote->port); + if (addr_family_local != addr_family_remote) { + TSK_DEBUG_INFO("Address family mismatch:%d<->%d", addr_family_local, addr_family_remote); + continue; + } + } + if (cand_local->turn.ss) { + if (tnet_turn_session_get_req_transport(cand_local->turn.ss, &e_req_transport) != 0) { + continue; + } + if (e_req_transport == tnet_turn_transport_udp && !TNET_SOCKET_TYPE_IS_DGRAM(cand_remote->transport_e)) { + continue; + } + if (e_req_transport == tnet_turn_transport_tcp && !TNET_SOCKET_TYPE_IS_STREAM(cand_remote->transport_e)) { + continue; + } + } + else { + if (cand_remote->transport_e != cand_local->transport_e) { + continue; + } + } + + if ((pair = tnet_ice_pair_create(cand_local, cand_remote, is_controlling, tie_breaker, is_ice_jingle))) { + TSK_DEBUG_INFO("ICE Pair(%llu, %llu): [%s %u %u %s %d] -> [%s %u %u %s %d]", + pair->id, + pair->priority, + + cand_local->foundation, + cand_local->priority, + cand_local->comp_id, + cand_local->connection_addr, + cand_local->port, + + cand_remote->foundation, + cand_remote->priority, + cand_remote->comp_id, + cand_remote->connection_addr, + cand_remote->port); + tsk_list_push_descending_data(result_pairs, (void**)&pair); + } + } + } #if 0 - tsk_list_foreach(item_local, result_pairs) { - if (!(pair = item_local->data)) { - continue; - } - - TSK_DEBUG_INFO("ICE Pair(%llu, %llu): [%s %u %s %d] -> [%s %u %s %d]", - pair->id, - pair->priority, - - pair->candidate_offer->foundation, - pair->candidate_offer->comp_id, - pair->candidate_offer->connection_addr, - pair->candidate_offer->port, - - pair->candidate_answer->foundation, - pair->candidate_answer->comp_id, - pair->candidate_answer->connection_addr, - pair->candidate_answer->port); - } + tsk_list_foreach(item_local, result_pairs) { + if (!(pair = item_local->data)) { + continue; + } + + TSK_DEBUG_INFO("ICE Pair(%llu, %llu): [%s %u %s %d] -> [%s %u %s %d]", + pair->id, + pair->priority, + + pair->candidate_offer->foundation, + pair->candidate_offer->comp_id, + pair->candidate_offer->connection_addr, + pair->candidate_offer->port, + + pair->candidate_answer->foundation, + pair->candidate_answer->comp_id, + pair->candidate_answer->connection_addr, + pair->candidate_answer->port); + } #endif - - tsk_list_unlock(local_candidates); - tsk_list_unlock(remote_candidates); - tsk_list_unlock(result_pairs); - - self->is_building_pairs = tsk_false; - tsk_condwait_broadcast(self->condwait_pairs); - TSK_DEBUG_INFO("ICE: end building pairs"); - - return 0; + + tsk_list_unlock(local_candidates); + tsk_list_unlock(remote_candidates); + tsk_list_unlock(result_pairs); + + self->is_building_pairs = tsk_false; + tsk_condwait_broadcast(self->condwait_pairs); + TSK_DEBUG_INFO("ICE: end building pairs"); + + return 0; } static int _tnet_ice_ctx_fsm_act(tnet_ice_ctx_t* self, tsk_fsm_action_id action_id) { - tnet_ice_action_t *action = tsk_null; - tnet_ice_event_t* e = tsk_null; - static const char* phrase = "$action$"; - int ret = 0; - - if (!self || !self->fsm){ - TSK_DEBUG_ERROR("Invalid parameter"); - return -1; - } - if (!(action = tnet_ice_action_create(action_id))){ - TSK_DEBUG_ERROR("Failed to create action"); - return -2; - } - - if (self->is_sync_mode) { - ret = tsk_fsm_act(self->fsm, action->id, self, action, self, action); - } - else { - if ((e = tnet_ice_event_create(self, tnet_ice_event_type_action, phrase, self->userdata))){ - tnet_ice_event_set_action(e, action); - TSK_RUNNABLE_ENQUEUE_OBJECT_SAFE(TSK_RUNNABLE(self), e); - goto bail; - } - else{ - TSK_DEBUG_ERROR("Failed to create ICE event"); - ret = -2; - goto bail; - } - } - + tnet_ice_action_t *action = tsk_null; + tnet_ice_event_t* e = tsk_null; + static const char* phrase = "$action$"; + int ret = 0; + + if (!self || !self->fsm){ + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + if (!(action = tnet_ice_action_create(action_id))){ + TSK_DEBUG_ERROR("Failed to create action"); + return -2; + } + + if (self->is_sync_mode) { + ret = tsk_fsm_act(self->fsm, action->id, self, action, self, action); + } + else { + if ((e = tnet_ice_event_create(self, tnet_ice_event_type_action, phrase, self->userdata))){ + tnet_ice_event_set_action(e, action); + TSK_RUNNABLE_ENQUEUE_OBJECT_SAFE(TSK_RUNNABLE(self), e); + goto bail; + } + else{ + TSK_DEBUG_ERROR("Failed to create ICE event"); + ret = -2; + goto bail; + } + } + bail: - TSK_OBJECT_SAFE_FREE(e); - TSK_OBJECT_SAFE_FREE(action); - return ret; + TSK_OBJECT_SAFE_FREE(e); + TSK_OBJECT_SAFE_FREE(action); + return ret; } static int _tnet_ice_ctx_signal_async(tnet_ice_ctx_t* self, tnet_ice_event_type_t type, const char* phrase) { - tnet_ice_event_t* e; - if (!self){ - TSK_DEBUG_ERROR("Invalid parameter"); - return -1; - } - - if (self->is_silent_mode && type != tnet_ice_event_type_action) { // silent mode ON and not action to move the FSM - TSK_DEBUG_INFO("ICE silent mode ON...to not notify '%d:%s'", type, phrase); - return 0; - } - - if ((e = tnet_ice_event_create(self, type, phrase, self->userdata))){ - TSK_RUNNABLE_ENQUEUE_OBJECT_SAFE(TSK_RUNNABLE(self), e); - return 0; - } - else{ - TSK_DEBUG_ERROR("Failed to create ICE event"); - return -2; - } + tnet_ice_event_t* e; + if (!self){ + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + + if (self->is_silent_mode && type != tnet_ice_event_type_action) { // silent mode ON and not action to move the FSM + TSK_DEBUG_INFO("ICE silent mode ON...to not notify '%d:%s'", type, phrase); + return 0; + } + + if ((e = tnet_ice_event_create(self, type, phrase, self->userdata))){ + TSK_RUNNABLE_ENQUEUE_OBJECT_SAFE(TSK_RUNNABLE(self), e); + return 0; + } + else{ + TSK_DEBUG_ERROR("Failed to create ICE event"); + return -2; + } } static int _tnet_ice_ctx_turn_callback(const struct tnet_turn_session_event_xs *e) { - tnet_ice_ctx_t *ctx = tsk_object_ref(TSK_OBJECT(e->pc_usr_data)); - struct tnet_turn_session_s* session = tsk_object_ref(TSK_OBJECT(e->pc_session)); - int ret = 0; - - if (!ctx) { - // the ICE context is being destroyed but TURN session not freed yet - goto bail; - } - - switch (e->e_type) { - case tnet_turn_session_event_type_alloc_ok: - case tnet_turn_session_event_type_refresh_ok: - case tnet_turn_session_event_type_chanbind_ok: - case tnet_turn_session_event_type_connect_ok: - default: - { - break; - } - - case tnet_turn_session_event_type_alloc_nok: - case tnet_turn_session_event_type_refresh_nok: - case tnet_turn_session_event_type_perm_nok: - case tnet_turn_session_event_type_chanbind_nok: - case tnet_turn_session_event_type_connect_nok: - { - // Do not raise error event if no nominated candidate because - // TURN error could be raised by the session when we're in "conncheck" state and this is a normal case. - if (ctx->is_active && ctx->is_started && ctx->turn.ss_nominated_rtp && ctx->turn.peer_id_rtp == e->u_peer_id) { - TSK_DEBUG_ERROR("TURN connection broken (peer-id=%ld)", e->u_peer_id); - if ((ret = _tnet_ice_ctx_signal_async(ctx, tnet_ice_event_type_turn_connection_broken, "TURN connection is broken"))) { - goto bail; - } - } - break; - } - - case tnet_turn_session_event_type_perm_ok: - { - enum tnet_turn_transport_e e_req_transport; - if ((ret = tnet_turn_session_get_req_transport(session, &e_req_transport))) { - goto bail; - } - - if (e_req_transport == tnet_turn_transport_tcp) { - // TCP-Connect: rfc6062 - 4.3. Initiating a Connection - if ((ret = tnet_turn_session_connect(session, e->u_peer_id))) { - goto bail; - } - } - else { - // Bind a channel (not required). If succeed, will be used to save bandwidth usage. - // TODO: should be done only if first "get_state(chanbind)==none". Not an issue, if it already exists then, will be refreshed. - if ((ret = tnet_turn_session_chanbind(session, e->u_peer_id))) { - goto bail; - } - } - break; - } - - case tnet_turn_session_event_type_recv_data: - { - tsk_bool_t role_conflict; - tnet_ice_pair_t* pair = tsk_null; - if (e->u_peer_id != kTurnPeerIdInvalid) { - const tsk_list_item_t *item; - tsk_list_lock(ctx->candidates_pairs); - tsk_list_foreach(item, ctx->candidates_pairs) { - if (((const tnet_ice_pair_t*)item->data)->turn_peer_id == e->u_peer_id) { - pair = tsk_object_ref((void*)item->data); - break; - } - } - tsk_list_unlock(ctx->candidates_pairs); - } - - ret = _tnet_ice_ctx_recv_stun_message_for_pair( - ctx, - pair, - e->data.pc_data_ptr, e->data.u_data_size, - e->pc_enet ? e->pc_enet->local_fd : TNET_INVALID_FD, - e->pc_enet ? &e->pc_enet->remote_addr : tsk_null, - &role_conflict); - TSK_OBJECT_SAFE_FREE(pair); - if (ret) { - goto bail; - } - - // rebuild candidates if role conflict - if (role_conflict) { - tsk_list_lock(ctx->candidates_pairs); - tsk_list_clear_items(ctx->candidates_pairs); - tsk_list_unlock(ctx->candidates_pairs); - - TSK_OBJECT_SAFE_FREE(ctx->turn.ss_nominated_rtp); - TSK_OBJECT_SAFE_FREE(ctx->turn.ss_nominated_rtcp); - - if ((ret = _tnet_ice_ctx_build_pairs(ctx, ctx->candidates_local, ctx->candidates_remote, ctx->candidates_pairs, ctx->is_controlling, ctx->tie_breaker, ctx->is_ice_jingle, ctx->use_rtcpmux))) { - TSK_DEBUG_ERROR("_tnet_ice_ctx_build_pairs() failed"); - goto bail; - } - } - - break; - } - } - - // alert() waiting threads - if ((ret = tsk_condwait_broadcast(ctx->turn.condwait))) { - goto bail; - } - + tnet_ice_ctx_t *ctx = tsk_object_ref(TSK_OBJECT(e->pc_usr_data)); + struct tnet_turn_session_s* session = tsk_object_ref(TSK_OBJECT(e->pc_session)); + int ret = 0; + + if (!ctx) { + // the ICE context is being destroyed but TURN session not freed yet + goto bail; + } + + switch (e->e_type) { + case tnet_turn_session_event_type_alloc_ok: + case tnet_turn_session_event_type_refresh_ok: + case tnet_turn_session_event_type_chanbind_ok: + case tnet_turn_session_event_type_connect_ok: + default: + { + break; + } + + case tnet_turn_session_event_type_alloc_nok: + case tnet_turn_session_event_type_refresh_nok: + case tnet_turn_session_event_type_perm_nok: + case tnet_turn_session_event_type_chanbind_nok: + case tnet_turn_session_event_type_connect_nok: + { + // Do not raise error event if no nominated candidate because + // TURN error could be raised by the session when we're in "conncheck" state and this is a normal case. + if (ctx->is_active && ctx->is_started && ctx->turn.ss_nominated_rtp && ctx->turn.peer_id_rtp == e->u_peer_id) { + TSK_DEBUG_ERROR("TURN connection broken (peer-id=%ld)", e->u_peer_id); + if ((ret = _tnet_ice_ctx_signal_async(ctx, tnet_ice_event_type_turn_connection_broken, "TURN connection is broken"))) { + goto bail; + } + } + break; + } + + case tnet_turn_session_event_type_perm_ok: + { + enum tnet_turn_transport_e e_req_transport; + if ((ret = tnet_turn_session_get_req_transport(session, &e_req_transport))) { + goto bail; + } + + if (e_req_transport == tnet_turn_transport_tcp) { + // TCP-Connect: rfc6062 - 4.3. Initiating a Connection + if ((ret = tnet_turn_session_connect(session, e->u_peer_id))) { + goto bail; + } + } + else { + // Bind a channel (not required). If succeed, will be used to save bandwidth usage. + // TODO: should be done only if first "get_state(chanbind)==none". Not an issue, if it already exists then, will be refreshed. + if ((ret = tnet_turn_session_chanbind(session, e->u_peer_id))) { + goto bail; + } + } + break; + } + + case tnet_turn_session_event_type_recv_data: + { + tsk_bool_t role_conflict; + tnet_ice_pair_t* pair = tsk_null; + if (e->u_peer_id != kTurnPeerIdInvalid) { + const tsk_list_item_t *item; + tsk_list_lock(ctx->candidates_pairs); + tsk_list_foreach(item, ctx->candidates_pairs) { + if (((const tnet_ice_pair_t*)item->data)->turn_peer_id == e->u_peer_id) { + pair = tsk_object_ref((void*)item->data); + break; + } + } + tsk_list_unlock(ctx->candidates_pairs); + } + + ret = _tnet_ice_ctx_recv_stun_message_for_pair( + ctx, + pair, + e->data.pc_data_ptr, e->data.u_data_size, + e->pc_enet ? e->pc_enet->local_fd : TNET_INVALID_FD, + e->pc_enet ? &e->pc_enet->remote_addr : tsk_null, + &role_conflict); + TSK_OBJECT_SAFE_FREE(pair); + if (ret) { + goto bail; + } + + // rebuild candidates if role conflict + if (role_conflict) { + tsk_list_lock(ctx->candidates_pairs); + tsk_list_clear_items(ctx->candidates_pairs); + tsk_list_unlock(ctx->candidates_pairs); + + TSK_OBJECT_SAFE_FREE(ctx->turn.ss_nominated_rtp); + TSK_OBJECT_SAFE_FREE(ctx->turn.ss_nominated_rtcp); + + if ((ret = _tnet_ice_ctx_build_pairs(ctx, ctx->candidates_local, ctx->candidates_remote, ctx->candidates_pairs, ctx->is_controlling, ctx->tie_breaker, ctx->is_ice_jingle, ctx->use_rtcpmux))) { + TSK_DEBUG_ERROR("_tnet_ice_ctx_build_pairs() failed"); + goto bail; + } + } + + break; + } + } + + // alert() waiting threads + if ((ret = tsk_condwait_broadcast(ctx->turn.condwait))) { + goto bail; + } + bail: - tsk_object_unref(ctx); - tsk_object_unref(session); - return ret; + tsk_object_unref(ctx); + tsk_object_unref(session); + return ret; } static void* TSK_STDCALL _tnet_ice_ctx_run(void* self) { - // No need to take ref(ctx) because this thread will be stopped by the dtor() before memory free. - tsk_list_item_t *curr; - tnet_ice_ctx_t *ctx = (tnet_ice_ctx_t *)(self); - tnet_ice_event_t *e; - - TSK_DEBUG_INFO("ICE CTX::run -- START"); - - TSK_RUNNABLE_RUN_BEGIN(ctx); - - // must because "ctx->callback(e);" could call a function trying to free "ctx" - // do not move before "TSK_RUNNABLE_RUN_BEGIN(ctx)", otherwise it'll be required to stop the "runnable" to have "ctx->refCount==0" - ctx = tsk_object_ref(ctx); - - if (ctx->is_started && (curr = TSK_RUNNABLE_POP_FIRST(ctx))) { - e = (tnet_ice_event_t*)curr->data; - switch (e->type) { - case tnet_ice_event_type_action: - { - if (e->action) { - tsk_fsm_act(ctx->fsm, e->action->id, ctx, e->action, ctx, e->action); - } - break; - } - default: - { - if (ctx->callback){ - ctx->callback(e); - } - break; - } - } - tsk_object_unref(curr); - } - - if (!(ctx = tsk_object_unref(ctx))) { - goto exit; - } - - TSK_RUNNABLE_RUN_END(ctx); - + // No need to take ref(ctx) because this thread will be stopped by the dtor() before memory free. + tsk_list_item_t *curr; + tnet_ice_ctx_t *ctx = (tnet_ice_ctx_t *)(self); + tnet_ice_event_t *e; + + TSK_DEBUG_INFO("ICE CTX::run -- START"); + + TSK_RUNNABLE_RUN_BEGIN(ctx); + + // must because "ctx->callback(e);" could call a function trying to free "ctx" + // do not move before "TSK_RUNNABLE_RUN_BEGIN(ctx)", otherwise it'll be required to stop the "runnable" to have "ctx->refCount==0" + ctx = tsk_object_ref(ctx); + + if (ctx->is_started && (curr = TSK_RUNNABLE_POP_FIRST(ctx))) { + e = (tnet_ice_event_t*)curr->data; + switch (e->type) { + case tnet_ice_event_type_action: + { + if (e->action) { + tsk_fsm_act(ctx->fsm, e->action->id, ctx, e->action, ctx, e->action); + } + break; + } + default: + { + if (ctx->callback){ + ctx->callback(e); + } + break; + } + } + tsk_object_unref(curr); + } + + if (!(ctx = tsk_object_unref(ctx))) { + goto exit; + } + + TSK_RUNNABLE_RUN_END(ctx); + exit: - if (ctx) { - tsk_list_clear_items(ctx->candidates_local); - tsk_list_clear_items(ctx->candidates_remote); - tsk_list_lock(ctx->candidates_pairs); // must - tsk_list_clear_items(ctx->candidates_pairs); - tsk_list_unlock(ctx->candidates_pairs); - } - - TSK_DEBUG_INFO("ICE CTX::run -- STOP"); - - return 0; + if (ctx) { + tsk_list_clear_items(ctx->candidates_local); + tsk_list_clear_items(ctx->candidates_remote); + tsk_list_lock(ctx->candidates_pairs); // must + tsk_list_clear_items(ctx->candidates_pairs); + tsk_list_unlock(ctx->candidates_pairs); + } + + TSK_DEBUG_INFO("ICE CTX::run -- STOP"); + + return 0; } static int _tnet_ice_ctx_servers_clear(struct tnet_ice_ctx_s* self) { - if (!self) { - TSK_DEBUG_ERROR("Invalid parameter"); - return -1; - } - tsk_list_lock(self->servers); - tsk_list_clear_items(self->servers); - tsk_list_unlock(self->servers); - return 0; + if (!self) { + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + tsk_list_lock(self->servers); + tsk_list_clear_items(self->servers); + tsk_list_unlock(self->servers); + return 0; } static int _tnet_ice_ctx_server_add(struct tnet_ice_ctx_s* self, enum tnet_ice_server_proto_e e_proto, -enum tnet_socket_type_e e_transport, - const char* str_server_addr, uint16_t u_server_port, - const char* str_software, - const char* str_username, const char* str_password) + enum tnet_socket_type_e e_transport, + const char* str_server_addr, uint16_t u_server_port, + const char* str_software, + const char* str_username, const char* str_password) { - struct tnet_ice_server_s* ice_server; - int ret = -1; - if (!self || !e_proto || !str_server_addr || !u_server_port) { - TSK_DEBUG_ERROR("Invalid parameter"); - return -1; - } - - // TURN requires credentials - if ((e_proto & tnet_ice_server_proto_turn) == tnet_ice_server_proto_turn && (tsk_strnullORempty(str_username) || tsk_strnullORempty(str_password))) { - /* rfc5766 - 4. General Behavior - The server MUST demand that all requests from the client - be authenticated using this mechanism, or that a equally strong or - stronger mechanism for client authentication is used.*/ - TSK_DEBUG_ERROR("TURN requires credentials"); - return -1; - } - // Create and add the ICE server - tsk_list_lock(self->servers); - if (_tnet_ice_ctx_server_exists(self, e_proto, e_transport, str_server_addr, u_server_port)) { - TSK_DEBUG_WARN("ICE server (proto=%d, transport=%d, addr=%s, port=%hu) already exists", e_proto, e_transport, str_server_addr, u_server_port); - ret = 0; // Not an error - goto bail; - } - if (!(ice_server = tnet_ice_server_create(e_proto, e_transport, str_server_addr, u_server_port, str_software, str_username, str_password))) { - TSK_DEBUG_ERROR("Failed to create ICE server(proto=%d, transport=%d, addr=%s, port=%hu)", e_proto, e_transport, str_server_addr, u_server_port); - goto bail; - } - tsk_list_push_back_data(self->servers, (void**)&ice_server); - TSK_OBJECT_SAFE_FREE(ice_server); - - ret = 0; + struct tnet_ice_server_s* ice_server; + int ret = -1; + if (!self || !e_proto || !str_server_addr || !u_server_port) { + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + + // TURN requires credentials + if ((e_proto & tnet_ice_server_proto_turn) == tnet_ice_server_proto_turn && (tsk_strnullORempty(str_username) || tsk_strnullORempty(str_password))) { + /* rfc5766 - 4. General Behavior + The server MUST demand that all requests from the client + be authenticated using this mechanism, or that a equally strong or + stronger mechanism for client authentication is used.*/ + TSK_DEBUG_ERROR("TURN requires credentials"); + return -1; + } + // Create and add the ICE server + tsk_list_lock(self->servers); + if (_tnet_ice_ctx_server_exists(self, e_proto, e_transport, str_server_addr, u_server_port)) { + TSK_DEBUG_WARN("ICE server (proto=%d, transport=%d, addr=%s, port=%hu) already exists", e_proto, e_transport, str_server_addr, u_server_port); + ret = 0; // Not an error + goto bail; + } + if (!(ice_server = tnet_ice_server_create(e_proto, e_transport, str_server_addr, u_server_port, str_software, str_username, str_password))) { + TSK_DEBUG_ERROR("Failed to create ICE server(proto=%d, transport=%d, addr=%s, port=%hu)", e_proto, e_transport, str_server_addr, u_server_port); + goto bail; + } + tsk_list_push_back_data(self->servers, (void**)&ice_server); + TSK_OBJECT_SAFE_FREE(ice_server); + + ret = 0; bail: - tsk_list_unlock(self->servers); - return ret; + tsk_list_unlock(self->servers); + return ret; } static int _tnet_ice_ctx_server_remove(struct tnet_ice_ctx_s* self, enum tnet_ice_server_proto_e e_proto, enum tnet_socket_type_e e_transport, const char* str_server_addr, uint16_t u_server_port) { - const struct tnet_ice_server_s* _pc_ice_srv; - const tsk_list_item_t *pc_item; - if (!self) { - TSK_DEBUG_ERROR("Invalid parameter"); - return -1; - } - tsk_list_lock(self->servers); - tsk_list_foreach(pc_item, self->servers) { - if ((_pc_ice_srv = pc_item->data)) { - if (_pc_ice_srv->e_proto == e_proto && _pc_ice_srv->e_transport == e_transport && _pc_ice_srv->u_server_port == u_server_port && tsk_striequals(_pc_ice_srv->str_server_addr, str_server_addr)) { - tsk_list_remove_item(self->servers, (tsk_list_item_t *)pc_item); - break; - } - } - } - tsk_list_unlock(self->servers); - return 0; + const struct tnet_ice_server_s* _pc_ice_srv; + const tsk_list_item_t *pc_item; + if (!self) { + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + tsk_list_lock(self->servers); + tsk_list_foreach(pc_item, self->servers) { + if ((_pc_ice_srv = pc_item->data)) { + if (_pc_ice_srv->e_proto == e_proto && _pc_ice_srv->e_transport == e_transport && _pc_ice_srv->u_server_port == u_server_port && tsk_striequals(_pc_ice_srv->str_server_addr, str_server_addr)) { + tsk_list_remove_item(self->servers, (tsk_list_item_t *)pc_item); + break; + } + } + } + tsk_list_unlock(self->servers); + return 0; } static const struct tnet_ice_server_s* _tnet_ice_ctx_server_find(struct tnet_ice_ctx_s* self, enum tnet_ice_server_proto_e e_proto, enum tnet_socket_type_e e_transport, const char* str_server_addr, uint16_t u_server_port) { - const struct tnet_ice_server_s* pc_ice_srv = tsk_null; - const struct tnet_ice_server_s* _pc_ice_srv; - const tsk_list_item_t *pc_item; - if (!self) { - TSK_DEBUG_ERROR("Invalid parameter"); - return tsk_null; - } - tsk_list_lock(self->servers); - tsk_list_foreach(pc_item, self->servers) { - if ((_pc_ice_srv = pc_item->data)) { - if (_pc_ice_srv->e_proto == e_proto && _pc_ice_srv->e_transport == e_transport && _pc_ice_srv->u_server_port == u_server_port && tsk_striequals(_pc_ice_srv->str_server_addr, str_server_addr)) { - pc_ice_srv = _pc_ice_srv; - break; - } - } - } - tsk_list_unlock(self->servers); - return pc_ice_srv; + const struct tnet_ice_server_s* pc_ice_srv = tsk_null; + const struct tnet_ice_server_s* _pc_ice_srv; + const tsk_list_item_t *pc_item; + if (!self) { + TSK_DEBUG_ERROR("Invalid parameter"); + return tsk_null; + } + tsk_list_lock(self->servers); + tsk_list_foreach(pc_item, self->servers) { + if ((_pc_ice_srv = pc_item->data)) { + if (_pc_ice_srv->e_proto == e_proto && _pc_ice_srv->e_transport == e_transport && _pc_ice_srv->u_server_port == u_server_port && tsk_striequals(_pc_ice_srv->str_server_addr, str_server_addr)) { + pc_ice_srv = _pc_ice_srv; + break; + } + } + } + tsk_list_unlock(self->servers); + return pc_ice_srv; } static tsk_bool_t _tnet_ice_ctx_server_exists(struct tnet_ice_ctx_s* self, enum tnet_ice_server_proto_e e_proto, enum tnet_socket_type_e e_transport, const char* str_server_addr, uint16_t u_server_port) { - return _tnet_ice_ctx_server_find(self, e_proto, e_transport, str_server_addr, u_server_port) ? tsk_true : tsk_false; + return _tnet_ice_ctx_server_find(self, e_proto, e_transport, str_server_addr, u_server_port) ? tsk_true : tsk_false; } static tsk_size_t _tnet_ice_ctx_servers_count_by_proto(struct tnet_ice_ctx_s* self, enum tnet_ice_server_proto_e e_proto) { - tsk_size_t count = 0; - if (self) { - const struct tnet_ice_server_s* _pc_ice_srv; - const tsk_list_item_t *pc_item; - tsk_list_lock(self->servers); - tsk_list_foreach(pc_item, self->servers) { - if ((_pc_ice_srv = pc_item->data) && (_pc_ice_srv->e_proto & e_proto) == e_proto) { - ++count; - } - } - tsk_list_unlock(self->servers); - } - return count; + tsk_size_t count = 0; + if (self) { + const struct tnet_ice_server_s* _pc_ice_srv; + const tsk_list_item_t *pc_item; + tsk_list_lock(self->servers); + tsk_list_foreach(pc_item, self->servers) { + if ((_pc_ice_srv = pc_item->data) && (_pc_ice_srv->e_proto & e_proto) == e_proto) { + ++count; + } + } + tsk_list_unlock(self->servers); + } + return count; } // Up to the caller to free the returned list static tnet_ice_servers_L_t* _tnet_ice_ctx_servers_copy(struct tnet_ice_ctx_s* self, enum tnet_ice_server_proto_e e_proto) { - tnet_ice_servers_L_t* copy = tsk_list_create(); - if (copy) { - const struct tnet_ice_server_s* _pc_ice_srv; - const tsk_list_item_t *pc_item; - tsk_list_lock(self->servers); - tsk_list_foreach(pc_item, self->servers) { - if ((_pc_ice_srv = pc_item->data) && (_pc_ice_srv->e_proto & e_proto) == e_proto) { - tnet_ice_server_t* srv = (tnet_ice_server_t*)tsk_object_ref(pc_item->data); - tsk_list_push_back_data(copy, (void**)&srv); - } - } - tsk_list_unlock(self->servers); - } - return copy; + tnet_ice_servers_L_t* copy = tsk_list_create(); + if (copy) { + const struct tnet_ice_server_s* _pc_ice_srv; + const tsk_list_item_t *pc_item; + tsk_list_lock(self->servers); + tsk_list_foreach(pc_item, self->servers) { + if ((_pc_ice_srv = pc_item->data) && (_pc_ice_srv->e_proto & e_proto) == e_proto) { + tnet_ice_server_t* srv = (tnet_ice_server_t*)tsk_object_ref(pc_item->data); + tsk_list_push_back_data(copy, (void**)&srv); + } + } + tsk_list_unlock(self->servers); + } + return copy; } diff --git a/branches/2.0/doubango/tinyNET/src/ice/tnet_ice_ctx.h b/branches/2.0/doubango/tinyNET/src/ice/tnet_ice_ctx.h index 184378bf..322d52e4 100644 --- a/branches/2.0/doubango/tinyNET/src/ice/tnet_ice_ctx.h +++ b/branches/2.0/doubango/tinyNET/src/ice/tnet_ice_ctx.h @@ -76,6 +76,7 @@ TINYNET_API int tnet_ice_ctx_start(struct tnet_ice_ctx_s* self); TINYNET_API int tnet_ice_ctx_rtp_callback(struct tnet_ice_ctx_s* self, tnet_ice_rtp_callback_f rtp_callback, const void* rtp_callback_data); TINYNET_API int tnet_ice_ctx_set_concheck_timeout(struct tnet_ice_ctx_s* self, int64_t timeout); TINYNET_API int tnet_ice_ctx_set_remote_candidates(struct tnet_ice_ctx_s* self, const char* candidates, const char* ufrag, const char* pwd, tsk_bool_t is_controlling, tsk_bool_t is_ice_jingle); +TINYNET_API int tnet_ice_ctx_set_remote_candidates_2(struct tnet_ice_ctx_s* self, const char* candidates, const char* ufrag, const char* pwd, tsk_bool_t is_controlling, tsk_bool_t is_ice_jingle, tsk_bool_t use_rtcpmux); TINYNET_API int tnet_ice_ctx_set_rtcpmux(struct tnet_ice_ctx_s* self, tsk_bool_t use_rtcpmux); TINYNET_API int tnet_ice_ctx_set_ssl_certs(struct tnet_ice_ctx_s* self, const char* path_priv, const char* path_pub, const char* path_ca, tsk_bool_t verify); TINYNET_API tsk_size_t tnet_ice_ctx_count_local_candidates(const struct tnet_ice_ctx_s* self); diff --git a/branches/2.0/doubango/tinyNET/src/ice/tnet_ice_event.c b/branches/2.0/doubango/tinyNET/src/ice/tnet_ice_event.c index 78f453d5..480202fe 100644 --- a/branches/2.0/doubango/tinyNET/src/ice/tnet_ice_event.c +++ b/branches/2.0/doubango/tinyNET/src/ice/tnet_ice_event.c @@ -1,85 +1,85 @@ -/* -* Copyright (C) 2012-2014 Mamadou DIOP -* Copyright (C) 2012-2014 Doubango Telecom . -* -* This file is part of Open Source Doubango Framework. -* -* DOUBANGO 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 3 of the License, or -* (at your option) any later version. -* -* DOUBANGO 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 DOUBANGO. -* -*/ -#include "tnet_ice_event.h" - -#include "tsk_string.h" -#include "tsk_memory.h" -#include "tsk_debug.h" - +/* + * Copyright (C) 2012-2014 Mamadou DIOP + * Copyright (C) 2012-2014 Doubango Telecom . + * + * This file is part of Open Source Doubango Framework. + * + * DOUBANGO 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 3 of the License, or + * (at your option) any later version. + * + * DOUBANGO 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 DOUBANGO. + * + */ +#include "tnet_ice_event.h" + +#include "tsk_string.h" +#include "tsk_memory.h" +#include "tsk_debug.h" + static tsk_object_t* tnet_ice_event_ctor(tsk_object_t * self, va_list * app) { - tnet_ice_event_t *e = self; - if(e){ - - } - return self; + tnet_ice_event_t *e = self; + if(e){ + + } + return self; } static tsk_object_t* tnet_ice_event_dtor(tsk_object_t * self) -{ - tnet_ice_event_t *e = self; - if(e){ - TSK_SAFE_FREE(e->phrase); - TSK_OBJECT_SAFE_FREE(e->action); - e->ctx = tsk_null; // not the owner (const) - } - - return self; -} -static const tsk_object_def_t tnet_ice_event_def_s = { - sizeof(tnet_ice_event_t), - tnet_ice_event_ctor, - tnet_ice_event_dtor, - tsk_null, + tnet_ice_event_t *e = self; + if(e){ + TSK_SAFE_FREE(e->phrase); + TSK_OBJECT_SAFE_FREE(e->action); + e->ctx = tsk_null; // not the owner (const) + } + + return self; +} +static const tsk_object_def_t tnet_ice_event_def_s = +{ + sizeof(tnet_ice_event_t), + tnet_ice_event_ctor, + tnet_ice_event_dtor, + tsk_null, }; const tsk_object_def_t *tnet_ice_event_def_t = &tnet_ice_event_def_s; tnet_ice_event_t* tnet_ice_event_create(const struct tnet_ice_ctx_s* ctx, tnet_ice_event_type_t type, const char* phrase, const void* userdata) { - tnet_ice_event_t* e; - - if((e = tsk_object_new(tnet_ice_event_def_t))){ - e->ctx = ctx; - e->type = type; - e->phrase = tsk_strdup(phrase); - e->userdata = userdata; - } - else{ - TSK_DEBUG_ERROR("Failed to create ICE event"); - } - - return e; + tnet_ice_event_t* e; + + if((e = tsk_object_new(tnet_ice_event_def_t))){ + e->ctx = ctx; + e->type = type; + e->phrase = tsk_strdup(phrase); + e->userdata = userdata; + } + else{ + TSK_DEBUG_ERROR("Failed to create ICE event"); + } + + return e; } int tnet_ice_event_set_action(tnet_ice_event_t* self, struct tnet_ice_action_s* action) { - if(!self){ - TSK_DEBUG_ERROR("Invalid parameter"); - return -1; - } - self->type = tnet_ice_event_type_action; - TSK_OBJECT_SAFE_FREE(self->action); - if(action){ - self->action = tsk_object_ref(action); - } - return 0; + if(!self){ + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + self->type = tnet_ice_event_type_action; + TSK_OBJECT_SAFE_FREE(self->action); + if(action){ + self->action = tsk_object_ref(action); + } + return 0; } diff --git a/branches/2.0/doubango/tinyNET/src/ice/tnet_ice_pair.c b/branches/2.0/doubango/tinyNET/src/ice/tnet_ice_pair.c index ad99d767..33e71a57 100644 --- a/branches/2.0/doubango/tinyNET/src/ice/tnet_ice_pair.c +++ b/branches/2.0/doubango/tinyNET/src/ice/tnet_ice_pair.c @@ -1,23 +1,23 @@ /* -* Copyright (C) 2012-2015 Mamadou DIOP -* Copyright (C) 2012-2015 Doubango Telecom . -* -* This file is part of Open Source Doubango Framework. -* -* DOUBANGO 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 3 of the License, or -* (at your option) any later version. -* -* DOUBANGO 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 DOUBANGO. -* -*/ + * Copyright (C) 2012-2015 Mamadou DIOP + * Copyright (C) 2012-2015 Doubango Telecom . + * + * This file is part of Open Source Doubango Framework. + * + * DOUBANGO 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 3 of the License, or + * (at your option) any later version. + * + * DOUBANGO 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 DOUBANGO. + * + */ #include "tnet_ice_pair.h" #include "tnet_ice_utils.h" #include "tnet_ice_candidate.h" @@ -58,931 +58,931 @@ static int __pred_find_by_pair(const tsk_list_item_t *item, const void *pair) { - if(item && item->data){ - int ret; - tsk_subsat_int32_ptr(item->data, pair, &ret); - return ret; - } - return -1; + if(item && item->data){ + int ret; + tsk_subsat_int32_ptr(item->data, pair, &ret); + return ret; + } + return -1; } static tsk_object_t* tnet_ice_pair_ctor(tsk_object_t * self, va_list * app) { - tnet_ice_pair_t *pair = self; - if(pair){ - pair->state_offer = tnet_ice_pair_state_frozen; - pair->state_answer = tnet_ice_pair_state_frozen; - } - return self; + tnet_ice_pair_t *pair = self; + if(pair){ + pair->state_offer = tnet_ice_pair_state_frozen; + pair->state_answer = tnet_ice_pair_state_frozen; + } + return self; } static tsk_object_t* tnet_ice_pair_dtor(tsk_object_t * self) -{ - tnet_ice_pair_t *pair = self; - if (pair) { - TSK_OBJECT_SAFE_FREE(pair->candidate_offer); - TSK_OBJECT_SAFE_FREE(pair->candidate_answer); - TSK_OBJECT_SAFE_FREE(pair->last_request); - } - return self; +{ + tnet_ice_pair_t *pair = self; + if (pair) { + TSK_OBJECT_SAFE_FREE(pair->candidate_offer); + TSK_OBJECT_SAFE_FREE(pair->candidate_answer); + TSK_OBJECT_SAFE_FREE(pair->last_request); + } + return self; } static int tnet_ice_pair_cmp(const tsk_object_t *_p1, const tsk_object_t *_p2) { - const tnet_ice_pair_t *p1 = _p1; - const tnet_ice_pair_t *p2 = _p2; - - if (p1 && p2) { + const tnet_ice_pair_t *p1 = _p1; + const tnet_ice_pair_t *p2 = _p2; + + if (p1 && p2) { #if 0 - // This is not correct and most differences (if not all) will be equal to "INT_MIN" or "INT_MAX" and this will produce invalid sorting. - static const int64_t __int_min = INT_MIN; - static const int64_t __int_max = INT_MAX; - return (int)(TSK_CLAMP(__int_min, (int64_t)(p1->priority - p2->priority), __int_max)); + // This is not correct and most differences (if not all) will be equal to "INT_MIN" or "INT_MAX" and this will produce invalid sorting. + static const int64_t __int_min = INT_MIN; + static const int64_t __int_max = INT_MAX; + return (int)(TSK_CLAMP(__int_min, (int64_t)(p1->priority - p2->priority), __int_max)); #else - // The comparison is used for sorting only which means the below code is correct - return (p1->priority == p2->priority) ? 0 : (p1->priority > p2->priority ? 1 : -1); + // The comparison is used for sorting only which means the below code is correct + return (p1->priority == p2->priority) ? 0 : (p1->priority > p2->priority ? 1 : -1); #endif - } - else if (!p1 && !p2) return 0; - else return -1; + } + else if (!p1 && !p2) return 0; + else return -1; } -static const tsk_object_def_t tnet_ice_pair_def_s = +static const tsk_object_def_t tnet_ice_pair_def_s = { - sizeof(tnet_ice_pair_t), - tnet_ice_pair_ctor, - tnet_ice_pair_dtor, - tnet_ice_pair_cmp, + sizeof(tnet_ice_pair_t), + tnet_ice_pair_ctor, + tnet_ice_pair_dtor, + tnet_ice_pair_cmp, }; tnet_ice_pair_t* tnet_ice_pair_create(const tnet_ice_candidate_t* candidate_offer, const tnet_ice_candidate_t* candidate_answer, tsk_bool_t is_controlling, uint64_t tie_breaker, tsk_bool_t is_ice_jingle) { - static uint64_t __unique_id = 0; - tnet_ice_pair_t *pair; - if(!candidate_offer || !candidate_answer){ - TSK_DEBUG_ERROR("Invalid parameter"); - return tsk_null; - } - - if ((pair = tsk_object_new(&tnet_ice_pair_def_s))) { - uint64_t G, D; - pair->id = ++__unique_id; // not part of the standard, used to ease debugging - pair->candidate_offer = tsk_object_ref((tsk_object_t*)candidate_offer); - pair->candidate_answer = tsk_object_ref((tsk_object_t*)candidate_answer); - pair->is_controlling = is_controlling; - pair->tie_breaker = tie_breaker; - pair->is_ice_jingle = is_ice_jingle; - // 5.7.2. Computing Pair Priority and Ordering Pairs: https://tools.ietf.org/html/rfc5245#section-5.7.2 - G = is_controlling ? candidate_offer->priority : candidate_answer->priority; // the priority for the candidate provided by the controlling agent - D = is_controlling ? candidate_answer->priority : candidate_offer->priority; // the priority for the candidate provided by the controlled agent - pair->priority = ((TSK_MIN(G, D)) << 32) + (TSK_MAX(G, D) << 1) + ((G > D) ? 1 : 0); - pair->turn_peer_id = kTurnPeerIdInvalid; - } - - return pair; + static uint64_t __unique_id = 0; + tnet_ice_pair_t *pair; + if(!candidate_offer || !candidate_answer){ + TSK_DEBUG_ERROR("Invalid parameter"); + return tsk_null; + } + + if ((pair = tsk_object_new(&tnet_ice_pair_def_s))) { + uint64_t G, D; + pair->id = ++__unique_id; // not part of the standard, used to ease debugging + pair->candidate_offer = tsk_object_ref((tsk_object_t*)candidate_offer); + pair->candidate_answer = tsk_object_ref((tsk_object_t*)candidate_answer); + pair->is_controlling = is_controlling; + pair->tie_breaker = tie_breaker; + pair->is_ice_jingle = is_ice_jingle; + // 5.7.2. Computing Pair Priority and Ordering Pairs: https://tools.ietf.org/html/rfc5245#section-5.7.2 + G = is_controlling ? candidate_offer->priority : candidate_answer->priority; // the priority for the candidate provided by the controlling agent + D = is_controlling ? candidate_answer->priority : candidate_offer->priority; // the priority for the candidate provided by the controlled agent + pair->priority = ((TSK_MIN(G, D)) << 32) + (TSK_MAX(G, D) << 1) + ((G > D) ? 1 : 0); + pair->turn_peer_id = kTurnPeerIdInvalid; + } + + return pair; } // rfc 5245 - 7.1.3.2.1. Discovering Peer Reflexive Candidates tnet_ice_pair_t* tnet_ice_pair_prflx_create(tnet_ice_pairs_L_t* pairs, tnet_fd_t local_fd, const struct sockaddr_storage *remote_addr) { - int ret; - const tsk_list_item_t *item; - const tnet_ice_pair_t *pair_local = tsk_null, *pair = tsk_null; - tnet_ip_t remote_ip; - tnet_port_t remote_port; - - if (!pairs || !remote_addr) { - TSK_DEBUG_ERROR("Invalid parameter"); - return tsk_null; - } - - tsk_list_foreach(item, pairs) { - if (!(pair = item->data) || !pair->candidate_offer || !pair->candidate_answer || !pair->candidate_offer->socket || pair->candidate_offer->socket->fd != local_fd) { - continue; - } - pair_local = pair; - break; - } - - if ((ret = tnet_get_sockip_n_port((const struct sockaddr*)remote_addr, &remote_ip, &remote_port))) { - TNET_PRINT_LAST_ERROR("tnet_get_sockip_n_port() failed"); - return tsk_null; - } - - if (!pair_local) { - TSK_DEBUG_ERROR("Cannot create prflx candidate with remote ip = %s, remote port = %u and local_fd = %d", remote_ip, remote_port, local_fd); - return tsk_null; - } - else { - tnet_ice_pair_t* pair_peer = tsk_null; - tnet_ice_candidate_t* cand_local; - tnet_ice_candidate_t* cand_remote; - cand_local = tnet_ice_candidate_create(tnet_ice_cand_type_prflx, pair_local->candidate_offer->socket, pair_local->is_ice_jingle, pair_local->candidate_offer->is_rtp, pair_local->candidate_offer->is_video, pair_local->candidate_offer->ufrag, pair_local->candidate_offer->pwd, pair_local->candidate_offer->foundation); - cand_remote = tnet_ice_candidate_create(tnet_ice_cand_type_prflx, tsk_null, pair_local->is_ice_jingle, pair_local->candidate_answer->is_rtp, pair_local->candidate_answer->is_video, pair_local->candidate_answer->ufrag, pair_local->candidate_answer->pwd, pair_local->candidate_answer->foundation); - if (cand_local && cand_remote) { - tsk_strupdate(&cand_remote->transport_str, pair_local->candidate_offer->transport_str); - cand_remote->comp_id = pair_local->candidate_offer->comp_id; - memcpy(cand_remote->connection_addr, remote_ip, sizeof(tnet_ip_t)); - cand_remote->port = remote_port; - - TSK_DEBUG_INFO("ICE Pair Reflexive Candidate (%llu, %llu): [%s %u %u %s %d] -> [%s %u %u %s %d]", - pair->id, - pair->priority, - - cand_local->foundation, - cand_local->priority, - cand_local->comp_id, - cand_local->connection_addr, - cand_local->port, - - cand_remote->foundation, - cand_remote->priority, - cand_remote->comp_id, - cand_remote->connection_addr, - cand_remote->port); - - pair_peer = tnet_ice_pair_create(cand_local, cand_remote, pair_local->is_controlling, pair_local->tie_breaker, pair_local->is_ice_jingle); - } - TSK_OBJECT_SAFE_FREE(cand_local); - TSK_OBJECT_SAFE_FREE(cand_remote); - return pair_peer; - } - - return tsk_null; + int ret; + const tsk_list_item_t *item; + const tnet_ice_pair_t *pair_local = tsk_null, *pair = tsk_null; + tnet_ip_t remote_ip; + tnet_port_t remote_port; + + if (!pairs || !remote_addr) { + TSK_DEBUG_ERROR("Invalid parameter"); + return tsk_null; + } + + tsk_list_foreach(item, pairs) { + if (!(pair = item->data) || !pair->candidate_offer || !pair->candidate_answer || !pair->candidate_offer->socket || pair->candidate_offer->socket->fd != local_fd) { + continue; + } + pair_local = pair; + break; + } + + if ((ret = tnet_get_sockip_n_port((const struct sockaddr*)remote_addr, &remote_ip, &remote_port))) { + TNET_PRINT_LAST_ERROR("tnet_get_sockip_n_port() failed"); + return tsk_null; + } + + if (!pair_local) { + TSK_DEBUG_ERROR("Cannot create prflx candidate with remote ip = %s, remote port = %u and local_fd = %d", remote_ip, remote_port, local_fd); + return tsk_null; + } + else { + tnet_ice_pair_t* pair_peer = tsk_null; + tnet_ice_candidate_t* cand_local; + tnet_ice_candidate_t* cand_remote; + cand_local = tnet_ice_candidate_create(tnet_ice_cand_type_prflx, pair_local->candidate_offer->socket, pair_local->is_ice_jingle, pair_local->candidate_offer->is_rtp, pair_local->candidate_offer->is_video, pair_local->candidate_offer->ufrag, pair_local->candidate_offer->pwd, pair_local->candidate_offer->foundation); + cand_remote = tnet_ice_candidate_create(tnet_ice_cand_type_prflx, tsk_null, pair_local->is_ice_jingle, pair_local->candidate_answer->is_rtp, pair_local->candidate_answer->is_video, pair_local->candidate_answer->ufrag, pair_local->candidate_answer->pwd, pair_local->candidate_answer->foundation); + if (cand_local && cand_remote) { + tsk_strupdate(&cand_remote->transport_str, pair_local->candidate_offer->transport_str); + cand_remote->comp_id = pair_local->candidate_offer->comp_id; + memcpy(cand_remote->connection_addr, remote_ip, sizeof(tnet_ip_t)); + cand_remote->port = remote_port; + + TSK_DEBUG_INFO("ICE Pair Reflexive Candidate (%llu, %llu): [%s %u %u %s %d] -> [%s %u %u %s %d]", + pair->id, + pair->priority, + + cand_local->foundation, + cand_local->priority, + cand_local->comp_id, + cand_local->connection_addr, + cand_local->port, + + cand_remote->foundation, + cand_remote->priority, + cand_remote->comp_id, + cand_remote->connection_addr, + cand_remote->port); + + pair_peer = tnet_ice_pair_create(cand_local, cand_remote, pair_local->is_controlling, pair_local->tie_breaker, pair_local->is_ice_jingle); + } + TSK_OBJECT_SAFE_FREE(cand_local); + TSK_OBJECT_SAFE_FREE(cand_remote); + return pair_peer; + } + + return tsk_null; } int tnet_ice_pair_send_conncheck(tnet_ice_pair_t *self) { - int ret; - - if(!self){ - TSK_DEBUG_ERROR("Invalid parameter"); - return -1; - } - - if (self->candidate_offer->turn.ss) { - enum tnet_stun_state_e e_state; - enum tnet_turn_transport_e e_req_transport; - if ((ret = tnet_turn_session_get_state_createperm(self->candidate_offer->turn.ss, self->turn_peer_id, &e_state))) { - goto bail; - } - if (e_state != tnet_stun_state_ok) { - TSK_DEBUG_INFO("TURN CreatePerm not ready yet... to send STUN ConnCheck (peer-id=%ld)", self->turn_peer_id); - goto bail; - } - - if ((ret = tnet_turn_session_get_req_transport(self->candidate_offer->turn.ss, &e_req_transport))) { - goto bail; - } - if (e_req_transport == tnet_turn_transport_tcp) { - // Make sure "ConnectionBind" sent and underlaying socket is connected - tsk_bool_t b_connected; - if ((ret = tnet_turn_session_is_stream_connected(self->candidate_offer->turn.ss, self->turn_peer_id, &b_connected))) { - goto bail; - } - if (!b_connected) { - TSK_DEBUG_INFO("TURN/TCP not connected yet... to send STUN ConnCheck"); - goto bail; - } - } - } - - if (!self->last_request) { - uint32_t priority; - - // Init remote address - if ((ret = tnet_sockaddr_init(self->candidate_answer->connection_addr, self->candidate_answer->port, self->candidate_offer->socket->type, &self->remote_addr))) { - TNET_PRINT_LAST_ERROR("tnet_sockaddr_init(%s:%d) failed", self->candidate_answer->connection_addr, self->candidate_answer->port); - goto bail; - } - - if ((ret = tnet_stun_pkt_create_empty(tnet_stun_pkt_type_binding_request, &self->last_request))) { - goto bail; - } - // 7.1.2.3. Forming Credentials + int ret; + + if(!self){ + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + + if (self->candidate_offer->turn.ss) { + enum tnet_stun_state_e e_state; + enum tnet_turn_transport_e e_req_transport; + if ((ret = tnet_turn_session_get_state_createperm(self->candidate_offer->turn.ss, self->turn_peer_id, &e_state))) { + goto bail; + } + if (e_state != tnet_stun_state_ok) { + TSK_DEBUG_INFO("TURN CreatePerm not ready yet... to send STUN ConnCheck (peer-id=%ld)", self->turn_peer_id); + goto bail; + } + + if ((ret = tnet_turn_session_get_req_transport(self->candidate_offer->turn.ss, &e_req_transport))) { + goto bail; + } + if (e_req_transport == tnet_turn_transport_tcp) { + // Make sure "ConnectionBind" sent and underlaying socket is connected + tsk_bool_t b_connected; + if ((ret = tnet_turn_session_is_stream_connected(self->candidate_offer->turn.ss, self->turn_peer_id, &b_connected))) { + goto bail; + } + if (!b_connected) { + TSK_DEBUG_INFO("TURN/TCP not connected yet... to send STUN ConnCheck"); + goto bail; + } + } + } + + if (!self->last_request) { + uint32_t priority; + + // Init remote address + if ((ret = tnet_sockaddr_init(self->candidate_answer->connection_addr, self->candidate_answer->port, self->candidate_offer->socket->type, &self->remote_addr))) { + TNET_PRINT_LAST_ERROR("tnet_sockaddr_init(%s:%d) failed", self->candidate_answer->connection_addr, self->candidate_answer->port); + goto bail; + } + + if ((ret = tnet_stun_pkt_create_empty(tnet_stun_pkt_type_binding_request, &self->last_request))) { + goto bail; + } + // 7.1.2.3. Forming Credentials // username= "RFRAG:LFRAG" // password= "RPASS" - { - char* p_uname = tsk_null; - const char* pc_pwd; - if (self->is_ice_jingle) { - tsk_sprintf(&p_uname, "%s%s", tnet_ice_candidate_get_ufrag(self->candidate_answer), tnet_ice_candidate_get_ufrag(self->candidate_offer)); - } - else{ - tsk_sprintf(&p_uname, "%s:%s", tnet_ice_candidate_get_ufrag(self->candidate_answer), tnet_ice_candidate_get_ufrag(self->candidate_offer)); - } - pc_pwd = tnet_ice_candidate_get_pwd(self->candidate_answer); - ret = tnet_stun_pkt_auth_prepare_shortterm(self->last_request, p_uname, pc_pwd); - TSK_FREE(p_uname); - if (ret) { - goto bail; - } - } - - priority = tnet_ice_utils_get_priority(tnet_ice_cand_type_prflx, self->candidate_offer->local_pref, self->candidate_offer->is_rtp); - // add attributes - self->last_request->opt.dontfrag = 0; - ret = tnet_stun_pkt_attrs_add(self->last_request, - TNET_STUN_PKT_ATTR_ADD_SOFTWARE_ZT(kStunSoftware), - // 7.1.2.1. PRIORITY - TNET_STUN_PKT_ATTR_ADD_ICE_PRIORITY(priority), - TNET_STUN_PKT_ATTR_ADD_NULL()); - if (ret) { - goto bail; - } - // 7.1.2.2. ICE-CONTROLLING / ICE-CONTROLLED - if (self->is_controlling) { - ret = tnet_stun_pkt_attrs_add(self->last_request, - // 7.1.2.2. ICE-CONTROLLING - TNET_STUN_PKT_ATTR_ADD_ICE_CONTROLLING(self->tie_breaker), - // 7.1.2.1. USE-CANDIDATE - aggressive mode + { + char* p_uname = tsk_null; + const char* pc_pwd; + if (self->is_ice_jingle) { + tsk_sprintf(&p_uname, "%s%s", tnet_ice_candidate_get_ufrag(self->candidate_answer), tnet_ice_candidate_get_ufrag(self->candidate_offer)); + } + else{ + tsk_sprintf(&p_uname, "%s:%s", tnet_ice_candidate_get_ufrag(self->candidate_answer), tnet_ice_candidate_get_ufrag(self->candidate_offer)); + } + pc_pwd = tnet_ice_candidate_get_pwd(self->candidate_answer); + ret = tnet_stun_pkt_auth_prepare_shortterm(self->last_request, p_uname, pc_pwd); + TSK_FREE(p_uname); + if (ret) { + goto bail; + } + } + + priority = tnet_ice_utils_get_priority(tnet_ice_cand_type_prflx, self->candidate_offer->local_pref, self->candidate_offer->is_rtp); + // add attributes + self->last_request->opt.dontfrag = 0; + ret = tnet_stun_pkt_attrs_add(self->last_request, + TNET_STUN_PKT_ATTR_ADD_SOFTWARE_ZT(kStunSoftware), + // 7.1.2.1. PRIORITY + TNET_STUN_PKT_ATTR_ADD_ICE_PRIORITY(priority), + TNET_STUN_PKT_ATTR_ADD_NULL()); + if (ret) { + goto bail; + } + // 7.1.2.2. ICE-CONTROLLING / ICE-CONTROLLED + if (self->is_controlling) { + ret = tnet_stun_pkt_attrs_add(self->last_request, + // 7.1.2.2. ICE-CONTROLLING + TNET_STUN_PKT_ATTR_ADD_ICE_CONTROLLING(self->tie_breaker), + // 7.1.2.1. USE-CANDIDATE - aggressive mode #if TNET_ICE_AGGRESSIVE_NOMINATION - TNET_STUN_PKT_ATTR_ADD_ICE_USE_CANDIDATE(), + TNET_STUN_PKT_ATTR_ADD_ICE_USE_CANDIDATE(), #endif - TNET_STUN_PKT_ATTR_ADD_NULL()); - if (self->is_nominated) { - ret = tnet_stun_pkt_attrs_add(self->last_request, - TNET_STUN_PKT_ATTR_ADD_ICE_USE_CANDIDATE(), - TNET_STUN_PKT_ATTR_ADD_NULL()); - } - if (ret) { - goto bail; - } - } - else { - ret = tnet_stun_pkt_attrs_add(self->last_request, - // 7.1.2.2. ICE-CONTROLLED - TNET_STUN_PKT_ATTR_ADD_ICE_CONTROLLED(self->tie_breaker), - TNET_STUN_PKT_ATTR_ADD_NULL()); - if (ret) { - goto bail; - } - } - } - else { - tsk_bool_t b_changed = tsk_false; - if (self->is_controlling) { - tnet_stun_pkt_attr_remove(self->last_request, tnet_stun_attr_type_ice_controlled); - if (!tnet_stun_pkt_attr_exists(self->last_request, tnet_stun_attr_type_ice_controlling)) { - ret = tnet_stun_pkt_attrs_add(self->last_request, - TNET_STUN_PKT_ATTR_ADD_ICE_CONTROLLING(self->tie_breaker), - TNET_STUN_PKT_ATTR_ADD_NULL()); - if (ret) { - goto bail; - } - b_changed = tsk_true; - } + TNET_STUN_PKT_ATTR_ADD_NULL()); + if (self->is_nominated) { + ret = tnet_stun_pkt_attrs_add(self->last_request, + TNET_STUN_PKT_ATTR_ADD_ICE_USE_CANDIDATE(), + TNET_STUN_PKT_ATTR_ADD_NULL()); + } + if (ret) { + goto bail; + } + } + else { + ret = tnet_stun_pkt_attrs_add(self->last_request, + // 7.1.2.2. ICE-CONTROLLED + TNET_STUN_PKT_ATTR_ADD_ICE_CONTROLLED(self->tie_breaker), + TNET_STUN_PKT_ATTR_ADD_NULL()); + if (ret) { + goto bail; + } + } + } + else { + tsk_bool_t b_changed = tsk_false; + if (self->is_controlling) { + tnet_stun_pkt_attr_remove(self->last_request, tnet_stun_attr_type_ice_controlled); + if (!tnet_stun_pkt_attr_exists(self->last_request, tnet_stun_attr_type_ice_controlling)) { + ret = tnet_stun_pkt_attrs_add(self->last_request, + TNET_STUN_PKT_ATTR_ADD_ICE_CONTROLLING(self->tie_breaker), + TNET_STUN_PKT_ATTR_ADD_NULL()); + if (ret) { + goto bail; + } + b_changed = tsk_true; + } #if TNET_ICE_AGGRESSIVE_NOMINATION - if (!tnet_stun_pkt_attr_exists(self->last_request, tnet_stun_attr_type_ice_use_candidate)) { + if (!tnet_stun_pkt_attr_exists(self->last_request, tnet_stun_attr_type_ice_use_candidate)) { #else - if (self->is_nominated && !tnet_stun_pkt_attr_exists(self->last_request, tnet_stun_attr_type_ice_use_candidate)) { -#endif - ret = tnet_stun_pkt_attrs_add(self->last_request, - TNET_STUN_PKT_ATTR_ADD_ICE_USE_CANDIDATE(), - TNET_STUN_PKT_ATTR_ADD_NULL()); - - if (ret) { - goto bail; - } - b_changed = tsk_true; - } - } - else { - tnet_stun_pkt_attr_remove(self->last_request, tnet_stun_attr_type_ice_use_candidate); - tnet_stun_pkt_attr_remove(self->last_request, tnet_stun_attr_type_ice_controlling); - if (!tnet_stun_pkt_attr_exists(self->last_request, tnet_stun_attr_type_ice_controlled)) { - ret = tnet_stun_pkt_attrs_add(self->last_request, - TNET_STUN_PKT_ATTR_ADD_ICE_CONTROLLED(self->tie_breaker), - TNET_STUN_PKT_ATTR_ADD_NULL()); - if (ret) { - goto bail; - } - b_changed = tsk_true; - } - } - // update transac-id if the request structure changed - if ((b_changed && (ret = tnet_stun_utils_transac_id_rand(&self->last_request->transac_id)))) { - goto bail; - } - } - - // Send request - { - tsk_buffer_t *req_buffer = tsk_null; - self->last_request->opt.fingerprint = !self->is_ice_jingle; - if ((ret = tnet_stun_pkt_write_with_padding_2(self->last_request, &req_buffer))) { - goto bail; - } - if (self->candidate_offer->turn.ss) { - // Send using TURN session. Above, we already checked that the TURN session is ready (Alloc=OK, Permission=OK) - ret = tnet_turn_session_send_data(self->candidate_offer->turn.ss, self->turn_peer_id, req_buffer->data, (uint16_t)req_buffer->size); - } - else { - int sendBytes = tnet_sockfd_sendto(self->candidate_offer->socket->fd, (const struct sockaddr*)&self->remote_addr, req_buffer->data, req_buffer->size); - ret = (sendBytes == req_buffer->size) ? 0 : -9; - } - TSK_OBJECT_SAFE_FREE(req_buffer); - if (ret) { - goto bail; - } - } - -bail: - if (ret == 0 && self->state_offer == tnet_ice_pair_state_frozen) { - self->state_offer = tnet_ice_pair_state_in_progress; - } - return ret; -} - -int tnet_ice_pair_send_response(tnet_ice_pair_t *self, const tnet_stun_pkt_req_t* request, const short code, const char* phrase, const struct sockaddr_storage *remote_addr) -{ - tnet_stun_pkt_t* message = tsk_null; - const char *password, *username; - int ret = -1; - tsk_bool_t is_error = ((code / 100) != 2); - - if(!self || !phrase || !request || !self->candidate_offer || !self->candidate_answer){ - TSK_DEBUG_ERROR("Invalid paramter"); - return -1; - } - - username = tsk_null; - password = tnet_ice_candidate_get_pwd(self->candidate_offer); - - if ((ret = tnet_stun_pkt_create_empty(is_error ? tnet_stun_pkt_type_binding_error_response : tnet_stun_pkt_type_binding_success_response, &message)) == 0) { - tsk_buffer_t *req_buffer = tsk_null; - memcpy(message->transac_id, request->transac_id, sizeof(request->transac_id)); - message->opt.fingerprint = !self->is_ice_jingle; - message->opt.dontfrag = 0; - - // SOFWARE - ret = tnet_stun_pkt_attrs_add(message, - TNET_STUN_PKT_ATTR_ADD_SOFTWARE_ZT(kStunSoftware), - TNET_STUN_PKT_ATTR_ADD_NULL()); - if (ret) { - goto bail; - } - - // SHORT-TERM authentication even for responses - if ((ret = tnet_stun_pkt_auth_prepare_shortterm_2(message, password))) { - goto bail; - } - - // ERROR - if (is_error) { - ret = tnet_stun_pkt_attrs_add(message, - TNET_STUN_PKT_ATTR_ADD_ERROR_CODE(((code / 100) & 0x07), (code - ((code / 100) * 100)), phrase), - TNET_STUN_PKT_ATTR_ADD_NULL()); - if (ret) { - goto bail; - } - } - else { - tnet_ip_t remote_ip; - tnet_port_t remote_port; - if (self->is_ice_jingle) { - const tnet_stun_attr_vdata_t *pc_attr_vdata; - // USERNAME - if ((ret = tnet_stun_pkt_attr_find_first(request, tnet_stun_attr_type_username, (const tnet_stun_attr_t **)&pc_attr_vdata)) == 0 && pc_attr_vdata) { - ret = tnet_stun_pkt_attrs_add(message, - TNET_STUN_PKT_ATTR_ADD_USERNAME(pc_attr_vdata->p_data_ptr, pc_attr_vdata->u_data_size), - TNET_STUN_PKT_ATTR_ADD_NULL()); - if (ret) { - goto bail; - } - } - } - // MAPPED-ADDRESS and XOR-MAPPED-ADDRESS - if ((ret = tnet_get_sockip_n_port((const struct sockaddr*)remote_addr, &remote_ip, &remote_port)) == 0) { - tnet_stun_addr_t _addr; - tnet_stun_address_family_t _familly = (remote_addr->ss_family == AF_INET6) ? tnet_stun_address_family_ipv6 : tnet_stun_address_family_ipv4; - if ((ret = tnet_stun_utils_inet_pton((_familly == tnet_stun_address_family_ipv6), remote_ip, &_addr)) == 0) { - ret = tnet_stun_pkt_attrs_add(message, - TNET_STUN_PKT_ATTR_ADD_MAPPED_ADDRESS(_familly, remote_port, &_addr), - TNET_STUN_PKT_ATTR_ADD_XOR_MAPPED_ADDRESS(_familly, remote_port, &_addr), - TNET_STUN_PKT_ATTR_ADD_NULL()); - if (ret) { - goto bail; - } - } - } - } - - if (self->candidate_offer->turn.ss) { - enum tnet_stun_state_e e_state; - if ((ret = tnet_turn_session_get_state_createperm(self->candidate_offer->turn.ss, self->turn_peer_id, &e_state))) { - goto bail; - } - if (e_state != tnet_stun_state_ok) { - TSK_DEBUG_INFO("TURN CreatePerm not ready yet... to send STUN response (peer-id=%ld)", self->turn_peer_id); - goto bail; - } - if ((ret = tnet_stun_pkt_write_with_padding_2(message, &req_buffer))) { - goto bail; - } - if ((ret = tnet_turn_session_send_data(self->candidate_offer->turn.ss, self->turn_peer_id, req_buffer->data, (uint16_t)req_buffer->size))) { - goto bail; - } - } - else { - struct sockaddr_storage dest_addr; - int sendBytes; - if ((ret = tnet_sockaddr_init(self->candidate_answer->connection_addr, self->candidate_answer->port, self->candidate_offer->socket->type, &dest_addr))) { - TNET_PRINT_LAST_ERROR("tnet_sockaddr_init(%s:%d) failed", self->candidate_answer->connection_addr, self->candidate_answer->port); - goto bail; - } - if ((ret = tnet_stun_pkt_write_with_padding_2(message, &req_buffer))) { - goto bail; - } - sendBytes = tnet_sockfd_sendto(self->candidate_offer->socket->fd, (const struct sockaddr*)&dest_addr, req_buffer->data, req_buffer->size); - TSK_OBJECT_SAFE_FREE(req_buffer); - ret = (sendBytes > 0) ? 0 : -2; - if (ret != 0) { - TSK_DEBUG_ERROR("ICE pair-answer: failed to send response"); - } - } - } - - if (ret == 0 && !is_error) { - tsk_bool_t change_state = - self->is_ice_jingle // ICE-JINGLE don't have ICE-CONTROLLING/ICE-CONTROLLED attributes - || (!self->is_controlling && tnet_stun_pkt_attr_exists(request, tnet_stun_attr_type_ice_use_candidate)) // Sender is controlling and uses "ICE-USE-CANDIDATE" attribute + if (self->is_nominated && !tnet_stun_pkt_attr_exists(self->last_request, tnet_stun_attr_type_ice_use_candidate)) { +#endif + ret = tnet_stun_pkt_attrs_add(self->last_request, + TNET_STUN_PKT_ATTR_ADD_ICE_USE_CANDIDATE(), + TNET_STUN_PKT_ATTR_ADD_NULL()); + + if (ret) { + goto bail; + } + b_changed = tsk_true; + } + } + else { + tnet_stun_pkt_attr_remove(self->last_request, tnet_stun_attr_type_ice_use_candidate); + tnet_stun_pkt_attr_remove(self->last_request, tnet_stun_attr_type_ice_controlling); + if (!tnet_stun_pkt_attr_exists(self->last_request, tnet_stun_attr_type_ice_controlled)) { + ret = tnet_stun_pkt_attrs_add(self->last_request, + TNET_STUN_PKT_ATTR_ADD_ICE_CONTROLLED(self->tie_breaker), + TNET_STUN_PKT_ATTR_ADD_NULL()); + if (ret) { + goto bail; + } + b_changed = tsk_true; + } + } + // update transac-id if the request structure changed + if ((b_changed && (ret = tnet_stun_utils_transac_id_rand(&self->last_request->transac_id)))) { + goto bail; + } + } + + // Send request + { + tsk_buffer_t *req_buffer = tsk_null; + self->last_request->opt.fingerprint = !self->is_ice_jingle; + if ((ret = tnet_stun_pkt_write_with_padding_2(self->last_request, &req_buffer))) { + goto bail; + } + if (self->candidate_offer->turn.ss) { + // Send using TURN session. Above, we already checked that the TURN session is ready (Alloc=OK, Permission=OK) + ret = tnet_turn_session_send_data(self->candidate_offer->turn.ss, self->turn_peer_id, req_buffer->data, (uint16_t)req_buffer->size); + } + else { + int sendBytes = tnet_sockfd_sendto(self->candidate_offer->socket->fd, (const struct sockaddr*)&self->remote_addr, req_buffer->data, req_buffer->size); + ret = (sendBytes == req_buffer->size) ? 0 : -9; + } + TSK_OBJECT_SAFE_FREE(req_buffer); + if (ret) { + goto bail; + } + } + + bail: + if (ret == 0 && self->state_offer == tnet_ice_pair_state_frozen) { + self->state_offer = tnet_ice_pair_state_in_progress; + } + return ret; + } + + int tnet_ice_pair_send_response(tnet_ice_pair_t *self, const tnet_stun_pkt_req_t* request, const short code, const char* phrase, const struct sockaddr_storage *remote_addr) + { + tnet_stun_pkt_t* message = tsk_null; + const char *password, *username; + int ret = -1; + tsk_bool_t is_error = ((code / 100) != 2); + + if(!self || !phrase || !request || !self->candidate_offer || !self->candidate_answer){ + TSK_DEBUG_ERROR("Invalid paramter"); + return -1; + } + + username = tsk_null; + password = tnet_ice_candidate_get_pwd(self->candidate_offer); + + if ((ret = tnet_stun_pkt_create_empty(is_error ? tnet_stun_pkt_type_binding_error_response : tnet_stun_pkt_type_binding_success_response, &message)) == 0) { + tsk_buffer_t *req_buffer = tsk_null; + memcpy(message->transac_id, request->transac_id, sizeof(request->transac_id)); + message->opt.fingerprint = !self->is_ice_jingle; + message->opt.dontfrag = 0; + + // SOFWARE + ret = tnet_stun_pkt_attrs_add(message, + TNET_STUN_PKT_ATTR_ADD_SOFTWARE_ZT(kStunSoftware), + TNET_STUN_PKT_ATTR_ADD_NULL()); + if (ret) { + goto bail; + } + + // SHORT-TERM authentication even for responses + if ((ret = tnet_stun_pkt_auth_prepare_shortterm_2(message, password))) { + goto bail; + } + + // ERROR + if (is_error) { + ret = tnet_stun_pkt_attrs_add(message, + TNET_STUN_PKT_ATTR_ADD_ERROR_CODE(((code / 100) & 0x07), (code - ((code / 100) * 100)), phrase), + TNET_STUN_PKT_ATTR_ADD_NULL()); + if (ret) { + goto bail; + } + } + else { + tnet_ip_t remote_ip; + tnet_port_t remote_port; + if (self->is_ice_jingle) { + const tnet_stun_attr_vdata_t *pc_attr_vdata; + // USERNAME + if ((ret = tnet_stun_pkt_attr_find_first(request, tnet_stun_attr_type_username, (const tnet_stun_attr_t **)&pc_attr_vdata)) == 0 && pc_attr_vdata) { + ret = tnet_stun_pkt_attrs_add(message, + TNET_STUN_PKT_ATTR_ADD_USERNAME(pc_attr_vdata->p_data_ptr, pc_attr_vdata->u_data_size), + TNET_STUN_PKT_ATTR_ADD_NULL()); + if (ret) { + goto bail; + } + } + } + // MAPPED-ADDRESS and XOR-MAPPED-ADDRESS + if ((ret = tnet_get_sockip_n_port((const struct sockaddr*)remote_addr, &remote_ip, &remote_port)) == 0) { + tnet_stun_addr_t _addr; + tnet_stun_address_family_t _familly = (remote_addr->ss_family == AF_INET6) ? tnet_stun_address_family_ipv6 : tnet_stun_address_family_ipv4; + if ((ret = tnet_stun_utils_inet_pton((_familly == tnet_stun_address_family_ipv6), remote_ip, &_addr)) == 0) { + ret = tnet_stun_pkt_attrs_add(message, + TNET_STUN_PKT_ATTR_ADD_MAPPED_ADDRESS(_familly, remote_port, &_addr), + TNET_STUN_PKT_ATTR_ADD_XOR_MAPPED_ADDRESS(_familly, remote_port, &_addr), + TNET_STUN_PKT_ATTR_ADD_NULL()); + if (ret) { + goto bail; + } + } + } + } + + if (self->candidate_offer->turn.ss) { + enum tnet_stun_state_e e_state; + if ((ret = tnet_turn_session_get_state_createperm(self->candidate_offer->turn.ss, self->turn_peer_id, &e_state))) { + goto bail; + } + if (e_state != tnet_stun_state_ok) { + TSK_DEBUG_INFO("TURN CreatePerm not ready yet... to send STUN response (peer-id=%ld)", self->turn_peer_id); + goto bail; + } + if ((ret = tnet_stun_pkt_write_with_padding_2(message, &req_buffer))) { + goto bail; + } + if ((ret = tnet_turn_session_send_data(self->candidate_offer->turn.ss, self->turn_peer_id, req_buffer->data, (uint16_t)req_buffer->size))) { + goto bail; + } + } + else { + struct sockaddr_storage dest_addr; + int sendBytes; + if ((ret = tnet_sockaddr_init(self->candidate_answer->connection_addr, self->candidate_answer->port, self->candidate_offer->socket->type, &dest_addr))) { + TNET_PRINT_LAST_ERROR("tnet_sockaddr_init(%s:%d) failed", self->candidate_answer->connection_addr, self->candidate_answer->port); + goto bail; + } + if ((ret = tnet_stun_pkt_write_with_padding_2(message, &req_buffer))) { + goto bail; + } + sendBytes = tnet_sockfd_sendto(self->candidate_offer->socket->fd, (const struct sockaddr*)&dest_addr, req_buffer->data, req_buffer->size); + TSK_OBJECT_SAFE_FREE(req_buffer); + ret = (sendBytes > 0) ? 0 : -2; + if (ret != 0) { + TSK_DEBUG_ERROR("ICE pair-answer: failed to send response"); + } + } + } + + if (ret == 0 && !is_error) { + tsk_bool_t change_state = + self->is_ice_jingle // ICE-JINGLE don't have ICE-CONTROLLING/ICE-CONTROLLED attributes + || (!self->is_controlling && tnet_stun_pkt_attr_exists(request, tnet_stun_attr_type_ice_use_candidate)) // Sender is controlling and uses "ICE-USE-CANDIDATE" attribute #if TNET_ICE_AGGRESSIVE_NOMINATION || 1 // This is not a typo but a *must*. We've to change the answer state regardless the nomination mode otherwise we'll never get a nominee. Only the offer state is controlled based on the mode and depends on "is_nominated". - || (self->is_controlling) // We are controlling and using aggressive mode + || (self->is_controlling) // We are controlling and using aggressive mode #else - || (self->is_controlling && self->is_nominated) // We're controlling and using regular mode + || (self->is_controlling && self->is_nominated) // We're controlling and using regular mode #endif - ; - TNET_ICE_PAIR_DEBUG_INFO("ICE pair-answer changing state to 'succeed' ? %s, comp-id=%d, found=%s, addr=%s", - change_state?"yes":"no", - self->candidate_answer->comp_id, - self->candidate_answer->foundation, - self->candidate_answer->connection_addr - ); - if (change_state) { - self->state_answer = tnet_ice_pair_state_succeed; - } - } - - -bail: - TSK_OBJECT_SAFE_FREE(message); - return ret; -} - -int tnet_ice_pair_auth_conncheck(const tnet_ice_pair_t *self, const tnet_stun_pkt_req_t* request, const void* request_buff, tsk_size_t request_buff_size, short* resp_code, char** resp_phrase) -{ - const uint8_t* _request_buff = (const uint8_t*)request_buff; - - const tnet_stun_attr_t* stun_att; - const tnet_stun_attr_vdata_t *stun_att_usr_name; - const tnet_stun_attr_vdata_t *stun_att_fingerprint; - const tnet_stun_attr_vdata_t *stun_att_integrity; - - const tsk_list_item_t *item; - tsk_sha1digest_t hmac; - const char* pwd; - - tsk_size_t msg_integrity_start = 0, length, i; - - if(!self || !request || !request_buff || !request_buff_size || !resp_code || !resp_phrase){ - TSK_DEBUG_ERROR("Invalid parameter"); - return -1; - } - - if(!TNET_STUN_BUFF_IS_STUN2(_request_buff, request_buff_size)){ - TSK_DEBUG_ERROR("Not STUN buffer"); - return -1; - } - - pwd = tnet_ice_candidate_get_pwd(self->candidate_offer); - stun_att_usr_name = tsk_null; - stun_att_fingerprint = tsk_null; - stun_att_integrity = tsk_null; - - tsk_list_foreach(item, request->p_list_attrs) { - if ((!(stun_att = (const tnet_stun_attr_t*)item->data))) { - continue; - } - - switch (stun_att->hdr.e_type) { - case tnet_stun_attr_type_username: - { - stun_att_usr_name = (const tnet_stun_attr_vdata_t *)stun_att; - break; - } - case tnet_stun_attr_type_fingerprint: - { - stun_att_fingerprint = (const tnet_stun_attr_vdata_t *)stun_att; - break; - } - case tnet_stun_attr_type_message_integrity: - { - stun_att_integrity = (const tnet_stun_attr_vdata_t *)stun_att; - break; - } - default: break; - } - - if (!stun_att_integrity) { - if ((length = (kStunAttrHdrSizeInOctets + stun_att->hdr.u_length)) & 0x03) { - length += (4 - (length & 0x03)); - } - msg_integrity_start += length; - } - } - - if (!stun_att_usr_name) { - TSK_DEBUG_ERROR("USERNAME is missing"); - *resp_code = 400; - tsk_strupdate(resp_phrase, "USERNAME is missing"); - return -2; - } - - if (!stun_att_integrity || stun_att_integrity->u_data_size != TSK_SHA1_DIGEST_SIZE) { - if (self->is_ice_jingle) { // Bug introduced in Chrome 20.0.1120.0 (Not security issue as ICE-JINGLE is deprecated and will never be ON) - *resp_code = 200; - tsk_strupdate(resp_phrase, "MESSAGE-INTEGRITY is missing but accepted"); - return 0; - } - else { - TSK_DEBUG_ERROR("MESSAGE-INTEGRITY is missing"); - *resp_code = 400; - tsk_strupdate(resp_phrase, "MESSAGE-INTEGRITY is missing"); - return -3; - } - } - - if ((kStunPktHdrSizeInOctets + msg_integrity_start) >= request_buff_size) { - TSK_DEBUG_ERROR("Invalid length"); - *resp_code = 400; - tsk_strupdate(resp_phrase, "Invalid length"); - return -20; - } - - if (request->u_length != msg_integrity_start) { - tsk_size_t size = (kStunPktHdrSizeInOctets + msg_integrity_start); - uint8_t* new_buffer = (uint8_t*)tsk_calloc(size, 1); - if (!new_buffer) { - TSK_DEBUG_ERROR("Failed to allocate buffer with size = %u", (unsigned)msg_integrity_start); - return -30; - } - memcpy(new_buffer, request_buff, size); - length = msg_integrity_start + (kStunAttrHdrSizeInOctets + TSK_SHA1_DIGEST_SIZE /* INTEGRITY VALUE*/); - new_buffer[2] = (length >> 8) & 0xFF; - new_buffer[3] = (length & 0xFF); - hmac_sha1digest_compute(new_buffer, size, pwd, tsk_strlen(pwd), hmac); - TSK_FREE(new_buffer); - } - else { - // must never happen - hmac_sha1digest_compute(request_buff, request_buff_size, pwd, tsk_strlen(pwd), hmac); - } - - for (i = 0; i < TSK_SHA1_DIGEST_SIZE; ++i) { - if (hmac[i] != stun_att_integrity->p_data_ptr[i]) { - TSK_DEBUG_ERROR("MESSAGE-INTEGRITY mismatch"); - *resp_code = 401; - tsk_strupdate(resp_phrase, "MESSAGE-INTEGRITY mismatch"); - return -40; - } - } - - *resp_code = 200; - tsk_strupdate(resp_phrase, "Ok"); - - return 0; -} - -int tnet_ice_pair_recv_response(tnet_ice_pair_t *self, const tnet_stun_pkt_resp_t* response, tsk_bool_t is_4conncheck) -{ - if (self && response && TNET_STUN_PKT_IS_RESP(response)) { - if (self->last_request && tnet_stun_utils_transac_id_equals(self->last_request->transac_id, response->transac_id)){ - // ignore errors (e.g. STALE-CREDENTIALS) which could happen in some special cases before success - if (TNET_STUN_PKT_RESP_IS_SUCCESS(response)) { - if (is_4conncheck) { - self->state_offer = tnet_ice_pair_state_succeed; // we must not change the state after connection cheking to make sure another pair won't be picked as nominated - TNET_ICE_PAIR_DEBUG_INFO("ICE pair-offer changing state to 'succeed', comp-id=%d, found=%s, addr=%s", - self->candidate_offer->comp_id, - self->candidate_offer->foundation, - self->candidate_offer->connection_addr - ); - } - } - else { - // The response is an error - uint16_t u_code; - int ret; - if ((ret = tnet_stun_pkt_get_errorcode(response, &u_code)) == 0 && u_code == kStunErrCodeIceConflict) { - TSK_DEBUG_INFO("ICE Pair %llu received conflict error message", self->id); - // If this code is called this means that we have lower tie-breaker and we must toggle our role - self->is_controlling = !self->is_controlling; - TSK_OBJECT_SAFE_FREE(self->last_request); // delete the "last_request" to make sure a new one will be created with right attributes - } - } - } - } - return 0; -} - -const tnet_ice_pair_t* tnet_ice_pairs_find_by_response(tnet_ice_pairs_L_t* pairs, const tnet_stun_pkt_t* response) -{ - if(pairs && response){ - const tsk_list_item_t *item; - const tnet_ice_pair_t *pair; - tnet_port_t mapped_port; - tnet_ip_t mapped_ip; - tsk_list_foreach(item, pairs){ - if(!(pair = item->data) || !pair->candidate_answer || !pair->candidate_offer){ - continue; - } - if(pair->last_request && tnet_stun_utils_transac_id_equals(pair->last_request->transac_id, response->transac_id)){ - // check that mapped/xmapped address match destination - const tnet_stun_attr_address_t *xmapped_addr = tsk_null; - const tnet_stun_attr_address_t* mapped_addr = tsk_null; - const tnet_stun_attr_address_t* _addr; - - tnet_stun_pkt_attr_find_first(response, tnet_stun_attr_type_xor_mapped_address, (const tnet_stun_attr_t **)&xmapped_addr); - tnet_stun_pkt_attr_find_first(response, tnet_stun_attr_type_mapped_address, (const tnet_stun_attr_t **)&mapped_addr); - _addr = xmapped_addr ? xmapped_addr : mapped_addr; - - if (!_addr) { - return pair; // do nothing if the client doesn't return mapped address STUN attribute - } - /* rfc 5245 7.1.3.2.1. Discovering Peer Reflexive Candidates - - The agent checks the mapped address from the STUN response. If the - transport 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. Like other candidates, it has a type, - base, priority, and foundation. They are computed as follows: - - o Its type is equal to peer reflexive. - - o Its base is set equal to the local candidate of the candidate pair - from which the STUN check was sent. - - o Its priority is set equal to the value of the PRIORITY attribute - in the Binding request. - - o Its foundation is selected as described in Section 4.1.1.3. - - This peer reflexive candidate is then added to the list of local - candidates for the media stream. Its username fragment and password - are the same as all other local candidates for that media stream. - */ - - tnet_stun_utils_inet_ntop((_addr->e_family == tnet_stun_address_family_ipv6), &_addr->address, &mapped_ip); - mapped_port = _addr->u_port; - if (pair->candidate_offer->type_e != tnet_ice_cand_type_host && (mapped_port != pair->candidate_offer->port || !tsk_striequals(mapped_ip, pair->candidate_offer->connection_addr))) { - TSK_DEBUG_INFO("Mapped address different than local connection address...probably symetric NAT: %s#%s or %u#%u", - pair->candidate_offer->connection_addr, mapped_ip, - pair->candidate_offer->port, mapped_port); - // do we really need to add new local candidate? - // continue; - } - return pair; - } - } - } - return tsk_null; -} - -const tnet_ice_pair_t* tnet_ice_pairs_find_by_fd_and_addr(tnet_ice_pairs_L_t* pairs, tnet_fd_t local_fd, const struct sockaddr_storage *remote_addr) -{ - int ret; - const tsk_list_item_t *item; - const tnet_ice_pair_t *pair; - tnet_ip_t remote_ip; - tnet_port_t remote_port; - - if(!pairs || !remote_addr){ - TSK_DEBUG_ERROR("Invalid parameter"); - return tsk_null; - } - - if((ret = tnet_get_sockip_n_port((const struct sockaddr*)remote_addr, &remote_ip, &remote_port))){ - TNET_PRINT_LAST_ERROR("tnet_get_sockip_n_port() failed"); - return tsk_null; - } - - tsk_list_foreach(item, pairs){ - if (!(pair = item->data) || !pair->candidate_offer || !pair->candidate_offer->socket || pair->candidate_offer->socket->fd != local_fd) { - continue; - } - if (!tsk_striequals(pair->candidate_answer->connection_addr, remote_ip) || pair->candidate_answer->port != remote_port) { - continue; - } - - return pair; - } - - TSK_DEBUG_INFO("No ICE candidate with remote ip = %s, port = %u and local_fd = %d could be found...probably symetric NAT", remote_ip, remote_port, local_fd); - - return tsk_null; -} - - -static tsk_bool_t _tnet_ice_pairs_none_succeed(const tnet_ice_pairs_L_t* pairs, uint32_t comp_id, const char* foundation, tsk_bool_t answer){ - if(pairs && foundation){ - const tsk_list_item_t *item; - const tnet_ice_pair_t *pair; - const tnet_ice_candidate_t* candidate; - tsk_list_foreach(item, pairs){ - if(!(pair = item->data) || !(candidate = (answer ? pair->candidate_answer : pair->candidate_offer))){ - continue; - } - if(candidate->comp_id != comp_id || !tsk_striequals(candidate->foundation, foundation)){ - continue; - } - if((answer ? pair->state_answer : pair->state_offer) == tnet_ice_pair_state_succeed){ - TNET_ICE_PAIR_DEBUG_INFO("_tnet_ice_pairs_none_succeed_%s(%u, %s):false", answer?"anwser":"offer", comp_id, foundation); - return tsk_false; - } - } - } - TNET_ICE_PAIR_DEBUG_INFO("_tnet_ice_pairs_none_succeed_%s(%u, %s):true", answer?"anwser":"offer", comp_id, foundation); - return tsk_true; -} + ; + TNET_ICE_PAIR_DEBUG_INFO("ICE pair-answer changing state to 'succeed' ? %s, comp-id=%d, found=%s, addr=%s", + change_state?"yes":"no", + self->candidate_answer->comp_id, + self->candidate_answer->foundation, + self->candidate_answer->connection_addr + ); + if (change_state) { + self->state_answer = tnet_ice_pair_state_succeed; + } + } + + + bail: + TSK_OBJECT_SAFE_FREE(message); + return ret; + } + + int tnet_ice_pair_auth_conncheck(const tnet_ice_pair_t *self, const tnet_stun_pkt_req_t* request, const void* request_buff, tsk_size_t request_buff_size, short* resp_code, char** resp_phrase) + { + const uint8_t* _request_buff = (const uint8_t*)request_buff; + + const tnet_stun_attr_t* stun_att; + const tnet_stun_attr_vdata_t *stun_att_usr_name; + const tnet_stun_attr_vdata_t *stun_att_fingerprint; + const tnet_stun_attr_vdata_t *stun_att_integrity; + + const tsk_list_item_t *item; + tsk_sha1digest_t hmac; + const char* pwd; + + tsk_size_t msg_integrity_start = 0, length, i; + + if(!self || !request || !request_buff || !request_buff_size || !resp_code || !resp_phrase){ + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + + if(!TNET_STUN_BUFF_IS_STUN2(_request_buff, request_buff_size)){ + TSK_DEBUG_ERROR("Not STUN buffer"); + return -1; + } + + pwd = tnet_ice_candidate_get_pwd(self->candidate_offer); + stun_att_usr_name = tsk_null; + stun_att_fingerprint = tsk_null; + stun_att_integrity = tsk_null; + + tsk_list_foreach(item, request->p_list_attrs) { + if ((!(stun_att = (const tnet_stun_attr_t*)item->data))) { + continue; + } + + switch (stun_att->hdr.e_type) { + case tnet_stun_attr_type_username: + { + stun_att_usr_name = (const tnet_stun_attr_vdata_t *)stun_att; + break; + } + case tnet_stun_attr_type_fingerprint: + { + stun_att_fingerprint = (const tnet_stun_attr_vdata_t *)stun_att; + break; + } + case tnet_stun_attr_type_message_integrity: + { + stun_att_integrity = (const tnet_stun_attr_vdata_t *)stun_att; + break; + } + default: break; + } + + if (!stun_att_integrity) { + if ((length = (kStunAttrHdrSizeInOctets + stun_att->hdr.u_length)) & 0x03) { + length += (4 - (length & 0x03)); + } + msg_integrity_start += length; + } + } + + if (!stun_att_usr_name) { + TSK_DEBUG_ERROR("USERNAME is missing"); + *resp_code = 400; + tsk_strupdate(resp_phrase, "USERNAME is missing"); + return -2; + } + + if (!stun_att_integrity || stun_att_integrity->u_data_size != TSK_SHA1_DIGEST_SIZE) { + if (self->is_ice_jingle) { // Bug introduced in Chrome 20.0.1120.0 (Not security issue as ICE-JINGLE is deprecated and will never be ON) + *resp_code = 200; + tsk_strupdate(resp_phrase, "MESSAGE-INTEGRITY is missing but accepted"); + return 0; + } + else { + TSK_DEBUG_ERROR("MESSAGE-INTEGRITY is missing"); + *resp_code = 400; + tsk_strupdate(resp_phrase, "MESSAGE-INTEGRITY is missing"); + return -3; + } + } + + if ((kStunPktHdrSizeInOctets + msg_integrity_start) >= request_buff_size) { + TSK_DEBUG_ERROR("Invalid length"); + *resp_code = 400; + tsk_strupdate(resp_phrase, "Invalid length"); + return -20; + } + + if (request->u_length != msg_integrity_start) { + tsk_size_t size = (kStunPktHdrSizeInOctets + msg_integrity_start); + uint8_t* new_buffer = (uint8_t*)tsk_calloc(size, 1); + if (!new_buffer) { + TSK_DEBUG_ERROR("Failed to allocate buffer with size = %u", (unsigned)msg_integrity_start); + return -30; + } + memcpy(new_buffer, request_buff, size); + length = msg_integrity_start + (kStunAttrHdrSizeInOctets + TSK_SHA1_DIGEST_SIZE /* INTEGRITY VALUE*/); + new_buffer[2] = (length >> 8) & 0xFF; + new_buffer[3] = (length & 0xFF); + hmac_sha1digest_compute(new_buffer, size, pwd, tsk_strlen(pwd), hmac); + TSK_FREE(new_buffer); + } + else { + // must never happen + hmac_sha1digest_compute(request_buff, request_buff_size, pwd, tsk_strlen(pwd), hmac); + } + + for (i = 0; i < TSK_SHA1_DIGEST_SIZE; ++i) { + if (hmac[i] != stun_att_integrity->p_data_ptr[i]) { + TSK_DEBUG_ERROR("MESSAGE-INTEGRITY mismatch"); + *resp_code = 401; + tsk_strupdate(resp_phrase, "MESSAGE-INTEGRITY mismatch"); + return -40; + } + } + + *resp_code = 200; + tsk_strupdate(resp_phrase, "Ok"); + + return 0; + } + + int tnet_ice_pair_recv_response(tnet_ice_pair_t *self, const tnet_stun_pkt_resp_t* response, tsk_bool_t is_4conncheck) + { + if (self && response && TNET_STUN_PKT_IS_RESP(response)) { + if (self->last_request && tnet_stun_utils_transac_id_equals(self->last_request->transac_id, response->transac_id)){ + // ignore errors (e.g. STALE-CREDENTIALS) which could happen in some special cases before success + if (TNET_STUN_PKT_RESP_IS_SUCCESS(response)) { + if (is_4conncheck) { + self->state_offer = tnet_ice_pair_state_succeed; // we must not change the state after connection cheking to make sure another pair won't be picked as nominated + TNET_ICE_PAIR_DEBUG_INFO("ICE pair-offer changing state to 'succeed', comp-id=%d, found=%s, addr=%s", + self->candidate_offer->comp_id, + self->candidate_offer->foundation, + self->candidate_offer->connection_addr + ); + } + } + else { + // The response is an error + uint16_t u_code; + int ret; + if ((ret = tnet_stun_pkt_get_errorcode(response, &u_code)) == 0 && u_code == kStunErrCodeIceConflict) { + TSK_DEBUG_INFO("ICE Pair %llu received conflict error message", self->id); + // If this code is called this means that we have lower tie-breaker and we must toggle our role + self->is_controlling = !self->is_controlling; + TSK_OBJECT_SAFE_FREE(self->last_request); // delete the "last_request" to make sure a new one will be created with right attributes + } + } + } + } + return 0; + } + + const tnet_ice_pair_t* tnet_ice_pairs_find_by_response(tnet_ice_pairs_L_t* pairs, const tnet_stun_pkt_t* response) + { + if(pairs && response){ + const tsk_list_item_t *item; + const tnet_ice_pair_t *pair; + tnet_port_t mapped_port; + tnet_ip_t mapped_ip; + tsk_list_foreach(item, pairs){ + if(!(pair = item->data) || !pair->candidate_answer || !pair->candidate_offer){ + continue; + } + if(pair->last_request && tnet_stun_utils_transac_id_equals(pair->last_request->transac_id, response->transac_id)){ + // check that mapped/xmapped address match destination + const tnet_stun_attr_address_t *xmapped_addr = tsk_null; + const tnet_stun_attr_address_t* mapped_addr = tsk_null; + const tnet_stun_attr_address_t* _addr; + + tnet_stun_pkt_attr_find_first(response, tnet_stun_attr_type_xor_mapped_address, (const tnet_stun_attr_t **)&xmapped_addr); + tnet_stun_pkt_attr_find_first(response, tnet_stun_attr_type_mapped_address, (const tnet_stun_attr_t **)&mapped_addr); + _addr = xmapped_addr ? xmapped_addr : mapped_addr; + + if (!_addr) { + return pair; // do nothing if the client doesn't return mapped address STUN attribute + } + /* rfc 5245 7.1.3.2.1. Discovering Peer Reflexive Candidates + + The agent checks the mapped address from the STUN response. If the + transport 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. Like other candidates, it has a type, + base, priority, and foundation. They are computed as follows: + + o Its type is equal to peer reflexive. + + o Its base is set equal to the local candidate of the candidate pair + from which the STUN check was sent. + + o Its priority is set equal to the value of the PRIORITY attribute + in the Binding request. + + o Its foundation is selected as described in Section 4.1.1.3. + + This peer reflexive candidate is then added to the list of local + candidates for the media stream. Its username fragment and password + are the same as all other local candidates for that media stream. + */ + + tnet_stun_utils_inet_ntop((_addr->e_family == tnet_stun_address_family_ipv6), &_addr->address, &mapped_ip); + mapped_port = _addr->u_port; + if (pair->candidate_offer->type_e != tnet_ice_cand_type_host && (mapped_port != pair->candidate_offer->port || !tsk_striequals(mapped_ip, pair->candidate_offer->connection_addr))) { + TSK_DEBUG_INFO("Mapped address different than local connection address...probably symetric NAT: %s#%s or %u#%u", + pair->candidate_offer->connection_addr, mapped_ip, + pair->candidate_offer->port, mapped_port); + // do we really need to add new local candidate? + // continue; + } + return pair; + } + } + } + return tsk_null; + } + + const tnet_ice_pair_t* tnet_ice_pairs_find_by_fd_and_addr(tnet_ice_pairs_L_t* pairs, tnet_fd_t local_fd, const struct sockaddr_storage *remote_addr) + { + int ret; + const tsk_list_item_t *item; + const tnet_ice_pair_t *pair; + tnet_ip_t remote_ip; + tnet_port_t remote_port; + + if(!pairs || !remote_addr){ + TSK_DEBUG_ERROR("Invalid parameter"); + return tsk_null; + } + + if((ret = tnet_get_sockip_n_port((const struct sockaddr*)remote_addr, &remote_ip, &remote_port))){ + TNET_PRINT_LAST_ERROR("tnet_get_sockip_n_port() failed"); + return tsk_null; + } + + tsk_list_foreach(item, pairs){ + if (!(pair = item->data) || !pair->candidate_offer || !pair->candidate_offer->socket || pair->candidate_offer->socket->fd != local_fd) { + continue; + } + if (!tsk_striequals(pair->candidate_answer->connection_addr, remote_ip) || pair->candidate_answer->port != remote_port) { + continue; + } + + return pair; + } + + TSK_DEBUG_INFO("No ICE candidate with remote ip = %s, port = %u and local_fd = %d could be found...probably symetric NAT", remote_ip, remote_port, local_fd); + + return tsk_null; + } + + + static tsk_bool_t _tnet_ice_pairs_none_succeed(const tnet_ice_pairs_L_t* pairs, uint32_t comp_id, const char* foundation, tsk_bool_t answer){ + if(pairs && foundation){ + const tsk_list_item_t *item; + const tnet_ice_pair_t *pair; + const tnet_ice_candidate_t* candidate; + tsk_list_foreach(item, pairs){ + if(!(pair = item->data) || !(candidate = (answer ? pair->candidate_answer : pair->candidate_offer))){ + continue; + } + if(candidate->comp_id != comp_id || !tsk_striequals(candidate->foundation, foundation)){ + continue; + } + if((answer ? pair->state_answer : pair->state_offer) == tnet_ice_pair_state_succeed){ + TNET_ICE_PAIR_DEBUG_INFO("_tnet_ice_pairs_none_succeed_%s(%u, %s):false", answer?"anwser":"offer", comp_id, foundation); + return tsk_false; + } + } + } + TNET_ICE_PAIR_DEBUG_INFO("_tnet_ice_pairs_none_succeed_%s(%u, %s):true", answer?"anwser":"offer", comp_id, foundation); + return tsk_true; + } #define _tnet_ice_pairs_none_succeed_answer(pairs, comp_id, foundation) _tnet_ice_pairs_none_succeed((pairs), (comp_id), (foundation), tsk_true) #define _tnet_ice_pairs_none_succeed_offer(pairs, comp_id, foundation) _tnet_ice_pairs_none_succeed((pairs), (comp_id), (foundation), tsk_false) - -// both RTP and RTCP have succeeded + + // both RTP and RTCP have succeeded #define _tnet_ice_pairs_get_nominated_offer_at(pairs, index, comp_id, check_fullness, ret) _tnet_ice_pairs_get_nominated_at((pairs), offer, answer, (index), (comp_id), (check_fullness), (ret)) #define _tnet_ice_pairs_get_nominated_answer_at(pairs, index, comp_id, check_fullness, ret) _tnet_ice_pairs_get_nominated_at((pairs), answer, offer, (index), (comp_id), (check_fullness), (ret)) #define _tnet_ice_pairs_get_nominated_at(pairs, dir_1, dir_2, index, _comp_id, check_fullness, ret) \ { \ - ret = tsk_null; \ - if (pairs) { \ - const tsk_list_item_t *item; \ - const tnet_ice_pair_t *pair; \ - tsk_size_t pos = 0; \ - tsk_bool_t nominated; \ - tsk_list_foreach(item, pairs) { \ - if (!(pair = item->data)) { \ - continue; \ - } \ - TNET_ICE_PAIR_DEBUG_INFO("ICE pair(%d,dir_1) state=%d, compid=%d, found=%s, addr=%s", _comp_id, pair->state_##dir_1, pair->candidate_##dir_1->comp_id, pair->candidate_##dir_1->foundation, pair->candidate_##dir_1->connection_addr); \ - TNET_ICE_PAIR_DEBUG_INFO("ICE pair(%d,dir_2) state=%d, compid=%d, found=%s, addr=%s", _comp_id, pair->state_##dir_2, pair->candidate_##dir_2->comp_id, pair->candidate_##dir_2->foundation, pair->candidate_##dir_2->connection_addr); \ - if (pair->state_##dir_1 == tnet_ice_pair_state_succeed && pair->candidate_##dir_1->comp_id == _comp_id) { \ - nominated = tsk_true; \ - if (check_fullness) { \ - /* find another pair with same foundation but different comp-id (e.g. RTCP) */ \ - const tsk_list_item_t *item2; \ - const tnet_ice_pair_t *pair2; \ - tsk_list_foreach(item2, pairs) { \ - if (!(pair2 = item2->data)) { \ - continue; \ - } \ - TNET_ICE_PAIR_DEBUG_INFO("ICE pair2(dir_1) state=%d, compid=%d, found=%s, addr=%s", pair2->state_##dir_1, pair2->candidate_##dir_1->comp_id, pair2->candidate_##dir_1->foundation, pair2->candidate_##dir_1->connection_addr); \ - TNET_ICE_PAIR_DEBUG_INFO("ICE pair2(dir_2) state=%d, compid=%d, found=%s, addr=%s", pair2->state_##dir_2, pair2->candidate_##dir_2->comp_id, pair2->candidate_##dir_2->foundation, pair2->candidate_##dir_2->connection_addr); \ - if ((tsk_striequals(pair2->candidate_##dir_2->foundation, pair->candidate_##dir_2->foundation)) \ - && (pair2->candidate_##dir_2->comp_id != pair->candidate_##dir_2->comp_id)) { \ - /* nominated = (pair2->state_##dir_2 == tnet_ice_pair_state_succeed); */ \ - nominated = !_tnet_ice_pairs_none_succeed_##dir_2(pairs, pair2->candidate_##dir_2->comp_id, pair2->candidate_##dir_2->foundation); \ - break; \ - } \ - } \ - } \ - \ - if (nominated && (pos++ >= index)) { \ - ret = pair; \ - break; \ - }\ - } \ - } \ - } \ +ret = tsk_null; \ +if (pairs) { \ +const tsk_list_item_t *item; \ +const tnet_ice_pair_t *pair; \ +tsk_size_t pos = 0; \ +tsk_bool_t nominated; \ +tsk_list_foreach(item, pairs) { \ +if (!(pair = item->data)) { \ +continue; \ +} \ +TNET_ICE_PAIR_DEBUG_INFO("ICE pair(%d,dir_1) state=%d, compid=%d, found=%s, addr=%s", _comp_id, pair->state_##dir_1, pair->candidate_##dir_1->comp_id, pair->candidate_##dir_1->foundation, pair->candidate_##dir_1->connection_addr); \ +TNET_ICE_PAIR_DEBUG_INFO("ICE pair(%d,dir_2) state=%d, compid=%d, found=%s, addr=%s", _comp_id, pair->state_##dir_2, pair->candidate_##dir_2->comp_id, pair->candidate_##dir_2->foundation, pair->candidate_##dir_2->connection_addr); \ +if (pair->state_##dir_1 == tnet_ice_pair_state_succeed && pair->candidate_##dir_1->comp_id == _comp_id) { \ +nominated = tsk_true; \ +if (check_fullness) { \ +/* find another pair with same foundation but different comp-id (e.g. RTCP) */ \ +const tsk_list_item_t *item2; \ +const tnet_ice_pair_t *pair2; \ +tsk_list_foreach(item2, pairs) { \ +if (!(pair2 = item2->data)) { \ +continue; \ +} \ +TNET_ICE_PAIR_DEBUG_INFO("ICE pair2(dir_1) state=%d, compid=%d, found=%s, addr=%s", pair2->state_##dir_1, pair2->candidate_##dir_1->comp_id, pair2->candidate_##dir_1->foundation, pair2->candidate_##dir_1->connection_addr); \ +TNET_ICE_PAIR_DEBUG_INFO("ICE pair2(dir_2) state=%d, compid=%d, found=%s, addr=%s", pair2->state_##dir_2, pair2->candidate_##dir_2->comp_id, pair2->candidate_##dir_2->foundation, pair2->candidate_##dir_2->connection_addr); \ +if ((tsk_striequals(pair2->candidate_##dir_2->foundation, pair->candidate_##dir_2->foundation)) \ +&& (pair2->candidate_##dir_2->comp_id != pair->candidate_##dir_2->comp_id)) { \ +/* nominated = (pair2->state_##dir_2 == tnet_ice_pair_state_succeed); */ \ +nominated = !_tnet_ice_pairs_none_succeed_##dir_2(pairs, pair2->candidate_##dir_2->comp_id, pair2->candidate_##dir_2->foundation); \ +break; \ +} \ +} \ +} \ +\ +if (nominated && (pos++ >= index)) { \ +ret = pair; \ +break; \ +}\ +} \ +} \ +} \ } \ -// true only if both RTP and RTCP are nominated -tsk_bool_t tnet_ice_pairs_have_nominated_offer(const tnet_ice_pairs_L_t* pairs, tsk_bool_t check_rtcp) -{ - const tnet_ice_pair_t *pair_ = tsk_null; - tsk_bool_t is_nominated_rtp, is_nominated_rtcp = tsk_true; - TNET_ICE_PAIR_DEBUG_INFO("tnet_ice_pairs_have_nominated_offer()"); - _tnet_ice_pairs_get_nominated_offer_at((pairs), 0, TNET_ICE_CANDIDATE_COMPID_RTP, check_rtcp, (pair_)); - if((is_nominated_rtp = (pair_ != tsk_null)) && check_rtcp){ - _tnet_ice_pairs_get_nominated_offer_at((pairs), 0, TNET_ICE_CANDIDATE_COMPID_RTCP, check_rtcp, (pair_)); - is_nominated_rtcp =(pair_ != tsk_null); - } - TNET_ICE_PAIR_DEBUG_INFO("is_nominated_rtp_offer=%s, is_nominated_rtcp_offer=%s", is_nominated_rtp?"yes":"no", is_nominated_rtcp?"yes":"no"); - return (is_nominated_rtp && is_nominated_rtcp); -} - -// true only if both RTP and RTCP are nominated -tsk_bool_t tnet_ice_pairs_have_nominated_answer(const tnet_ice_pairs_L_t* pairs, tsk_bool_t check_rtcp) -{ - const tnet_ice_pair_t *pair_ = tsk_null; - tsk_bool_t is_nominated_rtp, is_nominated_rtcp = tsk_true; - TNET_ICE_PAIR_DEBUG_INFO("tnet_ice_pairs_have_nominated_answer()"); - _tnet_ice_pairs_get_nominated_answer_at((pairs), 0, TNET_ICE_CANDIDATE_COMPID_RTP, check_rtcp, (pair_)); - if((is_nominated_rtp = (pair_ != tsk_null)) && check_rtcp){ - _tnet_ice_pairs_get_nominated_answer_at((pairs), 0, TNET_ICE_CANDIDATE_COMPID_RTCP, check_rtcp, (pair_)); - is_nominated_rtcp =(pair_ != tsk_null); - } - TNET_ICE_PAIR_DEBUG_INFO("is_nominated_rtp_answer=%s, is_nominated_rtcp_answer=%s", is_nominated_rtp?"yes":"no", is_nominated_rtcp?"yes":"no"); - return (is_nominated_rtp && is_nominated_rtcp); -} - -// true only if both RTP and RTCP are nominated in symetric way -tsk_bool_t tnet_ice_pairs_have_nominated_symetric_2(const tnet_ice_pairs_L_t* pairs, tsk_bool_t check_rtcp, tsk_bool_t *got_hosts) -{ - const tnet_ice_candidate_t *candidate_offer, *candidate_answer_src, *candidate_answer_dest; - tsk_bool_t is_nominated_rtp, is_nominated_rtcp = tsk_true; - int ret; - - if (got_hosts) { - *got_hosts = tsk_false; - } - ret = tnet_ice_pairs_get_nominated_symetric_candidates(pairs, TNET_ICE_CANDIDATE_COMPID_RTP, &candidate_offer, &candidate_answer_src, &candidate_answer_dest); - if ((is_nominated_rtp = (ret == 0 && candidate_offer && candidate_answer_src && candidate_answer_dest)) && got_hosts) { - *got_hosts = (candidate_offer->type_e == tnet_ice_cand_type_host - && candidate_answer_src->type_e == tnet_ice_cand_type_host - && candidate_answer_dest->type_e == tnet_ice_cand_type_host); - } - if(is_nominated_rtp && check_rtcp){ - ret = tnet_ice_pairs_get_nominated_symetric_candidates(pairs, TNET_ICE_CANDIDATE_COMPID_RTCP, &candidate_offer, &candidate_answer_src, &candidate_answer_dest); - if ((is_nominated_rtcp = (ret == 0 && candidate_offer && candidate_answer_src && candidate_answer_dest)) && got_hosts) { - *got_hosts &= (candidate_offer->type_e == tnet_ice_cand_type_host - && candidate_answer_src->type_e == tnet_ice_cand_type_host - && candidate_answer_dest->type_e == tnet_ice_cand_type_host); - } - } - return (is_nominated_rtp && is_nominated_rtcp); -} - -// true only if both RTP and RTCP are nominated in symetric way -tsk_bool_t tnet_ice_pairs_have_nominated_symetric(const tnet_ice_pairs_L_t* pairs, tsk_bool_t check_rtcp) -{ + // true only if both RTP and RTCP are nominated + tsk_bool_t tnet_ice_pairs_have_nominated_offer(const tnet_ice_pairs_L_t* pairs, tsk_bool_t check_rtcp) + { + const tnet_ice_pair_t *pair_ = tsk_null; + tsk_bool_t is_nominated_rtp, is_nominated_rtcp = tsk_true; + TNET_ICE_PAIR_DEBUG_INFO("tnet_ice_pairs_have_nominated_offer()"); + _tnet_ice_pairs_get_nominated_offer_at((pairs), 0, TNET_ICE_CANDIDATE_COMPID_RTP, check_rtcp, (pair_)); + if((is_nominated_rtp = (pair_ != tsk_null)) && check_rtcp){ + _tnet_ice_pairs_get_nominated_offer_at((pairs), 0, TNET_ICE_CANDIDATE_COMPID_RTCP, check_rtcp, (pair_)); + is_nominated_rtcp =(pair_ != tsk_null); + } + TNET_ICE_PAIR_DEBUG_INFO("is_nominated_rtp_offer=%s, is_nominated_rtcp_offer=%s", is_nominated_rtp?"yes":"no", is_nominated_rtcp?"yes":"no"); + return (is_nominated_rtp && is_nominated_rtcp); + } + + // true only if both RTP and RTCP are nominated + tsk_bool_t tnet_ice_pairs_have_nominated_answer(const tnet_ice_pairs_L_t* pairs, tsk_bool_t check_rtcp) + { + const tnet_ice_pair_t *pair_ = tsk_null; + tsk_bool_t is_nominated_rtp, is_nominated_rtcp = tsk_true; + TNET_ICE_PAIR_DEBUG_INFO("tnet_ice_pairs_have_nominated_answer()"); + _tnet_ice_pairs_get_nominated_answer_at((pairs), 0, TNET_ICE_CANDIDATE_COMPID_RTP, check_rtcp, (pair_)); + if((is_nominated_rtp = (pair_ != tsk_null)) && check_rtcp){ + _tnet_ice_pairs_get_nominated_answer_at((pairs), 0, TNET_ICE_CANDIDATE_COMPID_RTCP, check_rtcp, (pair_)); + is_nominated_rtcp =(pair_ != tsk_null); + } + TNET_ICE_PAIR_DEBUG_INFO("is_nominated_rtp_answer=%s, is_nominated_rtcp_answer=%s", is_nominated_rtp?"yes":"no", is_nominated_rtcp?"yes":"no"); + return (is_nominated_rtp && is_nominated_rtcp); + } + + // true only if both RTP and RTCP are nominated in symetric way + tsk_bool_t tnet_ice_pairs_have_nominated_symetric_2(const tnet_ice_pairs_L_t* pairs, tsk_bool_t check_rtcp, tsk_bool_t *got_hosts) + { + const tnet_ice_candidate_t *candidate_offer, *candidate_answer_src, *candidate_answer_dest; + tsk_bool_t is_nominated_rtp, is_nominated_rtcp = tsk_true; + int ret; + + if (got_hosts) { + *got_hosts = tsk_false; + } + ret = tnet_ice_pairs_get_nominated_symetric_candidates(pairs, TNET_ICE_CANDIDATE_COMPID_RTP, &candidate_offer, &candidate_answer_src, &candidate_answer_dest); + if ((is_nominated_rtp = (ret == 0 && candidate_offer && candidate_answer_src && candidate_answer_dest)) && got_hosts) { + *got_hosts = (candidate_offer->type_e == tnet_ice_cand_type_host + && candidate_answer_src->type_e == tnet_ice_cand_type_host + && candidate_answer_dest->type_e == tnet_ice_cand_type_host); + } + if(is_nominated_rtp && check_rtcp){ + ret = tnet_ice_pairs_get_nominated_symetric_candidates(pairs, TNET_ICE_CANDIDATE_COMPID_RTCP, &candidate_offer, &candidate_answer_src, &candidate_answer_dest); + if ((is_nominated_rtcp = (ret == 0 && candidate_offer && candidate_answer_src && candidate_answer_dest)) && got_hosts) { + *got_hosts &= (candidate_offer->type_e == tnet_ice_cand_type_host + && candidate_answer_src->type_e == tnet_ice_cand_type_host + && candidate_answer_dest->type_e == tnet_ice_cand_type_host); + } + } + return (is_nominated_rtp && is_nominated_rtcp); + } + + // true only if both RTP and RTCP are nominated in symetric way + tsk_bool_t tnet_ice_pairs_have_nominated_symetric(const tnet_ice_pairs_L_t* pairs, tsk_bool_t check_rtcp) + { #define got_hosts tsk_null - return tnet_ice_pairs_have_nominated_symetric_2(pairs, check_rtcp, got_hosts); -} - -// gets symetric nominated candidates with the highest priority -// will succeed only if both RTP and RTCP are ok -int tnet_ice_pairs_get_nominated_symetric_candidates(const tnet_ice_pairs_L_t* pairs, uint32_t comp_id, - const tnet_ice_candidate_t** candidate_offer, - const tnet_ice_candidate_t** candidate_answer_src, - const tnet_ice_candidate_t** candidate_answer_dest) -{ - int ret; - const tnet_ice_pair_t *pair_offer = tsk_null, *pair_answer_src = tsk_null, *pair_answer_dest = tsk_null; - if (!pairs || !candidate_offer || !candidate_answer_src || !candidate_answer_dest) { - TSK_DEBUG_ERROR("Invalid parameter"); - return -1; - } - - *candidate_offer = tsk_null; - *candidate_answer_src = tsk_null; - *candidate_answer_dest = tsk_null; - - if ((ret = tnet_ice_pairs_get_nominated_symetric_pairs(pairs, comp_id, &pair_offer, &pair_answer_src, &pair_answer_dest)) == 0) { - *candidate_offer = pair_offer ? pair_offer->candidate_offer : tsk_null; - *candidate_answer_src = pair_answer_src ? pair_answer_src->candidate_answer : tsk_null; - *candidate_answer_dest = pair_answer_dest ? pair_answer_dest->candidate_answer : tsk_null; - } - return ret; -} - -int tnet_ice_pairs_get_nominated_symetric_pairs(const tnet_ice_pairs_L_t* pairs, uint32_t comp_id, - const struct tnet_ice_pair_s** _pair_offer, - const struct tnet_ice_pair_s** _pair_answer_src, - const struct tnet_ice_pair_s** _pair_answer_dest) -{ - const tnet_ice_pair_t *pair_offer = tsk_null; - const tnet_ice_pair_t *pair_answer = tsk_null; - tsk_size_t i_offer, i_answer; - static const tsk_bool_t __check_fullness = tsk_false; // pairs will be checked seperatly - - if (!pairs || !_pair_offer || !_pair_answer_src || !_pair_answer_dest) { - TSK_DEBUG_ERROR("Invalid parameter"); - return -1; - } - - *_pair_offer = tsk_null; - *_pair_answer_src = tsk_null; - *_pair_answer_dest = tsk_null; - - i_offer = 0; - while (1) { - _tnet_ice_pairs_get_nominated_offer_at((pairs), i_offer, comp_id, __check_fullness, (pair_offer)); // pair with socket SO as sender - if(!pair_offer) return 0; - ++i_offer; - if (pair_offer->candidate_offer->comp_id != comp_id) continue; - // find another pair with socket SO as receiver - - i_answer = 0; - while (1) { - _tnet_ice_pairs_get_nominated_answer_at((pairs), i_answer, comp_id, __check_fullness, (pair_answer)); - if (!pair_answer) break; - ++i_answer; - if (pair_answer->candidate_offer->comp_id != comp_id) continue; - if (pair_answer->candidate_offer == pair_offer->candidate_offer) { - *_pair_offer = pair_offer; - *_pair_answer_src = pair_answer; - *_pair_answer_dest = pair_offer; - return 0; - } - } - } - return 0; - -} + return tnet_ice_pairs_have_nominated_symetric_2(pairs, check_rtcp, got_hosts); + } + + // gets symetric nominated candidates with the highest priority + // will succeed only if both RTP and RTCP are ok + int tnet_ice_pairs_get_nominated_symetric_candidates(const tnet_ice_pairs_L_t* pairs, uint32_t comp_id, + const tnet_ice_candidate_t** candidate_offer, + const tnet_ice_candidate_t** candidate_answer_src, + const tnet_ice_candidate_t** candidate_answer_dest) + { + int ret; + const tnet_ice_pair_t *pair_offer = tsk_null, *pair_answer_src = tsk_null, *pair_answer_dest = tsk_null; + if (!pairs || !candidate_offer || !candidate_answer_src || !candidate_answer_dest) { + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + + *candidate_offer = tsk_null; + *candidate_answer_src = tsk_null; + *candidate_answer_dest = tsk_null; + + if ((ret = tnet_ice_pairs_get_nominated_symetric_pairs(pairs, comp_id, &pair_offer, &pair_answer_src, &pair_answer_dest)) == 0) { + *candidate_offer = pair_offer ? pair_offer->candidate_offer : tsk_null; + *candidate_answer_src = pair_answer_src ? pair_answer_src->candidate_answer : tsk_null; + *candidate_answer_dest = pair_answer_dest ? pair_answer_dest->candidate_answer : tsk_null; + } + return ret; + } + + int tnet_ice_pairs_get_nominated_symetric_pairs(const tnet_ice_pairs_L_t* pairs, uint32_t comp_id, + const struct tnet_ice_pair_s** _pair_offer, + const struct tnet_ice_pair_s** _pair_answer_src, + const struct tnet_ice_pair_s** _pair_answer_dest) + { + const tnet_ice_pair_t *pair_offer = tsk_null; + const tnet_ice_pair_t *pair_answer = tsk_null; + tsk_size_t i_offer, i_answer; + static const tsk_bool_t __check_fullness = tsk_false; // pairs will be checked seperatly + + if (!pairs || !_pair_offer || !_pair_answer_src || !_pair_answer_dest) { + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + + *_pair_offer = tsk_null; + *_pair_answer_src = tsk_null; + *_pair_answer_dest = tsk_null; + + i_offer = 0; + while (1) { + _tnet_ice_pairs_get_nominated_offer_at((pairs), i_offer, comp_id, __check_fullness, (pair_offer)); // pair with socket SO as sender + if(!pair_offer) return 0; + ++i_offer; + if (pair_offer->candidate_offer->comp_id != comp_id) continue; + // find another pair with socket SO as receiver + + i_answer = 0; + while (1) { + _tnet_ice_pairs_get_nominated_answer_at((pairs), i_answer, comp_id, __check_fullness, (pair_answer)); + if (!pair_answer) break; + ++i_answer; + if (pair_answer->candidate_offer->comp_id != comp_id) continue; + if (pair_answer->candidate_offer == pair_offer->candidate_offer) { + *_pair_offer = pair_offer; + *_pair_answer_src = pair_answer; + *_pair_answer_dest = pair_offer; + return 0; + } + } + } + return 0; + + } diff --git a/branches/2.0/doubango/tinySIP/src/dialogs/tsip_dialog_invite.ice.c b/branches/2.0/doubango/tinySIP/src/dialogs/tsip_dialog_invite.ice.c index 84d55269..823e548c 100644 --- a/branches/2.0/doubango/tinySIP/src/dialogs/tsip_dialog_invite.ice.c +++ b/branches/2.0/doubango/tinySIP/src/dialogs/tsip_dialog_invite.ice.c @@ -1,24 +1,24 @@ /* -* Copyright (C) 2012 Doubango Telecom -* -* Contact: Mamadou Diop -* -* This file is part of Open Source Doubango Framework. -* -* DOUBANGO is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as publishd by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* DOUBANGO 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 DOUBANGO. -* -*/ + * Copyright (C) 2012 Doubango Telecom + * + * Contact: Mamadou Diop + * + * This file is part of Open Source Doubango Framework. + * + * DOUBANGO is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as publishd by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * DOUBANGO 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 DOUBANGO. + * + */ #include "tinysip/dialogs/tsip_dialog_invite.h" #include "tinysip/dialogs/tsip_dialog_invite.common.h" @@ -43,11 +43,11 @@ tsk_bool_t tsip_dialog_invite_ice_got_local_candidates(const tsip_dialog_invite_ int tsip_dialog_invite_ice_process_ro(tsip_dialog_invite_t * self, const tsdp_message_t* sdp_ro, tsk_bool_t is_remote_offer); #define tsip_dialog_invite_ice_cancel_silent_and_sync_ctx(_self) \ - tsip_dialog_invite_ice_set_sync_mode_ctx((_self), tsk_true); \ - tsip_dialog_invite_ice_set_silent_mode_ctx((_self), tsk_true); \ - tsip_dialog_invite_ice_cancel_ctx((_self)); /* "cancelled" event will not be sent and we're sure that cancel operation will be done when the function exit */ \ - tsip_dialog_invite_ice_set_sync_mode_ctx((_self), tsk_false); \ - tsip_dialog_invite_ice_set_silent_mode_ctx((_self), tsk_false); \ +tsip_dialog_invite_ice_set_sync_mode_ctx((_self), tsk_true); \ +tsip_dialog_invite_ice_set_silent_mode_ctx((_self), tsk_true); \ +tsip_dialog_invite_ice_cancel_ctx((_self)); /* "cancelled" event will not be sent and we're sure that cancel operation will be done when the function exit */ \ +tsip_dialog_invite_ice_set_sync_mode_ctx((_self), tsk_false); \ +tsip_dialog_invite_ice_set_silent_mode_ctx((_self), tsk_false); \ /* ======================== transitions ======================== */ // Use "Current" instead of "Any" to avoid priority reordering @@ -57,399 +57,397 @@ static int x0500_Current_2_Current_X_iINVITE(va_list *app); /* ======================== conds ======================== */ static tsk_bool_t _fsm_cond_get_local_candidates(tsip_dialog_invite_t* self, tsip_message_t* message) { - if(self->supported.ice){ - tsk_bool_t use_ice = tsk_false; - // "action->media.type" will be defined for locally initiated media update - tmedia_type_t new_media = TSIP_DIALOG(self)->curr_action ? TSIP_DIALOG(self)->curr_action->media.type : tmedia_none; - - if(message && TSIP_MESSAGE_HAS_CONTENT(message) && tsk_striequals("application/sdp", TSIP_MESSAGE_CONTENT_TYPE(message))){ - // If this code is called this means that we are the "answerer" - // only gets the candidates if ICE is enabled and the remote peer supports ICE - tsdp_message_t* sdp_ro; - const tsdp_header_M_t* M; - int index; - if(!(sdp_ro = tsdp_message_parse(TSIP_MESSAGE_CONTENT_DATA(message), TSIP_MESSAGE_CONTENT_DATA_LENGTH(message)))){ - TSK_DEBUG_ERROR("Failed to parse remote sdp message"); - return tsk_false; - } - - index = 0; - while((M = (const tsdp_header_M_t*)tsdp_message_get_headerAt(sdp_ro, tsdp_htype_M, index++))){ - if(!tsdp_header_M_findA(M, "candidate")){ - use_ice = tsk_false; // do not use ICE if at least on media is ICE-less (e.g. MSRP) - break; - } - use_ice = tsk_true; // only use ICE if there is a least one media line - } - - new_media = tmedia_type_from_sdp(sdp_ro); - - TSK_OBJECT_SAFE_FREE(sdp_ro); - } - else if(!message){ - // we are the "offerer" -> use ICE only for audio or video medias (ignore ice for MSRP) - use_ice = (new_media & tmedia_audio) || (new_media & tmedia_video); - } - - if(use_ice){ - if(!self->ice.ctx_audio && !self->ice.ctx_video){ // First time - return tsk_true; - } - else{ - if(self->ice.media_type != new_media){ - return tsk_true; - } - return !tsip_dialog_invite_ice_got_local_candidates(self); - } - } - } - return tsk_false; + if(self->supported.ice){ + tsk_bool_t use_ice = tsk_false; + // "action->media.type" will be defined for locally initiated media update + tmedia_type_t new_media = TSIP_DIALOG(self)->curr_action ? TSIP_DIALOG(self)->curr_action->media.type : tmedia_none; + + if(message && TSIP_MESSAGE_HAS_CONTENT(message) && tsk_striequals("application/sdp", TSIP_MESSAGE_CONTENT_TYPE(message))){ + // If this code is called this means that we are the "answerer" + // only gets the candidates if ICE is enabled and the remote peer supports ICE + tsdp_message_t* sdp_ro; + const tsdp_header_M_t* M; + int index; + if(!(sdp_ro = tsdp_message_parse(TSIP_MESSAGE_CONTENT_DATA(message), TSIP_MESSAGE_CONTENT_DATA_LENGTH(message)))){ + TSK_DEBUG_ERROR("Failed to parse remote sdp message"); + return tsk_false; + } + + index = 0; + while((M = (const tsdp_header_M_t*)tsdp_message_get_headerAt(sdp_ro, tsdp_htype_M, index++))){ + if(!tsdp_header_M_findA(M, "candidate")){ + use_ice = tsk_false; // do not use ICE if at least on media is ICE-less (e.g. MSRP) + break; + } + use_ice = tsk_true; // only use ICE if there is a least one media line + } + + new_media = tmedia_type_from_sdp(sdp_ro); + + TSK_OBJECT_SAFE_FREE(sdp_ro); + } + else if(!message){ + // we are the "offerer" -> use ICE only for audio or video medias (ignore ice for MSRP) + use_ice = (new_media & tmedia_audio) || (new_media & tmedia_video); + } + + if(use_ice){ + if(!self->ice.ctx_audio && !self->ice.ctx_video){ // First time + return tsk_true; + } + else{ + if(self->ice.media_type != new_media){ + return tsk_true; + } + return !tsip_dialog_invite_ice_got_local_candidates(self); + } + } + } + return tsk_false; } int tsip_dialog_invite_ice_init(tsip_dialog_invite_t *self) { - tsk_fsm_set(TSIP_DIALOG_GET_FSM(self), - // Current -> (oINVITE) -> Current - TSK_FSM_ADD(tsk_fsm_state_current, _fsm_action_oINVITE, _fsm_cond_get_local_candidates, tsk_fsm_state_current, x0500_Current_2_Current_X_oINVITE, "x0500_Current_2_Current_X_oINVITE"), - // Current -> (iINVITE) -> Current - TSK_FSM_ADD(tsk_fsm_state_current, _fsm_action_iINVITE, _fsm_cond_get_local_candidates, tsk_fsm_state_current, x0500_Current_2_Current_X_iINVITE, "x0500_Current_2_Current_X_iINVITE") - ); - - return 0; + tsk_fsm_set(TSIP_DIALOG_GET_FSM(self), + // Current -> (oINVITE) -> Current + TSK_FSM_ADD(tsk_fsm_state_current, _fsm_action_oINVITE, _fsm_cond_get_local_candidates, tsk_fsm_state_current, x0500_Current_2_Current_X_oINVITE, "x0500_Current_2_Current_X_oINVITE"), + // Current -> (iINVITE) -> Current + TSK_FSM_ADD(tsk_fsm_state_current, _fsm_action_iINVITE, _fsm_cond_get_local_candidates, tsk_fsm_state_current, x0500_Current_2_Current_X_iINVITE, "x0500_Current_2_Current_X_iINVITE") + ); + + return 0; } int tsip_dialog_invite_ice_timers_set(tsip_dialog_invite_t *self, int64_t timeout) { - if(/*tnet_ice_ctx_is_active*/(self->ice.ctx_audio)){ - tnet_ice_ctx_set_concheck_timeout(self->ice.ctx_audio, timeout); - } - if(/*tnet_ice_ctx_is_active*/(self->ice.ctx_video)){ - tnet_ice_ctx_set_concheck_timeout(self->ice.ctx_video, timeout); - } - return 0; + if(/*tnet_ice_ctx_is_active*/(self->ice.ctx_audio)){ + tnet_ice_ctx_set_concheck_timeout(self->ice.ctx_audio, timeout); + } + if(/*tnet_ice_ctx_is_active*/(self->ice.ctx_video)){ + tnet_ice_ctx_set_concheck_timeout(self->ice.ctx_video, timeout); + } + return 0; } static int tsip_dialog_invite_ice_create_ctx(tsip_dialog_invite_t * self, tmedia_type_t media_type) { - int32_t transport_idx; - int ret = 0; - if(!self){ - TSK_DEBUG_ERROR("Invalid parameter"); - return -1; - } - transport_idx = TSIP_DIALOG_GET_STACK(self)->network.transport_idx_default; - if (!self->ice.ctx_audio && (media_type & tmedia_audio)) { - self->ice.ctx_audio = tnet_ice_ctx_create(self->ice.is_jingle, TNET_SOCKET_TYPE_IS_IPV6(TSIP_DIALOG_GET_STACK(self)->network.proxy_cscf_type[transport_idx]), - self->use_rtcp, tsk_false, tsip_dialog_invite_ice_audio_callback, self); - if (!self->ice.ctx_audio) { - TSK_DEBUG_ERROR("Failed to create ICE audio context"); - return -2; - } + int32_t transport_idx; + int ret = 0; + if(!self){ + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + transport_idx = TSIP_DIALOG_GET_STACK(self)->network.transport_idx_default; + if (!self->ice.ctx_audio && (media_type & tmedia_audio)) { + self->ice.ctx_audio = tnet_ice_ctx_create(self->ice.is_jingle, TNET_SOCKET_TYPE_IS_IPV6(TSIP_DIALOG_GET_STACK(self)->network.proxy_cscf_type[transport_idx]), + self->use_rtcp, tsk_false, tsip_dialog_invite_ice_audio_callback, self); + if (!self->ice.ctx_audio) { + TSK_DEBUG_ERROR("Failed to create ICE audio context"); + return -2; + } #if 0 // @deprecated - ret = tnet_ice_ctx_set_stun(self->ice.ctx_audio, TSIP_DIALOG_GET_SS(self)->media.stun.hostname, TSIP_DIALOG_GET_SS(self)->media.stun.port, kStunSoftware, TSIP_DIALOG_GET_SS(self)->media.stun.username, TSIP_DIALOG_GET_SS(self)->media.stun.password); + ret = tnet_ice_ctx_set_stun(self->ice.ctx_audio, TSIP_DIALOG_GET_SS(self)->media.stun.hostname, TSIP_DIALOG_GET_SS(self)->media.stun.port, kStunSoftware, TSIP_DIALOG_GET_SS(self)->media.stun.username, TSIP_DIALOG_GET_SS(self)->media.stun.password); #else - ret = tnet_ice_ctx_add_server( - self->ice.ctx_audio, - "udp", // "tcp", "tls", "ws", "wss"... - TSIP_DIALOG_GET_SS(self)->media.stun.hostname, - TSIP_DIALOG_GET_SS(self)->media.stun.port, - TSIP_DIALOG_GET_SS(self)->media.enable_iceturn, - TSIP_DIALOG_GET_SS(self)->media.enable_icestun, - TSIP_DIALOG_GET_SS(self)->media.stun.username, - TSIP_DIALOG_GET_SS(self)->media.stun.password); + ret = tnet_ice_ctx_add_server( + self->ice.ctx_audio, + "udp", // "tcp", "tls", "ws", "wss"... + TSIP_DIALOG_GET_SS(self)->media.stun.hostname, + TSIP_DIALOG_GET_SS(self)->media.stun.port, + TSIP_DIALOG_GET_SS(self)->media.enable_iceturn, + TSIP_DIALOG_GET_SS(self)->media.enable_icestun, + TSIP_DIALOG_GET_SS(self)->media.stun.username, + TSIP_DIALOG_GET_SS(self)->media.stun.password); #endif - ret = tnet_ice_ctx_set_turn_enabled(self->ice.ctx_audio, TSIP_DIALOG_GET_SS(self)->media.enable_iceturn); - ret = tnet_ice_ctx_set_stun_enabled(self->ice.ctx_audio, TSIP_DIALOG_GET_SS(self)->media.enable_icestun); - ret = tnet_ice_ctx_set_rtcpmux(self->ice.ctx_audio, self->use_rtcpmux); - } - if (!self->ice.ctx_video && (media_type & tmedia_video)) { - self->ice.ctx_video = tnet_ice_ctx_create(self->ice.is_jingle, TNET_SOCKET_TYPE_IS_IPV6(TSIP_DIALOG_GET_STACK(self)->network.proxy_cscf_type[transport_idx]), - self->use_rtcp, tsk_true, tsip_dialog_invite_ice_video_callback, self); - if (!self->ice.ctx_video) { - TSK_DEBUG_ERROR("Failed to create ICE video context"); - return -2; - } + ret = tnet_ice_ctx_set_turn_enabled(self->ice.ctx_audio, TSIP_DIALOG_GET_SS(self)->media.enable_iceturn); + ret = tnet_ice_ctx_set_stun_enabled(self->ice.ctx_audio, TSIP_DIALOG_GET_SS(self)->media.enable_icestun); + ret = tnet_ice_ctx_set_rtcpmux(self->ice.ctx_audio, self->use_rtcpmux); + } + if (!self->ice.ctx_video && (media_type & tmedia_video)) { + self->ice.ctx_video = tnet_ice_ctx_create(self->ice.is_jingle, TNET_SOCKET_TYPE_IS_IPV6(TSIP_DIALOG_GET_STACK(self)->network.proxy_cscf_type[transport_idx]), + self->use_rtcp, tsk_true, tsip_dialog_invite_ice_video_callback, self); + if (!self->ice.ctx_video) { + TSK_DEBUG_ERROR("Failed to create ICE video context"); + return -2; + } #if 0 // @deprecated - ret = tnet_ice_ctx_set_stun(self->ice.ctx_video, TSIP_DIALOG_GET_SS(self)->media.stun.hostname, TSIP_DIALOG_GET_SS(self)->media.stun.port, kStunSoftware, TSIP_DIALOG_GET_SS(self)->media.stun.username, TSIP_DIALOG_GET_SS(self)->media.stun.password); + ret = tnet_ice_ctx_set_stun(self->ice.ctx_video, TSIP_DIALOG_GET_SS(self)->media.stun.hostname, TSIP_DIALOG_GET_SS(self)->media.stun.port, kStunSoftware, TSIP_DIALOG_GET_SS(self)->media.stun.username, TSIP_DIALOG_GET_SS(self)->media.stun.password); #else - ret = tnet_ice_ctx_add_server( - self->ice.ctx_video, - "udp", // "tcp", "tls", "ws", "wss"... - TSIP_DIALOG_GET_SS(self)->media.stun.hostname, - TSIP_DIALOG_GET_SS(self)->media.stun.port, - TSIP_DIALOG_GET_SS(self)->media.enable_iceturn, - TSIP_DIALOG_GET_SS(self)->media.enable_icestun, - TSIP_DIALOG_GET_SS(self)->media.stun.username, - TSIP_DIALOG_GET_SS(self)->media.stun.password); + ret = tnet_ice_ctx_add_server( + self->ice.ctx_video, + "udp", // "tcp", "tls", "ws", "wss"... + TSIP_DIALOG_GET_SS(self)->media.stun.hostname, + TSIP_DIALOG_GET_SS(self)->media.stun.port, + TSIP_DIALOG_GET_SS(self)->media.enable_iceturn, + TSIP_DIALOG_GET_SS(self)->media.enable_icestun, + TSIP_DIALOG_GET_SS(self)->media.stun.username, + TSIP_DIALOG_GET_SS(self)->media.stun.password); #endif - ret = tnet_ice_ctx_set_turn_enabled(self->ice.ctx_video, TSIP_DIALOG_GET_SS(self)->media.enable_iceturn); - ret = tnet_ice_ctx_set_stun_enabled(self->ice.ctx_video, TSIP_DIALOG_GET_SS(self)->media.enable_icestun); - ret = tnet_ice_ctx_set_rtcpmux(self->ice.ctx_video, self->use_rtcpmux); - } - - // set media type - ret = tsip_dialog_invite_ice_set_media_type(self, media_type); - - // update session manager with the right ICE contexts - if (self->msession_mgr) { - ret = tmedia_session_mgr_set_ice_ctx(self->msession_mgr, self->ice.ctx_audio, self->ice.ctx_video); - } - - return ret; + ret = tnet_ice_ctx_set_turn_enabled(self->ice.ctx_video, TSIP_DIALOG_GET_SS(self)->media.enable_iceturn); + ret = tnet_ice_ctx_set_stun_enabled(self->ice.ctx_video, TSIP_DIALOG_GET_SS(self)->media.enable_icestun); + ret = tnet_ice_ctx_set_rtcpmux(self->ice.ctx_video, self->use_rtcpmux); + } + + // set media type + ret = tsip_dialog_invite_ice_set_media_type(self, media_type); + + // update session manager with the right ICE contexts + if (self->msession_mgr) { + ret = tmedia_session_mgr_set_ice_ctx(self->msession_mgr, self->ice.ctx_audio, self->ice.ctx_video); + } + + return ret; } int tsip_dialog_invite_ice_set_media_type(tsip_dialog_invite_t * self, tmedia_type_t _media_type) { - if(self){ - tmedia_type_t av_media_type = (_media_type & tmedia_audiovideo); // filter to keep audio and video only - // "none" comparison is used to exclude the "first call" - if(self->ice.media_type != tmedia_none && self->ice.media_type != av_media_type){ - // cancels contexts associated to old medias - if(self->ice.ctx_audio && !(av_media_type & tmedia_audio)){ - tnet_ice_ctx_cancel(self->ice.ctx_audio); - } - if(self->ice.ctx_video && !(av_media_type & tmedia_video)){ - tnet_ice_ctx_cancel(self->ice.ctx_video); - } - // cancels contexts associated to new medias (e.g. session "remove" then "add") - // cancel() on newly created contexts don't have any effect - if(self->ice.ctx_audio && (!(av_media_type & tmedia_audio) && (self->ice.media_type & tmedia_audio))){ - //tnet_ice_ctx_cancel(self->ice.ctx_audio); - } - if(self->ice.ctx_video && (!(av_media_type & tmedia_video) && (self->ice.media_type & tmedia_video))){ - //tnet_ice_ctx_cancel(self->ice.ctx_video); - } - } - self->ice.media_type = av_media_type; - } - return 0; + if(self){ + tmedia_type_t av_media_type = (_media_type & tmedia_audiovideo); // filter to keep audio and video only + // "none" comparison is used to exclude the "first call" + if(self->ice.media_type != tmedia_none && self->ice.media_type != av_media_type){ + // cancels contexts associated to old medias + if(self->ice.ctx_audio && !(av_media_type & tmedia_audio)){ + tnet_ice_ctx_cancel(self->ice.ctx_audio); + } + if(self->ice.ctx_video && !(av_media_type & tmedia_video)){ + tnet_ice_ctx_cancel(self->ice.ctx_video); + } + // cancels contexts associated to new medias (e.g. session "remove" then "add") + // cancel() on newly created contexts don't have any effect + if(self->ice.ctx_audio && (!(av_media_type & tmedia_audio) && (self->ice.media_type & tmedia_audio))){ + //tnet_ice_ctx_cancel(self->ice.ctx_audio); + } + if(self->ice.ctx_video && (!(av_media_type & tmedia_video) && (self->ice.media_type & tmedia_video))){ + //tnet_ice_ctx_cancel(self->ice.ctx_video); + } + } + self->ice.media_type = av_media_type; + } + return 0; } static int tsip_dialog_invite_ice_start_ctx(tsip_dialog_invite_t * self) { - int ret = 0; - if(self){ - if((self->ice.media_type & tmedia_audio)){ - if(self->ice.ctx_audio && (ret = tnet_ice_ctx_start(self->ice.ctx_audio)) != 0){ - return ret; - } - } - if((self->ice.media_type & tmedia_video)){ - if(self->ice.ctx_video && (ret = tnet_ice_ctx_start(self->ice.ctx_video)) != 0){ - return ret; - } - } - } - return 0; + int ret = 0; + if(self){ + if((self->ice.media_type & tmedia_audio)){ + if(self->ice.ctx_audio && (ret = tnet_ice_ctx_start(self->ice.ctx_audio)) != 0){ + return ret; + } + } + if((self->ice.media_type & tmedia_video)){ + if(self->ice.ctx_video && (ret = tnet_ice_ctx_start(self->ice.ctx_video)) != 0){ + return ret; + } + } + } + return 0; } static int tsip_dialog_invite_ice_cancel_ctx(tsip_dialog_invite_t * self) { - int ret = 0; - if(self){ - if((self->ice.media_type & tmedia_audio)){ - if(self->ice.ctx_audio && (ret = tnet_ice_ctx_cancel(self->ice.ctx_audio)) != 0){ - return ret; - } - } - if((self->ice.media_type & tmedia_video)){ - if(self->ice.ctx_video && (ret = tnet_ice_ctx_cancel(self->ice.ctx_video)) != 0){ - return ret; - } - } - } - return 0; + int ret = 0; + if(self){ + if((self->ice.media_type & tmedia_audio)){ + if(self->ice.ctx_audio && (ret = tnet_ice_ctx_cancel(self->ice.ctx_audio)) != 0){ + return ret; + } + } + if((self->ice.media_type & tmedia_video)){ + if(self->ice.ctx_video && (ret = tnet_ice_ctx_cancel(self->ice.ctx_video)) != 0){ + return ret; + } + } + } + return 0; } static int tsip_dialog_invite_ice_set_sync_mode_ctx(tsip_dialog_invite_t * self, tsk_bool_t sync_mode) { - int ret = 0; - if(self){ - if((self->ice.media_type & tmedia_audio)){ - if(self->ice.ctx_audio && (ret = tnet_ice_ctx_set_sync_mode(self->ice.ctx_audio, sync_mode)) != 0){ - return ret; - } - } - if((self->ice.media_type & tmedia_video)){ - if(self->ice.ctx_video && (ret = tnet_ice_ctx_set_sync_mode(self->ice.ctx_video, sync_mode)) != 0){ - return ret; - } - } - } - return 0; + int ret = 0; + if(self){ + if((self->ice.media_type & tmedia_audio)){ + if(self->ice.ctx_audio && (ret = tnet_ice_ctx_set_sync_mode(self->ice.ctx_audio, sync_mode)) != 0){ + return ret; + } + } + if((self->ice.media_type & tmedia_video)){ + if(self->ice.ctx_video && (ret = tnet_ice_ctx_set_sync_mode(self->ice.ctx_video, sync_mode)) != 0){ + return ret; + } + } + } + return 0; } static int tsip_dialog_invite_ice_set_silent_mode_ctx(tsip_dialog_invite_t * self, tsk_bool_t silent_mode) { - int ret = 0; - if(self){ - if((self->ice.media_type & tmedia_audio)){ - if(self->ice.ctx_audio && (ret = tnet_ice_ctx_set_silent_mode(self->ice.ctx_audio, silent_mode)) != 0){ - return ret; - } - } - if((self->ice.media_type & tmedia_video)){ - if(self->ice.ctx_video && (ret = tnet_ice_ctx_set_silent_mode(self->ice.ctx_video, silent_mode)) != 0){ - return ret; - } - } - } - return 0; + int ret = 0; + if(self){ + if((self->ice.media_type & tmedia_audio)){ + if(self->ice.ctx_audio && (ret = tnet_ice_ctx_set_silent_mode(self->ice.ctx_audio, silent_mode)) != 0){ + return ret; + } + } + if((self->ice.media_type & tmedia_video)){ + if(self->ice.ctx_video && (ret = tnet_ice_ctx_set_silent_mode(self->ice.ctx_video, silent_mode)) != 0){ + return ret; + } + } + } + return 0; } tsk_bool_t tsip_dialog_invite_ice_is_enabled(const tsip_dialog_invite_t * self) { - if(self){ - return (self->supported.ice && (tnet_ice_ctx_is_active(self->ice.ctx_audio) || tnet_ice_ctx_is_active(self->ice.ctx_video))); - } - return tsk_false; + if(self){ + return (self->supported.ice && (tnet_ice_ctx_is_active(self->ice.ctx_audio) || tnet_ice_ctx_is_active(self->ice.ctx_video))); + } + return tsk_false; } tsk_bool_t tsip_dialog_invite_ice_is_connected(const tsip_dialog_invite_t * self) { - if(self){ - return (!tnet_ice_ctx_is_active(self->ice.ctx_audio) || tnet_ice_ctx_is_connected(self->ice.ctx_audio)) - && (!tnet_ice_ctx_is_active(self->ice.ctx_video) || tnet_ice_ctx_is_connected(self->ice.ctx_video)); - } - return tsk_false; + if(self){ + return (!tnet_ice_ctx_is_active(self->ice.ctx_audio) || tnet_ice_ctx_is_connected(self->ice.ctx_audio)) + && (!tnet_ice_ctx_is_active(self->ice.ctx_video) || tnet_ice_ctx_is_connected(self->ice.ctx_video)); + } + return tsk_false; } tsk_bool_t tsip_dialog_invite_ice_got_local_candidates(const tsip_dialog_invite_t * self) { - if(self){ - return (!tnet_ice_ctx_is_active(self->ice.ctx_audio) || tnet_ice_ctx_got_local_candidates(self->ice.ctx_audio)) - && (!tnet_ice_ctx_is_active(self->ice.ctx_video) || tnet_ice_ctx_got_local_candidates(self->ice.ctx_video)); - } - return tsk_false; + if(self){ + return (!tnet_ice_ctx_is_active(self->ice.ctx_audio) || tnet_ice_ctx_got_local_candidates(self->ice.ctx_audio)) + && (!tnet_ice_ctx_is_active(self->ice.ctx_video) || tnet_ice_ctx_got_local_candidates(self->ice.ctx_video)); + } + return tsk_false; } int tsip_dialog_invite_ice_save_action(tsip_dialog_invite_t * self, tsk_fsm_action_id action_id, const tsip_action_t* action, const tsip_message_t* message) { - if(!self){ - TSK_DEBUG_ERROR("Invalid parameter"); - return -1; - } - - // There are good reasons to ref() the action and message before safe_free() - // /!\ do not change - - self->ice.last_action_id = action_id; - action = tsk_object_ref((tsk_object_t*)action); - TSK_OBJECT_SAFE_FREE(self->ice.last_action); - self->ice.last_action = (tsip_action_t*)action; - - message = tsk_object_ref((tsk_object_t*)message); - TSK_OBJECT_SAFE_FREE(self->ice.last_message); - self->ice.last_message = (tsip_message_t*)message; - return 0; + if(!self){ + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + + // There are good reasons to ref() the action and message before safe_free() + // /!\ do not change + + self->ice.last_action_id = action_id; + action = tsk_object_ref((tsk_object_t*)action); + TSK_OBJECT_SAFE_FREE(self->ice.last_action); + self->ice.last_action = (tsip_action_t*)action; + + message = tsk_object_ref((tsk_object_t*)message); + TSK_OBJECT_SAFE_FREE(self->ice.last_message); + self->ice.last_message = (tsip_message_t*)message; + return 0; } int tsip_dialog_invite_ice_process_lo(tsip_dialog_invite_t * self, const tsdp_message_t* sdp_lo) { - const tsdp_header_M_t* M; - const tsdp_header_A_t *A; - int ret = 0, i; - - if(!self || !sdp_lo){ - TSK_DEBUG_ERROR("Invalid parameter"); - return -1; - } - - // cancels all ICE contexts without candidates - // this happens if codecs negotiations mismatch for one media out of two or three - for(i = 0; i < 2; ++i){ - struct tnet_ice_ctx_s *ctx = i == 0 ? self->ice.ctx_audio : self->ice.ctx_video; - const char* media = i == 0 ? "audio" : "video"; - if(tnet_ice_ctx_is_active(ctx)){ - tsk_bool_t cancel = tsk_true; - if((M = tsdp_message_find_media(sdp_lo, media))){ - if((A = tsdp_header_M_findA(M, "candidate"))){ - cancel = tsk_false; - } - } - if(cancel){ - ret = tnet_ice_ctx_cancel(ctx); - } - } - } - - return ret; + const tsdp_header_M_t* M; + const tsdp_header_A_t *A; + int ret = 0, i; + + if(!self || !sdp_lo){ + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + + // cancels all ICE contexts without candidates + // this happens if codecs negotiations mismatch for one media out of two or three + for(i = 0; i < 2; ++i){ + struct tnet_ice_ctx_s *ctx = i == 0 ? self->ice.ctx_audio : self->ice.ctx_video; + const char* media = i == 0 ? "audio" : "video"; + if(tnet_ice_ctx_is_active(ctx)){ + tsk_bool_t cancel = tsk_true; + if((M = tsdp_message_find_media(sdp_lo, media))){ + if((A = tsdp_header_M_findA(M, "candidate"))){ + cancel = tsk_false; + } + } + if(cancel){ + ret = tnet_ice_ctx_cancel(ctx); + } + } + } + + return ret; } int tsip_dialog_invite_ice_process_ro(tsip_dialog_invite_t * self, const tsdp_message_t* sdp_ro, tsk_bool_t is_remote_offer) { - char* ice_remote_candidates; - const tsdp_header_M_t* M; - tsk_size_t index; - const tsdp_header_A_t *A; - const tsdp_header_O_t *O; - const char* sess_ufrag = tsk_null; - const char* sess_pwd = tsk_null; - int ret = 0, i; - struct tnet_ice_ctx_s *ctx; - - if(!self || !sdp_ro){ - TSK_DEBUG_ERROR("Invalid parameter"); - return -1; - } - if(!self->ice.ctx_audio && !self->ice.ctx_video){ - return 0; - } - - // make sure this is different SDP - if((O = (const tsdp_header_O_t*)tsdp_message_get_header(sdp_ro, tsdp_htype_O))){ - if(self->ice.last_sdp_ro_ver == (int32_t)O->sess_version){ - TSK_DEBUG_INFO("ICE: ignore processing SDP RO because version haven't changed"); - return 0; - } - self->ice.last_sdp_ro_ver = (int32_t)O->sess_version; - } - - // session level attributes - - if((A = tsdp_message_get_headerA(sdp_ro, "ice-ufrag"))){ - sess_ufrag = A->value; - } - if((A = tsdp_message_get_headerA(sdp_ro, "ice-pwd"))){ - sess_pwd = A->value; - } - + char* ice_remote_candidates; + const tsdp_header_M_t* M; + tsk_size_t index; + const tsdp_header_A_t *A; + const tsdp_header_O_t *O; + const char* sess_ufrag = tsk_null; + const char* sess_pwd = tsk_null; + int ret = 0, i; + struct tnet_ice_ctx_s *ctx; + + if(!self || !sdp_ro){ + TSK_DEBUG_ERROR("Invalid parameter"); + return -1; + } + if(!self->ice.ctx_audio && !self->ice.ctx_video){ + return 0; + } + + // make sure this is different SDP + if((O = (const tsdp_header_O_t*)tsdp_message_get_header(sdp_ro, tsdp_htype_O))){ + if(self->ice.last_sdp_ro_ver == (int32_t)O->sess_version){ + TSK_DEBUG_INFO("ICE: ignore processing SDP RO because version haven't changed"); + return 0; + } + self->ice.last_sdp_ro_ver = (int32_t)O->sess_version; + } + + // session level attributes + + if((A = tsdp_message_get_headerA(sdp_ro, "ice-ufrag"))){ + sess_ufrag = A->value; + } + if((A = tsdp_message_get_headerA(sdp_ro, "ice-pwd"))){ + sess_pwd = A->value; + } + #if 0 // Use RTCWeb Profile (tmedia_profile_rtcweb) - { - const tsdp_header_S_t *S; - if((S = (const tsdp_header_S_t *)tsdp_message_get_header(sdp_ro, tsdp_htype_S)) && S->value){ - self->ice.is_jingle = tsk_strcontains(S->value, tsk_strlen(S->value), "webrtc"); - } - } + { + const tsdp_header_S_t *S; + if((S = (const tsdp_header_S_t *)tsdp_message_get_header(sdp_ro, tsdp_htype_S)) && S->value){ + self->ice.is_jingle = tsk_strcontains(S->value, tsk_strlen(S->value), "webrtc"); + } + } #endif - - for(i = 0; i < 2; ++i){ - if((M = tsdp_message_find_media(sdp_ro, i==0 ? "audio": "video"))){ - const char *ufrag = sess_ufrag, *pwd = sess_pwd; - tsk_bool_t remote_use_rtcpmux = (tsdp_header_M_findA(M, "rtcp-mux") != tsk_null); - ctx = (i==0 ? self->ice.ctx_audio : self->ice.ctx_video); - ice_remote_candidates = tsk_null; - index = 0; - if((A = tsdp_header_M_findA(M, "ice-ufrag"))){ - ufrag = A->value; - } - if((A = tsdp_header_M_findA(M, "ice-pwd"))){ - pwd = A->value; - } - - while((A = tsdp_header_M_findA_at(M, "candidate", index++))){ - tsk_strcat_2(&ice_remote_candidates, "%s\r\n", A->value); - } - // ICE processing will be automatically stopped if the remote candidates are not valid - // ICE-CONTROLLING role if we are the offerer - ret = tnet_ice_ctx_set_remote_candidates(ctx, ice_remote_candidates, ufrag, pwd, !is_remote_offer, self->ice.is_jingle); - TSK_SAFE_FREE(ice_remote_candidates); - // Now that 'rtcp-mux' option have been updated by the session pass the value to the ICE ctx - ret = tnet_ice_ctx_set_rtcpmux(ctx, (self->use_rtcpmux && remote_use_rtcpmux)); - } - } - - return ret; + + for(i = 0; i < 2; ++i){ + if((M = tsdp_message_find_media(sdp_ro, i==0 ? "audio": "video"))){ + const char *ufrag = sess_ufrag, *pwd = sess_pwd; + tsk_bool_t remote_use_rtcpmux = (tsdp_header_M_findA(M, "rtcp-mux") != tsk_null); + ctx = (i==0 ? self->ice.ctx_audio : self->ice.ctx_video); + ice_remote_candidates = tsk_null; + index = 0; + if((A = tsdp_header_M_findA(M, "ice-ufrag"))){ + ufrag = A->value; + } + if((A = tsdp_header_M_findA(M, "ice-pwd"))){ + pwd = A->value; + } + + while((A = tsdp_header_M_findA_at(M, "candidate", index++))){ + tsk_strcat_2(&ice_remote_candidates, "%s\r\n", A->value); + } + // ICE processing will be automatically stopped if the remote candidates are not valid + // ICE-CONTROLLING role if we are the offerer + ret = tnet_ice_ctx_set_remote_candidates_2(ctx, ice_remote_candidates, ufrag, pwd, !is_remote_offer, self->ice.is_jingle, (self->use_rtcpmux && remote_use_rtcpmux)); + TSK_SAFE_FREE(ice_remote_candidates); + } + } + + return ret; } @@ -461,91 +459,91 @@ int tsip_dialog_invite_ice_process_ro(tsip_dialog_invite_t * self, const tsdp_me // Current -> (oINVITE) -> Current static int x0500_Current_2_Current_X_oINVITE(va_list *app) { - int ret; - tsip_dialog_invite_t *self; - const tsip_action_t* action; - const tsip_message_t *message; - tmedia_type_t media_type; - static const tsk_bool_t __force_restart_is_yes = tsk_true; - - self = va_arg(*app, tsip_dialog_invite_t *); - message = va_arg(*app, const tsip_message_t *); - action = va_arg(*app, const tsip_action_t *); - - media_type = (action && action->media.type != tmedia_none) ? action->media.type : TSIP_DIALOG_GET_SS(self)->media.type; - self->is_client = tsk_true; - tsip_dialog_invite_ice_save_action(self, _fsm_action_oINVITE, action, message); - - // Cancel without notifying ("silent mode") and perform the operation right now ("sync mode") - tsip_dialog_invite_ice_cancel_silent_and_sync_ctx(self); - - // create ICE context - if((ret = tsip_dialog_invite_ice_create_ctx(self, media_type))){ - TSK_DEBUG_ERROR("tsip_dialog_invite_ice_create_ctx() failed"); - return ret; - } - - // For now disable ICE timers until we receive the 2xx - ret = tsip_dialog_invite_ice_timers_set(self, -1); - - // Start ICE - ret = tsip_dialog_invite_ice_start_ctx(self); - - // alert the user only if we are in initial state which means that it's not media update - if(TSIP_DIALOG(self)->state == tsip_initial){ - TSIP_DIALOG_SIGNAL(self, tsip_event_code_dialog_connecting, "Dialog connecting"); - } - - return ret; + int ret; + tsip_dialog_invite_t *self; + const tsip_action_t* action; + const tsip_message_t *message; + tmedia_type_t media_type; + static const tsk_bool_t __force_restart_is_yes = tsk_true; + + self = va_arg(*app, tsip_dialog_invite_t *); + message = va_arg(*app, const tsip_message_t *); + action = va_arg(*app, const tsip_action_t *); + + media_type = (action && action->media.type != tmedia_none) ? action->media.type : TSIP_DIALOG_GET_SS(self)->media.type; + self->is_client = tsk_true; + tsip_dialog_invite_ice_save_action(self, _fsm_action_oINVITE, action, message); + + // Cancel without notifying ("silent mode") and perform the operation right now ("sync mode") + tsip_dialog_invite_ice_cancel_silent_and_sync_ctx(self); + + // create ICE context + if((ret = tsip_dialog_invite_ice_create_ctx(self, media_type))){ + TSK_DEBUG_ERROR("tsip_dialog_invite_ice_create_ctx() failed"); + return ret; + } + + // For now disable ICE timers until we receive the 2xx + ret = tsip_dialog_invite_ice_timers_set(self, -1); + + // Start ICE + ret = tsip_dialog_invite_ice_start_ctx(self); + + // alert the user only if we are in initial state which means that it's not media update + if(TSIP_DIALOG(self)->state == tsip_initial){ + TSIP_DIALOG_SIGNAL(self, tsip_event_code_dialog_connecting, "Dialog connecting"); + } + + return ret; } // Current -> (iINVITE) -> Current static int x0500_Current_2_Current_X_iINVITE(va_list *app) { - int ret; - tsip_dialog_invite_t *self; - const tsip_action_t* action; - const tsip_message_t *message; - - self = va_arg(*app, tsip_dialog_invite_t *); - message = va_arg(*app, const tsip_message_t *); - action = va_arg(*app, const tsip_action_t *); - - self->is_client = tsk_false; - ret = tsip_dialog_invite_ice_save_action(self, _fsm_action_iINVITE, action, message); - - // Cancel without notifying ("silent mode") and perform the operation right now ("sync mode") - tsip_dialog_invite_ice_cancel_silent_and_sync_ctx(self); - - // set remote candidates - if(TSIP_MESSAGE_HAS_CONTENT(message)){ - if(tsk_striequals("application/sdp", TSIP_MESSAGE_CONTENT_TYPE(message))){ - tsdp_message_t* sdp_ro; - if(!(sdp_ro = tsdp_message_parse(TSIP_MESSAGE_CONTENT_DATA(message), TSIP_MESSAGE_CONTENT_DATA_LENGTH(message)))){ - TSK_DEBUG_ERROR("Failed to parse remote sdp message"); - return -2; - } - // create ICE context - if((ret = tsip_dialog_invite_ice_create_ctx(self, tmedia_type_from_sdp(sdp_ro)))){ - TSK_DEBUG_ERROR("tsip_dialog_invite_ice_create_ctx() failed"); - return ret; - } - ret = tsip_dialog_invite_ice_process_ro(self, sdp_ro, tsk_true); - TSK_OBJECT_SAFE_FREE(sdp_ro); - } - else{ - TSK_DEBUG_ERROR("[%s] content-type is not supportted", TSIP_MESSAGE_CONTENT_TYPE(message)); - return -3; - } - } - - // For now disable ICE timers until we send the 2xx and receive the ACK - ret = tsip_dialog_invite_ice_timers_set(self, -1); - - // Start ICE - ret = tsip_dialog_invite_ice_start_ctx(self); - - return ret; + int ret; + tsip_dialog_invite_t *self; + const tsip_action_t* action; + const tsip_message_t *message; + + self = va_arg(*app, tsip_dialog_invite_t *); + message = va_arg(*app, const tsip_message_t *); + action = va_arg(*app, const tsip_action_t *); + + self->is_client = tsk_false; + ret = tsip_dialog_invite_ice_save_action(self, _fsm_action_iINVITE, action, message); + + // Cancel without notifying ("silent mode") and perform the operation right now ("sync mode") + tsip_dialog_invite_ice_cancel_silent_and_sync_ctx(self); + + // set remote candidates + if(TSIP_MESSAGE_HAS_CONTENT(message)){ + if(tsk_striequals("application/sdp", TSIP_MESSAGE_CONTENT_TYPE(message))){ + tsdp_message_t* sdp_ro; + if(!(sdp_ro = tsdp_message_parse(TSIP_MESSAGE_CONTENT_DATA(message), TSIP_MESSAGE_CONTENT_DATA_LENGTH(message)))){ + TSK_DEBUG_ERROR("Failed to parse remote sdp message"); + return -2; + } + // create ICE context + if((ret = tsip_dialog_invite_ice_create_ctx(self, tmedia_type_from_sdp(sdp_ro)))){ + TSK_DEBUG_ERROR("tsip_dialog_invite_ice_create_ctx() failed"); + return ret; + } + ret = tsip_dialog_invite_ice_process_ro(self, sdp_ro, tsk_true); + TSK_OBJECT_SAFE_FREE(sdp_ro); + } + else{ + TSK_DEBUG_ERROR("[%s] content-type is not supportted", TSIP_MESSAGE_CONTENT_TYPE(message)); + return -3; + } + } + + // For now disable ICE timers until we send the 2xx and receive the ACK + ret = tsip_dialog_invite_ice_timers_set(self, -1); + + // Start ICE + ret = tsip_dialog_invite_ice_start_ctx(self); + + return ret; } @@ -557,63 +555,63 @@ static int x0500_Current_2_Current_X_iINVITE(va_list *app) static int tsip_dialog_invite_ice_callback(const tnet_ice_event_t *e) { - int ret = 0; - tsip_dialog_invite_t *dialog; - - TSK_DEBUG_INFO("ICE callback: %s", e->phrase); - - dialog = tsk_object_ref(TSK_OBJECT(e->userdata)); - - // Do not lock: caller is thread safe - - switch(e->type){ - case tnet_ice_event_type_gathering_completed: - case tnet_ice_event_type_conncheck_succeed: - case tnet_ice_event_type_conncheck_failed: - case tnet_ice_event_type_cancelled: - { - if(dialog->ice.last_action_id != tsk_fsm_state_none){ - if(tsip_dialog_invite_ice_got_local_candidates(dialog)){ - ret = tsip_dialog_fsm_act(TSIP_DIALOG(dialog), dialog->ice.last_action_id, dialog->ice.last_message, dialog->ice.last_action); - dialog->ice.last_action_id = tsk_fsm_state_none; - } - } - if(dialog->ice.start_smgr){ - ret = tsip_dialog_invite_msession_start(dialog); - } - break; - } - // fatal errors which discard ICE process - case tnet_ice_event_type_gathering_host_candidates_failed: - case tnet_ice_event_type_gathering_reflexive_candidates_failed: - case tnet_ice_event_type_gathering_relay_candidates_failed: - { - if (dialog->ice.last_action_id != tsk_fsm_state_none) { - ret = tsip_dialog_fsm_act(TSIP_DIALOG(dialog), dialog->ice.last_action_id, dialog->ice.last_message, dialog->ice.last_action); - dialog->ice.last_action_id = tsk_fsm_state_none; - } - break; - } - // TURN session disconnected while we're in call - case tnet_ice_event_type_turn_connection_broken: - { - ret = tsip_dialog_fsm_act_2(TSIP_DIALOG(dialog), _fsm_action_oBYE); - break; - } + int ret = 0; + tsip_dialog_invite_t *dialog; + + TSK_DEBUG_INFO("ICE callback: %s", e->phrase); + + dialog = tsk_object_ref(TSK_OBJECT(e->userdata)); + + // Do not lock: caller is thread safe + + switch(e->type){ + case tnet_ice_event_type_gathering_completed: + case tnet_ice_event_type_conncheck_succeed: + case tnet_ice_event_type_conncheck_failed: + case tnet_ice_event_type_cancelled: + { + if(dialog->ice.last_action_id != tsk_fsm_state_none){ + if(tsip_dialog_invite_ice_got_local_candidates(dialog)){ + ret = tsip_dialog_fsm_act(TSIP_DIALOG(dialog), dialog->ice.last_action_id, dialog->ice.last_message, dialog->ice.last_action); + dialog->ice.last_action_id = tsk_fsm_state_none; + } + } + if(dialog->ice.start_smgr){ + ret = tsip_dialog_invite_msession_start(dialog); + } + break; + } + // fatal errors which discard ICE process + case tnet_ice_event_type_gathering_host_candidates_failed: + case tnet_ice_event_type_gathering_reflexive_candidates_failed: + case tnet_ice_event_type_gathering_relay_candidates_failed: + { + if (dialog->ice.last_action_id != tsk_fsm_state_none) { + ret = tsip_dialog_fsm_act(TSIP_DIALOG(dialog), dialog->ice.last_action_id, dialog->ice.last_message, dialog->ice.last_action); + dialog->ice.last_action_id = tsk_fsm_state_none; + } + break; + } + // TURN session disconnected while we're in call + case tnet_ice_event_type_turn_connection_broken: + { + ret = tsip_dialog_fsm_act_2(TSIP_DIALOG(dialog), _fsm_action_oBYE); + break; + } default: break; - } - - TSK_OBJECT_SAFE_FREE(dialog); - - return ret; + } + + TSK_OBJECT_SAFE_FREE(dialog); + + return ret; } static int tsip_dialog_invite_ice_audio_callback(const tnet_ice_event_t *e) { - return tsip_dialog_invite_ice_callback(e); + return tsip_dialog_invite_ice_callback(e); } static int tsip_dialog_invite_ice_video_callback(const tnet_ice_event_t *e) { - return tsip_dialog_invite_ice_callback(e); + return tsip_dialog_invite_ice_callback(e); } \ No newline at end of file