~noskcaj/ubuntu/saucy/sflphone/merge-1.2.3-2

« back to all changes in this revision

Viewing changes to daemon/libs/pjproject-2.0.1/pjnath/src/pjnath/stun_transaction.c

  • Committer: Jackson Doak
  • Date: 2013-07-10 21:04:46 UTC
  • mfrom: (20.1.3 sid)
  • Revision ID: noskcaj@ubuntu.com-20130710210446-y8f587vza807icr9
Properly merged from upstream.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* $Id: stun_transaction.c 3753 2011-09-18 14:59:56Z bennylp $ */
 
2
/*
 
3
 * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
 
4
 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
 
5
 *
 
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.
 
10
 *
 
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.
 
15
 *
 
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
 
19
 */
 
20
#include <pjnath/stun_transaction.h>
 
21
#include <pjnath/errno.h>
 
22
#include <pj/assert.h>
 
23
#include <pj/log.h>
 
24
#include <pj/pool.h>
 
25
#include <pj/string.h>
 
26
#include <pj/timer.h>
 
27
 
 
28
 
 
29
#define TIMER_ACTIVE            1
 
30
 
 
31
 
 
32
struct pj_stun_client_tsx
 
33
{
 
34
    char                 obj_name[PJ_MAX_OBJ_NAME];
 
35
    pj_stun_tsx_cb       cb;
 
36
    void                *user_data;
 
37
 
 
38
    pj_bool_t            complete;
 
39
 
 
40
    pj_bool_t            require_retransmit;
 
41
    unsigned             rto_msec;
 
42
    pj_timer_entry       retransmit_timer;
 
43
    unsigned             transmit_count;
 
44
    pj_time_val          retransmit_time;
 
45
    pj_timer_heap_t     *timer_heap;
 
46
 
 
47
    pj_timer_entry       destroy_timer;
 
48
 
 
49
    void                *last_pkt;
 
50
    unsigned             last_pkt_size;
 
51
};
 
52
 
 
53
 
 
54
static void retransmit_timer_callback(pj_timer_heap_t *timer_heap,
 
55
                                      pj_timer_entry *timer);
 
56
static void destroy_timer_callback(pj_timer_heap_t *timer_heap,
 
57
                                   pj_timer_entry *timer);
 
58
 
 
59
#define stun_perror(tsx,msg,rc) pjnath_perror(tsx->obj_name, msg, rc)
 
60
 
 
61
/*
 
62
 * Create a STUN client transaction.
 
63
 */
 
64
PJ_DEF(pj_status_t) pj_stun_client_tsx_create(pj_stun_config *cfg,
 
65
                                              pj_pool_t *pool,
 
66
                                              const pj_stun_tsx_cb *cb,
 
67
                                              pj_stun_client_tsx **p_tsx)
 
68
{
 
69
    pj_stun_client_tsx *tsx;
 
70
 
 
71
    PJ_ASSERT_RETURN(cfg && cb && p_tsx, PJ_EINVAL);
 
72
    PJ_ASSERT_RETURN(cb->on_send_msg, PJ_EINVAL);
 
73
 
 
74
    tsx = PJ_POOL_ZALLOC_T(pool, pj_stun_client_tsx);
 
75
    tsx->rto_msec = cfg->rto_msec;
 
76
    tsx->timer_heap = cfg->timer_heap;
 
77
    pj_memcpy(&tsx->cb, cb, sizeof(*cb));
 
78
 
 
79
    tsx->retransmit_timer.cb = &retransmit_timer_callback;
 
80
    tsx->retransmit_timer.user_data = tsx;
 
81
 
 
82
    tsx->destroy_timer.cb = &destroy_timer_callback;
 
83
    tsx->destroy_timer.user_data = tsx;
 
84
 
 
85
    pj_ansi_snprintf(tsx->obj_name, sizeof(tsx->obj_name), "stuntsx%p", tsx);
 
86
 
 
87
    *p_tsx = tsx;
 
88
 
 
89
    PJ_LOG(5,(tsx->obj_name, "STUN client transaction created"));
 
90
    return PJ_SUCCESS;
 
91
}
 
92
 
 
93
 
 
94
PJ_DEF(pj_status_t) pj_stun_client_tsx_schedule_destroy(
 
95
                                    pj_stun_client_tsx *tsx,
 
96
                                    const pj_time_val *delay)
 
97
{
 
98
    pj_status_t status;
 
99
 
 
100
    PJ_ASSERT_RETURN(tsx && delay, PJ_EINVAL);
 
101
    PJ_ASSERT_RETURN(tsx->cb.on_destroy, PJ_EINVAL);
 
102
 
 
103
    /* Cancel previously registered timer */
 
104
    if (tsx->destroy_timer.id != 0) {
 
105
        pj_timer_heap_cancel(tsx->timer_heap, &tsx->destroy_timer);
 
106
        tsx->destroy_timer.id = 0;
 
107
    }
 
108
 
 
109
    /* Stop retransmission, just in case */
 
110
    if (tsx->retransmit_timer.id != 0) {
 
111
        pj_timer_heap_cancel(tsx->timer_heap, &tsx->retransmit_timer);
 
112
        tsx->retransmit_timer.id = 0;
 
113
    }
 
114
 
 
115
    status = pj_timer_heap_schedule(tsx->timer_heap,
 
116
                                    &tsx->destroy_timer, delay);
 
117
    if (status != PJ_SUCCESS)
 
118
        return status;
 
119
 
 
120
    tsx->destroy_timer.id = TIMER_ACTIVE;
 
121
    tsx->cb.on_complete = NULL;
 
122
 
 
123
    return PJ_SUCCESS;
 
124
}
 
125
 
 
126
 
 
127
/*
 
128
 * Destroy transaction immediately.
 
129
 */
 
130
PJ_DEF(pj_status_t) pj_stun_client_tsx_destroy(pj_stun_client_tsx *tsx)
 
131
{
 
132
    PJ_ASSERT_RETURN(tsx, PJ_EINVAL);
 
133
 
 
134
    if (tsx->retransmit_timer.id != 0) {
 
135
        pj_timer_heap_cancel(tsx->timer_heap, &tsx->retransmit_timer);
 
136
        tsx->retransmit_timer.id = 0;
 
137
    }
 
138
    if (tsx->destroy_timer.id != 0) {
 
139
        pj_timer_heap_cancel(tsx->timer_heap, &tsx->destroy_timer);
 
140
        tsx->destroy_timer.id = 0;
 
141
    }
 
142
 
 
143
    PJ_LOG(5,(tsx->obj_name, "STUN client transaction destroyed"));
 
144
    return PJ_SUCCESS;
 
145
}
 
146
 
 
147
 
 
148
/*
 
149
 * Check if transaction has completed.
 
150
 */
 
151
PJ_DEF(pj_bool_t) pj_stun_client_tsx_is_complete(pj_stun_client_tsx *tsx)
 
152
{
 
153
    PJ_ASSERT_RETURN(tsx, PJ_FALSE);
 
154
    return tsx->complete;
 
155
}
 
156
 
 
157
 
 
158
/*
 
159
 * Set user data.
 
160
 */
 
161
PJ_DEF(pj_status_t) pj_stun_client_tsx_set_data(pj_stun_client_tsx *tsx,
 
162
                                                void *data)
 
163
{
 
164
    PJ_ASSERT_RETURN(tsx, PJ_EINVAL);
 
165
    tsx->user_data = data;
 
166
    return PJ_SUCCESS;
 
167
}
 
168
 
 
169
 
 
170
/*
 
171
 * Get the user data
 
172
 */
 
173
PJ_DEF(void*) pj_stun_client_tsx_get_data(pj_stun_client_tsx *tsx)
 
174
{
 
175
    PJ_ASSERT_RETURN(tsx, NULL);
 
176
    return tsx->user_data;
 
177
}
 
178
 
 
179
 
 
180
/*
 
181
 * Transmit message.
 
182
 */
 
183
static pj_status_t tsx_transmit_msg(pj_stun_client_tsx *tsx)
 
184
{
 
185
    pj_status_t status;
 
186
 
 
187
    PJ_ASSERT_RETURN(tsx->retransmit_timer.id == 0 ||
 
188
                     !tsx->require_retransmit, PJ_EBUSY);
 
189
 
 
190
    if (tsx->require_retransmit) {
 
191
        /* Calculate retransmit/timeout delay */
 
192
        if (tsx->transmit_count == 0) {
 
193
            tsx->retransmit_time.sec = 0;
 
194
            tsx->retransmit_time.msec = tsx->rto_msec;
 
195
 
 
196
        } else if (tsx->transmit_count < PJ_STUN_MAX_TRANSMIT_COUNT-1) {
 
197
            unsigned msec;
 
198
 
 
199
            msec = PJ_TIME_VAL_MSEC(tsx->retransmit_time);
 
200
            msec <<= 1;
 
201
            tsx->retransmit_time.sec = msec / 1000;
 
202
            tsx->retransmit_time.msec = msec % 1000;
 
203
 
 
204
        } else {
 
205
            tsx->retransmit_time.sec = PJ_STUN_TIMEOUT_VALUE / 1000;
 
206
            tsx->retransmit_time.msec = PJ_STUN_TIMEOUT_VALUE % 1000;
 
207
        }
 
208
 
 
209
        /* Schedule timer first because when send_msg() failed we can
 
210
         * cancel it (as opposed to when schedule_timer() failed we cannot
 
211
         * cancel transmission).
 
212
         */;
 
213
        status = pj_timer_heap_schedule(tsx->timer_heap,
 
214
                                        &tsx->retransmit_timer,
 
215
                                        &tsx->retransmit_time);
 
216
        if (status != PJ_SUCCESS) {
 
217
            tsx->retransmit_timer.id = 0;
 
218
            return status;
 
219
        }
 
220
        tsx->retransmit_timer.id = TIMER_ACTIVE;
 
221
    }
 
222
 
 
223
 
 
224
    tsx->transmit_count++;
 
225
 
 
226
    PJ_LOG(5,(tsx->obj_name, "STUN sending message (transmit count=%d)",
 
227
              tsx->transmit_count));
 
228
    pj_log_push_indent();
 
229
 
 
230
    /* Send message */
 
231
    status = tsx->cb.on_send_msg(tsx, tsx->last_pkt, tsx->last_pkt_size);
 
232
 
 
233
    if (status == PJNATH_ESTUNDESTROYED) {
 
234
        /* We've been destroyed, don't access the object. */
 
235
    } else if (status != PJ_SUCCESS) {
 
236
        if (tsx->retransmit_timer.id != 0) {
 
237
            pj_timer_heap_cancel(tsx->timer_heap,
 
238
                                 &tsx->retransmit_timer);
 
239
            tsx->retransmit_timer.id = 0;
 
240
        }
 
241
        stun_perror(tsx, "STUN error sending message", status);
 
242
    }
 
243
 
 
244
    pj_log_pop_indent();
 
245
    return status;
 
246
}
 
247
 
 
248
 
 
249
/*
 
250
 * Send outgoing message and start STUN transaction.
 
251
 */
 
252
PJ_DEF(pj_status_t) pj_stun_client_tsx_send_msg(pj_stun_client_tsx *tsx,
 
253
                                                pj_bool_t retransmit,
 
254
                                                void *pkt,
 
255
                                                unsigned pkt_len)
 
256
{
 
257
    pj_status_t status;
 
258
 
 
259
    PJ_ASSERT_RETURN(tsx && pkt && pkt_len, PJ_EINVAL);
 
260
    PJ_ASSERT_RETURN(tsx->retransmit_timer.id == 0, PJ_EBUSY);
 
261
 
 
262
    /* Encode message */
 
263
    tsx->last_pkt = pkt;
 
264
    tsx->last_pkt_size = pkt_len;
 
265
 
 
266
    /* Update STUN retransmit flag */
 
267
    tsx->require_retransmit = retransmit;
 
268
 
 
269
    /* For TCP, schedule timeout timer after PJ_STUN_TIMEOUT_VALUE.
 
270
     * Since we don't have timeout timer, simulate this by using
 
271
     * retransmit timer.
 
272
     */
 
273
    if (!retransmit) {
 
274
        unsigned timeout;
 
275
 
 
276
        pj_assert(tsx->retransmit_timer.id == 0);
 
277
        tsx->transmit_count = PJ_STUN_MAX_TRANSMIT_COUNT;
 
278
 
 
279
        timeout = tsx->rto_msec * 16;
 
280
        tsx->retransmit_time.sec = timeout / 1000;
 
281
        tsx->retransmit_time.msec = timeout % 1000;
 
282
 
 
283
        /* Schedule timer first because when send_msg() failed we can
 
284
         * cancel it (as opposed to when schedule_timer() failed we cannot
 
285
         * cancel transmission).
 
286
         */;
 
287
        status = pj_timer_heap_schedule(tsx->timer_heap,
 
288
                                        &tsx->retransmit_timer,
 
289
                                        &tsx->retransmit_time);
 
290
        if (status != PJ_SUCCESS) {
 
291
            tsx->retransmit_timer.id = 0;
 
292
            return status;
 
293
        }
 
294
        tsx->retransmit_timer.id = TIMER_ACTIVE;
 
295
    }
 
296
 
 
297
    /* Send the message */
 
298
    status = tsx_transmit_msg(tsx);
 
299
    if (status != PJ_SUCCESS) {
 
300
        if (tsx->retransmit_timer.id != 0) {
 
301
            pj_timer_heap_cancel(tsx->timer_heap,
 
302
                                 &tsx->retransmit_timer);
 
303
            tsx->retransmit_timer.id = 0;
 
304
        }
 
305
        return status;
 
306
    }
 
307
 
 
308
    return PJ_SUCCESS;
 
309
}
 
310
 
 
311
 
 
312
/* Retransmit timer callback */
 
313
static void retransmit_timer_callback(pj_timer_heap_t *timer_heap,
 
314
                                      pj_timer_entry *timer)
 
315
{
 
316
    pj_stun_client_tsx *tsx = (pj_stun_client_tsx *) timer->user_data;
 
317
    pj_status_t status;
 
318
 
 
319
    PJ_UNUSED_ARG(timer_heap);
 
320
 
 
321
    if (tsx->transmit_count >= PJ_STUN_MAX_TRANSMIT_COUNT) {
 
322
        /* Retransmission count exceeded. Transaction has failed */
 
323
        tsx->retransmit_timer.id = 0;
 
324
        PJ_LOG(4,(tsx->obj_name, "STUN timeout waiting for response"));
 
325
        pj_log_push_indent();
 
326
        if (!tsx->complete) {
 
327
            tsx->complete = PJ_TRUE;
 
328
            if (tsx->cb.on_complete) {
 
329
                tsx->cb.on_complete(tsx, PJNATH_ESTUNTIMEDOUT, NULL, NULL, 0);
 
330
            }
 
331
        }
 
332
        /* We might have been destroyed, don't try to access the object */
 
333
        pj_log_pop_indent();
 
334
        return;
 
335
    }
 
336
 
 
337
    tsx->retransmit_timer.id = 0;
 
338
    status = tsx_transmit_msg(tsx);
 
339
    if (status == PJNATH_ESTUNDESTROYED) {
 
340
        /* We've been destroyed, don't try to access the object */
 
341
    } else if (status != PJ_SUCCESS) {
 
342
        tsx->retransmit_timer.id = 0;
 
343
        if (!tsx->complete) {
 
344
            tsx->complete = PJ_TRUE;
 
345
            if (tsx->cb.on_complete) {
 
346
                tsx->cb.on_complete(tsx, status, NULL, NULL, 0);
 
347
            }
 
348
        }
 
349
        /* We might have been destroyed, don't try to access the object */
 
350
    }
 
351
}
 
352
 
 
353
/*
 
354
 * Request to retransmit the request.
 
355
 */
 
356
PJ_DEF(pj_status_t) pj_stun_client_tsx_retransmit(pj_stun_client_tsx *tsx)
 
357
{
 
358
    if (tsx->destroy_timer.id != 0) {
 
359
        return PJ_SUCCESS;
 
360
    }
 
361
 
 
362
    if (tsx->retransmit_timer.id != 0) {
 
363
        pj_timer_heap_cancel(tsx->timer_heap, &tsx->retransmit_timer);
 
364
        tsx->retransmit_timer.id = 0;
 
365
    }
 
366
 
 
367
    return tsx_transmit_msg(tsx);
 
368
}
 
369
 
 
370
/* Timer callback to destroy transaction */
 
371
static void destroy_timer_callback(pj_timer_heap_t *timer_heap,
 
372
                                   pj_timer_entry *timer)
 
373
{
 
374
    pj_stun_client_tsx *tsx = (pj_stun_client_tsx *) timer->user_data;
 
375
 
 
376
    PJ_UNUSED_ARG(timer_heap);
 
377
 
 
378
    tsx->destroy_timer.id = PJ_FALSE;
 
379
    tsx->cb.on_destroy(tsx);
 
380
    /* Don't access transaction after this */
 
381
}
 
382
 
 
383
 
 
384
/*
 
385
 * Notify the STUN transaction about the arrival of STUN response.
 
386
 */
 
387
PJ_DEF(pj_status_t) pj_stun_client_tsx_on_rx_msg(pj_stun_client_tsx *tsx,
 
388
                                                 const pj_stun_msg *msg,
 
389
                                                 const pj_sockaddr_t *src_addr,
 
390
                                                 unsigned src_addr_len)
 
391
{
 
392
    pj_stun_errcode_attr *err_attr;
 
393
    pj_status_t status;
 
394
 
 
395
    /* Must be STUN response message */
 
396
    if (!PJ_STUN_IS_SUCCESS_RESPONSE(msg->hdr.type) &&
 
397
        !PJ_STUN_IS_ERROR_RESPONSE(msg->hdr.type))
 
398
    {
 
399
        PJ_LOG(4,(tsx->obj_name,
 
400
                  "STUN rx_msg() error: not response message"));
 
401
        return PJNATH_EINSTUNMSGTYPE;
 
402
    }
 
403
 
 
404
 
 
405
    /* We have a response with matching transaction ID.
 
406
     * We can cancel retransmit timer now.
 
407
     */
 
408
    if (tsx->retransmit_timer.id) {
 
409
        pj_timer_heap_cancel(tsx->timer_heap, &tsx->retransmit_timer);
 
410
        tsx->retransmit_timer.id = 0;
 
411
    }
 
412
 
 
413
    /* Find STUN error code attribute */
 
414
    err_attr = (pj_stun_errcode_attr*)
 
415
                pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_ERROR_CODE, 0);
 
416
 
 
417
    if (err_attr && err_attr->err_code <= 200) {
 
418
        /* draft-ietf-behave-rfc3489bis-05.txt Section 8.3.2:
 
419
         * Any response between 100 and 299 MUST result in the cessation
 
420
         * of request retransmissions, but otherwise is discarded.
 
421
         */
 
422
        PJ_LOG(4,(tsx->obj_name,
 
423
                  "STUN rx_msg() error: received provisional %d code (%.*s)",
 
424
                  err_attr->err_code,
 
425
                  (int)err_attr->reason.slen,
 
426
                  err_attr->reason.ptr));
 
427
        return PJ_SUCCESS;
 
428
    }
 
429
 
 
430
    if (err_attr == NULL) {
 
431
        status = PJ_SUCCESS;
 
432
    } else {
 
433
        status = PJ_STATUS_FROM_STUN_CODE(err_attr->err_code);
 
434
    }
 
435
 
 
436
    /* Call callback */
 
437
    if (!tsx->complete) {
 
438
        tsx->complete = PJ_TRUE;
 
439
        if (tsx->cb.on_complete) {
 
440
            tsx->cb.on_complete(tsx, status, msg, src_addr, src_addr_len);
 
441
        }
 
442
        /* We might have been destroyed, don't try to access the object */
 
443
    }
 
444
 
 
445
    return PJ_SUCCESS;
 
446
 
 
447
}