summaryrefslogtreecommitdiff
path: root/pjnath/src/pjnath/stun_transaction.c
diff options
context:
space:
mode:
Diffstat (limited to 'pjnath/src/pjnath/stun_transaction.c')
-rw-r--r--pjnath/src/pjnath/stun_transaction.c157
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*)