1
/* $Id: stun_transaction.c 4717 2014-01-29 06:33:38Z nanang $ */
3
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
4
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
6
* This program is free software; you can redistribute it and/or modify
7
* it under the terms of the GNU General Public License as published by
8
* the Free Software Foundation; either version 2 of the License, or
9
* (at your option) any later version.
11
* This program is distributed in the hope that it will be useful,
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
* GNU General Public License for more details.
16
* You should have received a copy of the GNU General Public License
17
* along with this program; if not, write to the Free Software
18
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20
#include <pjnath/stun_transaction.h>
21
#include <pjnath/errno.h>
22
#include <pj/assert.h>
25
#include <pj/string.h>
29
#define THIS_FILE "stun_transaction.c"
30
#define TIMER_INACTIVE 0
31
#define TIMER_ACTIVE 1
34
struct pj_stun_client_tsx
36
char obj_name[PJ_MAX_OBJ_NAME];
39
pj_grp_lock_t *grp_lock;
43
pj_bool_t require_retransmit;
45
pj_timer_entry retransmit_timer;
46
unsigned transmit_count;
47
pj_time_val retransmit_time;
48
pj_timer_heap_t *timer_heap;
50
pj_timer_entry destroy_timer;
53
unsigned last_pkt_size;
58
# define TRACE_(expr) PJ_LOG(5,expr)
64
static void retransmit_timer_callback(pj_timer_heap_t *timer_heap,
65
pj_timer_entry *timer);
66
static void destroy_timer_callback(pj_timer_heap_t *timer_heap,
67
pj_timer_entry *timer);
70
* Create a STUN client transaction.
72
PJ_DEF(pj_status_t) pj_stun_client_tsx_create(pj_stun_config *cfg,
74
pj_grp_lock_t *grp_lock,
75
const pj_stun_tsx_cb *cb,
76
pj_stun_client_tsx **p_tsx)
78
pj_stun_client_tsx *tsx;
80
PJ_ASSERT_RETURN(cfg && cb && p_tsx, PJ_EINVAL);
81
PJ_ASSERT_RETURN(cb->on_send_msg, PJ_EINVAL);
83
tsx = PJ_POOL_ZALLOC_T(pool, pj_stun_client_tsx);
84
tsx->rto_msec = cfg->rto_msec;
85
tsx->timer_heap = cfg->timer_heap;
86
tsx->grp_lock = grp_lock;
87
pj_memcpy(&tsx->cb, cb, sizeof(*cb));
89
tsx->retransmit_timer.cb = &retransmit_timer_callback;
90
tsx->retransmit_timer.user_data = tsx;
92
tsx->destroy_timer.cb = &destroy_timer_callback;
93
tsx->destroy_timer.user_data = tsx;
95
pj_ansi_snprintf(tsx->obj_name, sizeof(tsx->obj_name), "utsx%p", tsx);
99
PJ_LOG(5,(tsx->obj_name, "STUN client transaction created"));
104
PJ_DEF(pj_status_t) pj_stun_client_tsx_schedule_destroy(
105
pj_stun_client_tsx *tsx,
106
const pj_time_val *delay)
110
PJ_ASSERT_RETURN(tsx && delay, PJ_EINVAL);
111
PJ_ASSERT_RETURN(tsx->cb.on_destroy, PJ_EINVAL);
113
pj_grp_lock_acquire(tsx->grp_lock);
115
/* Cancel previously registered timer */
116
pj_timer_heap_cancel_if_active(tsx->timer_heap, &tsx->destroy_timer,
119
/* Stop retransmission, just in case */
120
pj_timer_heap_cancel_if_active(tsx->timer_heap, &tsx->retransmit_timer,
123
status = pj_timer_heap_schedule_w_grp_lock(tsx->timer_heap,
124
&tsx->destroy_timer, delay,
125
TIMER_ACTIVE, tsx->grp_lock);
126
if (status != PJ_SUCCESS) {
127
pj_grp_lock_release(tsx->grp_lock);
131
tsx->cb.on_complete = NULL;
133
pj_grp_lock_release(tsx->grp_lock);
135
TRACE_((tsx->obj_name, "STUN transaction %p schedule destroy", tsx));
142
* Destroy transaction immediately.
144
PJ_DEF(pj_status_t) pj_stun_client_tsx_stop(pj_stun_client_tsx *tsx)
146
PJ_ASSERT_RETURN(tsx, PJ_EINVAL);
148
/* Don't call grp_lock_acquire() because we might be called on
149
* group lock's destructor.
151
pj_timer_heap_cancel_if_active(tsx->timer_heap, &tsx->retransmit_timer,
153
pj_timer_heap_cancel_if_active(tsx->timer_heap, &tsx->destroy_timer,
156
PJ_LOG(5,(tsx->obj_name, "STUN client transaction %p stopped, ref_cnt=%d",
157
tsx, pj_grp_lock_get_ref(tsx->grp_lock)));
164
* Check if transaction has completed.
166
PJ_DEF(pj_bool_t) pj_stun_client_tsx_is_complete(pj_stun_client_tsx *tsx)
168
PJ_ASSERT_RETURN(tsx, PJ_FALSE);
169
return tsx->complete;
176
PJ_DEF(pj_status_t) pj_stun_client_tsx_set_data(pj_stun_client_tsx *tsx,
179
PJ_ASSERT_RETURN(tsx, PJ_EINVAL);
180
tsx->user_data = data;
188
PJ_DEF(void*) pj_stun_client_tsx_get_data(pj_stun_client_tsx *tsx)
190
PJ_ASSERT_RETURN(tsx, NULL);
191
return tsx->user_data;
198
static pj_status_t tsx_transmit_msg(pj_stun_client_tsx *tsx,
203
PJ_ASSERT_RETURN(tsx->retransmit_timer.id == TIMER_INACTIVE ||
204
!tsx->require_retransmit || !mod_count, PJ_EBUSY);
206
if (tsx->require_retransmit && mod_count) {
207
/* Calculate retransmit/timeout delay */
208
if (tsx->transmit_count == 0) {
209
tsx->retransmit_time.sec = 0;
210
tsx->retransmit_time.msec = tsx->rto_msec;
212
} else if (tsx->transmit_count < PJ_STUN_MAX_TRANSMIT_COUNT-1) {
215
msec = PJ_TIME_VAL_MSEC(tsx->retransmit_time);
217
tsx->retransmit_time.sec = msec / 1000;
218
tsx->retransmit_time.msec = msec % 1000;
221
tsx->retransmit_time.sec = PJ_STUN_TIMEOUT_VALUE / 1000;
222
tsx->retransmit_time.msec = PJ_STUN_TIMEOUT_VALUE % 1000;
225
/* Schedule timer first because when send_msg() failed we can
226
* cancel it (as opposed to when schedule_timer() failed we cannot
227
* cancel transmission).
229
status = pj_timer_heap_schedule_w_grp_lock(tsx->timer_heap,
230
&tsx->retransmit_timer,
231
&tsx->retransmit_time,
234
if (status != PJ_SUCCESS) {
235
tsx->retransmit_timer.id = TIMER_INACTIVE;
242
tsx->transmit_count++;
244
PJ_LOG(5,(tsx->obj_name, "STUN sending message (transmit count=%d)",
245
tsx->transmit_count));
246
pj_log_push_indent();
249
status = tsx->cb.on_send_msg(tsx, tsx->last_pkt, tsx->last_pkt_size);
251
if (status == PJNATH_ESTUNDESTROYED) {
252
/* We've been destroyed, don't access the object. */
253
} else if (status != PJ_SUCCESS) {
255
pj_timer_heap_cancel_if_active( tsx->timer_heap,
256
&tsx->retransmit_timer,
259
PJ_PERROR(4, (tsx->obj_name, status, "STUN error sending message"));
268
* Send outgoing message and start STUN transaction.
270
PJ_DEF(pj_status_t) pj_stun_client_tsx_send_msg(pj_stun_client_tsx *tsx,
271
pj_bool_t retransmit,
277
PJ_ASSERT_RETURN(tsx && pkt && pkt_len, PJ_EINVAL);
278
PJ_ASSERT_RETURN(tsx->retransmit_timer.id == 0, PJ_EBUSY);
280
pj_grp_lock_acquire(tsx->grp_lock);
284
tsx->last_pkt_size = pkt_len;
286
/* Update STUN retransmit flag */
287
tsx->require_retransmit = retransmit;
289
/* For TCP, schedule timeout timer after PJ_STUN_TIMEOUT_VALUE.
290
* Since we don't have timeout timer, simulate this by using
296
pj_assert(tsx->retransmit_timer.id == 0);
297
tsx->transmit_count = PJ_STUN_MAX_TRANSMIT_COUNT;
299
timeout = tsx->rto_msec * 16;
300
tsx->retransmit_time.sec = timeout / 1000;
301
tsx->retransmit_time.msec = timeout % 1000;
303
/* Schedule timer first because when send_msg() failed we can
304
* cancel it (as opposed to when schedule_timer() failed we cannot
305
* cancel transmission).
307
status = pj_timer_heap_schedule_w_grp_lock(tsx->timer_heap,
308
&tsx->retransmit_timer,
309
&tsx->retransmit_time,
312
if (status != PJ_SUCCESS) {
313
tsx->retransmit_timer.id = TIMER_INACTIVE;
314
pj_grp_lock_release(tsx->grp_lock);
319
/* Send the message */
320
status = tsx_transmit_msg(tsx, PJ_TRUE);
321
if (status != PJ_SUCCESS) {
322
pj_timer_heap_cancel_if_active(tsx->timer_heap,
323
&tsx->retransmit_timer,
325
pj_grp_lock_release(tsx->grp_lock);
329
pj_grp_lock_release(tsx->grp_lock);
334
/* Retransmit timer callback */
335
static void retransmit_timer_callback(pj_timer_heap_t *timer_heap,
336
pj_timer_entry *timer)
338
pj_stun_client_tsx *tsx = (pj_stun_client_tsx *) timer->user_data;
341
PJ_UNUSED_ARG(timer_heap);
342
pj_grp_lock_acquire(tsx->grp_lock);
344
if (tsx->transmit_count >= PJ_STUN_MAX_TRANSMIT_COUNT) {
345
/* tsx may be destroyed when calling the callback below */
346
pj_grp_lock_t *grp_lock = tsx->grp_lock;
348
/* Retransmission count exceeded. Transaction has failed */
349
tsx->retransmit_timer.id = 0;
350
PJ_LOG(4,(tsx->obj_name, "STUN timeout waiting for response"));
351
pj_log_push_indent();
352
if (!tsx->complete) {
353
tsx->complete = PJ_TRUE;
354
if (tsx->cb.on_complete) {
355
tsx->cb.on_complete(tsx, PJNATH_ESTUNTIMEDOUT, NULL, NULL, 0);
358
pj_grp_lock_release(grp_lock);
359
/* We might have been destroyed, don't try to access the object */
364
tsx->retransmit_timer.id = 0;
365
status = tsx_transmit_msg(tsx, PJ_TRUE);
366
if (status != PJ_SUCCESS) {
367
tsx->retransmit_timer.id = 0;
368
if (!tsx->complete) {
369
tsx->complete = PJ_TRUE;
370
if (tsx->cb.on_complete) {
371
tsx->cb.on_complete(tsx, status, NULL, NULL, 0);
376
pj_grp_lock_release(tsx->grp_lock);
377
/* We might have been destroyed, don't try to access the object */
381
* Request to retransmit the request.
383
PJ_DEF(pj_status_t) pj_stun_client_tsx_retransmit(pj_stun_client_tsx *tsx,
386
if (tsx->destroy_timer.id != 0) {
391
pj_timer_heap_cancel_if_active(tsx->timer_heap, &tsx->retransmit_timer,
395
return tsx_transmit_msg(tsx, mod_count);
398
/* Timer callback to destroy transaction */
399
static void destroy_timer_callback(pj_timer_heap_t *timer_heap,
400
pj_timer_entry *timer)
402
pj_stun_client_tsx *tsx = (pj_stun_client_tsx *) timer->user_data;
404
PJ_UNUSED_ARG(timer_heap);
406
tsx->destroy_timer.id = PJ_FALSE;
408
tsx->cb.on_destroy(tsx);
409
/* Don't access transaction after this */
414
* Notify the STUN transaction about the arrival of STUN response.
416
PJ_DEF(pj_status_t) pj_stun_client_tsx_on_rx_msg(pj_stun_client_tsx *tsx,
417
const pj_stun_msg *msg,
418
const pj_sockaddr_t *src_addr,
419
unsigned src_addr_len)
421
pj_stun_errcode_attr *err_attr;
424
/* Must be STUN response message */
425
if (!PJ_STUN_IS_SUCCESS_RESPONSE(msg->hdr.type) &&
426
!PJ_STUN_IS_ERROR_RESPONSE(msg->hdr.type))
428
PJ_LOG(4,(tsx->obj_name,
429
"STUN rx_msg() error: not response message"));
430
return PJNATH_EINSTUNMSGTYPE;
434
/* We have a response with matching transaction ID.
435
* We can cancel retransmit timer now.
437
pj_timer_heap_cancel_if_active(tsx->timer_heap, &tsx->retransmit_timer,
440
/* Find STUN error code attribute */
441
err_attr = (pj_stun_errcode_attr*)
442
pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_ERROR_CODE, 0);
444
if (err_attr && err_attr->err_code <= 200) {
445
/* draft-ietf-behave-rfc3489bis-05.txt Section 8.3.2:
446
* Any response between 100 and 299 MUST result in the cessation
447
* of request retransmissions, but otherwise is discarded.
449
PJ_LOG(4,(tsx->obj_name,
450
"STUN rx_msg() error: received provisional %d code (%.*s)",
452
(int)err_attr->reason.slen,
453
err_attr->reason.ptr));
457
if (err_attr == NULL) {
460
status = PJ_STATUS_FROM_STUN_CODE(err_attr->err_code);
464
if (!tsx->complete) {
465
tsx->complete = PJ_TRUE;
466
if (tsx->cb.on_complete) {
467
tsx->cb.on_complete(tsx, status, msg, src_addr, src_addr_len);
469
/* We might have been destroyed, don't try to access the object */