diff options
Diffstat (limited to 'pjnath/src/pjnath/stun_session.c')
-rw-r--r-- | pjnath/src/pjnath/stun_session.c | 253 |
1 files changed, 197 insertions, 56 deletions
diff --git a/pjnath/src/pjnath/stun_session.c b/pjnath/src/pjnath/stun_session.c index 6545c750..6e7d4ef3 100644 --- a/pjnath/src/pjnath/stun_session.c +++ b/pjnath/src/pjnath/stun_session.c @@ -29,14 +29,23 @@ struct pj_stun_session pj_stun_session_cb cb; void *user_data; + pj_atomic_t *busy; + pj_bool_t destroy_request; + pj_bool_t use_fingerprint; + pj_pool_t *rx_pool; + +#if PJ_LOG_MAX_LEVEL >= 5 char dump_buf[1000]; +#endif + unsigned log_flag; pj_stun_auth_type auth_type; pj_stun_auth_cred cred; int auth_retry; pj_str_t next_nonce; + pj_str_t server_realm; pj_str_t srv_name; @@ -79,7 +88,7 @@ static pj_stun_tsx_cb tsx_cb = static pj_status_t tsx_add(pj_stun_session *sess, pj_stun_tx_data *tdata) { - pj_list_push_back(&sess->pending_request_list, tdata); + pj_list_push_front(&sess->pending_request_list, tdata); return PJ_SUCCESS; } @@ -138,11 +147,13 @@ 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); + tsx_erase(tdata->sess, tdata); + pj_stun_client_tsx_destroy(tsx); pj_pool_release(tdata->pool); } -static void destroy_tdata(pj_stun_tx_data *tdata) +static void destroy_tdata(pj_stun_tx_data *tdata, pj_bool_t force) { if (tdata->res_timer.id != PJ_FALSE) { pj_timer_heap_cancel(tdata->sess->cfg->timer_heap, @@ -151,14 +162,21 @@ static void destroy_tdata(pj_stun_tx_data *tdata) pj_list_erase(tdata); } - 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; + if (force) { + if (tdata->client_tsx) { + tsx_erase(tdata->sess, tdata); + pj_stun_client_tsx_destroy(tdata->client_tsx); + } + pj_pool_release(tdata->pool); } else { - pj_pool_release(tdata->pool); + if (tdata->client_tsx) { + pj_time_val delay = {2, 0}; + pj_stun_client_tsx_schedule_destroy(tdata->client_tsx, &delay); + + } else { + pj_pool_release(tdata->pool); + } } } @@ -169,7 +187,7 @@ PJ_DEF(void) pj_stun_msg_destroy_tdata( pj_stun_session *sess, pj_stun_tx_data *tdata) { PJ_UNUSED_ARG(sess); - destroy_tdata(tdata); + destroy_tdata(tdata, PJ_FALSE); } @@ -289,6 +307,7 @@ static pj_status_t handle_auth_challenge(pj_stun_session *sess, ea->err_code == PJ_STUN_SC_STALE_NONCE) { const pj_stun_nonce_attr *anonce; + const pj_stun_realm_attr *arealm; pj_stun_tx_data *tdata; unsigned i; pj_status_t status; @@ -316,6 +335,13 @@ static pj_status_t handle_auth_challenge(pj_stun_session *sess, /* Save next_nonce */ pj_strdup(sess->pool, &sess->next_nonce, &anonce->value); + /* Copy the realm from the response */ + arealm = (pj_stun_realm_attr*) + pj_stun_msg_find_attr(response, PJ_STUN_ATTR_REALM, 0); + if (arealm) { + pj_strdup(sess->pool, &sess->server_realm, &arealm->value); + } + /* Create new request */ status = pj_stun_session_create_req(sess, request->msg->hdr.type, request->msg->hdr.magic, @@ -324,7 +350,8 @@ static pj_status_t handle_auth_challenge(pj_stun_session *sess, return status; /* Duplicate all the attributes in the old request, except - * USERNAME, REALM, M-I, and NONCE + * USERNAME, REALM, M-I, and NONCE, which will be filled in + * later. */ for (i=0; i<request->msg->attr_count; ++i) { const pj_stun_attr_hdr *asrc = request->msg->attr[i]; @@ -373,6 +400,10 @@ static void stun_tsx_on_complete(pj_stun_client_tsx *tsx, tdata = (pj_stun_tx_data*) pj_stun_client_tsx_get_data(tsx); sess = tdata->sess; + /* Lock the session and prevent user from destroying us in the callback */ + pj_atomic_inc(sess->busy); + pj_lock_acquire(sess->lock); + /* Handle authentication challenge */ handle_auth_challenge(sess, tdata, response, src_addr, src_addr_len, ¬ify_user); @@ -387,6 +418,13 @@ static void stun_tsx_on_complete(pj_stun_client_tsx *tsx, */ pj_stun_msg_destroy_tdata(sess, tdata); tdata = NULL; + + pj_lock_release(sess->lock); + + if (pj_atomic_dec_and_get(sess->busy)==0 && sess->destroy_request) { + pj_stun_session_destroy(sess); + return; + } } static pj_status_t stun_tsx_on_send_msg(pj_stun_client_tsx *tsx, @@ -394,12 +432,27 @@ static pj_status_t stun_tsx_on_send_msg(pj_stun_client_tsx *tsx, pj_size_t pkt_size) { pj_stun_tx_data *tdata; + pj_stun_session *sess; + pj_status_t status; tdata = (pj_stun_tx_data*) pj_stun_client_tsx_get_data(tsx); + sess = tdata->sess; + + /* Lock the session and prevent user from destroying us in the callback */ + pj_atomic_inc(sess->busy); + pj_lock_acquire(sess->lock); + + status = sess->cb.on_send_msg(tdata->sess, tdata->token, stun_pkt, + pkt_size, tdata->dst_addr, + tdata->addr_len); + pj_lock_release(sess->lock); - return tdata->sess->cb.on_send_msg(tdata->sess, tdata->token, stun_pkt, - pkt_size, tdata->dst_addr, - tdata->addr_len); + if (pj_atomic_dec_and_get(sess->busy)==0 && sess->destroy_request) { + pj_stun_session_destroy(sess); + return PJNATH_ESTUNDESTROYED; + } else { + return status; + } } /* **************************************************************************/ @@ -428,11 +481,16 @@ PJ_DEF(pj_status_t) pj_stun_session_create( pj_stun_config *cfg, sess->pool = pool; pj_memcpy(&sess->cb, cb, sizeof(*cb)); sess->use_fingerprint = fingerprint; + sess->log_flag = 0xFFFF; sess->srv_name.ptr = (char*) pj_pool_alloc(pool, 32); sess->srv_name.slen = pj_ansi_snprintf(sess->srv_name.ptr, 32, "pj_stun-%s", pj_get_version()); + sess->rx_pool = pj_pool_create(sess->cfg->pf, "name", + PJNATH_POOL_LEN_STUN_TDATA, + PJNATH_POOL_INC_STUN_TDATA, NULL); + pj_list_init(&sess->pending_request_list); pj_list_init(&sess->cached_response_list); @@ -443,6 +501,13 @@ PJ_DEF(pj_status_t) pj_stun_session_create( pj_stun_config *cfg, } sess->delete_lock = PJ_TRUE; + status = pj_atomic_create(pool, 0, &sess->busy); + if (status != PJ_SUCCESS) { + pj_lock_destroy(sess->lock); + pj_pool_release(pool); + return status; + } + *p_sess = sess; return PJ_SUCCESS; @@ -453,13 +518,22 @@ PJ_DEF(pj_status_t) pj_stun_session_destroy(pj_stun_session *sess) PJ_ASSERT_RETURN(sess, PJ_EINVAL); pj_lock_acquire(sess->lock); + + /* Can't destroy if we're in a callback */ + sess->destroy_request = PJ_TRUE; + if (pj_atomic_get(sess->busy)) { + pj_lock_release(sess->lock); + return PJ_EPENDING; + } + while (!pj_list_empty(&sess->pending_request_list)) { pj_stun_tx_data *tdata = sess->pending_request_list.next; - destroy_tdata(tdata); + destroy_tdata(tdata, PJ_TRUE); } + while (!pj_list_empty(&sess->cached_response_list)) { pj_stun_tx_data *tdata = sess->cached_response_list.next; - destroy_tdata(tdata); + destroy_tdata(tdata, PJ_TRUE); } pj_lock_release(sess->lock); @@ -467,6 +541,11 @@ PJ_DEF(pj_status_t) pj_stun_session_destroy(pj_stun_session *sess) pj_lock_destroy(sess->lock); } + if (sess->rx_pool) { + pj_pool_release(sess->rx_pool); + sess->rx_pool = NULL; + } + pj_pool_release(sess->pool); return PJ_SUCCESS; @@ -538,12 +617,19 @@ PJ_DEF(pj_status_t) pj_stun_session_set_credential(pj_stun_session *sess, return PJ_SUCCESS; } +PJ_DEF(void) pj_stun_session_set_log( pj_stun_session *sess, + unsigned flags) +{ + PJ_ASSERT_ON_FAIL(sess, return); + sess->log_flag = flags; +} static pj_status_t get_auth(pj_stun_session *sess, pj_stun_tx_data *tdata) { if (sess->cred.type == PJ_STUN_AUTH_CRED_STATIC) { - tdata->auth_info.realm = sess->cred.data.static_cred.realm; + //tdata->auth_info.realm = sess->cred.data.static_cred.realm; + tdata->auth_info.realm = sess->server_realm; tdata->auth_info.username = sess->cred.data.static_cred.username; tdata->auth_info.nonce = sess->cred.data.static_cred.nonce; @@ -633,6 +719,7 @@ PJ_DEF(pj_status_t) pj_stun_session_create_req(pj_stun_session *sess, return status; } tdata->auth_info.nonce = sess->next_nonce; + tdata->auth_info.realm = sess->server_realm; } } else { @@ -714,8 +801,18 @@ PJ_DEF(pj_status_t) pj_stun_session_create_res( pj_stun_session *sess, static void dump_tx_msg(pj_stun_session *sess, const pj_stun_msg *msg, unsigned pkt_size, const pj_sockaddr_t *addr) { - char dst_name[80]; + char dst_name[PJ_INET6_ADDRSTRLEN+10]; + if ((PJ_STUN_IS_REQUEST(msg->hdr.type) && + (sess->log_flag & PJ_STUN_SESS_LOG_TX_REQ)==0) || + (PJ_STUN_IS_RESPONSE(msg->hdr.type) && + (sess->log_flag & PJ_STUN_SESS_LOG_TX_RES)==0) || + (PJ_STUN_IS_INDICATION(msg->hdr.type) && + (sess->log_flag & PJ_STUN_SESS_LOG_TX_IND)==0)) + { + return; + } + pj_sockaddr_print(addr, dst_name, sizeof(dst_name), 3); PJ_LOG(5,(SNAME(sess), @@ -749,7 +846,8 @@ PJ_DEF(pj_status_t) pj_stun_session_send_msg( pj_stun_session *sess, tdata->token = token; tdata->retransmit = retransmit; - /* Start locking the session now */ + /* Lock the session and prevent user from destroying us in the callback */ + pj_atomic_inc(sess->busy); pj_lock_acquire(sess->lock); /* Apply options */ @@ -757,9 +855,8 @@ PJ_DEF(pj_status_t) pj_stun_session_send_msg( pj_stun_session *sess, tdata->msg); if (status != PJ_SUCCESS) { pj_stun_msg_destroy_tdata(sess, tdata); - pj_lock_release(sess->lock); LOG_ERR_(sess, "Error applying options", status); - return status; + goto on_return; } /* Encode message */ @@ -769,9 +866,8 @@ PJ_DEF(pj_status_t) pj_stun_session_send_msg( pj_stun_session *sess, &tdata->pkt_size); if (status != PJ_SUCCESS) { pj_stun_msg_destroy_tdata(sess, tdata); - pj_lock_release(sess->lock); LOG_ERR_(sess, "STUN encode() error", status); - return status; + goto on_return; } /* Dump packet */ @@ -797,9 +893,8 @@ PJ_DEF(pj_status_t) pj_stun_session_send_msg( pj_stun_session *sess, tdata->pkt, tdata->pkt_size); if (status != PJ_SUCCESS && status != PJ_EPENDING) { pj_stun_msg_destroy_tdata(sess, tdata); - pj_lock_release(sess->lock); LOG_ERR_(sess, "Error sending STUN request", status); - return status; + goto on_return; } /* Add to pending request list */ @@ -824,10 +919,10 @@ PJ_DEF(pj_status_t) pj_stun_session_send_msg( pj_stun_session *sess, &tdata->res_timer, &timeout); if (status != PJ_SUCCESS) { + tdata->res_timer.id = PJ_FALSE; pj_stun_msg_destroy_tdata(sess, tdata); - pj_lock_release(sess->lock); LOG_ERR_(sess, "Error scheduling response timer", status); - return status; + goto on_return; } pj_list_push_back(&sess->cached_response_list, tdata); @@ -838,7 +933,9 @@ PJ_DEF(pj_status_t) pj_stun_session_send_msg( pj_stun_session *sess, tdata->pkt_size, server, addr_len); if (status != PJ_SUCCESS && status != PJ_EPENDING) { + pj_stun_msg_destroy_tdata(sess, tdata); LOG_ERR_(sess, "Error sending STUN request", status); + goto on_return; } /* Destroy only when response is not cached*/ @@ -847,8 +944,15 @@ PJ_DEF(pj_status_t) pj_stun_session_send_msg( pj_stun_session *sess, } } - +on_return: pj_lock_release(sess->lock); + + /* Check if application has called destroy() in the callback */ + if (pj_atomic_dec_and_get(sess->busy)==0 && sess->destroy_request) { + pj_stun_session_destroy(sess); + return PJNATH_ESTUNDESTROYED; + } + return status; } @@ -892,6 +996,8 @@ PJ_DEF(pj_status_t) pj_stun_session_cancel_req( pj_stun_session *sess, PJ_ASSERT_RETURN(!notify || notify_status!=PJ_SUCCESS, PJ_EINVAL); PJ_ASSERT_RETURN(PJ_STUN_IS_REQUEST(tdata->msg->hdr.type), PJ_EINVAL); + /* Lock the session and prevent user from destroying us in the callback */ + pj_atomic_inc(sess->busy); pj_lock_acquire(sess->lock); if (notify) { @@ -903,6 +1009,12 @@ PJ_DEF(pj_status_t) pj_stun_session_cancel_req( pj_stun_session *sess, pj_stun_msg_destroy_tdata(sess, tdata); pj_lock_release(sess->lock); + + if (pj_atomic_dec_and_get(sess->busy)==0 && sess->destroy_request) { + pj_stun_session_destroy(sess); + return PJNATH_ESTUNDESTROYED; + } + return PJ_SUCCESS; } @@ -917,12 +1029,19 @@ PJ_DEF(pj_status_t) pj_stun_session_retransmit_req(pj_stun_session *sess, PJ_ASSERT_RETURN(sess && tdata, PJ_EINVAL); PJ_ASSERT_RETURN(PJ_STUN_IS_REQUEST(tdata->msg->hdr.type), PJ_EINVAL); + /* Lock the session and prevent user from destroying us in the callback */ + pj_atomic_inc(sess->busy); pj_lock_acquire(sess->lock); status = pj_stun_client_tsx_retransmit(tdata->client_tsx); pj_lock_release(sess->lock); + if (pj_atomic_dec_and_get(sess->busy)==0 && sess->destroy_request) { + pj_stun_session_destroy(sess); + return PJNATH_ESTUNDESTROYED; + } + return status; } @@ -1165,6 +1284,36 @@ static pj_status_t on_incoming_indication(pj_stun_session *sess, } +/* Print outgoing message to log */ +static void dump_rx_msg(pj_stun_session *sess, const pj_stun_msg *msg, + unsigned pkt_size, const pj_sockaddr_t *addr) +{ + char src_info[PJ_INET6_ADDRSTRLEN+10]; + + if ((PJ_STUN_IS_REQUEST(msg->hdr.type) && + (sess->log_flag & PJ_STUN_SESS_LOG_RX_REQ)==0) || + (PJ_STUN_IS_RESPONSE(msg->hdr.type) && + (sess->log_flag & PJ_STUN_SESS_LOG_RX_RES)==0) || + (PJ_STUN_IS_INDICATION(msg->hdr.type) && + (sess->log_flag & PJ_STUN_SESS_LOG_RX_IND)==0)) + { + return; + } + + pj_sockaddr_print(addr, src_info, sizeof(src_info), 3); + + PJ_LOG(5,(SNAME(sess), + "RX %d bytes STUN message from %s:\n" + "--- begin STUN message ---\n" + "%s" + "--- end of STUN message ---\n", + pkt_size, src_info, + pj_stun_msg_dump(msg, sess->dump_buf, sizeof(sess->dump_buf), + NULL))); + +} + +/* Incoming packet */ PJ_DEF(pj_status_t) pj_stun_session_on_rx_pkt(pj_stun_session *sess, const void *packet, pj_size_t pkt_size, @@ -1175,47 +1324,34 @@ PJ_DEF(pj_status_t) pj_stun_session_on_rx_pkt(pj_stun_session *sess, unsigned src_addr_len) { pj_stun_msg *msg, *response; - pj_pool_t *tmp_pool; - char *dump; pj_status_t status; PJ_ASSERT_RETURN(sess && packet && pkt_size, PJ_EINVAL); - tmp_pool = pj_pool_create(sess->cfg->pf, "tmpstun", - PJNATH_POOL_LEN_STUN_TDATA, - PJNATH_POOL_INC_STUN_TDATA, NULL); - if (!tmp_pool) - return PJ_ENOMEM; + /* Lock the session and prevent user from destroying us in the callback */ + pj_atomic_inc(sess->busy); + pj_lock_acquire(sess->lock); + + /* Reset pool */ + pj_pool_reset(sess->rx_pool); /* Try to parse the message */ - status = pj_stun_msg_decode(tmp_pool, (const pj_uint8_t*)packet, + status = pj_stun_msg_decode(sess->rx_pool, (const pj_uint8_t*)packet, pkt_size, options, &msg, parsed_len, &response); if (status != PJ_SUCCESS) { LOG_ERR_(sess, "STUN msg_decode() error", status); if (response) { - send_response(sess, token, tmp_pool, response, NULL, + send_response(sess, token, sess->rx_pool, response, NULL, PJ_FALSE, src_addr, src_addr_len); } - pj_pool_release(tmp_pool); - return status; + goto on_return; } - dump = (char*) pj_pool_alloc(tmp_pool, PJ_STUN_MAX_PKT_LEN); - - PJ_LOG(5,(SNAME(sess), - "RX STUN message from %s:%d:\n" - "--- begin STUN message ---\n" - "%s" - "--- end of STUN message ---\n", - pj_inet_ntoa(((pj_sockaddr_in*)src_addr)->sin_addr), - pj_ntohs(((pj_sockaddr_in*)src_addr)->sin_port), - pj_stun_msg_dump(msg, dump, PJ_STUN_MAX_PKT_LEN, NULL))); - - pj_lock_acquire(sess->lock); + dump_rx_msg(sess, msg, pkt_size, src_addr); /* For requests, check if we have cached response */ - status = check_cached_response(sess, tmp_pool, msg, + status = check_cached_response(sess, sess->rx_pool, msg, src_addr, src_addr_len); if (status == PJ_SUCCESS) { goto on_return; @@ -1231,13 +1367,13 @@ PJ_DEF(pj_status_t) pj_stun_session_on_rx_pkt(pj_stun_session *sess, } else if (PJ_STUN_IS_REQUEST(msg->hdr.type)) { - status = on_incoming_request(sess, options, token, tmp_pool, + status = on_incoming_request(sess, options, token, sess->rx_pool, (const pj_uint8_t*) packet, pkt_size, msg, src_addr, src_addr_len); } else if (PJ_STUN_IS_INDICATION(msg->hdr.type)) { - status = on_incoming_indication(sess, token, tmp_pool, + status = on_incoming_indication(sess, token, sess->rx_pool, (const pj_uint8_t*) packet, pkt_size, msg, src_addr, src_addr_len); @@ -1249,9 +1385,14 @@ PJ_DEF(pj_status_t) pj_stun_session_on_rx_pkt(pj_stun_session *sess, on_return: pj_lock_release(sess->lock); - pj_pool_release(tmp_pool); + /* If we've received destroy request while we're on the callback, + * destroy the session now. + */ + if (pj_atomic_dec_and_get(sess->busy)==0 && sess->destroy_request) { + pj_stun_session_destroy(sess); + return PJNATH_ESTUNDESTROYED; + } + return status; } - - |