diff options
author | Benny Prijono <bennylp@teluu.com> | 2008-06-06 14:47:10 +0000 |
---|---|---|
committer | Benny Prijono <bennylp@teluu.com> | 2008-06-06 14:47:10 +0000 |
commit | 8ec5bd6b3d5bafb1d3ab11236a3adc45ac5f04d8 (patch) | |
tree | 75c82cf12d34d58e3ccf64e6eb08e8628e8f94cf /pjnath/src/pjnath/ice_strans.c | |
parent | 77825e4c00356383e69f3fc4c63eccd0a3aa103f (diff) |
Major major modifications related to ticket #485 (support for TURN-07):
- Added STUN socket transport pj_stun_sock
- Integration of TURN-07 to ICE
- Major refactoring in ICE stream transport to make it simpler
- Major modification (i.e. API change) in almost everywhere else
- Much more elaborate STUN, TURN, and ICE tests in pjnath-test
git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@1988 74dad513-b988-da41-8d7b-12977e46ad98
Diffstat (limited to 'pjnath/src/pjnath/ice_strans.c')
-rw-r--r-- | pjnath/src/pjnath/ice_strans.c | 1708 |
1 files changed, 898 insertions, 810 deletions
diff --git a/pjnath/src/pjnath/ice_strans.c b/pjnath/src/pjnath/ice_strans.c index 7266e079..34b74f0d 100644 --- a/pjnath/src/pjnath/ice_strans.c +++ b/pjnath/src/pjnath/ice_strans.c @@ -19,9 +19,11 @@ #include <pjnath/ice_strans.h> #include <pjnath/errno.h> #include <pj/addr_resolv.h> +#include <pj/array.h> #include <pj/assert.h> #include <pj/ip_helper.h> #include <pj/log.h> +#include <pj/os.h> #include <pj/pool.h> #include <pj/rand.h> #include <pj/string.h> @@ -35,792 +37,571 @@ #endif +/* Transport IDs */ +enum tp_type +{ + TP_NONE, + TP_STUN, + TP_TURN +}; + +/* Candidate preference default values */ +#define SRFLX_PREF 65535 +#define HOST_PREF 65530 +#define RELAY_PREF 65525 + /* ICE callbacks */ static void on_ice_complete(pj_ice_sess *ice, pj_status_t status); static pj_status_t ice_tx_pkt(pj_ice_sess *ice, unsigned comp_id, + unsigned transport_id, const void *pkt, pj_size_t size, const pj_sockaddr_t *dst_addr, unsigned dst_addr_len); static void ice_rx_data(pj_ice_sess *ice, - unsigned comp_id, - void *pkt, pj_size_t size, - const pj_sockaddr_t *src_addr, - unsigned src_addr_len); - -/* Ioqueue callback */ -static void on_read_complete(pj_ioqueue_key_t *key, - pj_ioqueue_op_key_t *op_key, - pj_ssize_t bytes_read); - -static void destroy_component(pj_ice_strans_comp *comp); -static void destroy_ice_st(pj_ice_strans *ice_st, pj_status_t reason); - - -/* STUN session callback */ -static pj_status_t stun_on_send_msg(pj_stun_session *sess, - void *token, - const void *pkt, - pj_size_t pkt_size, - const pj_sockaddr_t *dst_addr, - unsigned addr_len); -static void stun_on_request_complete(pj_stun_session *sess, - pj_status_t status, - void *token, - pj_stun_tx_data *tdata, - const pj_stun_msg *response, - const pj_sockaddr_t *src_addr, - unsigned src_addr_len); - -/* Keep-alive timer */ -static void start_ka_timer(pj_ice_strans *ice_st); -static void stop_ka_timer(pj_ice_strans *ice_st); - -/* Utility: print error */ + unsigned comp_id, + unsigned transport_id, + void *pkt, pj_size_t size, + const pj_sockaddr_t *src_addr, + unsigned src_addr_len); + + +/* STUN socket callbacks */ +/* Notification when incoming packet has been received. */ +static pj_bool_t stun_on_rx_data(pj_stun_sock *stun_sock, + void *pkt, + unsigned pkt_len, + const pj_sockaddr_t *src_addr, + unsigned addr_len); +/* Notifification when asynchronous send operation has completed. */ +static pj_bool_t stun_on_data_sent(pj_stun_sock *stun_sock, + pj_ioqueue_op_key_t *send_key, + pj_ssize_t sent); +/* Notification when the status of the STUN transport has changed. */ +static pj_bool_t stun_on_status(pj_stun_sock *stun_sock, + pj_stun_sock_op op, + pj_status_t status); + + +/* TURN callbacks */ +static void turn_on_rx_data(pj_turn_sock *turn_sock, + void *pkt, + unsigned pkt_len, + const pj_sockaddr_t *peer_addr, + unsigned addr_len); +static void turn_on_state(pj_turn_sock *turn_sock, pj_turn_state_t old_state, + pj_turn_state_t new_state); + + + +/* Forward decls */ +static void destroy_ice_st(pj_ice_strans *ice_st); #define ice_st_perror(ice_st,msg,rc) pjnath_perror(ice_st->obj_name,msg,rc) - -/* - * Create ICE stream transport +static void sess_init_update(pj_ice_strans *ice_st); + +static void sess_add_ref(pj_ice_strans *ice_st); +static pj_bool_t sess_dec_ref(pj_ice_strans *ice_st); + +/** + * This structure describes an ICE stream transport component. A component + * in ICE stream transport typically corresponds to a single socket created + * for this component, and bound to a specific transport address. This + * component may have multiple alias addresses, for example one alias + * address for each interfaces in multi-homed host, another for server + * reflexive alias, and another for relayed alias. For each transport + * address alias, an ICE stream transport candidate (#pj_ice_sess_cand) will + * be created, and these candidates will eventually registered to the ICE + * session. */ -PJ_DEF(pj_status_t) pj_ice_strans_create( pj_stun_config *stun_cfg, - const char *name, - unsigned comp_cnt, - void *user_data, - const pj_ice_strans_cb *cb, - pj_ice_strans **p_ice_st) +typedef struct pj_ice_strans_comp { - pj_pool_t *pool; - pj_ice_strans *ice_st; + pj_ice_strans *ice_st; /**< ICE stream transport. */ + unsigned comp_id; /**< Component ID. */ - PJ_ASSERT_RETURN(stun_cfg && comp_cnt && cb && p_ice_st, PJ_EINVAL); - PJ_ASSERT_RETURN(stun_cfg->ioqueue && stun_cfg->timer_heap, PJ_EINVAL); + pj_stun_sock *stun_sock; /**< STUN transport. */ + pj_turn_sock *turn_sock; /**< TURN relay transport. */ - if (name == NULL) - name = "icstr%p"; + unsigned cand_cnt; /**< # of candidates/aliaes. */ + pj_ice_sess_cand cand_list[PJ_ICE_ST_MAX_CAND]; /**< Cand array */ - pool = pj_pool_create(stun_cfg->pf, name, PJNATH_POOL_LEN_ICE_STRANS, - PJNATH_POOL_INC_ICE_STRANS, NULL); - ice_st = PJ_POOL_ZALLOC_T(pool, pj_ice_strans); - ice_st->pool = pool; - pj_memcpy(ice_st->obj_name, pool->obj_name, PJ_MAX_OBJ_NAME); - ice_st->user_data = user_data; - - ice_st->comp_cnt = comp_cnt; - ice_st->comp = (pj_ice_strans_comp**) pj_pool_calloc(pool, comp_cnt, - sizeof(void*)); + unsigned default_cand; /**< Default candidate. */ - pj_memcpy(&ice_st->cb, cb, sizeof(*cb)); - pj_memcpy(&ice_st->stun_cfg, stun_cfg, sizeof(*stun_cfg)); +} pj_ice_strans_comp; - PJ_LOG(4,(ice_st->obj_name, "ICE stream transport created")); +/** + * This structure represents the ICE stream transport. + */ +struct pj_ice_strans +{ + char *obj_name; /**< Log ID. */ + pj_pool_t *pool; /**< Pool used by this object. */ + void *user_data; /**< Application data. */ + pj_ice_strans_cfg cfg; /**< Configuration. */ + pj_ice_strans_cb cb; /**< Application callback. */ - *p_ice_st = ice_st; - return PJ_SUCCESS; -} + pj_ice_sess *ice; /**< ICE session. */ + pj_time_val start_time;/**< Time when ICE was started */ -/* Destroy ICE */ -static void destroy_ice_st(pj_ice_strans *ice_st, pj_status_t reason) -{ - unsigned i; - char obj_name[PJ_MAX_OBJ_NAME]; + unsigned comp_cnt; /**< Number of components. */ + pj_ice_strans_comp **comp; /**< Components array. */ - if (reason == PJ_SUCCESS) { - pj_memcpy(obj_name, ice_st->obj_name, PJ_MAX_OBJ_NAME); - PJ_LOG(4,(obj_name, "ICE stream transport shutting down")); - } + pj_timer_entry ka_timer; /**< STUN keep-alive timer. */ - /* Kill keep-alive timer, if any */ - stop_ka_timer(ice_st); + pj_atomic_t *busy_cnt; /**< To prevent destroy */ + pj_bool_t destroy_req;/**< Destroy has been called? */ + pj_bool_t cb_called; /**< Init error callback called?*/ +}; - /* Destroy ICE if we have ICE */ - if (ice_st->ice) { - pj_ice_sess_destroy(ice_st->ice); - ice_st->ice = NULL; - } - /* Destroy all components */ - for (i=0; i<ice_st->comp_cnt; ++i) { - if (ice_st->comp[i]) { - destroy_component(ice_st->comp[i]); - ice_st->comp[i] = NULL; - } - } - ice_st->comp_cnt = 0; +/* Validate configuration */ +static pj_status_t pj_ice_strans_cfg_check_valid(const pj_ice_strans_cfg *cfg) +{ + pj_status_t status; - /* Done */ - pj_pool_release(ice_st->pool); + status = pj_stun_config_check_valid(&cfg->stun_cfg); + if (!status) + return status; - if (reason == PJ_SUCCESS) { - PJ_LOG(4,(obj_name, "ICE stream transport destroyed")); - } + return PJ_SUCCESS; } + /* - * Destroy ICE stream transport. + * Initialize ICE transport configuration with default values. */ -PJ_DEF(pj_status_t) pj_ice_strans_destroy(pj_ice_strans *ice_st) +PJ_DEF(void) pj_ice_strans_cfg_default(pj_ice_strans_cfg *cfg) { - destroy_ice_st(ice_st, PJ_SUCCESS); - return PJ_SUCCESS; + pj_bzero(cfg, sizeof(*cfg)); + + pj_stun_config_init(&cfg->stun_cfg, NULL, 0, NULL, NULL); + pj_stun_sock_cfg_default(&cfg->stun.cfg); + pj_turn_alloc_param_default(&cfg->turn.alloc_param); + + cfg->af = pj_AF_INET(); + cfg->stun.port = PJ_STUN_PORT; + cfg->turn.conn_type = PJ_TURN_TP_UDP; } + /* - * Resolve STUN server + * Copy configuration. */ -PJ_DEF(pj_status_t) pj_ice_strans_set_stun_domain(pj_ice_strans *ice_st, - pj_dns_resolver *resolver, - const pj_str_t *domain) +PJ_DEF(void) pj_ice_strans_cfg_copy( pj_pool_t *pool, + pj_ice_strans_cfg *dst, + const pj_ice_strans_cfg *src) { - /* Yeah, TODO */ - PJ_UNUSED_ARG(ice_st); - PJ_UNUSED_ARG(resolver); - PJ_UNUSED_ARG(domain); - return -1; + pj_memcpy(dst, src, sizeof(*src)); + + if (src->stun.server.slen) + pj_strdup(pool, &dst->stun.server, &src->stun.server); + if (src->turn.server.slen) + pj_strdup(pool, &dst->turn.server, &src->turn.server); + pj_stun_auth_cred_dup(pool, &dst->turn.auth_cred, + &src->turn.auth_cred); } /* - * Set STUN server address. + * Create the component. */ -PJ_DEF(pj_status_t) pj_ice_strans_set_stun_srv( pj_ice_strans *ice_st, - const pj_sockaddr_in *stun_srv, - const pj_sockaddr_in *turn_srv) +static pj_status_t create_comp(pj_ice_strans *ice_st, unsigned comp_id) { - PJ_ASSERT_RETURN(ice_st, PJ_EINVAL); - /* Must not have pending resolver job */ - PJ_ASSERT_RETURN(ice_st->has_rjob==PJ_FALSE, PJ_EINVALIDOP); - - if (stun_srv) { - pj_memcpy(&ice_st->stun_srv, stun_srv, sizeof(pj_sockaddr_in)); - } else { - pj_bzero(&ice_st->stun_srv, sizeof(pj_sockaddr_in)); - } - - if (turn_srv) { - pj_memcpy(&ice_st->turn_srv, turn_srv, sizeof(pj_sockaddr_in)); - } else { - pj_bzero(&ice_st->turn_srv, sizeof(pj_sockaddr_in)); - } - - return PJ_SUCCESS; -} - -/* Add new candidate */ -static pj_status_t add_cand( pj_ice_strans *ice_st, - pj_ice_strans_comp *comp, - unsigned comp_id, - pj_ice_cand_type type, - pj_uint16_t local_pref, - const pj_sockaddr_in *addr, - pj_bool_t set_default) -{ - pj_ice_strans_cand *cand; - unsigned i; - - PJ_ASSERT_RETURN(ice_st && comp && addr, PJ_EINVAL); - PJ_ASSERT_RETURN(comp->cand_cnt < PJ_ICE_ST_MAX_CAND, PJ_ETOOMANY); - - /* Check that we don't have candidate with the same - * address. - */ - for (i=0; i<comp->cand_cnt; ++i) { - if (pj_memcmp(addr, &comp->cand_list[i].addr, - sizeof(pj_sockaddr_in))==0) - { - /* Duplicate */ - PJ_LOG(5,(ice_st->obj_name, "Duplicate candidate not added")); - return PJ_SUCCESS; - } - } + pj_ice_strans_comp *comp = NULL; + pj_status_t status; - cand = &comp->cand_list[comp->cand_cnt]; - - pj_bzero(cand, sizeof(*cand)); - cand->type = type; - cand->status = PJ_SUCCESS; - pj_memcpy(&cand->addr, addr, sizeof(pj_sockaddr_in)); - cand->ice_cand_id = -1; - cand->local_pref = local_pref; - pj_ice_calc_foundation(ice_st->pool, &cand->foundation, type, - &comp->local_addr); - - if (set_default) - comp->default_cand = comp->cand_cnt; - - PJ_LOG(5,(ice_st->obj_name, - "Candidate %s:%d (type=%s) added to component %d", - pj_inet_ntoa(addr->sin_addr), - (int)pj_ntohs(addr->sin_port), - pj_ice_get_cand_type_name(type), - comp_id)); - - comp->cand_cnt++; - return PJ_SUCCESS; -} + /* Verify arguments */ + PJ_ASSERT_RETURN(ice_st && comp_id, PJ_EINVAL); -/* Create new component (i.e. socket) */ -static pj_status_t create_component(pj_ice_strans *ice_st, - unsigned comp_id, - pj_uint32_t options, - const pj_sockaddr_in *addr, - pj_ice_strans_comp **p_comp) -{ - enum { MAX_RETRY=100, PORT_INC=2 }; - pj_ioqueue_callback ioqueue_cb; - pj_ice_strans_comp *comp; - int retry, addr_len; - struct { - pj_uint32_t a1, a2, a3; - } tsx_id; - pj_status_t status; + /* Check that component ID present */ + PJ_ASSERT_RETURN(comp_id <= ice_st->comp_cnt, PJNATH_EICEINCOMPID); + /* Create component */ comp = PJ_POOL_ZALLOC_T(ice_st->pool, pj_ice_strans_comp); comp->ice_st = ice_st; comp->comp_id = comp_id; - comp->options = options; - comp->sock = PJ_INVALID_SOCKET; - comp->last_status = PJ_SUCCESS; - - /* Create transaction ID for STUN keep alives */ - tsx_id.a1 = 0; - tsx_id.a2 = comp_id; - tsx_id.a3 = (pj_uint32_t) (unsigned long) ice_st; - pj_memcpy(comp->ka_tsx_id, &tsx_id, sizeof(comp->ka_tsx_id)); - - /* Create socket */ - status = pj_sock_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, &comp->sock); - if (status != PJ_SUCCESS) - return status; - - /* Init address */ - if (addr) - pj_memcpy(&comp->local_addr, addr, sizeof(pj_sockaddr_in)); - else - pj_sockaddr_in_init(&comp->local_addr.ipv4, NULL, 0); - - /* Retry binding socket */ - for (retry=0; retry<MAX_RETRY; ++retry) { - pj_uint16_t port; - - status = pj_sock_bind(comp->sock, &comp->local_addr, - sizeof(pj_sockaddr_in)); - if (status == PJ_SUCCESS) - break; - - if (options & PJ_ICE_ST_OPT_NO_PORT_RETRY) - goto on_error; - - port = pj_ntohs(comp->local_addr.ipv4.sin_port); - port += PORT_INC; - comp->local_addr.ipv4.sin_port = pj_htons(port); - } - - /* Get the actual port where the socket is bound to. - * (don't care about the address, it will be retrieved later) - */ - addr_len = sizeof(comp->local_addr); - status = pj_sock_getsockname(comp->sock, &comp->local_addr, &addr_len); - if (status != PJ_SUCCESS) - goto on_error; - - /* Register to ioqueue */ - pj_bzero(&ioqueue_cb, sizeof(ioqueue_cb)); - ioqueue_cb.on_read_complete = &on_read_complete; - status = pj_ioqueue_register_sock(ice_st->pool, ice_st->stun_cfg.ioqueue, - comp->sock, comp, &ioqueue_cb, - &comp->key); - if (status != PJ_SUCCESS) - goto on_error; - - /* Disable concurrency */ - status = pj_ioqueue_set_concurrency(comp->key, PJ_FALSE); - if (status != PJ_SUCCESS) - goto on_error; - - pj_ioqueue_op_key_init(&comp->read_op, sizeof(comp->read_op)); - pj_ioqueue_op_key_init(&comp->write_op, sizeof(comp->write_op)); - - /* Kick start reading the socket */ - on_read_complete(comp->key, &comp->read_op, 0); - /* If the socket is bound to INADDR_ANY, then lookup all interfaces in - * the host and add them into cand_list. Otherwise if the socket is bound - * to a specific interface, then only add that specific interface to - * cand_list. - */ - if (((options & PJ_ICE_ST_OPT_DONT_ADD_CAND)==0) && - comp->local_addr.ipv4.sin_addr.s_addr == 0) - { - /* Socket is bound to INADDR_ANY */ - unsigned i, ifs_cnt; - pj_sockaddr ifs[PJ_ICE_ST_MAX_CAND-2]; - - /* Reset default candidate */ - comp->default_cand = -1; - - /* Enum all IP interfaces in the host */ - ifs_cnt = PJ_ARRAY_SIZE(ifs); - status = pj_enum_ip_interface(pj_AF_INET(), &ifs_cnt, ifs); - if (status != PJ_SUCCESS) - goto on_error; + ice_st->comp[comp_id-1] = comp; - /* Set default IP interface as the base address */ - status = pj_gethostip(pj_AF_INET(), &comp->local_addr); + /* Initialize default candidate */ + comp->default_cand = 0; + + /* Create STUN transport if configured */ + if (ice_st->cfg.stun.server.slen || !ice_st->cfg.stun.no_host_cands) { + pj_stun_sock_cb stun_sock_cb; + pj_ice_sess_cand *cand; + + pj_bzero(&stun_sock_cb, sizeof(stun_sock_cb)); + stun_sock_cb.on_rx_data = &stun_on_rx_data; + stun_sock_cb.on_status = &stun_on_status; + stun_sock_cb.on_data_sent = &stun_on_data_sent; + + /* Create the STUN transport */ + status = pj_stun_sock_create(&ice_st->cfg.stun_cfg, NULL, + ice_st->cfg.af, &stun_sock_cb, + &ice_st->cfg.stun.cfg, + comp, &comp->stun_sock); if (status != PJ_SUCCESS) - goto on_error; - - /* Add candidate entry for each interface */ - for (i=0; i<ifs_cnt; ++i) { - pj_sockaddr_in cand_addr; - pj_bool_t set_default; - pj_uint16_t local_pref; - - /* Ignore 127.0.0.0/24 address */ - if ((pj_ntohl(ifs[i].ipv4.sin_addr.s_addr) >> 24)==127) - continue; - - pj_memcpy(&cand_addr, &comp->local_addr, sizeof(pj_sockaddr_in)); - cand_addr.sin_addr.s_addr = ifs[i].ipv4.sin_addr.s_addr; + return status; + /* Start STUN Binding resolution and add srflx candidate + * only if server is set + */ + if (ice_st->cfg.stun.server.slen) { + pj_stun_sock_info stun_sock_info; + + /* Add pending job */ + ///sess_add_ref(ice_st); + + /* Start Binding resolution */ + status = pj_stun_sock_start(comp->stun_sock, + &ice_st->cfg.stun.server, + ice_st->cfg.stun.port, + ice_st->cfg.resolver); + if (status != PJ_SUCCESS) { + ///sess_dec_ref(ice_st); + return status; + } - /* If the IP address is equal to local address, assign it - * as default candidate. - */ - if (ifs[i].ipv4.sin_addr.s_addr == comp->local_addr.ipv4.sin_addr.s_addr) { - set_default = PJ_TRUE; - local_pref = 65535; - } else { - set_default = PJ_FALSE; - local_pref = 0; + /* Enumerate addresses */ + status = pj_stun_sock_get_info(comp->stun_sock, &stun_sock_info); + if (status != PJ_SUCCESS) { + ///sess_dec_ref(ice_st); + return status; } - status = add_cand(ice_st, comp, comp_id, - PJ_ICE_CAND_TYPE_HOST, - local_pref, &cand_addr, set_default); - if (status != PJ_SUCCESS) - goto on_error; + /* Add srflx candidate with pending status */ + cand = &comp->cand_list[comp->cand_cnt++]; + cand->type = PJ_ICE_CAND_TYPE_SRFLX; + cand->status = PJ_EPENDING; + cand->local_pref = SRFLX_PREF; + cand->transport_id = TP_STUN; + cand->comp_id = (pj_uint8_t) comp_id; + pj_sockaddr_cp(&cand->base_addr, &stun_sock_info.aliases[0]); + pj_sockaddr_cp(&cand->rel_addr, &cand->base_addr); + pj_ice_calc_foundation(ice_st->pool, &cand->foundation, + cand->type, &cand->base_addr); + PJ_LOG(4,(ice_st->obj_name, + "Comp %d: srflx candidate starts Binding discovery", + comp_id)); + + /* Set default candidate to srflx */ + comp->default_cand = cand - comp->cand_list; } - - } else if ((options & PJ_ICE_ST_OPT_DONT_ADD_CAND)==0) { - /* Socket is bound to specific address. - * In this case only add that address as a single entry in the - * cand_list table. + /* Add local addresses to host candidates, unless no_host_cands + * flag is set. */ - status = add_cand(ice_st, comp, comp_id, - PJ_ICE_CAND_TYPE_HOST, - 65535, &comp->local_addr.ipv4, - PJ_TRUE); - if (status != PJ_SUCCESS) - goto on_error; + if (ice_st->cfg.stun.no_host_cands == PJ_FALSE) { + pj_stun_sock_info stun_sock_info; + unsigned i; - } else if (options & PJ_ICE_ST_OPT_DONT_ADD_CAND) { - /* If application doesn't want to add candidate, just fix local_addr - * in case its value is zero. - */ - if (comp->local_addr.ipv4.sin_addr.s_addr == 0) { - status = pj_gethostip(pj_AF_INET(), &comp->local_addr); + /* Enumerate addresses */ + status = pj_stun_sock_get_info(comp->stun_sock, &stun_sock_info); if (status != PJ_SUCCESS) return status; + + for (i=0; i<stun_sock_info.alias_cnt; ++i) { + char addrinfo[PJ_INET6_ADDRSTRLEN+10]; + const pj_sockaddr *addr = &stun_sock_info.aliases[i]; + + /* Leave one candidate for relay */ + if (comp->cand_cnt >= PJ_ICE_ST_MAX_CAND-1) { + PJ_LOG(4,(ice_st->obj_name, "Too many host candidates")); + break; + } + + /* Ignore loopback addresses unless cfg->stun.loop_addr + * is set + */ + if ((pj_ntohl(addr->ipv4.sin_addr.s_addr)>>24)==127) { + if (ice_st->cfg.stun.loop_addr==PJ_FALSE) + continue; + } + + cand = &comp->cand_list[comp->cand_cnt++]; + + cand->type = PJ_ICE_CAND_TYPE_HOST; + cand->status = PJ_SUCCESS; + cand->local_pref = HOST_PREF; + cand->transport_id = TP_STUN; + cand->comp_id = (pj_uint8_t) comp_id; + pj_sockaddr_cp(&cand->addr, addr); + pj_sockaddr_cp(&cand->base_addr, addr); + pj_bzero(&cand->rel_addr, sizeof(cand->rel_addr)); + pj_ice_calc_foundation(ice_st->pool, &cand->foundation, + cand->type, &cand->base_addr); + + PJ_LOG(4,(ice_st->obj_name, + "Comp %d: host candidate %s added", + comp_id, pj_sockaddr_print(&cand->addr, addrinfo, + sizeof(addrinfo), 3))); + } } } + /* Create TURN relay if configured. */ + if (ice_st->cfg.turn.server.slen) { + pj_turn_sock_cb turn_sock_cb; + pj_ice_sess_cand *cand; - /* Done */ - if (p_comp) - *p_comp = comp; - - return PJ_SUCCESS; - -on_error: - destroy_component(comp); - return status; -} + /* Init TURN socket */ + pj_bzero(&turn_sock_cb, sizeof(turn_sock_cb)); + turn_sock_cb.on_rx_data = &turn_on_rx_data; + turn_sock_cb.on_state = &turn_on_state; -/* - * This is callback called by ioqueue on incoming packet - */ -static void on_read_complete(pj_ioqueue_key_t *key, - pj_ioqueue_op_key_t *op_key, - pj_ssize_t bytes_read) -{ - pj_ice_strans_comp *comp = (pj_ice_strans_comp*) - pj_ioqueue_get_user_data(key); - pj_ice_strans *ice_st = comp->ice_st; - pj_ssize_t pkt_size; - enum { RETRY = 10 }; - unsigned retry; - pj_status_t status; + status = pj_turn_sock_create(&ice_st->cfg.stun_cfg, ice_st->cfg.af, + ice_st->cfg.turn.conn_type, + &turn_sock_cb, 0, comp, + &comp->turn_sock); + if (status != PJ_SUCCESS) { + return status; + } - if (bytes_read > 0) { - /* - * Okay, we got a packet from the socket for the component. There is - * a bit of situation here, since this packet could be one of these: - * - * 1) this could be the response of STUN binding request sent by - * this component to a) an initial request to get the STUN mapped - * address of this component, or b) subsequent request to keep - * the binding alive. - * - * 2) this could be a packet (STUN or not STUN) sent from the STUN - * relay server. In this case, still there are few options to do - * for this packet: a) process this locally if this packet is - * related to TURN session management (e.g. Allocate response), - * b) forward this packet to ICE if this is related to ICE - * discovery process. - * - * 3) this could be a STUN request or response sent as part of ICE - * discovery process. - * - * 4) this could be application's packet, e.g. when ICE processing - * is done and agents start sending RTP/RTCP packets to each - * other, or when ICE processing is not done and this ICE stream - * transport decides to allow sending data. - * - * So far we don't have good solution for this. - * The process below is just a workaround. - */ - status = pj_stun_msg_check(comp->pkt, bytes_read, - PJ_STUN_IS_DATAGRAM); + /* Add pending job */ + ///sess_add_ref(ice_st); - if (status == PJ_SUCCESS) { - if (comp->stun_sess && - PJ_STUN_IS_RESPONSE(((pj_stun_msg_hdr*)comp->pkt)->type) && - pj_memcmp(comp->pkt+8, comp->ka_tsx_id, 12) == 0) - { - status = pj_stun_session_on_rx_pkt(comp->stun_sess, comp->pkt, - bytes_read, - PJ_STUN_IS_DATAGRAM, NULL, - NULL, &comp->src_addr, - comp->src_addr_len); - } else if (ice_st->ice) { - PJ_TODO(DISTINGUISH_BETWEEN_LOCAL_AND_RELAY); - - TRACE_PKT((comp->ice_st->obj_name, - "Component %d RX packet from %s:%d", - comp->comp_id, - pj_inet_ntoa(comp->src_addr.ipv4.sin_addr), - (int)pj_ntohs(comp->src_addr.ipv4.sin_port))); - - status = pj_ice_sess_on_rx_pkt(ice_st->ice, comp->comp_id, - comp->pkt, bytes_read, - &comp->src_addr, - comp->src_addr_len); - } else { - /* This must have been a very late STUN reponse, - * or an early STUN Binding Request when our local - * ICE has not been created yet. */ - } - } else { - (*ice_st->cb.on_rx_data)(ice_st, comp->comp_id, - comp->pkt, bytes_read, - &comp->src_addr, comp->src_addr_len); + /* Start allocation */ + status=pj_turn_sock_alloc(comp->turn_sock, + &ice_st->cfg.turn.server, + ice_st->cfg.turn.port, + ice_st->cfg.resolver, + &ice_st->cfg.turn.auth_cred, + &ice_st->cfg.turn.alloc_param); + if (status != PJ_SUCCESS) { + ///sess_dec_ref(ice_st); + return status; } - } else if (bytes_read < 0) { - ice_st_perror(comp->ice_st, "ioqueue read callback error", - -bytes_read); - } + /* Add relayed candidate with pending status */ + cand = &comp->cand_list[comp->cand_cnt++]; + cand->type = PJ_ICE_CAND_TYPE_RELAYED; + cand->status = PJ_EPENDING; + cand->local_pref = RELAY_PREF; + cand->transport_id = TP_TURN; + cand->comp_id = (pj_uint8_t) comp_id; - /* Read next packet */ - for (retry=0; retry<RETRY;) { - pkt_size = sizeof(comp->pkt); - comp->src_addr_len = sizeof(comp->src_addr); - status = pj_ioqueue_recvfrom(key, op_key, comp->pkt, &pkt_size, - PJ_IOQUEUE_ALWAYS_ASYNC, - &comp->src_addr, &comp->src_addr_len); - if (status == PJ_STATUS_FROM_OS(OSERR_EWOULDBLOCK) || - status == PJ_STATUS_FROM_OS(OSERR_EINPROGRESS) || - status == PJ_STATUS_FROM_OS(OSERR_ECONNRESET)) - { - ice_st_perror(comp->ice_st, "ioqueue recvfrom() error", status); - ++retry; - continue; - } else if (status != PJ_SUCCESS && status != PJ_EPENDING) { - retry += 2; - ice_st_perror(comp->ice_st, "ioqueue recvfrom() error", status); - } else { - break; - } - } -} + PJ_LOG(4,(ice_st->obj_name, + "Comp %d: TURN relay candidate waiting for allocation", + comp_id)); -/* - * Destroy a component - */ -static void destroy_component(pj_ice_strans_comp *comp) -{ - if (comp->stun_sess) { - pj_stun_session_destroy(comp->stun_sess); - comp->stun_sess = NULL; + /* Set default candidate to relay */ + comp->default_cand = cand - comp->cand_list; } - if (comp->key) { - pj_ioqueue_unregister(comp->key); - comp->key = NULL; - comp->sock = PJ_INVALID_SOCKET; - } else if (comp->sock != PJ_INVALID_SOCKET && comp->sock != 0) { - pj_sock_close(comp->sock); - comp->sock = PJ_INVALID_SOCKET; - } + return PJ_SUCCESS; } -/* STUN keep-alive timer callback */ -static void ka_timer_cb(pj_timer_heap_t *th, pj_timer_entry *te) +/* + * Create ICE stream transport + */ +PJ_DEF(pj_status_t) pj_ice_strans_create( const char *name, + const pj_ice_strans_cfg *cfg, + unsigned comp_cnt, + void *user_data, + const pj_ice_strans_cb *cb, + pj_ice_strans **p_ice_st) { - pj_ice_strans *ice_st = (pj_ice_strans*)te->user_data; + pj_pool_t *pool; + pj_ice_strans *ice_st; unsigned i; pj_status_t status; - PJ_UNUSED_ARG(th); + status = pj_ice_strans_cfg_check_valid(cfg); + if (status != PJ_SUCCESS) + return status; - ice_st->ka_timer.id = PJ_FALSE; + PJ_ASSERT_RETURN(comp_cnt && cb && p_ice_st, PJ_EINVAL); - for (i=0; i<ice_st->comp_cnt; ++i) { - pj_ice_strans_comp *comp = ice_st->comp[i]; - pj_stun_tx_data *tdata; - unsigned j; + if (name == NULL) + name = "ice%p"; - /* Does this component have STUN server reflexive candidate? */ - for (j=0; j<comp->cand_cnt; ++j) { - if (comp->cand_list[j].type == PJ_ICE_CAND_TYPE_SRFLX) - break; - } - if (j == comp->cand_cnt) - continue; - - /* Create STUN binding request */ - status = pj_stun_session_create_req(comp->stun_sess, - PJ_STUN_BINDING_REQUEST, - PJ_STUN_MAGIC, - comp->ka_tsx_id, &tdata); - if (status != PJ_SUCCESS) - continue; + pool = pj_pool_create(cfg->stun_cfg.pf, name, PJNATH_POOL_LEN_ICE_STRANS, + PJNATH_POOL_INC_ICE_STRANS, NULL); + ice_st = PJ_POOL_ZALLOC_T(pool, pj_ice_strans); + ice_st->pool = pool; + ice_st->obj_name = pool->obj_name; + ice_st->user_data = user_data; - /* tdata->user_data is NULL for keep-alive */ - //tdata->user_data = NULL; + PJ_LOG(4,(ice_st->obj_name, + "Creating ICE stream transport with %d component(s)", + comp_cnt)); - ++comp->pending_cnt; + pj_ice_strans_cfg_copy(pool, &ice_st->cfg, cfg); + pj_memcpy(&ice_st->cb, cb, sizeof(*cb)); + + status = pj_atomic_create(pool, 0, &ice_st->busy_cnt); + if (status != PJ_SUCCESS) { + destroy_ice_st(ice_st); + return status; + } + ice_st->comp_cnt = comp_cnt; + ice_st->comp = (pj_ice_strans_comp**) + pj_pool_calloc(pool, comp_cnt, sizeof(pj_ice_strans_comp*)); - /* Send STUN binding request */ - PJ_LOG(5,(ice_st->obj_name, "Sending STUN keep-alive from %s;%d", - pj_inet_ntoa(comp->local_addr.ipv4.sin_addr), - pj_ntohs(comp->local_addr.ipv4.sin_port))); - status = pj_stun_session_send_msg(comp->stun_sess, &comp->cand_list[j], - PJ_FALSE, PJ_TRUE, &ice_st->stun_srv, - sizeof(pj_sockaddr_in), tdata); + for (i=0; i<comp_cnt; ++i) { + status = create_comp(ice_st, i+1); if (status != PJ_SUCCESS) { - --comp->pending_cnt; + destroy_ice_st(ice_st); + return status; } } - /* Start next timer */ - start_ka_timer(ice_st); -} + /* Check if all candidates are ready (this may call callback) */ + sess_init_update(ice_st); -/* Start STUN keep-alive timer */ -static void start_ka_timer(pj_ice_strans *ice_st) -{ - pj_time_val delay; + PJ_LOG(4,(ice_st->obj_name, "ICE stream transport created")); - /* Skip if timer is already running */ - if (ice_st->ka_timer.id != PJ_FALSE) - return; + *p_ice_st = ice_st; + return PJ_SUCCESS; +} - delay.sec = PJ_ICE_ST_KEEP_ALIVE_MIN; - delay.msec = pj_rand() % (PJ_ICE_ST_KEEP_ALIVE_MAX_RAND * 1000); - pj_time_val_normalize(&delay); +/* Destroy ICE */ +static void destroy_ice_st(pj_ice_strans *ice_st) +{ + unsigned i; - ice_st->ka_timer.cb = &ka_timer_cb; - ice_st->ka_timer.user_data = ice_st; - - if (pj_timer_heap_schedule(ice_st->stun_cfg.timer_heap, - &ice_st->ka_timer, &delay)==PJ_SUCCESS) - { - ice_st->ka_timer.id = PJ_TRUE; + /* Destroy ICE if we have ICE */ + if (ice_st->ice) { + pj_ice_sess_destroy(ice_st->ice); + ice_st->ice = NULL; } -} + /* Destroy all components */ + for (i=0; i<ice_st->comp_cnt; ++i) { + if (ice_st->comp[i]) { + if (ice_st->comp[i]->stun_sock) { + pj_stun_sock_set_user_data(ice_st->comp[i]->stun_sock, NULL); + pj_stun_sock_destroy(ice_st->comp[i]->stun_sock); + ice_st->comp[i]->stun_sock = NULL; + } + if (ice_st->comp[i]->turn_sock) { + pj_turn_sock_set_user_data(ice_st->comp[i]->turn_sock, NULL); + pj_turn_sock_destroy(ice_st->comp[i]->turn_sock); + ice_st->comp[i]->turn_sock = NULL; + } + } + } + ice_st->comp_cnt = 0; -/* Stop STUN keep-alive timer */ -static void stop_ka_timer(pj_ice_strans *ice_st) -{ - /* Skip if timer is already stop */ - if (ice_st->ka_timer.id == PJ_FALSE) - return; + /* Destroy reference counter */ + if (ice_st->busy_cnt) { + pj_assert(pj_atomic_get(ice_st->busy_cnt)==0); + pj_atomic_destroy(ice_st->busy_cnt); + ice_st->busy_cnt = NULL; + } - pj_timer_heap_cancel(ice_st->stun_cfg.timer_heap, &ice_st->ka_timer); - ice_st->ka_timer.id = PJ_FALSE; + /* Done */ + pj_pool_release(ice_st->pool); } - -/* - * Add STUN mapping to a component. - */ -static pj_status_t get_stun_mapped_addr(pj_ice_strans *ice_st, - pj_ice_strans_comp *comp) +/* Notification about failure */ +static void sess_fail(pj_ice_strans *ice_st, pj_ice_strans_op op, + const char *title, pj_status_t status) { - pj_ice_strans_cand *cand; - pj_stun_session_cb sess_cb; - pj_stun_tx_data *tdata; - pj_status_t status; + char errmsg[PJ_ERR_MSG_SIZE]; - PJ_ASSERT_RETURN(ice_st && comp, PJ_EINVAL); - - /* Bail out if STUN server is still being resolved */ - if (ice_st->has_rjob) - return PJ_EBUSY; + pj_strerror(status, errmsg, sizeof(errmsg)); + PJ_LOG(4,(ice_st->obj_name, "%s: %s", title, errmsg)); + + if (op==PJ_ICE_STRANS_OP_INIT && ice_st->cb_called) + return; - /* Just return (successfully) if STUN server is not configured */ - if (ice_st->stun_srv.sin_family == 0) - return PJ_SUCCESS; + ice_st->cb_called = PJ_TRUE; + if (ice_st->cb.on_ice_complete) + (*ice_st->cb.on_ice_complete)(ice_st, op, status); +} - /* Create STUN session for this component */ - pj_bzero(&sess_cb, sizeof(sess_cb)); - sess_cb.on_request_complete = &stun_on_request_complete; - sess_cb.on_send_msg = &stun_on_send_msg; - status = pj_stun_session_create(&ice_st->stun_cfg, ice_st->obj_name, - &sess_cb, PJ_FALSE, &comp->stun_sess); - if (status != PJ_SUCCESS) - return status; +/* Update initialization status */ +static void sess_init_update(pj_ice_strans *ice_st) +{ + unsigned i; - /* Associate component with STUN session */ - pj_stun_session_set_user_data(comp->stun_sess, (void*)comp); + /* Ignore if init callback has been called */ + if (ice_st->cb_called) + return; - /* Create STUN binding request */ - status = pj_stun_session_create_req(comp->stun_sess, - PJ_STUN_BINDING_REQUEST, - PJ_STUN_MAGIC, - comp->ka_tsx_id, - &tdata); - if (status != PJ_SUCCESS) - return status; + /* Notify application when all candidates have been gathered */ + for (i=0; i<ice_st->comp_cnt; ++i) { + unsigned j; + pj_ice_strans_comp *comp = ice_st->comp[i]; - /* Will be attached to tdata in send_msg() */ - cand = &comp->cand_list[comp->cand_cnt]; + for (j=0; j<comp->cand_cnt; ++j) { + pj_ice_sess_cand *cand = &comp->cand_list[j]; - /* Add pending count first, since stun_on_request_complete() - * may be called before this function completes - */ - comp->pending_cnt++; - - /* Add new alias to this component */ - cand->type = PJ_ICE_CAND_TYPE_SRFLX; - cand->status = PJ_EPENDING; - cand->ice_cand_id = -1; - cand->local_pref = 65535; - pj_ice_calc_foundation(ice_st->pool, &cand->foundation, - PJ_ICE_CAND_TYPE_SRFLX, &comp->local_addr); - - ++comp->cand_cnt; - - /* Send STUN binding request */ - status = pj_stun_session_send_msg(comp->stun_sess, (void*)cand, PJ_FALSE, - PJ_TRUE, &ice_st->stun_srv, - sizeof(pj_sockaddr_in), tdata); - if (status != PJ_SUCCESS) { - --comp->pending_cnt; - --comp->cand_cnt; - return status; + if (cand->status == PJ_EPENDING) + return; + } } - return PJ_SUCCESS; + /* All candidates have been gathered */ + ice_st->cb_called = PJ_TRUE; + if (ice_st->cb.on_ice_complete) + (*ice_st->cb.on_ice_complete)(ice_st, PJ_ICE_STRANS_OP_INIT, + PJ_SUCCESS); } - /* - * Create the component. + * Destroy ICE stream transport. */ -PJ_DEF(pj_status_t) pj_ice_strans_create_comp(pj_ice_strans *ice_st, - unsigned comp_id, - pj_uint32_t options, - const pj_sockaddr_in *addr) +PJ_DEF(pj_status_t) pj_ice_strans_destroy(pj_ice_strans *ice_st) { - pj_ice_strans_comp *comp = NULL; - pj_status_t status; - - /* Verify arguments */ - PJ_ASSERT_RETURN(ice_st && comp_id, PJ_EINVAL); - - /* Check that component ID present */ - PJ_ASSERT_RETURN(comp_id <= ice_st->comp_cnt, PJNATH_EICEINCOMPID); - - /* Can't add new component while ICE is running */ - PJ_ASSERT_RETURN(ice_st->ice == NULL, PJ_EBUSY); - - /* Can't add new component while resolver is running */ - PJ_ASSERT_RETURN(ice_st->has_rjob == PJ_FALSE, PJ_EBUSY); - + char obj_name[PJ_MAX_OBJ_NAME]; - /* Create component */ - status = create_component(ice_st, comp_id, options, addr, &comp); - if (status != PJ_SUCCESS) - return status; + PJ_ASSERT_RETURN(ice_st, PJ_EINVAL); - if ((options & PJ_ICE_ST_OPT_DISABLE_STUN) == 0) { - status = get_stun_mapped_addr(ice_st, comp); - if (status != PJ_SUCCESS) { - destroy_component(comp); - return status; - } + ice_st->destroy_req = PJ_TRUE; + if (pj_atomic_get(ice_st->busy_cnt) > 0) { + PJ_LOG(5,(ice_st->obj_name, + "ICE strans object is busy, will destroy later")); + return PJ_EPENDING; } + + pj_memcpy(obj_name, ice_st->obj_name, PJ_MAX_OBJ_NAME); + destroy_ice_st(ice_st); - /* Store this component */ - ice_st->comp[comp_id-1] = comp; - + PJ_LOG(4,(obj_name, "ICE stream transport destroyed")); return PJ_SUCCESS; } -PJ_DEF(pj_status_t) pj_ice_strans_add_cand( pj_ice_strans *ice_st, - unsigned comp_id, - pj_ice_cand_type type, - pj_uint16_t local_pref, - const pj_sockaddr_in *addr, - pj_bool_t set_default) +/* + * Increment busy counter. + */ +static void sess_add_ref(pj_ice_strans *ice_st) { - pj_ice_strans_comp *comp; - - - PJ_ASSERT_RETURN(ice_st && comp_id && addr, PJ_EINVAL); - PJ_ASSERT_RETURN(comp_id <= ice_st->comp_cnt, PJ_EINVAL); - PJ_ASSERT_RETURN(ice_st->comp[comp_id-1] != NULL, PJ_EINVALIDOP); - - comp = ice_st->comp[comp_id-1]; - return add_cand(ice_st, comp, comp_id, type, local_pref, addr, - set_default); + pj_atomic_inc(ice_st->busy_cnt); } - -PJ_DEF(pj_status_t) pj_ice_strans_get_comps_status(pj_ice_strans *ice_st) +/* + * Decrement busy counter. If the counter has reached zero and destroy + * has been requested, destroy the object and return FALSE. + */ +static pj_bool_t sess_dec_ref(pj_ice_strans *ice_st) { - unsigned i; - pj_status_t worst = PJ_SUCCESS; - - for (i=0; i<ice_st->comp_cnt; ++i) { - pj_ice_strans_comp *comp = ice_st->comp[i]; - - if (comp->last_status == PJ_SUCCESS) { - /* okay */ - } else if (comp->pending_cnt && worst==PJ_SUCCESS) { - worst = PJ_EPENDING; - break; - } else if (comp->last_status != PJ_SUCCESS) { - worst = comp->last_status; - break; - } - - if (worst != PJ_SUCCESS) - break; + int count = pj_atomic_dec_and_get(ice_st->busy_cnt); + pj_assert(count >= 0); + if (count==0 && ice_st->destroy_req) { + pj_ice_strans_destroy(ice_st); + return PJ_FALSE; + } else { + return PJ_TRUE; } +} - return worst; +/* + * Get user data + */ +PJ_DEF(void*) pj_ice_strans_get_user_data(pj_ice_strans *ice_st) +{ + PJ_ASSERT_RETURN(ice_st, NULL); + return ice_st->user_data; } + /* * Create ICE! */ @@ -832,7 +613,7 @@ PJ_DEF(pj_status_t) pj_ice_strans_init_ice(pj_ice_strans *ice_st, pj_status_t status; unsigned i; pj_ice_sess_cb ice_cb; - const pj_uint8_t srflx_prio[4] = { 100, 126, 110, 0 }; + //const pj_uint8_t srflx_prio[4] = { 100, 126, 110, 0 }; /* Check arguments */ PJ_ASSERT_RETURN(ice_st, PJ_EINVAL); @@ -848,7 +629,7 @@ PJ_DEF(pj_status_t) pj_ice_strans_init_ice(pj_ice_strans *ice_st, ice_cb.on_tx_pkt = &ice_tx_pkt; /* Create! */ - status = pj_ice_sess_create(&ice_st->stun_cfg, ice_st->obj_name, role, + status = pj_ice_sess_create(&ice_st->cfg.stun_cfg, ice_st->obj_name, role, ice_st->comp_cnt, &ice_cb, local_ufrag, local_passwd, &ice_st->ice); if (status != PJ_SUCCESS) @@ -857,6 +638,7 @@ PJ_DEF(pj_status_t) pj_ice_strans_init_ice(pj_ice_strans *ice_st, /* Associate user data */ ice_st->ice->user_data = (void*)ice_st; +#if 0 /* If default candidate for components are SRFLX one, upload a custom * type priority to ICE session so that SRFLX candidates will get * checked first. @@ -867,30 +649,44 @@ PJ_DEF(pj_status_t) pj_ice_strans_init_ice(pj_ice_strans *ice_st, { pj_ice_sess_set_prefs(ice_st->ice, srflx_prio); } +#endif - - /* Add candidates */ + /* Add components/candidates */ for (i=0; i<ice_st->comp_cnt; ++i) { unsigned j; - pj_ice_strans_comp *comp= ice_st->comp[i]; + pj_ice_strans_comp *comp = ice_st->comp[i]; + + /* Re-enable logging for Send/Data indications */ + if (comp->turn_sock) { + PJ_LOG(5,(ice_st->obj_name, + "Disabling STUN Indication logging for " + "component %d", i+1)); + pj_turn_sock_set_log(comp->turn_sock, 0xFFFF); + } for (j=0; j<comp->cand_cnt; ++j) { - pj_ice_strans_cand *cand = &comp->cand_list[j]; + pj_ice_sess_cand *cand = &comp->cand_list[j]; + unsigned ice_cand_id; /* Skip if candidate is not ready */ if (cand->status != PJ_SUCCESS) { PJ_LOG(5,(ice_st->obj_name, - "Candidate %d in component %d is not added", + "Candidate %d of comp %d is not added (pending)", j, i)); continue; } + /* Must have address */ + pj_assert(pj_sockaddr_has_addr(&cand->addr)); + + /* Add the candidate */ status = pj_ice_sess_add_cand(ice_st->ice, comp->comp_id, - cand->type, cand->local_pref, + cand->transport_id, cand->type, + cand->local_pref, &cand->foundation, &cand->addr, - &comp->local_addr, NULL, - sizeof(pj_sockaddr_in), - (unsigned*)&cand->ice_cand_id); + &cand->base_addr, &cand->rel_addr, + pj_sockaddr_get_len(&cand->addr), + (unsigned*)&ice_cand_id); if (status != PJ_SUCCESS) goto on_error; } @@ -907,22 +703,22 @@ on_error: * Enum candidates */ PJ_DEF(pj_status_t) pj_ice_strans_enum_cands(pj_ice_strans *ice_st, - unsigned *count, - pj_ice_sess_cand cand[]) + unsigned comp_id, + unsigned *count, + pj_ice_sess_cand cand[]) { unsigned i, cnt; - pj_ice_sess_cand *pcand; + pj_ice_strans_comp *comp; - PJ_ASSERT_RETURN(ice_st && count && cand, PJ_EINVAL); - PJ_ASSERT_RETURN(ice_st->ice, PJ_EINVALIDOP); + PJ_ASSERT_RETURN(ice_st && comp_id && comp_id <= ice_st->comp_cnt && + count && cand, PJ_EINVAL); - cnt = ice_st->ice->lcand_cnt; + comp = ice_st->comp[comp_id - 1]; + cnt = comp->cand_cnt; cnt = (cnt > *count) ? *count : cnt; - *count = 0; for (i=0; i<cnt; ++i) { - pcand = &ice_st->ice->lcand[i]; - pj_memcpy(&cand[i], pcand, sizeof(pj_ice_sess_cand)); + pj_memcpy(&cand[i], &comp->cand_list[i], sizeof(pj_ice_sess_cand)); } *count = cnt; @@ -930,6 +726,49 @@ PJ_DEF(pj_status_t) pj_ice_strans_enum_cands(pj_ice_strans *ice_st, } /* + * Get default candidate. + */ +PJ_DEF(pj_status_t) pj_ice_strans_get_def_cand( pj_ice_strans *ice_st, + unsigned comp_id, + pj_ice_sess_cand *cand) +{ + const pj_ice_sess_check *valid_pair; + + PJ_ASSERT_RETURN(ice_st && comp_id && comp_id <= ice_st->comp_cnt && + cand, PJ_EINVAL); + + valid_pair = pj_ice_strans_get_valid_pair(ice_st, comp_id); + if (valid_pair) { + pj_memcpy(cand, valid_pair->lcand, sizeof(pj_ice_sess_cand)); + } else { + pj_ice_strans_comp *comp = ice_st->comp[comp_id - 1]; + pj_assert(comp->default_cand>=0 && comp->default_cand<comp->cand_cnt); + pj_memcpy(cand, &comp->cand_list[comp->default_cand], + sizeof(pj_ice_sess_cand)); + } + return PJ_SUCCESS; +} + +/* + * Get the current ICE role. + */ +PJ_DEF(pj_ice_sess_role) pj_ice_strans_get_role(pj_ice_strans *ice_st) +{ + PJ_ASSERT_RETURN(ice_st && ice_st->ice, PJ_ICE_SESS_ROLE_UNKNOWN); + return ice_st->ice->role; +} + +/* + * Change session role. + */ +PJ_DEF(pj_status_t) pj_ice_strans_change_role( pj_ice_strans *ice_st, + pj_ice_sess_role new_role) +{ + PJ_ASSERT_RETURN(ice_st && ice_st->ice, PJ_EINVALIDOP); + return pj_ice_sess_change_role(ice_st->ice, new_role); +} + +/* * Start ICE processing ! */ PJ_DEF(pj_status_t) pj_ice_strans_start_ice( pj_ice_strans *ice_st, @@ -940,11 +779,19 @@ PJ_DEF(pj_status_t) pj_ice_strans_start_ice( pj_ice_strans *ice_st, { pj_status_t status; + PJ_ASSERT_RETURN(ice_st && rem_ufrag && rem_passwd && + rem_cand_cnt && rem_cand, PJ_EINVAL); + + /* Mark start time */ + pj_gettimeofday(&ice_st->start_time); + + /* Build check list */ status = pj_ice_sess_create_check_list(ice_st->ice, rem_ufrag, rem_passwd, rem_cand_cnt, rem_cand); if (status != PJ_SUCCESS) return status; + /* Start ICE negotiation! */ status = pj_ice_sess_start_check(ice_st->ice); if (status != PJ_SUCCESS) { pj_ice_strans_stop_ice(ice_st); @@ -954,30 +801,36 @@ PJ_DEF(pj_status_t) pj_ice_strans_start_ice( pj_ice_strans *ice_st, } /* + * Get valid pair. + */ +PJ_DEF(const pj_ice_sess_check*) +pj_ice_strans_get_valid_pair(const pj_ice_strans *ice_st, + unsigned comp_id) +{ + PJ_ASSERT_RETURN(ice_st && comp_id && comp_id <= ice_st->comp_cnt, + NULL); + + if (ice_st->ice == NULL) + return NULL; + + return ice_st->ice->comp[comp_id-1].valid_check; +} + +/* * Stop ICE! */ PJ_DEF(pj_status_t) pj_ice_strans_stop_ice(pj_ice_strans *ice_st) { - unsigned i; - if (ice_st->ice) { pj_ice_sess_destroy(ice_st->ice); ice_st->ice = NULL; } - /* Invalidate all candidate Ids */ - for (i=0; i<ice_st->comp_cnt; ++i) { - unsigned j; - for (j=0; j<ice_st->comp[i]->cand_cnt; ++j) { - ice_st->comp[i]->cand_list[j].ice_cand_id = -1; - } - } - return PJ_SUCCESS; } /* - * Send packet using non-ICE means (e.g. when ICE was not negotiated). + * Application wants to send outgoing packet. */ PJ_DEF(pj_status_t) pj_ice_strans_sendto( pj_ice_strans *ice_st, unsigned comp_id, @@ -997,18 +850,24 @@ PJ_DEF(pj_status_t) pj_ice_strans_sendto( pj_ice_strans *ice_st, /* If ICE is available, send data with ICE */ if (ice_st->ice) { - return pj_ice_sess_send_data(ice_st->ice, comp_id, data, data_len); - } + if (comp->turn_sock) { + pj_turn_sock_lock(comp->turn_sock); + } + status = pj_ice_sess_send_data(ice_st->ice, comp_id, data, data_len); + if (comp->turn_sock) { + pj_turn_sock_unlock(comp->turn_sock); + } + return status; - /* Otherwise send direcly with the socket. This is for compatibility - * with remote that doesn't support ICE. - */ - pkt_size = data_len; - status = pj_ioqueue_sendto(comp->key, &comp->write_op, - data, &pkt_size, 0, - dst_addr, dst_addr_len); - - return (status==PJ_SUCCESS||status==PJ_EPENDING) ? PJ_SUCCESS : status; + } else if (comp->stun_sock) { + + pkt_size = data_len; + status = pj_stun_sock_sendto(comp->stun_sock, NULL, data, data_len, + 0, dst_addr, dst_addr_len); + return (status==PJ_SUCCESS||status==PJ_EPENDING) ? PJ_SUCCESS : status; + + } else + return PJ_EINVALIDOP; } /* @@ -1018,9 +877,79 @@ PJ_DEF(pj_status_t) pj_ice_strans_sendto( pj_ice_strans *ice_st, static void on_ice_complete(pj_ice_sess *ice, pj_status_t status) { pj_ice_strans *ice_st = (pj_ice_strans*)ice->user_data; + pj_time_val t; + unsigned msec; + + sess_add_ref(ice_st); + + pj_gettimeofday(&t); + PJ_TIME_VAL_SUB(t, ice_st->start_time); + msec = PJ_TIME_VAL_MSEC(t); + if (ice_st->cb.on_ice_complete) { - (*ice_st->cb.on_ice_complete)(ice_st, status); + if (status != PJ_SUCCESS) { + char errmsg[PJ_ERR_MSG_SIZE]; + pj_strerror(status, errmsg, sizeof(errmsg)); + PJ_LOG(4,(ice_st->obj_name, + "ICE negotiation failed after %ds:%03d: %s", + msec/1000, msec%1000, errmsg)); + } else { + unsigned i; + enum { + msg_disable_ind = 0xFFFF & + ~(PJ_STUN_SESS_LOG_TX_IND| + PJ_STUN_SESS_LOG_RX_IND) + }; + + PJ_LOG(4,(ice_st->obj_name, + "ICE negotiation success after %ds:%03d", + msec/1000, msec%1000)); + + for (i=0; i<ice_st->comp_cnt; ++i) { + const pj_ice_sess_check *check; + + check = pj_ice_strans_get_valid_pair(ice_st, i+1); + if (check) { + char lip[PJ_INET6_ADDRSTRLEN+10]; + char rip[PJ_INET6_ADDRSTRLEN+10]; + + pj_sockaddr_print(&check->lcand->addr, lip, + sizeof(lip), 3); + pj_sockaddr_print(&check->rcand->addr, rip, + sizeof(rip), 3); + + if (check->lcand->transport_id == TP_TURN) { + /* Disable logging for Send/Data indications */ + PJ_LOG(5,(ice_st->obj_name, + "Disabling STUN Indication logging for " + "component %d", i+1)); + pj_turn_sock_set_log(ice_st->comp[i]->turn_sock, + msg_disable_ind); + } + + PJ_LOG(4,(ice_st->obj_name, " Comp %d: " + "sending from %s candidate %s to " + "%s candidate %s", + i+1, + pj_ice_get_cand_type_name(check->lcand->type), + lip, + pj_ice_get_cand_type_name(check->rcand->type), + rip)); + + } else { + PJ_LOG(4,(ice_st->obj_name, + "Comp %d: disabled", i+1)); + } + } + } + + (*ice_st->cb.on_ice_complete)(ice_st, PJ_ICE_STRANS_OP_NEGOTIATION, + status); + + } + + sess_dec_ref(ice_st); } /* @@ -1028,30 +957,42 @@ static void on_ice_complete(pj_ice_sess *ice, pj_status_t status) */ static pj_status_t ice_tx_pkt(pj_ice_sess *ice, unsigned comp_id, + unsigned transport_id, const void *pkt, pj_size_t size, const pj_sockaddr_t *dst_addr, unsigned dst_addr_len) { pj_ice_strans *ice_st = (pj_ice_strans*)ice->user_data; - pj_ice_strans_comp *comp = NULL; - pj_ssize_t pkt_size; + pj_ice_strans_comp *comp; pj_status_t status; - PJ_TODO(TX_TO_RELAY); - PJ_ASSERT_RETURN(comp_id && comp_id <= ice_st->comp_cnt, PJ_EINVAL); + comp = ice_st->comp[comp_id-1]; TRACE_PKT((comp->ice_st->obj_name, - "Component %d TX packet to %s:%d", - comp_id, + "Component %d TX packet to %s:%d with transport %d", + comp_id, pj_inet_ntoa(((pj_sockaddr_in*)dst_addr)->sin_addr), - (int)pj_ntohs(((pj_sockaddr_in*)dst_addr)->sin_port))); - - pkt_size = size; - status = pj_ioqueue_sendto(comp->key, &comp->write_op, - pkt, &pkt_size, 0, - dst_addr, dst_addr_len); + (int)pj_ntohs(((pj_sockaddr_in*)dst_addr)->sin_port), + transport_id)); + + if (transport_id == TP_TURN) { + if (comp->turn_sock) { + status = pj_turn_sock_sendto(comp->turn_sock, + (const pj_uint8_t*)pkt, size, + dst_addr, dst_addr_len); + } else { + status = PJ_EINVALIDOP; + } + } else if (transport_id == TP_STUN) { + status = pj_stun_sock_sendto(comp->stun_sock, NULL, + pkt, size, 0, + dst_addr, dst_addr_len); + } else { + pj_assert(!"Invalid transport ID"); + status = PJ_EINVALIDOP; + } return (status==PJ_SUCCESS||status==PJ_EPENDING) ? PJ_SUCCESS : status; } @@ -1061,148 +1002,295 @@ static pj_status_t ice_tx_pkt(pj_ice_sess *ice, */ static void ice_rx_data(pj_ice_sess *ice, unsigned comp_id, + unsigned transport_id, void *pkt, pj_size_t size, const pj_sockaddr_t *src_addr, unsigned src_addr_len) { pj_ice_strans *ice_st = (pj_ice_strans*)ice->user_data; + PJ_UNUSED_ARG(transport_id); + if (ice_st->cb.on_rx_data) { (*ice_st->cb.on_rx_data)(ice_st, comp_id, pkt, size, src_addr, src_addr_len); } } -/* - * Callback called by STUN session to send outgoing packet. +/* Notification when incoming packet has been received from + * the STUN socket. */ -static pj_status_t stun_on_send_msg(pj_stun_session *sess, - void *token, - const void *pkt, - pj_size_t size, - const pj_sockaddr_t *dst_addr, - unsigned dst_addr_len) +static pj_bool_t stun_on_rx_data(pj_stun_sock *stun_sock, + void *pkt, + unsigned pkt_len, + const pj_sockaddr_t *src_addr, + unsigned addr_len) { pj_ice_strans_comp *comp; - pj_ssize_t pkt_size; + pj_ice_strans *ice_st; pj_status_t status; - PJ_UNUSED_ARG(token); + comp = (pj_ice_strans_comp*) pj_stun_sock_get_user_data(stun_sock); + ice_st = comp->ice_st; - comp = (pj_ice_strans_comp*) pj_stun_session_get_user_data(sess); - pkt_size = size; - status = pj_ioqueue_sendto(comp->key, &comp->write_op, - pkt, &pkt_size, 0, - dst_addr, dst_addr_len); - - return (status==PJ_SUCCESS||status==PJ_EPENDING) ? PJ_SUCCESS : status; + sess_add_ref(ice_st); + + if (ice_st->ice == NULL) { + /* The ICE session is gone, but we're still receiving packets. + * This could also happen if remote doesn't do ICE. So just + * report this to application. + */ + if (ice_st->cb.on_rx_data) { + (*ice_st->cb.on_rx_data)(ice_st, comp->comp_id, pkt, pkt_len, + src_addr, addr_len); + } + + } else { + + /* Hand over the packet to ICE session */ + status = pj_ice_sess_on_rx_pkt(comp->ice_st->ice, comp->comp_id, + TP_STUN, pkt, pkt_len, + src_addr, addr_len); + + if (status != PJ_SUCCESS) { + ice_st_perror(comp->ice_st, "Error processing packet", + status); + } + } + + return sess_dec_ref(ice_st); } -/* - * Callback sent by STUN session when outgoing STUN request has - * completed. +/* Notifification when asynchronous send operation to the STUN socket + * has completed. */ -static void stun_on_request_complete(pj_stun_session *sess, - pj_status_t status, - void *token, - pj_stun_tx_data *tdata, - const pj_stun_msg *response, - const pj_sockaddr_t *src_addr, - unsigned src_addr_len) +static pj_bool_t stun_on_data_sent(pj_stun_sock *stun_sock, + pj_ioqueue_op_key_t *send_key, + pj_ssize_t sent) +{ + PJ_UNUSED_ARG(stun_sock); + PJ_UNUSED_ARG(send_key); + PJ_UNUSED_ARG(sent); + return PJ_TRUE; +} + +/* Notification when the status of the STUN transport has changed. */ +static pj_bool_t stun_on_status(pj_stun_sock *stun_sock, + pj_stun_sock_op op, + pj_status_t status) { pj_ice_strans_comp *comp; - pj_ice_strans_cand *cand = NULL; - pj_stun_xor_mapped_addr_attr *xa; - pj_stun_mapped_addr_attr *ma; - pj_sockaddr *mapped_addr; - char ip[20]; - - comp = (pj_ice_strans_comp*) pj_stun_session_get_user_data(sess); - cand = (pj_ice_strans_cand*) token; - - PJ_UNUSED_ARG(token); - PJ_UNUSED_ARG(tdata); - PJ_UNUSED_ARG(src_addr); - PJ_UNUSED_ARG(src_addr_len); - - if (cand == NULL) { - /* This is keep-alive */ + pj_ice_strans *ice_st; + pj_ice_sess_cand *cand = NULL; + unsigned i; + + comp = (pj_ice_strans_comp*) pj_stun_sock_get_user_data(stun_sock); + ice_st = comp->ice_st; + + sess_add_ref(ice_st); + + /* Find the srflx cancidate */ + for (i=0; i<comp->cand_cnt; ++i) { + if (comp->cand_list[i].type == PJ_ICE_CAND_TYPE_SRFLX) { + cand = &comp->cand_list[i]; + break; + } + } + + pj_assert(status != PJ_EPENDING); + + switch (op) { + case PJ_STUN_SOCK_DNS_OP: if (status != PJ_SUCCESS) { - ice_st_perror(comp->ice_st, "STUN keep-alive request failed", - status); + /* May not have cand, e.g. when error during init */ + if (cand) + cand->status = status; + sess_fail(ice_st, PJ_ICE_STRANS_OP_INIT, "DNS resolution failed", + status); + } + break; + case PJ_STUN_SOCK_BINDING_OP: + if (status == PJ_SUCCESS) { + pj_stun_sock_info info; + + status = pj_stun_sock_get_info(stun_sock, &info); + if (status == PJ_SUCCESS) { + char ipaddr[PJ_INET6_ADDRSTRLEN+10]; + pj_bool_t dup = PJ_FALSE; + + /* Eliminate the srflx candidate if the address is + * equal to other (host) candidates. + */ + for (i=0; i<comp->cand_cnt; ++i) { + if (comp->cand_list[i].type == PJ_ICE_CAND_TYPE_HOST && + pj_sockaddr_cmp(&comp->cand_list[i].addr, + &info.mapped_addr) == 0) + { + dup = PJ_TRUE; + break; + } + } + + if (dup) { + /* Duplicate found, remove the srflx candidate */ + pj_array_erase(comp->cand_list, sizeof(comp->cand_list[0]), + comp->cand_cnt, cand - comp->cand_list); + --comp->cand_cnt; + } else { + /* Otherwise update the address */ + pj_sockaddr_cp(&cand->addr, &info.mapped_addr); + cand->status = PJ_SUCCESS; + } + + PJ_LOG(4,(comp->ice_st->obj_name, + "Comp %d: Binding discovery complete, " + "srflx address is %s", + comp->comp_id, + pj_sockaddr_print(&info.mapped_addr, ipaddr, + sizeof(ipaddr), 3))); + + sess_init_update(ice_st); + } + } + + if (status != PJ_SUCCESS) { + /* May not have cand, e.g. when error during init */ + if (cand) + cand->status = status; + sess_fail(ice_st, PJ_ICE_STRANS_OP_INIT, + "STUN binding request failed", status); } + break; + case PJ_STUN_SOCK_KEEP_ALIVE_OP: + if (status != PJ_SUCCESS) { + pj_assert(cand != NULL); + cand->status = status; + sess_fail(ice_st, PJ_ICE_STRANS_OP_INIT, + "STUN keep-alive failed", status); + } + break; + } + + return sess_dec_ref(ice_st); +} + +/* Callback when TURN socket has received a packet */ +static void turn_on_rx_data(pj_turn_sock *turn_sock, + void *pkt, + unsigned pkt_len, + const pj_sockaddr_t *peer_addr, + unsigned addr_len) +{ + pj_ice_strans_comp *comp; + pj_status_t status; + + comp = (pj_ice_strans_comp*) pj_turn_sock_get_user_data(turn_sock); + if (comp == NULL) { + /* We have disassociated ourselves from the TURN socket */ return; } - /* Decrement pending count for this component */ - pj_assert(comp->pending_cnt > 0); - comp->pending_cnt--; + sess_add_ref(comp->ice_st); - if (status == PJNATH_ESTUNTIMEDOUT) { + if (comp->ice_st->ice == NULL) { + /* The ICE session is gone, but we're still receiving packets. + * This could also happen if remote doesn't do ICE and application + * specifies TURN as the default address in SDP. + * So in this case just give the packet to application. + */ + if (comp->ice_st->cb.on_rx_data) { + (*comp->ice_st->cb.on_rx_data)(comp->ice_st, comp->comp_id, pkt, + pkt_len, peer_addr, addr_len); + } - PJ_LOG(4,(comp->ice_st->obj_name, - "STUN Binding request has timed-out, will retry " - "again alter")); + } else { - /* Restart keep-alive timer */ - start_ka_timer(comp->ice_st); - return; + /* Hand over the packet to ICE */ + status = pj_ice_sess_on_rx_pkt(comp->ice_st->ice, comp->comp_id, + TP_TURN, pkt, pkt_len, + peer_addr, addr_len); - } else if (status != PJ_SUCCESS) { - comp->last_status = cand->status = status; - ice_st_perror(comp->ice_st, "STUN Binding request failed", - cand->status); - return; + if (status != PJ_SUCCESS) { + ice_st_perror(comp->ice_st, + "Error processing packet from TURN relay", + status); + } } - xa = (pj_stun_xor_mapped_addr_attr*) - pj_stun_msg_find_attr(response, PJ_STUN_ATTR_XOR_MAPPED_ADDR, 0); - ma = (pj_stun_mapped_addr_attr*) - pj_stun_msg_find_attr(response, PJ_STUN_ATTR_MAPPED_ADDR, 0); - - if (xa) - mapped_addr = &xa->sockaddr; - else if (ma) - mapped_addr = &ma->sockaddr; - else { - cand->status = PJNATH_ESTUNNOMAPPEDADDR; - ice_st_perror(comp->ice_st, "STUN Binding request failed", - cand->status); + sess_dec_ref(comp->ice_st); +} + + +/* Callback when TURN client state has changed */ +static void turn_on_state(pj_turn_sock *turn_sock, pj_turn_state_t old_state, + pj_turn_state_t new_state) +{ + pj_ice_strans_comp *comp; + + comp = (pj_ice_strans_comp*) pj_turn_sock_get_user_data(turn_sock); + if (comp == NULL) { + /* Not interested in further state notification once the relay is + * disconnecting. + */ return; } - /* Save IP address for logging */ - pj_ansi_strcpy(ip, pj_inet_ntoa(comp->local_addr.ipv4.sin_addr)); + PJ_LOG(5,(comp->ice_st->obj_name, "TURN client state changed %s --> %s", + pj_turn_state_name(old_state), pj_turn_state_name(new_state))); + + sess_add_ref(comp->ice_st); + + if (new_state == PJ_TURN_STATE_READY) { + pj_turn_session_info rel_info; + char ipaddr[PJ_INET6_ADDRSTRLEN+8]; + pj_ice_sess_cand *cand = NULL; + unsigned i; + + /* Get allocation info */ + pj_turn_sock_get_info(turn_sock, &rel_info); + + /* Find relayed candidate in the component */ + for (i=0; i<comp->cand_cnt; ++i) { + if (comp->cand_list[i].type == PJ_ICE_CAND_TYPE_RELAYED) { + cand = &comp->cand_list[i]; + break; + } + } + pj_assert(cand != NULL); + + /* Update candidate */ + pj_sockaddr_cp(&cand->addr, &rel_info.relay_addr); + pj_sockaddr_cp(&cand->base_addr, &rel_info.relay_addr); + pj_sockaddr_cp(&cand->rel_addr, &rel_info.mapped_addr); + pj_ice_calc_foundation(comp->ice_st->pool, &cand->foundation, + PJ_ICE_CAND_TYPE_RELAYED, + &rel_info.relay_addr); + cand->status = PJ_SUCCESS; - /* Ignore response if it reports the same address */ - if (comp->local_addr.ipv4.sin_addr.s_addr == mapped_addr->ipv4.sin_addr.s_addr && - comp->local_addr.ipv4.sin_port == mapped_addr->ipv4.sin_port) - { PJ_LOG(4,(comp->ice_st->obj_name, - "Candidate %s:%d is directly connected to Internet, " - "STUN mapped address is ignored", - ip, pj_ntohs(comp->local_addr.ipv4.sin_port))); - return; + "Comp %d: TURN allocation complete, relay address is %s", + comp->comp_id, + pj_sockaddr_print(&rel_info.relay_addr, ipaddr, + sizeof(ipaddr), 3))); + + sess_init_update(comp->ice_st); + + } else if (new_state >= PJ_TURN_STATE_DEALLOCATING) { + pj_turn_session_info info; + + pj_turn_sock_get_info(turn_sock, &info); + + /* Unregister ourself from the TURN relay */ + pj_turn_sock_set_user_data(turn_sock, NULL); + comp->turn_sock = NULL; + + /* Set session to fail if we're still initializing */ + if (old_state < PJ_TURN_STATE_READY) { + sess_fail(comp->ice_st, PJ_ICE_STRANS_OP_INIT, + "TURN relay failed", info.last_status); + } } - PJ_LOG(5,(comp->ice_st->obj_name, - "STUN mapped address for %s:%d is %s:%d", - ip, (int)pj_ntohs(comp->local_addr.ipv4.sin_port), - pj_inet_ntoa(mapped_addr->ipv4.sin_addr), - (int)pj_ntohs(mapped_addr->ipv4.sin_port))); - pj_memcpy(&cand->addr, mapped_addr, sizeof(pj_sockaddr_in)); - cand->status = PJ_SUCCESS; - - /* Set this candidate as the default candidate */ - comp->default_cand = (cand - comp->cand_list); - comp->last_status = PJ_SUCCESS; - - /* We have STUN, so we must start the keep-alive timer */ - start_ka_timer(comp->ice_st); - - /* Notify app that STUN address has changed. */ - if (comp->ice_st->cb.on_addr_change) - (*comp->ice_st->cb.on_addr_change)(comp->ice_st, comp->comp_id, - (cand - comp->cand_list)); + sess_dec_ref(comp->ice_st); } |