diff options
Diffstat (limited to 'pjnath/src/pjnath/stun_transaction.c')
-rw-r--r-- | pjnath/src/pjnath/stun_transaction.c | 157 |
1 files changed, 91 insertions, 66 deletions
diff --git a/pjnath/src/pjnath/stun_transaction.c b/pjnath/src/pjnath/stun_transaction.c index d714ecf..58eca26 100644 --- a/pjnath/src/pjnath/stun_transaction.c +++ b/pjnath/src/pjnath/stun_transaction.c @@ -1,4 +1,4 @@ -/* $Id: stun_transaction.c 3753 2011-09-18 14:59:56Z bennylp $ */ +/* $Id: stun_transaction.c 4413 2013-03-05 06:29:15Z ming $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org> @@ -26,6 +26,8 @@ #include <pj/timer.h> +#define THIS_FILE "stun_transaction.c" +#define TIMER_INACTIVE 0 #define TIMER_ACTIVE 1 @@ -34,6 +36,7 @@ struct pj_stun_client_tsx char obj_name[PJ_MAX_OBJ_NAME]; pj_stun_tsx_cb cb; void *user_data; + pj_grp_lock_t *grp_lock; pj_bool_t complete; @@ -51,18 +54,24 @@ struct pj_stun_client_tsx }; +#if 1 +# define TRACE_(expr) PJ_LOG(5,expr) +#else +# define TRACE_(expr) +#endif + + 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) - /* * Create a STUN client transaction. */ PJ_DEF(pj_status_t) pj_stun_client_tsx_create(pj_stun_config *cfg, pj_pool_t *pool, + pj_grp_lock_t *grp_lock, const pj_stun_tsx_cb *cb, pj_stun_client_tsx **p_tsx) { @@ -74,6 +83,7 @@ PJ_DEF(pj_status_t) pj_stun_client_tsx_create(pj_stun_config *cfg, tsx = PJ_POOL_ZALLOC_T(pool, pj_stun_client_tsx); tsx->rto_msec = cfg->rto_msec; tsx->timer_heap = cfg->timer_heap; + tsx->grp_lock = grp_lock; pj_memcpy(&tsx->cb, cb, sizeof(*cb)); tsx->retransmit_timer.cb = &retransmit_timer_callback; @@ -82,7 +92,7 @@ PJ_DEF(pj_status_t) pj_stun_client_tsx_create(pj_stun_config *cfg, 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); + pj_ansi_snprintf(tsx->obj_name, sizeof(tsx->obj_name), "utsx%p", tsx); *p_tsx = tsx; @@ -100,26 +110,30 @@ PJ_DEF(pj_status_t) pj_stun_client_tsx_schedule_destroy( PJ_ASSERT_RETURN(tsx && delay, PJ_EINVAL); PJ_ASSERT_RETURN(tsx->cb.on_destroy, PJ_EINVAL); + pj_grp_lock_acquire(tsx->grp_lock); + /* Cancel previously registered timer */ - if (tsx->destroy_timer.id != 0) { - pj_timer_heap_cancel(tsx->timer_heap, &tsx->destroy_timer); - tsx->destroy_timer.id = 0; - } + pj_timer_heap_cancel_if_active(tsx->timer_heap, &tsx->destroy_timer, + TIMER_INACTIVE); /* Stop retransmission, just in case */ - if (tsx->retransmit_timer.id != 0) { - pj_timer_heap_cancel(tsx->timer_heap, &tsx->retransmit_timer); - tsx->retransmit_timer.id = 0; - } + pj_timer_heap_cancel_if_active(tsx->timer_heap, &tsx->retransmit_timer, + TIMER_INACTIVE); - status = pj_timer_heap_schedule(tsx->timer_heap, - &tsx->destroy_timer, delay); - if (status != PJ_SUCCESS) + status = pj_timer_heap_schedule_w_grp_lock(tsx->timer_heap, + &tsx->destroy_timer, delay, + TIMER_ACTIVE, tsx->grp_lock); + if (status != PJ_SUCCESS) { + pj_grp_lock_release(tsx->grp_lock); return status; + } - tsx->destroy_timer.id = TIMER_ACTIVE; tsx->cb.on_complete = NULL; + pj_grp_lock_release(tsx->grp_lock); + + TRACE_((tsx->obj_name, "STUN transaction %p schedule destroy", tsx)); + return PJ_SUCCESS; } @@ -127,20 +141,21 @@ PJ_DEF(pj_status_t) pj_stun_client_tsx_schedule_destroy( /* * Destroy transaction immediately. */ -PJ_DEF(pj_status_t) pj_stun_client_tsx_destroy(pj_stun_client_tsx *tsx) +PJ_DEF(pj_status_t) pj_stun_client_tsx_stop(pj_stun_client_tsx *tsx) { PJ_ASSERT_RETURN(tsx, PJ_EINVAL); - if (tsx->retransmit_timer.id != 0) { - pj_timer_heap_cancel(tsx->timer_heap, &tsx->retransmit_timer); - tsx->retransmit_timer.id = 0; - } - if (tsx->destroy_timer.id != 0) { - pj_timer_heap_cancel(tsx->timer_heap, &tsx->destroy_timer); - tsx->destroy_timer.id = 0; - } + /* Don't call grp_lock_acquire() because we might be called on + * group lock's destructor. + */ + pj_timer_heap_cancel_if_active(tsx->timer_heap, &tsx->retransmit_timer, + TIMER_INACTIVE); + pj_timer_heap_cancel_if_active(tsx->timer_heap, &tsx->destroy_timer, + TIMER_INACTIVE); + + PJ_LOG(5,(tsx->obj_name, "STUN client transaction %p stopped, ref_cnt=%d", + tsx, pj_grp_lock_get_ref(tsx->grp_lock))); - PJ_LOG(5,(tsx->obj_name, "STUN client transaction destroyed")); return PJ_SUCCESS; } @@ -180,14 +195,15 @@ PJ_DEF(void*) pj_stun_client_tsx_get_data(pj_stun_client_tsx *tsx) /* * Transmit message. */ -static pj_status_t tsx_transmit_msg(pj_stun_client_tsx *tsx) +static pj_status_t tsx_transmit_msg(pj_stun_client_tsx *tsx, + pj_bool_t mod_count) { pj_status_t status; - PJ_ASSERT_RETURN(tsx->retransmit_timer.id == 0 || + PJ_ASSERT_RETURN(tsx->retransmit_timer.id == TIMER_INACTIVE || !tsx->require_retransmit, PJ_EBUSY); - if (tsx->require_retransmit) { + if (tsx->require_retransmit && mod_count) { /* Calculate retransmit/timeout delay */ if (tsx->transmit_count == 0) { tsx->retransmit_time.sec = 0; @@ -210,18 +226,20 @@ 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->timer_heap, - &tsx->retransmit_timer, - &tsx->retransmit_time); + status = pj_timer_heap_schedule_w_grp_lock(tsx->timer_heap, + &tsx->retransmit_timer, + &tsx->retransmit_time, + TIMER_ACTIVE, + tsx->grp_lock); if (status != PJ_SUCCESS) { - tsx->retransmit_timer.id = 0; + tsx->retransmit_timer.id = TIMER_INACTIVE; return status; } - tsx->retransmit_timer.id = TIMER_ACTIVE; } - tsx->transmit_count++; + if (mod_count) + tsx->transmit_count++; PJ_LOG(5,(tsx->obj_name, "STUN sending message (transmit count=%d)", tsx->transmit_count)); @@ -233,12 +251,12 @@ static pj_status_t tsx_transmit_msg(pj_stun_client_tsx *tsx) if (status == PJNATH_ESTUNDESTROYED) { /* We've been destroyed, don't access the object. */ } else if (status != PJ_SUCCESS) { - if (tsx->retransmit_timer.id != 0) { - pj_timer_heap_cancel(tsx->timer_heap, - &tsx->retransmit_timer); - tsx->retransmit_timer.id = 0; + if (mod_count) { + pj_timer_heap_cancel_if_active( tsx->timer_heap, + &tsx->retransmit_timer, + TIMER_INACTIVE); } - stun_perror(tsx, "STUN error sending message", status); + PJ_PERROR(4, (tsx->obj_name, status, "STUN error sending message")); } pj_log_pop_indent(); @@ -259,6 +277,8 @@ PJ_DEF(pj_status_t) pj_stun_client_tsx_send_msg(pj_stun_client_tsx *tsx, PJ_ASSERT_RETURN(tsx && pkt && pkt_len, PJ_EINVAL); PJ_ASSERT_RETURN(tsx->retransmit_timer.id == 0, PJ_EBUSY); + pj_grp_lock_acquire(tsx->grp_lock); + /* Encode message */ tsx->last_pkt = pkt; tsx->last_pkt_size = pkt_len; @@ -284,27 +304,29 @@ PJ_DEF(pj_status_t) pj_stun_client_tsx_send_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->timer_heap, - &tsx->retransmit_timer, - &tsx->retransmit_time); + status = pj_timer_heap_schedule_w_grp_lock(tsx->timer_heap, + &tsx->retransmit_timer, + &tsx->retransmit_time, + TIMER_ACTIVE, + tsx->grp_lock); if (status != PJ_SUCCESS) { - tsx->retransmit_timer.id = 0; + tsx->retransmit_timer.id = TIMER_INACTIVE; + pj_grp_lock_release(tsx->grp_lock); return status; } - tsx->retransmit_timer.id = TIMER_ACTIVE; } /* Send the message */ - status = tsx_transmit_msg(tsx); + status = tsx_transmit_msg(tsx, PJ_TRUE); if (status != PJ_SUCCESS) { - if (tsx->retransmit_timer.id != 0) { - pj_timer_heap_cancel(tsx->timer_heap, - &tsx->retransmit_timer); - tsx->retransmit_timer.id = 0; - } + pj_timer_heap_cancel_if_active(tsx->timer_heap, + &tsx->retransmit_timer, + TIMER_INACTIVE); + pj_grp_lock_release(tsx->grp_lock); return status; } + pj_grp_lock_release(tsx->grp_lock); return PJ_SUCCESS; } @@ -317,8 +339,12 @@ static void retransmit_timer_callback(pj_timer_heap_t *timer_heap, pj_status_t status; PJ_UNUSED_ARG(timer_heap); + pj_grp_lock_acquire(tsx->grp_lock); if (tsx->transmit_count >= PJ_STUN_MAX_TRANSMIT_COUNT) { + /* tsx may be destroyed when calling the callback below */ + pj_grp_lock_t *grp_lock = tsx->grp_lock; + /* Retransmission count exceeded. Transaction has failed */ tsx->retransmit_timer.id = 0; PJ_LOG(4,(tsx->obj_name, "STUN timeout waiting for response")); @@ -329,16 +355,15 @@ static void retransmit_timer_callback(pj_timer_heap_t *timer_heap, tsx->cb.on_complete(tsx, PJNATH_ESTUNTIMEDOUT, NULL, NULL, 0); } } + pj_grp_lock_release(grp_lock); /* We might have been destroyed, don't try to access the object */ pj_log_pop_indent(); return; } tsx->retransmit_timer.id = 0; - status = tsx_transmit_msg(tsx); - if (status == PJNATH_ESTUNDESTROYED) { - /* We've been destroyed, don't try to access the object */ - } else if (status != PJ_SUCCESS) { + status = tsx_transmit_msg(tsx, PJ_TRUE); + if (status != PJ_SUCCESS) { tsx->retransmit_timer.id = 0; if (!tsx->complete) { tsx->complete = PJ_TRUE; @@ -346,25 +371,26 @@ static void retransmit_timer_callback(pj_timer_heap_t *timer_heap, tsx->cb.on_complete(tsx, status, NULL, NULL, 0); } } - /* We might have been destroyed, don't try to access the object */ } + + pj_grp_lock_release(tsx->grp_lock); + /* We might have been destroyed, don't try to access the object */ } /* * Request to retransmit the request. */ -PJ_DEF(pj_status_t) pj_stun_client_tsx_retransmit(pj_stun_client_tsx *tsx) +PJ_DEF(pj_status_t) pj_stun_client_tsx_retransmit(pj_stun_client_tsx *tsx, + pj_bool_t mod_count) { if (tsx->destroy_timer.id != 0) { return PJ_SUCCESS; } - if (tsx->retransmit_timer.id != 0) { - pj_timer_heap_cancel(tsx->timer_heap, &tsx->retransmit_timer); - tsx->retransmit_timer.id = 0; - } + pj_timer_heap_cancel_if_active(tsx->timer_heap, &tsx->retransmit_timer, + TIMER_INACTIVE); - return tsx_transmit_msg(tsx); + return tsx_transmit_msg(tsx, mod_count); } /* Timer callback to destroy transaction */ @@ -376,6 +402,7 @@ static void destroy_timer_callback(pj_timer_heap_t *timer_heap, PJ_UNUSED_ARG(timer_heap); tsx->destroy_timer.id = PJ_FALSE; + tsx->cb.on_destroy(tsx); /* Don't access transaction after this */ } @@ -405,10 +432,8 @@ 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->retransmit_timer.id) { - pj_timer_heap_cancel(tsx->timer_heap, &tsx->retransmit_timer); - tsx->retransmit_timer.id = 0; - } + pj_timer_heap_cancel_if_active(tsx->timer_heap, &tsx->retransmit_timer, + TIMER_INACTIVE); /* Find STUN error code attribute */ err_attr = (pj_stun_errcode_attr*) |