diff options
author | Benny Prijono <bennylp@teluu.com> | 2007-04-03 18:01:27 +0000 |
---|---|---|
committer | Benny Prijono <bennylp@teluu.com> | 2007-04-03 18:01:27 +0000 |
commit | 89c220218ff5245488bf78514c79dfaf86b13743 (patch) | |
tree | 6e2fac9d12995fe188f19226cafe233d83e4c691 /pjnath | |
parent | ba0f43431650452e9a2977c965edd1ea460633c0 (diff) |
Fixed misc bugs with ICE: (1) moved STUN session from candidate to component since it causes STUN response to wrong session, and (2) keep-alive transaction timed-out when ICE is active
git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@1140 74dad513-b988-da41-8d7b-12977e46ad98
Diffstat (limited to 'pjnath')
-rw-r--r-- | pjnath/include/pjnath/config.h | 31 | ||||
-rw-r--r-- | pjnath/include/pjnath/ice_session.h | 16 | ||||
-rw-r--r-- | pjnath/include/pjnath/ice_strans.h | 1 | ||||
-rw-r--r-- | pjnath/include/pjnath/stun_session.h | 2 | ||||
-rw-r--r-- | pjnath/include/pjnath/stun_transaction.h | 38 | ||||
-rw-r--r-- | pjnath/src/pjnath/ice_session.c | 183 | ||||
-rw-r--r-- | pjnath/src/pjnath/ice_strans.c | 78 | ||||
-rw-r--r-- | pjnath/src/pjnath/stun_session.c | 64 | ||||
-rw-r--r-- | pjnath/src/pjnath/stun_transaction.c | 128 |
9 files changed, 384 insertions, 157 deletions
diff --git a/pjnath/include/pjnath/config.h b/pjnath/include/pjnath/config.h index dec268f8..50db9420 100644 --- a/pjnath/include/pjnath/config.h +++ b/pjnath/include/pjnath/config.h @@ -164,6 +164,37 @@ #endif +/** + * Minimum interval value to be used for sending STUN keep-alive on the ICE + * stream transport, in seconds. This minimum interval, plus a random value + * which maximum is PJ_ICE_ST_KEEP_ALIVE_MAX_RAND, specify the actual interval + * of the STUN keep-alive. + * + * Default: 20 seconds + * + * @see PJ_ICE_ST_KEEP_ALIVE_MAX_RAND + */ +#ifndef PJ_ICE_ST_KEEP_ALIVE_MIN +# define PJ_ICE_ST_KEEP_ALIVE_MIN 20 +#endif + + +/** + * To prevent STUN keep-alives to be sent simultaneously, application should + * add random interval to minimum interval (PJ_ICE_ST_KEEP_ALIVE_MIN). This + * setting specifies the maximum random value to be added to the minimum + * interval, in seconds. + * + * Default: 5 seconds + * + * @see PJ_ICE_ST_KEEP_ALIVE_MIN + */ +#ifndef PJ_ICE_ST_KEEP_ALIVE_MAX_RAND +# define PJ_ICE_ST_KEEP_ALIVE_MAX_RAND 5 +#endif + + + /** * @} diff --git a/pjnath/include/pjnath/ice_session.h b/pjnath/include/pjnath/ice_session.h index 14b9e3d3..761153d8 100644 --- a/pjnath/include/pjnath/ice_session.h +++ b/pjnath/include/pjnath/ice_session.h @@ -120,6 +120,12 @@ typedef struct pj_ice_sess_comp */ pj_ice_sess_check *valid_check; + /** + * The STUN session to be used to send and receive STUN messages for this + * component. + */ + pj_stun_session *stun_sess; + } pj_ice_sess_comp; @@ -187,12 +193,6 @@ typedef struct pj_ice_sess_cand */ pj_sockaddr rel_addr; - /** - * The STUN session to be used to send and receive STUN messages for this - * candidate. - */ - pj_stun_session *stun_sess; - } pj_ice_sess_cand; @@ -366,14 +366,12 @@ typedef struct pj_ice_sess_cb * * @param ice The ICE session. * @param comp_id ICE component ID. - * @param cand_id ICE candidate ID. * @param pkt The STUN packet. * @param size The size of the packet. * @param dst_addr Packet destination address. * @param dst_addr_len Length of destination address. */ pj_status_t (*on_tx_pkt)(pj_ice_sess *ice, unsigned comp_id, - unsigned cand_id, const void *pkt, pj_size_t size, const pj_sockaddr_t *dst_addr, unsigned dst_addr_len); @@ -695,7 +693,6 @@ PJ_DECL(pj_status_t) pj_ice_sess_send_data(pj_ice_sess *ice, * * @param ice The ICE session. * @param comp_id Component ID. - * @param cand_id Candidate ID. * @param pkt Incoming packet. * @param pkt_size Size of incoming packet. * @param src_addr Source address of the packet. @@ -705,7 +702,6 @@ PJ_DECL(pj_status_t) pj_ice_sess_send_data(pj_ice_sess *ice, */ PJ_DECL(pj_status_t) pj_ice_sess_on_rx_pkt(pj_ice_sess *ice, unsigned comp_id, - unsigned cand_id, void *pkt, pj_size_t pkt_size, const pj_sockaddr_t *src_addr, diff --git a/pjnath/include/pjnath/ice_strans.h b/pjnath/include/pjnath/ice_strans.h index 59243a00..c145b277 100644 --- a/pjnath/include/pjnath/ice_strans.h +++ b/pjnath/include/pjnath/ice_strans.h @@ -274,6 +274,7 @@ typedef struct pj_ice_strans_comp pj_sock_t sock; /**< Socket descriptor. */ pj_stun_session *stun_sess; /**< STUN session. */ + pj_uint8_t ka_tsx_id[12]; /**< ID for keep STUN alives */ pj_sockaddr local_addr; /**< Local/base address. */ diff --git a/pjnath/include/pjnath/stun_session.h b/pjnath/include/pjnath/stun_session.h index 9f5a0d60..6b45adf2 100644 --- a/pjnath/include/pjnath/stun_session.h +++ b/pjnath/include/pjnath/stun_session.h @@ -245,6 +245,7 @@ PJ_DECL(void) pj_stun_session_set_credential(pj_stun_session *sess, * @param sess The STUN session instance. * @param msg_type The STUN request message type, from pj_stun_method_e or * from pj_stun_msg_type. + * @param tsx_id Optional transaction ID. * @param p_tdata Pointer to receive STUN transmit data instance containing * the request. * @@ -252,6 +253,7 @@ PJ_DECL(void) pj_stun_session_set_credential(pj_stun_session *sess, */ PJ_DECL(pj_status_t) pj_stun_session_create_req(pj_stun_session *sess, int msg_type, + const pj_uint8_t tsx_id[12], pj_stun_tx_data **p_tdata); /** diff --git a/pjnath/include/pjnath/stun_transaction.h b/pjnath/include/pjnath/stun_transaction.h index f296c9f5..23f63771 100644 --- a/pjnath/include/pjnath/stun_transaction.h +++ b/pjnath/include/pjnath/stun_transaction.h @@ -90,6 +90,19 @@ typedef struct pj_stun_tsx_cb const void *stun_pkt, pj_size_t pkt_size); + /** + * This callback is called after the timer that was scheduled by + * #pj_stun_client_tsx_schedule_destroy() has elapsed. Application + * should call #pj_stun_client_tsx_destroy() upon receiving this + * callback. + * + * This callback is optional if application will not call + * #pj_stun_client_tsx_schedule_destroy(). + * + * @param tsx The STUN transaction instance. + */ + void (*on_destroy)(pj_stun_client_tsx *tsx); + } pj_stun_tsx_cb; @@ -116,7 +129,30 @@ PJ_DECL(pj_status_t) pj_stun_client_tsx_create( pj_stun_config *cfg, pj_stun_client_tsx **p_tsx); /** - * Destroy a STUN client transaction. + * Schedule timer to destroy the transaction after the transaction is + * complete. Application normally calls this function in the on_complete() + * callback. When this timer elapsed, the on_destroy() callback will be + * called. + * + * This is convenient to let the STUN transaction absorbs any response + * for the previous request retransmissions. If application doesn't want + * this, it can destroy the transaction immediately by calling + * #pj_stun_client_tsx_destroy(). + * + * @param tsx The STUN transaction. + * @param delay The delay interval before on_destroy() callback + * is called. + * + * @return PJ_SUCCESS on success, or the appropriate error code. + */ +PJ_DECL(pj_status_t) +pj_stun_client_tsx_schedule_destroy(pj_stun_client_tsx *tsx, + const pj_time_val *delay); + + +/** + * Destroy a STUN client transaction immediately. This function can be + * called at any time to stop the transaction and destroy it. * * @param tsx The STUN transaction. * diff --git a/pjnath/src/pjnath/ice_session.c b/pjnath/src/pjnath/ice_session.c index 5365ca6a..f3b9ce87 100644 --- a/pjnath/src/pjnath/ice_session.c +++ b/pjnath/src/pjnath/ice_session.c @@ -83,7 +83,8 @@ static pj_uint8_t cand_type_prefs[4] = typedef struct stun_data { pj_ice_sess *ice; - pj_ice_sess_cand *lcand; + unsigned comp_id; + pj_ice_sess_comp *comp; } stun_data; typedef struct timer_data @@ -179,6 +180,50 @@ PJ_DEF(void) pj_ice_calc_foundation(pj_pool_t *pool, } +/* Init component */ +static pj_status_t init_comp(pj_ice_sess *ice, + unsigned comp_id, + pj_ice_sess_comp *comp) +{ + pj_stun_session_cb sess_cb; + pj_stun_auth_cred auth_cred; + stun_data *sd; + pj_status_t status; + + /* Init STUN callbacks */ + pj_bzero(&sess_cb, sizeof(sess_cb)); + sess_cb.on_request_complete = &on_stun_request_complete; + sess_cb.on_rx_indication = &on_stun_rx_indication; + sess_cb.on_rx_request = &on_stun_rx_request; + sess_cb.on_send_msg = &on_stun_send_msg; + + /* Create STUN session for this candidate */ + status = pj_stun_session_create(&ice->stun_cfg, NULL, + &sess_cb, PJ_FALSE, + &comp->stun_sess); + if (status != PJ_SUCCESS) + return status; + + /* Associate data with this STUN session */ + sd = PJ_POOL_ZALLOC_T(ice->pool, struct stun_data); + sd->ice = ice; + sd->comp_id = comp_id; + sd->comp = comp; + pj_stun_session_set_user_data(comp->stun_sess, sd); + + /* Init STUN authentication credential */ + pj_bzero(&auth_cred, sizeof(auth_cred)); + auth_cred.type = PJ_STUN_AUTH_CRED_DYNAMIC; + auth_cred.data.dyn_cred.get_auth = &stun_auth_get_auth; + auth_cred.data.dyn_cred.get_cred = &stun_auth_get_cred; + auth_cred.data.dyn_cred.get_password = &stun_auth_get_password; + auth_cred.data.dyn_cred.user_data = comp->stun_sess; + pj_stun_session_set_credential(comp->stun_sess, &auth_cred); + + return PJ_SUCCESS; +} + + /* * Create ICE session. */ @@ -227,11 +272,18 @@ PJ_DEF(pj_status_t) pj_ice_sess_create(pj_stun_config *stun_cfg, pj_ice_sess_comp *comp; comp = &ice->comp[i]; comp->valid_check = NULL; + + status = init_comp(ice, i+1, comp); + if (status != PJ_SUCCESS) { + destroy_ice(ice, status); + return status; + } } if (local_ufrag == NULL) { ice->rx_ufrag.ptr = pj_pool_alloc(ice->pool, 16); pj_create_random_string(ice->rx_ufrag.ptr, 16); + ice->rx_ufrag.slen = 16; } else { pj_strdup(ice->pool, &ice->rx_ufrag, local_ufrag); } @@ -239,6 +291,7 @@ PJ_DEF(pj_status_t) pj_ice_sess_create(pj_stun_config *stun_cfg, if (local_passwd == NULL) { ice->rx_pass.ptr = pj_pool_alloc(ice->pool, 16); pj_create_random_string(ice->rx_pass.ptr, 16); + ice->rx_pass.slen = 16; } else { pj_strdup(ice->pool, &ice->rx_pass, local_passwd); } @@ -267,10 +320,10 @@ static void destroy_ice(pj_ice_sess *ice, LOG4((ice->obj_name, "Destroying ICE session")); } - for (i=0; i<ice->lcand_cnt; ++i) { - if (ice->lcand[i].stun_sess) { - pj_stun_session_destroy(ice->lcand[i].stun_sess); - ice->lcand[i].stun_sess = NULL; + for (i=0; i<ice->comp_cnt; ++i) { + if (ice->comp[i].stun_sess) { + pj_stun_session_destroy(ice->comp[i].stun_sess); + ice->comp[i].stun_sess = NULL; } } @@ -465,9 +518,6 @@ PJ_DEF(pj_status_t) pj_ice_sess_add_cand(pj_ice_sess *ice, unsigned *p_cand_id) { pj_ice_sess_cand *lcand; - pj_stun_session_cb sess_cb; - pj_stun_auth_cred auth_cred; - stun_data *sd; pj_status_t status = PJ_SUCCESS; char tmp[128]; @@ -496,38 +546,6 @@ PJ_DEF(pj_status_t) pj_ice_sess_add_cand(pj_ice_sess *ice, pj_bzero(&lcand->rel_addr, sizeof(lcand->rel_addr)); - /* Init STUN callbacks */ - pj_bzero(&sess_cb, sizeof(sess_cb)); - sess_cb.on_request_complete = &on_stun_request_complete; - sess_cb.on_rx_indication = &on_stun_rx_indication; - sess_cb.on_rx_request = &on_stun_rx_request; - sess_cb.on_send_msg = &on_stun_send_msg; - - /* Create STUN session for this candidate */ - status = pj_stun_session_create(&ice->stun_cfg, ice->obj_name, - &sess_cb, PJ_FALSE, - &lcand->stun_sess); - if (status != PJ_SUCCESS) { - pj_mutex_unlock(ice->mutex); - return status; - } - - /* Associate data with this STUN session */ - sd = PJ_POOL_ZALLOC_T(ice->pool, struct stun_data); - sd->ice = ice; - sd->lcand = lcand; - pj_stun_session_set_user_data(lcand->stun_sess, sd); - - /* Init STUN authentication credential */ - pj_bzero(&auth_cred, sizeof(auth_cred)); - auth_cred.type = PJ_STUN_AUTH_CRED_DYNAMIC; - auth_cred.data.dyn_cred.get_auth = &stun_auth_get_auth; - auth_cred.data.dyn_cred.get_cred = &stun_auth_get_cred; - auth_cred.data.dyn_cred.get_password = &stun_auth_get_password; - auth_cred.data.dyn_cred.user_data = lcand->stun_sess; - pj_stun_session_set_credential(lcand->stun_sess, &auth_cred); - - pj_ansi_strcpy(tmp, pj_inet_ntoa(lcand->addr.ipv4.sin_addr)); LOG4((ice->obj_name, "Candidate %d added: comp_id=%d, type=%s, foundation=%.*s, " @@ -908,6 +926,8 @@ static pj_bool_t on_check_complete(pj_ice_sess *ice, LOG5((ice->obj_name, "Check %d is successful and nominated", GET_CHECK_ID(&ice->clist, check))); + comp = find_comp(ice, check->lcand->comp_id); + for (i=0; i<ice->clist.count; ++i) { pj_ice_sess_check *c = &ice->clist.checks[i]; if (c->lcand->comp_id == check->lcand->comp_id) { @@ -926,7 +946,7 @@ static pj_bool_t on_check_complete(pj_ice_sess *ice, LOG5((ice->obj_name, "Cancelling check %s (In Progress)", dump_check(buf, sizeof(buf), &ice->clist, c))); - pj_stun_session_cancel_req(c->lcand->stun_sess, + pj_stun_session_cancel_req(comp->stun_sess, c->tdata, PJ_FALSE, 0); c->tdata = NULL; check_set_state(ice, c, PJ_ICE_SESS_CHECK_STATE_FAILED, @@ -937,7 +957,6 @@ static pj_bool_t on_check_complete(pj_ice_sess *ice, } /* Update the nominated check for the component */ - comp = find_comp(ice, check->lcand->comp_id); if (comp->valid_check == NULL) { comp->valid_check = check; } else { @@ -1164,9 +1183,9 @@ static pj_status_t perform_check(pj_ice_sess *ice, dump_check(buffer, sizeof(buffer), clist, check))); /* Create request */ - status = pj_stun_session_create_req(lcand->stun_sess, + status = pj_stun_session_create_req(comp->stun_sess, PJ_STUN_BINDING_REQUEST, - &check->tdata); + NULL, &check->tdata); if (status != PJ_SUCCESS) { pjnath_perror(ice->obj_name, "Error creating STUN request", status); return status; @@ -1199,7 +1218,7 @@ static pj_status_t perform_check(pj_ice_sess *ice, */ /* Initiate STUN transaction to send the request */ - status = pj_stun_session_send_msg(lcand->stun_sess, PJ_FALSE, + status = pj_stun_session_send_msg(comp->stun_sess, PJ_FALSE, &rcand->addr, sizeof(pj_sockaddr_in), check->tdata); if (status != PJ_SUCCESS) { @@ -1399,10 +1418,9 @@ static pj_status_t on_stun_send_msg(pj_stun_session *sess, { stun_data *sd = (stun_data*) pj_stun_session_get_user_data(sess); pj_ice_sess *ice = sd->ice; - return (*sd->ice->cb.on_tx_pkt)(sd->ice, sd->lcand->comp_id, - GET_LCAND_ID(sd->lcand), - pkt, pkt_size, - dst_addr, addr_len); + return (*ice->cb.on_tx_pkt)(ice, sd->comp_id, + pkt, pkt_size, + dst_addr, addr_len); } @@ -1599,7 +1617,7 @@ static pj_status_t on_stun_rx_request(pj_stun_session *sess, pj_stun_priority_attr *ap; pj_stun_use_candidate_attr *uc; pj_ice_sess_comp *comp; - pj_ice_sess_cand *lcand; + pj_ice_sess_cand *lcand = NULL; pj_ice_sess_cand *rcand; unsigned i; pj_stun_tx_data *tdata; @@ -1624,8 +1642,7 @@ static pj_status_t on_stun_rx_request(pj_stun_session *sess, sd = (stun_data*) pj_stun_session_get_user_data(sess); ice = sd->ice; - lcand = sd->lcand; - comp = find_comp(ice, lcand->comp_id); + comp = sd->comp; pj_mutex_lock(ice->mutex); @@ -1682,7 +1699,7 @@ static pj_status_t on_stun_rx_request(pj_stun_session *sess, */ if (i == ice->rcand_cnt) { rcand = &ice->rcand[ice->rcand_cnt++]; - rcand->comp_id = lcand->comp_id; + rcand->comp_id = sd->comp_id; rcand->type = PJ_ICE_CAND_TYPE_PRFLX; rcand->prio = ap->value; pj_memcpy(&rcand->addr, src_addr, src_addr_len); @@ -1703,6 +1720,36 @@ static pj_status_t on_stun_rx_request(pj_stun_session *sess, rcand = &ice->rcand[i]; } +#if 0 + /* Find again the local candidate by matching the base address + * with the local candidates in the checklist. Checks may have + * been pruned before, so it's possible that if we use the lcand + * as it is, we wouldn't be able to find the check in the checklist + * and we will end up creating a new check unnecessarily. + */ + for (i=0; i<ice->clist.count; ++i) { + pj_ice_sess_check *c = &ice->clist.checks[i]; + if (/*c->lcand == lcand ||*/ + sockaddr_cmp(&c->lcand->base_addr, &lcand->base_addr)==0) + { + lcand = c->lcand; + break; + } + } +#else + /* Just get candidate with the highest priority for the specified + * component ID in the checklist. + */ + for (i=0; i<ice->clist.count; ++i) { + pj_ice_sess_check *c = &ice->clist.checks[i]; + if (c->lcand->comp_id == sd->comp_id) { + lcand = c->lcand; + break; + } + } + pj_assert(lcand != NULL); +#endif + /* * Create candidate pair for this request. */ @@ -1822,14 +1869,13 @@ static pj_status_t on_stun_rx_indication(pj_stun_session *sess, } -PJ_DEF(pj_status_t) pj_ice_sess_send_data( pj_ice_sess *ice, - unsigned comp_id, - const void *data, - pj_size_t data_len) +PJ_DEF(pj_status_t) pj_ice_sess_send_data(pj_ice_sess *ice, + unsigned comp_id, + const void *data, + pj_size_t data_len) { pj_status_t status = PJ_SUCCESS; pj_ice_sess_comp *comp; - unsigned cand_id; PJ_ASSERT_RETURN(ice && comp_id && comp_id <= ice->comp_cnt, PJ_EINVAL); @@ -1846,8 +1892,7 @@ PJ_DEF(pj_status_t) pj_ice_sess_send_data( pj_ice_sess *ice, goto on_return; } - cand_id = GET_LCAND_ID(comp->valid_check->lcand); - status = (*ice->cb.on_tx_pkt)(ice, comp_id, cand_id, data, data_len, + status = (*ice->cb.on_tx_pkt)(ice, comp_id, data, data_len, &comp->valid_check->rcand->addr, sizeof(pj_sockaddr_in)); @@ -1857,17 +1902,15 @@ on_return: } -PJ_DEF(pj_status_t) pj_ice_sess_on_rx_pkt( pj_ice_sess *ice, - unsigned comp_id, - unsigned cand_id, - void *pkt, - pj_size_t pkt_size, - const pj_sockaddr_t *src_addr, - int src_addr_len) +PJ_DEF(pj_status_t) pj_ice_sess_on_rx_pkt(pj_ice_sess *ice, + unsigned comp_id, + void *pkt, + pj_size_t pkt_size, + const pj_sockaddr_t *src_addr, + int src_addr_len) { pj_status_t status = PJ_SUCCESS; pj_ice_sess_comp *comp; - pj_ice_sess_cand *lcand; pj_status_t stun_status; PJ_ASSERT_RETURN(ice, PJ_EINVAL); @@ -1880,11 +1923,9 @@ PJ_DEF(pj_status_t) pj_ice_sess_on_rx_pkt( pj_ice_sess *ice, goto on_return; } - lcand = &ice->lcand[cand_id]; - stun_status = pj_stun_msg_check(pkt, pkt_size, PJ_STUN_IS_DATAGRAM); if (stun_status == PJ_SUCCESS) { - status = pj_stun_session_on_rx_pkt(lcand->stun_sess, pkt, pkt_size, + status = pj_stun_session_on_rx_pkt(comp->stun_sess, pkt, pkt_size, PJ_STUN_IS_DATAGRAM, NULL, src_addr, src_addr_len); } else { diff --git a/pjnath/src/pjnath/ice_strans.c b/pjnath/src/pjnath/ice_strans.c index 082d310a..297e1f86 100644 --- a/pjnath/src/pjnath/ice_strans.c +++ b/pjnath/src/pjnath/ice_strans.c @@ -23,14 +23,22 @@ #include <pj/ip_helper.h> #include <pj/log.h> #include <pj/pool.h> +#include <pj/rand.h> #include <pj/string.h> +#if 0 +# define TRACE_PKT(expr) PJ_LOG(5,expr) +#else +# define TRACE_PKT(expr) +#endif + + /* 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 cand_id, + unsigned comp_id, const void *pkt, pj_size_t size, const pj_sockaddr_t *dst_addr, unsigned dst_addr_len); @@ -84,7 +92,7 @@ PJ_DECL(pj_status_t) pj_ice_strans_create(pj_stun_config *stun_cfg, PJ_ASSERT_RETURN(stun_cfg->ioqueue && stun_cfg->timer_heap, PJ_EINVAL); if (name == NULL) - name = "icest%p"; + name = "icstr%p"; pool = pj_pool_create(stun_cfg->pf, name, 4000, 4000, NULL); ice_st = PJ_POOL_ZALLOC_T(pool, pj_ice_strans); @@ -242,6 +250,9 @@ static pj_status_t create_component(pj_ice_strans *ice_st, 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; comp = PJ_POOL_ZALLOC_T(ice_st->pool, pj_ice_strans_comp); @@ -251,6 +262,12 @@ static pj_status_t create_component(pj_ice_strans *ice_st, 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) 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) @@ -437,26 +454,31 @@ static void on_read_complete(pj_ioqueue_key_t *key, * So far we don't have good solution for this. * The process below is just a workaround. */ - if (ice_st->ice) { - PJ_TODO(DISTINGUISH_BETWEEN_LOCAL_AND_RELAY); - status = pj_ice_sess_on_rx_pkt(ice_st->ice, comp->comp_id, - comp->cand_list[0].ice_cand_id, - comp->pkt, bytes_read, - &comp->src_addr, comp->src_addr_len); - } else if (comp->stun_sess) { - status = pj_stun_msg_check(comp->pkt, bytes_read, - PJ_STUN_IS_DATAGRAM); - if (status == PJ_SUCCESS) { + status = pj_stun_msg_check(comp->pkt, bytes_read, + PJ_STUN_IS_DATAGRAM); + + if (status == PJ_SUCCESS) { + if (ice_st->ice==NULL || + 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, &comp->src_addr, comp->src_addr_len); } else { - (*ice_st->cb.on_rx_data)(ice_st, comp->comp_id, - comp->pkt, bytes_read, - &comp->src_addr, comp->src_addr_len); - + 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 { (*ice_st->cb.on_rx_data)(ice_st, comp->comp_id, @@ -527,7 +549,8 @@ static void ka_timer_cb(pj_timer_heap_t *th, pj_timer_entry *te) /* Create STUN binding request */ status = pj_stun_session_create_req(comp->stun_sess, - PJ_STUN_BINDING_REQUEST, &tdata); + PJ_STUN_BINDING_REQUEST, + comp->ka_tsx_id, &tdata); if (status != PJ_SUCCESS) continue; @@ -554,8 +577,9 @@ static void start_ka_timer(pj_ice_strans *ice_st) if (ice_st->ka_timer.id != PJ_FALSE) return; - delay.sec = 20; - delay.msec = 0; + 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); ice_st->ka_timer.cb = &ka_timer_cb; ice_st->ka_timer.user_data = ice_st; @@ -616,7 +640,9 @@ static pj_status_t get_stun_mapped_addr(pj_ice_strans *ice_st, /* Create STUN binding request */ status = pj_stun_session_create_req(comp->stun_sess, - PJ_STUN_BINDING_REQUEST, &tdata); + PJ_STUN_BINDING_REQUEST, + comp->ka_tsx_id, + &tdata); if (status != PJ_SUCCESS) return status; @@ -944,7 +970,7 @@ static void on_ice_complete(pj_ice_sess *ice, pj_status_t status) * Callback called by ICE session when it wants to send outgoing packet. */ static pj_status_t ice_tx_pkt(pj_ice_sess *ice, - unsigned comp_id, unsigned cand_id, + unsigned comp_id, const void *pkt, pj_size_t size, const pj_sockaddr_t *dst_addr, unsigned dst_addr_len) @@ -959,6 +985,12 @@ static pj_status_t ice_tx_pkt(pj_ice_sess *ice, 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, + 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, @@ -1026,6 +1058,10 @@ static void stun_on_request_complete(pj_stun_session *sess, if (cand == NULL) { /* This is keep-alive */ + if (status != PJ_SUCCESS) { + ice_st_perror(comp->ice_st, "STUN keep-alive request failed", + status); + } return; } diff --git a/pjnath/src/pjnath/stun_session.c b/pjnath/src/pjnath/stun_session.c index 5f308fcd..db1339d7 100644 --- a/pjnath/src/pjnath/stun_session.c +++ b/pjnath/src/pjnath/stun_session.c @@ -49,17 +49,19 @@ struct pj_stun_session #define TDATA_POOL_INC 1024 -static void tsx_on_complete(pj_stun_client_tsx *tsx, - pj_status_t status, - const pj_stun_msg *response); -static pj_status_t tsx_on_send_msg(pj_stun_client_tsx *tsx, - const void *stun_pkt, - pj_size_t pkt_size); +static void stun_tsx_on_complete(pj_stun_client_tsx *tsx, + pj_status_t status, + const pj_stun_msg *response); +static pj_status_t stun_tsx_on_send_msg(pj_stun_client_tsx *tsx, + const void *stun_pkt, + pj_size_t pkt_size); +static void stun_tsx_on_destroy(pj_stun_client_tsx *tsx); static pj_stun_tsx_cb tsx_cb = { - &tsx_on_complete, - &tsx_on_send_msg + &stun_tsx_on_complete, + &stun_tsx_on_send_msg, + &stun_tsx_on_destroy }; @@ -120,6 +122,7 @@ static pj_status_t create_tdata(pj_stun_session *sess, static pj_status_t create_request_tdata(pj_stun_session *sess, unsigned msg_type, + const pj_uint8_t tsx_id[12], pj_stun_tx_data **p_tdata) { pj_status_t status; @@ -131,7 +134,7 @@ static pj_status_t create_request_tdata(pj_stun_session *sess, /* Create STUN message */ status = pj_stun_msg_create(tdata->pool, msg_type, PJ_STUN_MAGIC, - NULL, &tdata->msg); + tsx_id, &tdata->msg); if (status != PJ_SUCCESS) { pj_pool_release(tdata->pool); return status; @@ -148,20 +151,34 @@ static pj_status_t create_request_tdata(pj_stun_session *sess, return PJ_SUCCESS; } + +static void stun_tsx_on_destroy(pj_stun_client_tsx *tsx) +{ + pj_stun_tx_data *tdata; + + tdata = (pj_stun_tx_data*) pj_stun_client_tsx_get_data(tsx); + pj_stun_client_tsx_destroy(tsx); + pj_pool_release(tdata->pool); +} + static void destroy_tdata(pj_stun_tx_data *tdata) { - if (tdata->client_tsx) { - tsx_erase(tdata->sess, tdata); - pj_stun_client_tsx_destroy(tdata->client_tsx); - tdata->client_tsx = NULL; - } if (tdata->res_timer.id != PJ_FALSE) { pj_timer_heap_cancel(tdata->sess->cfg->timer_heap, &tdata->res_timer); tdata->res_timer.id = PJ_FALSE; pj_list_erase(tdata); } - pj_pool_release(tdata->pool); + + if (tdata->client_tsx) { + pj_time_val delay = {2, 0}; + tsx_erase(tdata->sess, tdata); + pj_stun_client_tsx_schedule_destroy(tdata->client_tsx, &delay); + tdata->client_tsx = NULL; + + } else { + pj_pool_release(tdata->pool); + } } /* @@ -306,9 +323,9 @@ static pj_status_t apply_msg_options(pj_stun_session *sess, } -static void tsx_on_complete(pj_stun_client_tsx *tsx, - pj_status_t status, - const pj_stun_msg *response) +static void stun_tsx_on_complete(pj_stun_client_tsx *tsx, + pj_status_t status, + const pj_stun_msg *response) { pj_stun_tx_data *tdata; @@ -320,9 +337,9 @@ static void tsx_on_complete(pj_stun_client_tsx *tsx, } } -static pj_status_t tsx_on_send_msg(pj_stun_client_tsx *tsx, - const void *stun_pkt, - pj_size_t pkt_size) +static pj_status_t stun_tsx_on_send_msg(pj_stun_client_tsx *tsx, + const void *stun_pkt, + pj_size_t pkt_size) { pj_stun_tx_data *tdata; @@ -441,6 +458,7 @@ PJ_DEF(void) pj_stun_session_set_credential(pj_stun_session *sess, PJ_DEF(pj_status_t) pj_stun_session_create_req(pj_stun_session *sess, int method, + const pj_uint8_t tsx_id[12], pj_stun_tx_data **p_tdata) { pj_stun_tx_data *tdata = NULL; @@ -448,7 +466,7 @@ PJ_DEF(pj_status_t) pj_stun_session_create_req(pj_stun_session *sess, PJ_ASSERT_RETURN(sess && p_tdata, PJ_EINVAL); - status = create_request_tdata(sess, method, &tdata); + status = create_request_tdata(sess, method, tsx_id, &tdata); if (status != PJ_SUCCESS) return status; @@ -765,7 +783,7 @@ static pj_status_t on_incoming_response(pj_stun_session *sess, /* Lookup pending client transaction */ tdata = tsx_lookup(sess, msg); if (tdata == NULL) { - PJ_LOG(5,(SNAME(sess), + PJ_LOG(4,(SNAME(sess), "Transaction not found, response silently discarded")); return PJ_SUCCESS; } diff --git a/pjnath/src/pjnath/stun_transaction.c b/pjnath/src/pjnath/stun_transaction.c index 5d6f80f6..9c5bc11a 100644 --- a/pjnath/src/pjnath/stun_transaction.c +++ b/pjnath/src/pjnath/stun_transaction.c @@ -36,12 +36,14 @@ struct pj_stun_client_tsx void *user_data; pj_bool_t complete; - \ + pj_bool_t require_retransmit; - pj_timer_entry timer; + pj_timer_entry retransmit_timer; unsigned transmit_count; pj_time_val retransmit_time; + pj_timer_entry destroy_timer; + void *last_pkt; unsigned last_pkt_size; }; @@ -49,6 +51,8 @@ struct pj_stun_client_tsx static void retransmit_timer_callback(pj_timer_heap_t *timer_heap, pj_timer_entry *timer); +static void destroy_timer_callback(pj_timer_heap_t *timer_heap, + pj_timer_entry *timer); #define stun_perror(tsx,msg,rc) pjnath_perror(tsx->obj_name, msg, rc) @@ -69,8 +73,11 @@ PJ_DEF(pj_status_t) pj_stun_client_tsx_create(pj_stun_config *cfg, tsx->cfg = cfg; pj_memcpy(&tsx->cb, cb, sizeof(*cb)); - tsx->timer.cb = &retransmit_timer_callback; - tsx->timer.user_data = tsx; + tsx->retransmit_timer.cb = &retransmit_timer_callback; + tsx->retransmit_timer.user_data = tsx; + + tsx->destroy_timer.cb = &destroy_timer_callback; + tsx->destroy_timer.user_data = tsx; pj_ansi_snprintf(tsx->obj_name, sizeof(tsx->obj_name), "stuntsx%p", tsx); @@ -81,17 +88,55 @@ PJ_DEF(pj_status_t) pj_stun_client_tsx_create(pj_stun_config *cfg, } +PJ_DEF(pj_status_t) +pj_stun_client_tsx_schedule_destroy(pj_stun_client_tsx *tsx, + const pj_time_val *delay) +{ + pj_status_t status; + + PJ_ASSERT_RETURN(tsx && delay, PJ_EINVAL); + PJ_ASSERT_RETURN(tsx->cb.on_destroy, PJ_EINVAL); + + /* Cancel previously registered timer */ + if (tsx->destroy_timer.id != 0) { + pj_timer_heap_cancel(tsx->cfg->timer_heap, &tsx->destroy_timer); + tsx->destroy_timer.id = 0; + } + + /* Stop retransmission, just in case */ + if (tsx->retransmit_timer.id != 0) { + pj_timer_heap_cancel(tsx->cfg->timer_heap, &tsx->retransmit_timer); + tsx->retransmit_timer.id = 0; + } + + status = pj_timer_heap_schedule(tsx->cfg->timer_heap, + &tsx->destroy_timer, delay); + if (status != PJ_SUCCESS) + return status; + + tsx->destroy_timer.id = TIMER_ACTIVE; + + return PJ_SUCCESS; +} + + /* - * . + * Destroy transaction immediately. */ PJ_DEF(pj_status_t) pj_stun_client_tsx_destroy(pj_stun_client_tsx *tsx) { PJ_ASSERT_RETURN(tsx, PJ_EINVAL); - if (tsx->timer.id != 0) { - pj_timer_heap_cancel(tsx->cfg->timer_heap, &tsx->timer); - tsx->timer.id = 0; + if (tsx->retransmit_timer.id != 0) { + pj_timer_heap_cancel(tsx->cfg->timer_heap, &tsx->retransmit_timer); + tsx->retransmit_timer.id = 0; + } + if (tsx->destroy_timer.id != 0) { + pj_timer_heap_cancel(tsx->cfg->timer_heap, &tsx->destroy_timer); + tsx->destroy_timer.id = 0; } + + PJ_LOG(5,(tsx->obj_name, "STUN client transaction destroyed")); return PJ_SUCCESS; } @@ -135,7 +180,7 @@ static pj_status_t tsx_transmit_msg(pj_stun_client_tsx *tsx) { pj_status_t status; - PJ_ASSERT_RETURN(tsx->timer.id == 0, PJ_EBUSY); + PJ_ASSERT_RETURN(tsx->retransmit_timer.id == 0, PJ_EBUSY); if (tsx->require_retransmit) { /* Calculate retransmit/timeout delay */ @@ -160,22 +205,24 @@ static pj_status_t tsx_transmit_msg(pj_stun_client_tsx *tsx) * cancel it (as opposed to when schedule_timer() failed we cannot * cancel transmission). */; - status = pj_timer_heap_schedule(tsx->cfg->timer_heap, &tsx->timer, + status = pj_timer_heap_schedule(tsx->cfg->timer_heap, + &tsx->retransmit_timer, &tsx->retransmit_time); if (status != PJ_SUCCESS) { - tsx->timer.id = 0; + tsx->retransmit_timer.id = 0; return status; } - tsx->timer.id = TIMER_ACTIVE; + tsx->retransmit_timer.id = TIMER_ACTIVE; } /* Send message */ status = tsx->cb.on_send_msg(tsx, tsx->last_pkt, tsx->last_pkt_size); if (status != PJ_SUCCESS) { - if (tsx->timer.id != 0) { - pj_timer_heap_cancel(tsx->cfg->timer_heap, &tsx->timer); - tsx->timer.id = 0; + if (tsx->retransmit_timer.id != 0) { + pj_timer_heap_cancel(tsx->cfg->timer_heap, + &tsx->retransmit_timer); + tsx->retransmit_timer.id = 0; } stun_perror(tsx, "STUN error sending message", status); return status; @@ -198,7 +245,7 @@ PJ_DEF(pj_status_t) pj_stun_client_tsx_send_msg(pj_stun_client_tsx *tsx, unsigned pkt_len) { PJ_ASSERT_RETURN(tsx && pkt && pkt_len, PJ_EINVAL); - PJ_ASSERT_RETURN(tsx->timer.id == 0, PJ_EBUSY); + PJ_ASSERT_RETURN(tsx->retransmit_timer.id == 0, PJ_EBUSY); /* Encode message */ tsx->last_pkt = pkt; @@ -223,27 +270,44 @@ static void retransmit_timer_callback(pj_timer_heap_t *timer_heap, if (tsx->transmit_count >= PJ_STUN_MAX_RETRANSMIT_COUNT) { /* Retransmission count exceeded. Transaction has failed */ - tsx->timer.id = 0; + tsx->retransmit_timer.id = 0; PJ_LOG(4,(tsx->obj_name, "STUN timeout waiting for response")); - tsx->complete = PJ_TRUE; - if (tsx->cb.on_complete) { - tsx->cb.on_complete(tsx, PJNATH_ESTUNTIMEDOUT, NULL); + if (!tsx->complete) { + tsx->complete = PJ_TRUE; + if (tsx->cb.on_complete) { + tsx->cb.on_complete(tsx, PJNATH_ESTUNTIMEDOUT, NULL); + } } return; } - tsx->timer.id = 0; + tsx->retransmit_timer.id = 0; status = tsx_transmit_msg(tsx); if (status != PJ_SUCCESS) { - tsx->timer.id = 0; - tsx->complete = PJ_TRUE; - if (tsx->cb.on_complete) { - tsx->cb.on_complete(tsx, status, NULL); + tsx->retransmit_timer.id = 0; + if (!tsx->complete) { + tsx->complete = PJ_TRUE; + if (tsx->cb.on_complete) { + tsx->cb.on_complete(tsx, status, NULL); + } } } } +/* Timer callback to destroy transaction */ +static void destroy_timer_callback(pj_timer_heap_t *timer_heap, + pj_timer_entry *timer) +{ + pj_stun_client_tsx *tsx = (pj_stun_client_tsx *) timer->user_data; + + PJ_UNUSED_ARG(timer_heap); + + tsx->destroy_timer.id = PJ_FALSE; + tsx->cb.on_destroy(tsx); + /* Don't access transaction after this */ +} + /* * Notify the STUN transaction about the arrival of STUN response. @@ -267,9 +331,9 @@ PJ_DEF(pj_status_t) pj_stun_client_tsx_on_rx_msg(pj_stun_client_tsx *tsx, /* We have a response with matching transaction ID. * We can cancel retransmit timer now. */ - if (tsx->timer.id) { - pj_timer_heap_cancel(tsx->cfg->timer_heap, &tsx->timer); - tsx->timer.id = 0; + if (tsx->retransmit_timer.id) { + pj_timer_heap_cancel(tsx->cfg->timer_heap, &tsx->retransmit_timer); + tsx->retransmit_timer.id = 0; } /* Find STUN error code attribute */ @@ -296,9 +360,11 @@ PJ_DEF(pj_status_t) pj_stun_client_tsx_on_rx_msg(pj_stun_client_tsx *tsx, } /* Call callback */ - tsx->complete = PJ_TRUE; - if (tsx->cb.on_complete) { - tsx->cb.on_complete(tsx, status, msg); + if (!tsx->complete) { + tsx->complete = PJ_TRUE; + if (tsx->cb.on_complete) { + tsx->cb.on_complete(tsx, status, msg); + } } return PJ_SUCCESS; |