~ubuntu-branches/ubuntu/vivid/sflphone/vivid

« back to all changes in this revision

Viewing changes to daemon/libs/pjproject/pjnath/src/pjnath/stun_session.c

  • Committer: Package Import Robot
  • Author(s): Mark Purcell
  • Date: 2013-06-30 11:40:56 UTC
  • mfrom: (4.1.18 saucy-proposed)
  • Revision ID: package-import@ubuntu.com-20130630114056-0np50jkyqo6vnmii
Tags: 1.2.3-2
* changeset_r92d62cfc54732bbbcfff2b1d36c096b120b981a5.diff 
  - fixes automatic endian detection 
* Update Vcs: fixes vcs-field-not-canonical

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* $Id: stun_session.c 3553 2011-05-05 06:14:19Z nanang $ */
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_session.h>
21
 
#include <pjnath/errno.h>
22
 
#include <pjlib.h>
23
 
 
24
 
struct pj_stun_session
25
 
{
26
 
    pj_stun_config      *cfg;
27
 
    pj_pool_t           *pool;
28
 
    pj_lock_t           *lock;
29
 
    pj_bool_t            delete_lock;
30
 
    pj_stun_session_cb   cb;
31
 
    void                *user_data;
32
 
 
33
 
    pj_atomic_t         *busy;
34
 
    pj_bool_t            destroy_request;
35
 
 
36
 
    pj_bool_t            use_fingerprint;
37
 
 
38
 
    pj_pool_t           *rx_pool;
39
 
 
40
 
#if PJ_LOG_MAX_LEVEL >= 5
41
 
    char                 dump_buf[1000];
42
 
#endif
43
 
    unsigned             log_flag;
44
 
 
45
 
    pj_stun_auth_type    auth_type;
46
 
    pj_stun_auth_cred    cred;
47
 
    int                  auth_retry;
48
 
    pj_str_t             next_nonce;
49
 
    pj_str_t             server_realm;
50
 
 
51
 
    pj_str_t             srv_name;
52
 
 
53
 
    pj_stun_tx_data      pending_request_list;
54
 
    pj_stun_tx_data      cached_response_list;
55
 
};
56
 
 
57
 
#define SNAME(s_)                   ((s_)->pool->obj_name)
58
 
 
59
 
#if PJ_LOG_MAX_LEVEL >= 5
60
 
#   define TRACE_(expr)             PJ_LOG(5,expr)
61
 
#else
62
 
#   define TRACE_(expr)
63
 
#endif
64
 
 
65
 
#define LOG_ERR_(sess,title,rc) pjnath_perror(sess->pool->obj_name,title,rc)
66
 
 
67
 
#define TDATA_POOL_SIZE             PJNATH_POOL_LEN_STUN_TDATA
68
 
#define TDATA_POOL_INC              PJNATH_POOL_INC_STUN_TDATA
69
 
 
70
 
 
71
 
static void stun_tsx_on_complete(pj_stun_client_tsx *tsx,
72
 
                                 pj_status_t status, 
73
 
                                 const pj_stun_msg *response,
74
 
                                 const pj_sockaddr_t *src_addr,
75
 
                                 unsigned src_addr_len);
76
 
static pj_status_t stun_tsx_on_send_msg(pj_stun_client_tsx *tsx,
77
 
                                        const void *stun_pkt,
78
 
                                        pj_size_t pkt_size);
79
 
static void stun_tsx_on_destroy(pj_stun_client_tsx *tsx);
80
 
 
81
 
static pj_stun_tsx_cb tsx_cb = 
82
 
{
83
 
    &stun_tsx_on_complete,
84
 
    &stun_tsx_on_send_msg,
85
 
    &stun_tsx_on_destroy
86
 
};
87
 
 
88
 
 
89
 
static pj_status_t tsx_add(pj_stun_session *sess,
90
 
                           pj_stun_tx_data *tdata)
91
 
{
92
 
    pj_list_push_front(&sess->pending_request_list, tdata);
93
 
    return PJ_SUCCESS;
94
 
}
95
 
 
96
 
static pj_status_t tsx_erase(pj_stun_session *sess,
97
 
                             pj_stun_tx_data *tdata)
98
 
{
99
 
    PJ_UNUSED_ARG(sess);
100
 
    pj_list_erase(tdata);
101
 
    return PJ_SUCCESS;
102
 
}
103
 
 
104
 
static pj_stun_tx_data* tsx_lookup(pj_stun_session *sess,
105
 
                                   const pj_stun_msg *msg)
106
 
{
107
 
    pj_stun_tx_data *tdata;
108
 
 
109
 
    tdata = sess->pending_request_list.next;
110
 
    while (tdata != &sess->pending_request_list) {
111
 
        pj_assert(sizeof(tdata->msg_key)==sizeof(msg->hdr.tsx_id));
112
 
        if (tdata->msg_magic == msg->hdr.magic &&
113
 
            pj_memcmp(tdata->msg_key, msg->hdr.tsx_id, 
114
 
                      sizeof(msg->hdr.tsx_id))==0)
115
 
        {
116
 
            return tdata;
117
 
        }
118
 
        tdata = tdata->next;
119
 
    }
120
 
 
121
 
    return NULL;
122
 
}
123
 
 
124
 
static pj_status_t create_tdata(pj_stun_session *sess,
125
 
                                pj_stun_tx_data **p_tdata)
126
 
{
127
 
    pj_pool_t *pool;
128
 
    pj_stun_tx_data *tdata;
129
 
 
130
 
    /* Create pool and initialize basic tdata attributes */
131
 
    pool = pj_pool_create(sess->cfg->pf, "tdata%p", 
132
 
                          TDATA_POOL_SIZE, TDATA_POOL_INC, NULL);
133
 
    PJ_ASSERT_RETURN(pool, PJ_ENOMEM);
134
 
 
135
 
    tdata = PJ_POOL_ZALLOC_T(pool, pj_stun_tx_data);
136
 
    tdata->pool = pool;
137
 
    tdata->sess = sess;
138
 
 
139
 
    pj_list_init(tdata);
140
 
 
141
 
    *p_tdata = tdata;
142
 
 
143
 
    return PJ_SUCCESS;
144
 
}
145
 
 
146
 
static void stun_tsx_on_destroy(pj_stun_client_tsx *tsx)
147
 
{
148
 
    pj_stun_tx_data *tdata;
149
 
 
150
 
    tdata = (pj_stun_tx_data*) pj_stun_client_tsx_get_data(tsx);
151
 
    tsx_erase(tdata->sess, tdata);
152
 
 
153
 
    pj_stun_client_tsx_destroy(tsx);
154
 
    pj_pool_release(tdata->pool);
155
 
}
156
 
 
157
 
static void destroy_tdata(pj_stun_tx_data *tdata, pj_bool_t force)
158
 
{
159
 
    if (tdata->res_timer.id != PJ_FALSE) {
160
 
        pj_timer_heap_cancel(tdata->sess->cfg->timer_heap, 
161
 
                             &tdata->res_timer);
162
 
        tdata->res_timer.id = PJ_FALSE;
163
 
        pj_list_erase(tdata);
164
 
    }
165
 
 
166
 
    if (force) {
167
 
        if (tdata->client_tsx) {
168
 
            tsx_erase(tdata->sess, tdata);
169
 
            pj_stun_client_tsx_destroy(tdata->client_tsx);
170
 
        }
171
 
        pj_pool_release(tdata->pool);
172
 
 
173
 
    } else {
174
 
        if (tdata->client_tsx) {
175
 
            pj_time_val delay = {2, 0};
176
 
            pj_stun_client_tsx_schedule_destroy(tdata->client_tsx, &delay);
177
 
 
178
 
        } else {
179
 
            pj_pool_release(tdata->pool);
180
 
        }
181
 
    }
182
 
}
183
 
 
184
 
/*
185
 
 * Destroy the transmit data.
186
 
 */
187
 
PJ_DEF(void) pj_stun_msg_destroy_tdata( pj_stun_session *sess,
188
 
                                        pj_stun_tx_data *tdata)
189
 
{
190
 
    PJ_UNUSED_ARG(sess);
191
 
    destroy_tdata(tdata, PJ_FALSE);
192
 
}
193
 
 
194
 
 
195
 
/* Timer callback to be called when it's time to destroy response cache */
196
 
static void on_cache_timeout(pj_timer_heap_t *timer_heap,
197
 
                             struct pj_timer_entry *entry)
198
 
{
199
 
    pj_stun_tx_data *tdata;
200
 
 
201
 
    PJ_UNUSED_ARG(timer_heap);
202
 
 
203
 
    entry->id = PJ_FALSE;
204
 
    tdata = (pj_stun_tx_data*) entry->user_data;
205
 
 
206
 
    PJ_LOG(5,(SNAME(tdata->sess), "Response cache deleted"));
207
 
 
208
 
    pj_list_erase(tdata);
209
 
    pj_stun_msg_destroy_tdata(tdata->sess, tdata);
210
 
}
211
 
 
212
 
static pj_status_t apply_msg_options(pj_stun_session *sess,
213
 
                                     pj_pool_t *pool,
214
 
                                     const pj_stun_req_cred_info *auth_info,
215
 
                                     pj_stun_msg *msg)
216
 
{
217
 
    pj_status_t status = 0;
218
 
    pj_str_t realm, username, nonce, auth_key;
219
 
 
220
 
    /* If the agent is sending a request, it SHOULD add a SOFTWARE attribute
221
 
     * to the request. The server SHOULD include a SOFTWARE attribute in all 
222
 
     * responses.
223
 
     *
224
 
     * If magic value is not PJ_STUN_MAGIC, only apply the attribute for
225
 
     * responses.
226
 
     */
227
 
    if (sess->srv_name.slen && 
228
 
        pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_SOFTWARE, 0)==NULL &&
229
 
        (PJ_STUN_IS_RESPONSE(msg->hdr.type) ||
230
 
         (PJ_STUN_IS_REQUEST(msg->hdr.type) && msg->hdr.magic==PJ_STUN_MAGIC))) 
231
 
    {
232
 
        pj_stun_msg_add_string_attr(pool, msg, PJ_STUN_ATTR_SOFTWARE,
233
 
                                    &sess->srv_name);
234
 
    }
235
 
 
236
 
    if (pj_stun_auth_valid_for_msg(msg) && auth_info) {
237
 
        realm = auth_info->realm;
238
 
        username = auth_info->username;
239
 
        nonce = auth_info->nonce;
240
 
        auth_key = auth_info->auth_key;
241
 
    } else {
242
 
        realm.slen = username.slen = nonce.slen = auth_key.slen = 0;
243
 
    }
244
 
 
245
 
    /* Create and add USERNAME attribute if needed */
246
 
    if (username.slen && PJ_STUN_IS_REQUEST(msg->hdr.type)) {
247
 
        status = pj_stun_msg_add_string_attr(pool, msg,
248
 
                                             PJ_STUN_ATTR_USERNAME,
249
 
                                             &username);
250
 
        PJ_ASSERT_RETURN(status==PJ_SUCCESS, status);
251
 
    }
252
 
 
253
 
    /* Add REALM only when long term credential is used */
254
 
    if (realm.slen &&  PJ_STUN_IS_REQUEST(msg->hdr.type)) {
255
 
        status = pj_stun_msg_add_string_attr(pool, msg,
256
 
                                            PJ_STUN_ATTR_REALM,
257
 
                                            &realm);
258
 
        PJ_ASSERT_RETURN(status==PJ_SUCCESS, status);
259
 
    }
260
 
 
261
 
    /* Add NONCE when desired */
262
 
    if (nonce.slen && 
263
 
        (PJ_STUN_IS_REQUEST(msg->hdr.type) ||
264
 
         PJ_STUN_IS_ERROR_RESPONSE(msg->hdr.type))) 
265
 
    {
266
 
        status = pj_stun_msg_add_string_attr(pool, msg,
267
 
                                            PJ_STUN_ATTR_NONCE,
268
 
                                            &nonce);
269
 
    }
270
 
 
271
 
    /* Add MESSAGE-INTEGRITY attribute */
272
 
    if (username.slen && auth_key.slen) {
273
 
        status = pj_stun_msg_add_msgint_attr(pool, msg);
274
 
        PJ_ASSERT_RETURN(status==PJ_SUCCESS, status);
275
 
    }
276
 
 
277
 
 
278
 
    /* Add FINGERPRINT attribute if necessary */
279
 
    if (sess->use_fingerprint) {
280
 
        status = pj_stun_msg_add_uint_attr(pool, msg, 
281
 
                                          PJ_STUN_ATTR_FINGERPRINT, 0);
282
 
        PJ_ASSERT_RETURN(status==PJ_SUCCESS, status);
283
 
    }
284
 
 
285
 
    return PJ_SUCCESS;
286
 
}
287
 
 
288
 
static pj_status_t handle_auth_challenge(pj_stun_session *sess,
289
 
                                         const pj_stun_tx_data *request,
290
 
                                         const pj_stun_msg *response,
291
 
                                         const pj_sockaddr_t *src_addr,
292
 
                                         unsigned src_addr_len,
293
 
                                         pj_bool_t *notify_user)
294
 
{
295
 
    const pj_stun_errcode_attr *ea;
296
 
 
297
 
    *notify_user = PJ_TRUE;
298
 
 
299
 
    if (response==NULL)
300
 
        return PJ_SUCCESS;
301
 
 
302
 
    if (sess->auth_type != PJ_STUN_AUTH_LONG_TERM)
303
 
        return PJ_SUCCESS;
304
 
    
305
 
    if (!PJ_STUN_IS_ERROR_RESPONSE(response->hdr.type)) {
306
 
        sess->auth_retry = 0;
307
 
        return PJ_SUCCESS;
308
 
    }
309
 
 
310
 
    ea = (const pj_stun_errcode_attr*)
311
 
         pj_stun_msg_find_attr(response, PJ_STUN_ATTR_ERROR_CODE, 0);
312
 
    if (!ea) {
313
 
        PJ_LOG(4,(SNAME(sess), "Invalid error response: no ERROR-CODE"
314
 
                  " attribute"));
315
 
        *notify_user = PJ_FALSE;
316
 
        return PJNATH_EINSTUNMSG;
317
 
    }
318
 
 
319
 
    if (ea->err_code == PJ_STUN_SC_UNAUTHORIZED || 
320
 
        ea->err_code == PJ_STUN_SC_STALE_NONCE)
321
 
    {
322
 
        const pj_stun_nonce_attr *anonce;
323
 
        const pj_stun_realm_attr *arealm;
324
 
        pj_stun_tx_data *tdata;
325
 
        unsigned i;
326
 
        pj_status_t status;
327
 
 
328
 
        anonce = (const pj_stun_nonce_attr*)
329
 
                 pj_stun_msg_find_attr(response, PJ_STUN_ATTR_NONCE, 0);
330
 
        if (!anonce) {
331
 
            PJ_LOG(4,(SNAME(sess), "Invalid response: missing NONCE"));
332
 
            *notify_user = PJ_FALSE;
333
 
            return PJNATH_EINSTUNMSG;
334
 
        }
335
 
 
336
 
        /* Bail out if we've supplied the correct nonce */
337
 
        if (pj_strcmp(&anonce->value, &sess->next_nonce)==0) {
338
 
            return PJ_SUCCESS;
339
 
        }
340
 
 
341
 
        /* Bail out if we've tried too many */
342
 
        if (++sess->auth_retry > 3) {
343
 
            PJ_LOG(4,(SNAME(sess), "Error: authentication failed (too "
344
 
                      "many retries)"));
345
 
            return PJ_STATUS_FROM_STUN_CODE(401);
346
 
        }
347
 
 
348
 
        /* Save next_nonce */
349
 
        pj_strdup(sess->pool, &sess->next_nonce, &anonce->value);
350
 
 
351
 
        /* Copy the realm from the response */
352
 
        arealm = (pj_stun_realm_attr*)
353
 
                 pj_stun_msg_find_attr(response, PJ_STUN_ATTR_REALM, 0);
354
 
        if (arealm) {
355
 
            pj_strdup(sess->pool, &sess->server_realm, &arealm->value);
356
 
        }
357
 
 
358
 
        /* Create new request */
359
 
        status = pj_stun_session_create_req(sess, request->msg->hdr.type,
360
 
                                            request->msg->hdr.magic,
361
 
                                            NULL, &tdata);
362
 
        if (status != PJ_SUCCESS)
363
 
            return status;
364
 
 
365
 
        /* Duplicate all the attributes in the old request, except
366
 
         * USERNAME, REALM, M-I, and NONCE, which will be filled in
367
 
         * later.
368
 
         */
369
 
        for (i=0; i<request->msg->attr_count; ++i) {
370
 
            const pj_stun_attr_hdr *asrc = request->msg->attr[i];
371
 
 
372
 
            if (asrc->type == PJ_STUN_ATTR_USERNAME ||
373
 
                asrc->type == PJ_STUN_ATTR_REALM ||
374
 
                asrc->type == PJ_STUN_ATTR_MESSAGE_INTEGRITY ||
375
 
                asrc->type == PJ_STUN_ATTR_NONCE)
376
 
            {
377
 
                continue;
378
 
            }
379
 
 
380
 
            tdata->msg->attr[tdata->msg->attr_count++] = 
381
 
                pj_stun_attr_clone(tdata->pool, asrc);
382
 
        }
383
 
 
384
 
        /* Will retry the request with authentication, no need to
385
 
         * notify user.
386
 
         */
387
 
        *notify_user = PJ_FALSE;
388
 
 
389
 
        PJ_LOG(4,(SNAME(sess), "Retrying request with new authentication"));
390
 
 
391
 
        /* Retry the request */
392
 
        status = pj_stun_session_send_msg(sess, request->token, PJ_TRUE, 
393
 
                                          request->retransmit, src_addr, 
394
 
                                          src_addr_len, tdata);
395
 
 
396
 
    } else {
397
 
        sess->auth_retry = 0;
398
 
    }
399
 
 
400
 
    return PJ_SUCCESS;
401
 
}
402
 
 
403
 
static void stun_tsx_on_complete(pj_stun_client_tsx *tsx,
404
 
                                 pj_status_t status, 
405
 
                                 const pj_stun_msg *response,
406
 
                                 const pj_sockaddr_t *src_addr,
407
 
                                 unsigned src_addr_len)
408
 
{
409
 
    pj_stun_session *sess;
410
 
    pj_bool_t notify_user = PJ_TRUE;
411
 
    pj_stun_tx_data *tdata;
412
 
 
413
 
    tdata = (pj_stun_tx_data*) pj_stun_client_tsx_get_data(tsx);
414
 
    sess = tdata->sess;
415
 
 
416
 
    /* Lock the session and prevent user from destroying us in the callback */
417
 
    pj_atomic_inc(sess->busy);
418
 
    pj_lock_acquire(sess->lock);
419
 
 
420
 
    /* Handle authentication challenge */
421
 
    handle_auth_challenge(sess, tdata, response, src_addr,
422
 
                          src_addr_len, &notify_user);
423
 
 
424
 
    if (notify_user && sess->cb.on_request_complete) {
425
 
        (*sess->cb.on_request_complete)(sess, status, tdata->token, tdata, 
426
 
                                        response, src_addr, src_addr_len);
427
 
    }
428
 
 
429
 
    /* Destroy the transmit data. This will remove the transaction
430
 
     * from the pending list too. 
431
 
     */
432
 
    pj_stun_msg_destroy_tdata(sess, tdata);
433
 
    tdata = NULL;
434
 
 
435
 
    pj_lock_release(sess->lock);
436
 
 
437
 
    if (pj_atomic_dec_and_get(sess->busy)==0 && sess->destroy_request) {
438
 
        pj_stun_session_destroy(sess);
439
 
        return;
440
 
    }
441
 
}
442
 
 
443
 
static pj_status_t stun_tsx_on_send_msg(pj_stun_client_tsx *tsx,
444
 
                                        const void *stun_pkt,
445
 
                                        pj_size_t pkt_size)
446
 
{
447
 
    pj_stun_tx_data *tdata;
448
 
    pj_stun_session *sess;
449
 
    pj_status_t status;
450
 
 
451
 
    tdata = (pj_stun_tx_data*) pj_stun_client_tsx_get_data(tsx);
452
 
    sess = tdata->sess;
453
 
 
454
 
    /* Lock the session and prevent user from destroying us in the callback */
455
 
    pj_atomic_inc(sess->busy);
456
 
    pj_lock_acquire(sess->lock);
457
 
    
458
 
    status = sess->cb.on_send_msg(tdata->sess, tdata->token, stun_pkt, 
459
 
                                  pkt_size, tdata->dst_addr, 
460
 
                                  tdata->addr_len);
461
 
    pj_lock_release(sess->lock);
462
 
 
463
 
    if (pj_atomic_dec_and_get(sess->busy)==0 && sess->destroy_request) {
464
 
        pj_stun_session_destroy(sess);
465
 
        return PJNATH_ESTUNDESTROYED;
466
 
    } else {
467
 
        return status;
468
 
    }
469
 
}
470
 
 
471
 
/* **************************************************************************/
472
 
 
473
 
PJ_DEF(pj_status_t) pj_stun_session_create( pj_stun_config *cfg,
474
 
                                            const char *name,
475
 
                                            const pj_stun_session_cb *cb,
476
 
                                            pj_bool_t fingerprint,
477
 
                                            pj_stun_session **p_sess)
478
 
{
479
 
    pj_pool_t   *pool;
480
 
    pj_stun_session *sess;
481
 
    pj_status_t status;
482
 
 
483
 
    PJ_ASSERT_RETURN(cfg && cb && p_sess, PJ_EINVAL);
484
 
 
485
 
    if (name==NULL)
486
 
        name = "stuse%p";
487
 
 
488
 
    pool = pj_pool_create(cfg->pf, name, PJNATH_POOL_LEN_STUN_SESS, 
489
 
                          PJNATH_POOL_INC_STUN_SESS, NULL);
490
 
    PJ_ASSERT_RETURN(pool, PJ_ENOMEM);
491
 
 
492
 
    sess = PJ_POOL_ZALLOC_T(pool, pj_stun_session);
493
 
    sess->cfg = cfg;
494
 
    sess->pool = pool;
495
 
    pj_memcpy(&sess->cb, cb, sizeof(*cb));
496
 
    sess->use_fingerprint = fingerprint;
497
 
    sess->log_flag = 0xFFFF;
498
 
    
499
 
    sess->srv_name.ptr = (char*) pj_pool_alloc(pool, 32);
500
 
    sess->srv_name.slen = pj_ansi_snprintf(sess->srv_name.ptr, 32,
501
 
                                           "pjnath-%s", pj_get_version());
502
 
 
503
 
    sess->rx_pool = pj_pool_create(sess->cfg->pf, name, 
504
 
                                   PJNATH_POOL_LEN_STUN_TDATA, 
505
 
                                   PJNATH_POOL_INC_STUN_TDATA, NULL);
506
 
 
507
 
    pj_list_init(&sess->pending_request_list);
508
 
    pj_list_init(&sess->cached_response_list);
509
 
 
510
 
    status = pj_lock_create_recursive_mutex(pool, name, &sess->lock);
511
 
    if (status != PJ_SUCCESS) {
512
 
        pj_pool_release(pool);
513
 
        return status;
514
 
    }
515
 
    sess->delete_lock = PJ_TRUE;
516
 
 
517
 
    status = pj_atomic_create(pool, 0, &sess->busy);
518
 
    if (status != PJ_SUCCESS) {
519
 
        pj_lock_destroy(sess->lock);
520
 
        pj_pool_release(pool);
521
 
        return status;
522
 
    }
523
 
 
524
 
    *p_sess = sess;
525
 
 
526
 
    return PJ_SUCCESS;
527
 
}
528
 
 
529
 
PJ_DEF(pj_status_t) pj_stun_session_destroy(pj_stun_session *sess)
530
 
{
531
 
    PJ_ASSERT_RETURN(sess, PJ_EINVAL);
532
 
 
533
 
    pj_lock_acquire(sess->lock);
534
 
 
535
 
    /* Can't destroy if we're in a callback */
536
 
    sess->destroy_request = PJ_TRUE;
537
 
    if (pj_atomic_get(sess->busy)) {
538
 
        pj_lock_release(sess->lock);
539
 
        return PJ_EPENDING;
540
 
    }
541
 
 
542
 
    while (!pj_list_empty(&sess->pending_request_list)) {
543
 
        pj_stun_tx_data *tdata = sess->pending_request_list.next;
544
 
        destroy_tdata(tdata, PJ_TRUE);
545
 
    }
546
 
 
547
 
    while (!pj_list_empty(&sess->cached_response_list)) {
548
 
        pj_stun_tx_data *tdata = sess->cached_response_list.next;
549
 
        destroy_tdata(tdata, PJ_TRUE);
550
 
    }
551
 
    pj_lock_release(sess->lock);
552
 
 
553
 
    if (sess->delete_lock) {
554
 
        pj_lock_destroy(sess->lock);
555
 
    }
556
 
 
557
 
    if (sess->rx_pool) {
558
 
        pj_pool_release(sess->rx_pool);
559
 
        sess->rx_pool = NULL;
560
 
    }
561
 
 
562
 
    pj_pool_release(sess->pool);
563
 
 
564
 
    return PJ_SUCCESS;
565
 
}
566
 
 
567
 
 
568
 
PJ_DEF(pj_status_t) pj_stun_session_set_user_data( pj_stun_session *sess,
569
 
                                                   void *user_data)
570
 
{
571
 
    PJ_ASSERT_RETURN(sess, PJ_EINVAL);
572
 
    pj_lock_acquire(sess->lock);
573
 
    sess->user_data = user_data;
574
 
    pj_lock_release(sess->lock);
575
 
    return PJ_SUCCESS;
576
 
}
577
 
 
578
 
PJ_DEF(void*) pj_stun_session_get_user_data(pj_stun_session *sess)
579
 
{
580
 
    PJ_ASSERT_RETURN(sess, NULL);
581
 
    return sess->user_data;
582
 
}
583
 
 
584
 
PJ_DEF(pj_status_t) pj_stun_session_set_lock( pj_stun_session *sess,
585
 
                                              pj_lock_t *lock,
586
 
                                              pj_bool_t auto_del)
587
 
{
588
 
    pj_lock_t *old_lock = sess->lock;
589
 
    pj_bool_t old_del;
590
 
 
591
 
    PJ_ASSERT_RETURN(sess && lock, PJ_EINVAL);
592
 
 
593
 
    pj_lock_acquire(old_lock);
594
 
    sess->lock = lock;
595
 
    old_del = sess->delete_lock;
596
 
    sess->delete_lock = auto_del;
597
 
    pj_lock_release(old_lock);
598
 
 
599
 
    if (old_lock)
600
 
        pj_lock_destroy(old_lock);
601
 
 
602
 
    return PJ_SUCCESS;
603
 
}
604
 
 
605
 
PJ_DEF(pj_status_t) pj_stun_session_set_software_name(pj_stun_session *sess,
606
 
                                                      const pj_str_t *sw)
607
 
{
608
 
    PJ_ASSERT_RETURN(sess, PJ_EINVAL);
609
 
    if (sw && sw->slen)
610
 
        pj_strdup(sess->pool, &sess->srv_name, sw);
611
 
    else
612
 
        sess->srv_name.slen = 0;
613
 
    return PJ_SUCCESS;
614
 
}
615
 
 
616
 
PJ_DEF(pj_status_t) pj_stun_session_set_credential(pj_stun_session *sess,
617
 
                                                 pj_stun_auth_type auth_type,
618
 
                                                 const pj_stun_auth_cred *cred)
619
 
{
620
 
    PJ_ASSERT_RETURN(sess, PJ_EINVAL);
621
 
 
622
 
    sess->auth_type = auth_type;
623
 
    if (cred) {
624
 
        pj_stun_auth_cred_dup(sess->pool, &sess->cred, cred);
625
 
    } else {
626
 
        sess->auth_type = PJ_STUN_AUTH_NONE;
627
 
        pj_bzero(&sess->cred, sizeof(sess->cred));
628
 
    }
629
 
 
630
 
    return PJ_SUCCESS;
631
 
}
632
 
 
633
 
PJ_DEF(void) pj_stun_session_set_log( pj_stun_session *sess,
634
 
                                      unsigned flags)
635
 
{
636
 
    PJ_ASSERT_ON_FAIL(sess, return);
637
 
    sess->log_flag = flags;
638
 
}
639
 
 
640
 
PJ_DEF(pj_bool_t) pj_stun_session_use_fingerprint(pj_stun_session *sess,
641
 
                                                  pj_bool_t use)
642
 
{
643
 
    pj_bool_t old_use;
644
 
 
645
 
    PJ_ASSERT_RETURN(sess, PJ_FALSE);
646
 
 
647
 
    old_use = sess->use_fingerprint;
648
 
    sess->use_fingerprint = use;
649
 
    return old_use;
650
 
}
651
 
 
652
 
static pj_status_t get_auth(pj_stun_session *sess,
653
 
                            pj_stun_tx_data *tdata)
654
 
{
655
 
    if (sess->cred.type == PJ_STUN_AUTH_CRED_STATIC) {
656
 
        //tdata->auth_info.realm = sess->cred.data.static_cred.realm;
657
 
        tdata->auth_info.realm = sess->server_realm;
658
 
        tdata->auth_info.username = sess->cred.data.static_cred.username;
659
 
        tdata->auth_info.nonce = sess->cred.data.static_cred.nonce;
660
 
 
661
 
        pj_stun_create_key(tdata->pool, &tdata->auth_info.auth_key, 
662
 
                           &tdata->auth_info.realm,
663
 
                           &tdata->auth_info.username,
664
 
                           sess->cred.data.static_cred.data_type,
665
 
                           &sess->cred.data.static_cred.data);
666
 
 
667
 
    } else if (sess->cred.type == PJ_STUN_AUTH_CRED_DYNAMIC) {
668
 
        pj_str_t password;
669
 
        void *user_data = sess->cred.data.dyn_cred.user_data;
670
 
        pj_stun_passwd_type data_type = PJ_STUN_PASSWD_PLAIN;
671
 
        pj_status_t rc;
672
 
 
673
 
        rc = (*sess->cred.data.dyn_cred.get_cred)(tdata->msg, user_data, 
674
 
                                                  tdata->pool,
675
 
                                                  &tdata->auth_info.realm, 
676
 
                                                  &tdata->auth_info.username,
677
 
                                                  &tdata->auth_info.nonce, 
678
 
                                                  &data_type, &password);
679
 
        if (rc != PJ_SUCCESS)
680
 
            return rc;
681
 
 
682
 
        pj_stun_create_key(tdata->pool, &tdata->auth_info.auth_key, 
683
 
                           &tdata->auth_info.realm, &tdata->auth_info.username,
684
 
                           data_type, &password);
685
 
 
686
 
    } else {
687
 
        pj_assert(!"Unknown credential type");
688
 
        return PJ_EBUG;
689
 
    }
690
 
 
691
 
    return PJ_SUCCESS;
692
 
}
693
 
 
694
 
PJ_DEF(pj_status_t) pj_stun_session_create_req(pj_stun_session *sess,
695
 
                                               int method,
696
 
                                               pj_uint32_t magic,
697
 
                                               const pj_uint8_t tsx_id[12],
698
 
                                               pj_stun_tx_data **p_tdata)
699
 
{
700
 
    pj_stun_tx_data *tdata = NULL;
701
 
    pj_status_t status;
702
 
 
703
 
    PJ_ASSERT_RETURN(sess && p_tdata, PJ_EINVAL);
704
 
 
705
 
    status = create_tdata(sess, &tdata);
706
 
    if (status != PJ_SUCCESS)
707
 
        return status;
708
 
 
709
 
    /* Create STUN message */
710
 
    status = pj_stun_msg_create(tdata->pool, method,  magic, 
711
 
                                tsx_id, &tdata->msg);
712
 
    if (status != PJ_SUCCESS) {
713
 
        pj_pool_release(tdata->pool);
714
 
        return status;
715
 
    }
716
 
 
717
 
    /* copy the request's transaction ID as the transaction key. */
718
 
    pj_assert(sizeof(tdata->msg_key)==sizeof(tdata->msg->hdr.tsx_id));
719
 
    tdata->msg_magic = tdata->msg->hdr.magic;
720
 
    pj_memcpy(tdata->msg_key, tdata->msg->hdr.tsx_id,
721
 
              sizeof(tdata->msg->hdr.tsx_id));
722
 
 
723
 
    
724
 
    /* Get authentication information for the request */
725
 
    if (sess->auth_type == PJ_STUN_AUTH_NONE) {
726
 
        /* No authentication */
727
 
 
728
 
    } else if (sess->auth_type == PJ_STUN_AUTH_SHORT_TERM) {
729
 
        /* MUST put authentication in request */
730
 
        status = get_auth(sess, tdata);
731
 
        if (status != PJ_SUCCESS) {
732
 
            pj_pool_release(tdata->pool);
733
 
            return status;
734
 
        }
735
 
 
736
 
    } else if (sess->auth_type == PJ_STUN_AUTH_LONG_TERM) {
737
 
        /* Only put authentication information if we've received
738
 
         * response from server.
739
 
         */
740
 
        if (sess->next_nonce.slen != 0) {
741
 
            status = get_auth(sess, tdata);
742
 
            if (status != PJ_SUCCESS) {
743
 
                pj_pool_release(tdata->pool);
744
 
                return status;
745
 
            }
746
 
            tdata->auth_info.nonce = sess->next_nonce;
747
 
            tdata->auth_info.realm = sess->server_realm;
748
 
        }
749
 
 
750
 
    } else {
751
 
        pj_assert(!"Invalid authentication type");
752
 
        pj_pool_release(tdata->pool);
753
 
        return PJ_EBUG;
754
 
    }
755
 
 
756
 
    *p_tdata = tdata;
757
 
    return PJ_SUCCESS;
758
 
}
759
 
 
760
 
PJ_DEF(pj_status_t) pj_stun_session_create_ind(pj_stun_session *sess,
761
 
                                               int msg_type,
762
 
                                               pj_stun_tx_data **p_tdata)
763
 
{
764
 
    pj_stun_tx_data *tdata = NULL;
765
 
    pj_status_t status;
766
 
 
767
 
    PJ_ASSERT_RETURN(sess && p_tdata, PJ_EINVAL);
768
 
 
769
 
    status = create_tdata(sess, &tdata);
770
 
    if (status != PJ_SUCCESS)
771
 
        return status;
772
 
 
773
 
    /* Create STUN message */
774
 
    msg_type |= PJ_STUN_INDICATION_BIT;
775
 
    status = pj_stun_msg_create(tdata->pool, msg_type,  PJ_STUN_MAGIC, 
776
 
                                NULL, &tdata->msg);
777
 
    if (status != PJ_SUCCESS) {
778
 
        pj_pool_release(tdata->pool);
779
 
        return status;
780
 
    }
781
 
 
782
 
    *p_tdata = tdata;
783
 
    return PJ_SUCCESS;
784
 
}
785
 
 
786
 
/*
787
 
 * Create a STUN response message.
788
 
 */
789
 
PJ_DEF(pj_status_t) pj_stun_session_create_res( pj_stun_session *sess,
790
 
                                                const pj_stun_rx_data *rdata,
791
 
                                                unsigned err_code,
792
 
                                                const pj_str_t *err_msg,
793
 
                                                pj_stun_tx_data **p_tdata)
794
 
{
795
 
    pj_status_t status;
796
 
    pj_stun_tx_data *tdata = NULL;
797
 
 
798
 
    status = create_tdata(sess, &tdata);
799
 
    if (status != PJ_SUCCESS)
800
 
        return status;
801
 
 
802
 
    /* Create STUN response message */
803
 
    status = pj_stun_msg_create_response(tdata->pool, rdata->msg, 
804
 
                                         err_code, err_msg, &tdata->msg);
805
 
    if (status != PJ_SUCCESS) {
806
 
        pj_pool_release(tdata->pool);
807
 
        return status;
808
 
    }
809
 
 
810
 
    /* copy the request's transaction ID as the transaction key. */
811
 
    pj_assert(sizeof(tdata->msg_key)==sizeof(rdata->msg->hdr.tsx_id));
812
 
    tdata->msg_magic = rdata->msg->hdr.magic;
813
 
    pj_memcpy(tdata->msg_key, rdata->msg->hdr.tsx_id, 
814
 
              sizeof(rdata->msg->hdr.tsx_id));
815
 
 
816
 
    /* copy the credential found in the request */
817
 
    pj_stun_req_cred_info_dup(tdata->pool, &tdata->auth_info, &rdata->info);
818
 
 
819
 
    *p_tdata = tdata;
820
 
 
821
 
    return PJ_SUCCESS;
822
 
}
823
 
 
824
 
 
825
 
/* Print outgoing message to log */
826
 
static void dump_tx_msg(pj_stun_session *sess, const pj_stun_msg *msg,
827
 
                        unsigned pkt_size, const pj_sockaddr_t *addr)
828
 
{
829
 
    char dst_name[PJ_INET6_ADDRSTRLEN+10];
830
 
    
831
 
    if ((PJ_STUN_IS_REQUEST(msg->hdr.type) && 
832
 
         (sess->log_flag & PJ_STUN_SESS_LOG_TX_REQ)==0) ||
833
 
        (PJ_STUN_IS_RESPONSE(msg->hdr.type) &&
834
 
         (sess->log_flag & PJ_STUN_SESS_LOG_TX_RES)==0) ||
835
 
        (PJ_STUN_IS_INDICATION(msg->hdr.type) &&
836
 
         (sess->log_flag & PJ_STUN_SESS_LOG_TX_IND)==0))
837
 
    {
838
 
        return;
839
 
    }
840
 
 
841
 
    pj_sockaddr_print(addr, dst_name, sizeof(dst_name), 3);
842
 
 
843
 
    PJ_LOG(5,(SNAME(sess), 
844
 
              "TX %d bytes STUN message to %s:\n"
845
 
              "--- begin STUN message ---\n"
846
 
              "%s"
847
 
              "--- end of STUN message ---\n",
848
 
              pkt_size, dst_name, 
849
 
              pj_stun_msg_dump(msg, sess->dump_buf, sizeof(sess->dump_buf), 
850
 
                               NULL)));
851
 
 
852
 
}
853
 
 
854
 
 
855
 
PJ_DEF(pj_status_t) pj_stun_session_send_msg( pj_stun_session *sess,
856
 
                                              void *token,
857
 
                                              pj_bool_t cache_res,
858
 
                                              pj_bool_t retransmit,
859
 
                                              const pj_sockaddr_t *server,
860
 
                                              unsigned addr_len,
861
 
                                              pj_stun_tx_data *tdata)
862
 
{
863
 
    pj_status_t status;
864
 
 
865
 
    PJ_ASSERT_RETURN(sess && addr_len && server && tdata, PJ_EINVAL);
866
 
 
867
 
    /* Allocate packet */
868
 
    tdata->max_len = PJ_STUN_MAX_PKT_LEN;
869
 
    tdata->pkt = pj_pool_alloc(tdata->pool, tdata->max_len);
870
 
 
871
 
    tdata->token = token;
872
 
    tdata->retransmit = retransmit;
873
 
 
874
 
    /* Lock the session and prevent user from destroying us in the callback */
875
 
    pj_atomic_inc(sess->busy);
876
 
    pj_lock_acquire(sess->lock);
877
 
 
878
 
    /* Apply options */
879
 
    status = apply_msg_options(sess, tdata->pool, &tdata->auth_info, 
880
 
                               tdata->msg);
881
 
    if (status != PJ_SUCCESS) {
882
 
        pj_stun_msg_destroy_tdata(sess, tdata);
883
 
        LOG_ERR_(sess, "Error applying options", status);
884
 
        goto on_return;
885
 
    }
886
 
 
887
 
    /* Encode message */
888
 
    status = pj_stun_msg_encode(tdata->msg, (pj_uint8_t*)tdata->pkt, 
889
 
                                tdata->max_len, 0, 
890
 
                                &tdata->auth_info.auth_key,
891
 
                                &tdata->pkt_size);
892
 
    if (status != PJ_SUCCESS) {
893
 
        pj_stun_msg_destroy_tdata(sess, tdata);
894
 
        LOG_ERR_(sess, "STUN encode() error", status);
895
 
        goto on_return;
896
 
    }
897
 
 
898
 
    /* Dump packet */
899
 
    dump_tx_msg(sess, tdata->msg, tdata->pkt_size, server);
900
 
 
901
 
    /* If this is a STUN request message, then send the request with
902
 
     * a new STUN client transaction.
903
 
     */
904
 
    if (PJ_STUN_IS_REQUEST(tdata->msg->hdr.type)) {
905
 
 
906
 
        /* Create STUN client transaction */
907
 
        status = pj_stun_client_tsx_create(sess->cfg, tdata->pool, 
908
 
                                           &tsx_cb, &tdata->client_tsx);
909
 
        PJ_ASSERT_RETURN(status==PJ_SUCCESS, status);
910
 
        pj_stun_client_tsx_set_data(tdata->client_tsx, (void*)tdata);
911
 
 
912
 
        /* Save the remote address */
913
 
        tdata->addr_len = addr_len;
914
 
        tdata->dst_addr = server;
915
 
 
916
 
        /* Send the request! */
917
 
        status = pj_stun_client_tsx_send_msg(tdata->client_tsx, retransmit,
918
 
                                             tdata->pkt, tdata->pkt_size);
919
 
        if (status != PJ_SUCCESS && status != PJ_EPENDING) {
920
 
            pj_stun_msg_destroy_tdata(sess, tdata);
921
 
            LOG_ERR_(sess, "Error sending STUN request", status);
922
 
            goto on_return;
923
 
        }
924
 
 
925
 
        /* Add to pending request list */
926
 
        tsx_add(sess, tdata);
927
 
 
928
 
    } else {
929
 
        if (cache_res && 
930
 
            (PJ_STUN_IS_SUCCESS_RESPONSE(tdata->msg->hdr.type) ||
931
 
             PJ_STUN_IS_ERROR_RESPONSE(tdata->msg->hdr.type))) 
932
 
        {
933
 
            /* Requested to keep the response in the cache */
934
 
            pj_time_val timeout;
935
 
            
936
 
            pj_memset(&tdata->res_timer, 0, sizeof(tdata->res_timer));
937
 
            pj_timer_entry_init(&tdata->res_timer, PJ_TRUE, tdata, 
938
 
                                &on_cache_timeout);
939
 
 
940
 
            timeout.sec = sess->cfg->res_cache_msec / 1000;
941
 
            timeout.msec = sess->cfg->res_cache_msec % 1000;
942
 
 
943
 
            status = pj_timer_heap_schedule(sess->cfg->timer_heap, 
944
 
                                            &tdata->res_timer,
945
 
                                            &timeout);
946
 
            if (status != PJ_SUCCESS) {
947
 
                tdata->res_timer.id = PJ_FALSE;
948
 
                pj_stun_msg_destroy_tdata(sess, tdata);
949
 
                LOG_ERR_(sess, "Error scheduling response timer", status);
950
 
                goto on_return;
951
 
            }
952
 
 
953
 
            pj_list_push_back(&sess->cached_response_list, tdata);
954
 
        }
955
 
    
956
 
        /* Otherwise for non-request message, send directly to transport. */
957
 
        status = sess->cb.on_send_msg(sess, token, tdata->pkt, 
958
 
                                      tdata->pkt_size, server, addr_len);
959
 
 
960
 
        if (status != PJ_SUCCESS && status != PJ_EPENDING) {
961
 
            pj_stun_msg_destroy_tdata(sess, tdata);
962
 
            LOG_ERR_(sess, "Error sending STUN request", status);
963
 
            goto on_return;
964
 
        }
965
 
 
966
 
        /* Destroy only when response is not cached*/
967
 
        if (tdata->res_timer.id == 0) {
968
 
            pj_stun_msg_destroy_tdata(sess, tdata);
969
 
        }
970
 
    }
971
 
 
972
 
on_return:
973
 
    pj_lock_release(sess->lock);
974
 
 
975
 
    /* Check if application has called destroy() in the callback */
976
 
    if (pj_atomic_dec_and_get(sess->busy)==0 && sess->destroy_request) {
977
 
        pj_stun_session_destroy(sess);
978
 
        return PJNATH_ESTUNDESTROYED;
979
 
    }
980
 
 
981
 
    return status;
982
 
}
983
 
 
984
 
 
985
 
/*
986
 
 * Create and send STUN response message.
987
 
 */
988
 
PJ_DEF(pj_status_t) pj_stun_session_respond( pj_stun_session *sess, 
989
 
                                             const pj_stun_rx_data *rdata,
990
 
                                             unsigned code, 
991
 
                                             const char *errmsg,
992
 
                                             void *token,
993
 
                                             pj_bool_t cache, 
994
 
                                             const pj_sockaddr_t *dst_addr, 
995
 
                                             unsigned addr_len)
996
 
{
997
 
    pj_status_t status;
998
 
    pj_str_t reason;
999
 
    pj_stun_tx_data *tdata;
1000
 
 
1001
 
    status = pj_stun_session_create_res(sess, rdata, code, 
1002
 
                                        (errmsg?pj_cstr(&reason,errmsg):NULL), 
1003
 
                                        &tdata);
1004
 
    if (status != PJ_SUCCESS)
1005
 
        return status;
1006
 
 
1007
 
    return pj_stun_session_send_msg(sess, token, cache, PJ_FALSE,
1008
 
                                    dst_addr,  addr_len, tdata);
1009
 
}
1010
 
 
1011
 
 
1012
 
/*
1013
 
 * Cancel outgoing STUN transaction. 
1014
 
 */
1015
 
PJ_DEF(pj_status_t) pj_stun_session_cancel_req( pj_stun_session *sess,
1016
 
                                                pj_stun_tx_data *tdata,
1017
 
                                                pj_bool_t notify,
1018
 
                                                pj_status_t notify_status)
1019
 
{
1020
 
    PJ_ASSERT_RETURN(sess && tdata, PJ_EINVAL);
1021
 
    PJ_ASSERT_RETURN(!notify || notify_status!=PJ_SUCCESS, PJ_EINVAL);
1022
 
    PJ_ASSERT_RETURN(PJ_STUN_IS_REQUEST(tdata->msg->hdr.type), PJ_EINVAL);
1023
 
 
1024
 
    /* Lock the session and prevent user from destroying us in the callback */
1025
 
    pj_atomic_inc(sess->busy);
1026
 
    pj_lock_acquire(sess->lock);
1027
 
 
1028
 
    if (notify) {
1029
 
        (sess->cb.on_request_complete)(sess, notify_status, tdata->token, 
1030
 
                                       tdata, NULL, NULL, 0);
1031
 
    }
1032
 
 
1033
 
    /* Just destroy tdata. This will destroy the transaction as well */
1034
 
    pj_stun_msg_destroy_tdata(sess, tdata);
1035
 
 
1036
 
    pj_lock_release(sess->lock);
1037
 
 
1038
 
    if (pj_atomic_dec_and_get(sess->busy)==0 && sess->destroy_request) {
1039
 
        pj_stun_session_destroy(sess);
1040
 
        return PJNATH_ESTUNDESTROYED;
1041
 
    }
1042
 
 
1043
 
    return PJ_SUCCESS;
1044
 
}
1045
 
 
1046
 
/*
1047
 
 * Explicitly request retransmission of the request.
1048
 
 */
1049
 
PJ_DEF(pj_status_t) pj_stun_session_retransmit_req(pj_stun_session *sess,
1050
 
                                                   pj_stun_tx_data *tdata)
1051
 
{
1052
 
    pj_status_t status;
1053
 
 
1054
 
    PJ_ASSERT_RETURN(sess && tdata, PJ_EINVAL);
1055
 
    PJ_ASSERT_RETURN(PJ_STUN_IS_REQUEST(tdata->msg->hdr.type), PJ_EINVAL);
1056
 
 
1057
 
    /* Lock the session and prevent user from destroying us in the callback */
1058
 
    pj_atomic_inc(sess->busy);
1059
 
    pj_lock_acquire(sess->lock);
1060
 
 
1061
 
    status = pj_stun_client_tsx_retransmit(tdata->client_tsx);
1062
 
 
1063
 
    pj_lock_release(sess->lock);
1064
 
 
1065
 
    if (pj_atomic_dec_and_get(sess->busy)==0 && sess->destroy_request) {
1066
 
        pj_stun_session_destroy(sess);
1067
 
        return PJNATH_ESTUNDESTROYED;
1068
 
    }
1069
 
 
1070
 
    return status;
1071
 
}
1072
 
 
1073
 
 
1074
 
/* Send response */
1075
 
static pj_status_t send_response(pj_stun_session *sess, void *token,
1076
 
                                 pj_pool_t *pool, pj_stun_msg *response,
1077
 
                                 const pj_stun_req_cred_info *auth_info,
1078
 
                                 pj_bool_t retransmission,
1079
 
                                 const pj_sockaddr_t *addr, unsigned addr_len)
1080
 
{
1081
 
    pj_uint8_t *out_pkt;
1082
 
    pj_size_t out_max_len, out_len;
1083
 
    pj_status_t status;
1084
 
 
1085
 
    /* Apply options */
1086
 
    if (!retransmission) {
1087
 
        status = apply_msg_options(sess, pool, auth_info, response);
1088
 
        if (status != PJ_SUCCESS)
1089
 
            return status;
1090
 
    }
1091
 
 
1092
 
    /* Alloc packet buffer */
1093
 
    out_max_len = PJ_STUN_MAX_PKT_LEN;
1094
 
    out_pkt = (pj_uint8_t*) pj_pool_alloc(pool, out_max_len);
1095
 
 
1096
 
    /* Encode */
1097
 
    status = pj_stun_msg_encode(response, out_pkt, out_max_len, 0, 
1098
 
                                &auth_info->auth_key, &out_len);
1099
 
    if (status != PJ_SUCCESS) {
1100
 
        LOG_ERR_(sess, "Error encoding message", status);
1101
 
        return status;
1102
 
    }
1103
 
 
1104
 
    /* Print log */
1105
 
    dump_tx_msg(sess, response, out_len, addr);
1106
 
 
1107
 
    /* Send packet */
1108
 
    status = sess->cb.on_send_msg(sess, token, out_pkt, out_len, 
1109
 
                                  addr, addr_len);
1110
 
 
1111
 
    return status;
1112
 
}
1113
 
 
1114
 
/* Authenticate incoming message */
1115
 
static pj_status_t authenticate_req(pj_stun_session *sess,
1116
 
                                    void *token,
1117
 
                                    const pj_uint8_t *pkt,
1118
 
                                    unsigned pkt_len,
1119
 
                                    pj_stun_rx_data *rdata,
1120
 
                                    pj_pool_t *tmp_pool,
1121
 
                                    const pj_sockaddr_t *src_addr,
1122
 
                                    unsigned src_addr_len)
1123
 
{
1124
 
    pj_stun_msg *response;
1125
 
    pj_status_t status;
1126
 
 
1127
 
    if (PJ_STUN_IS_ERROR_RESPONSE(rdata->msg->hdr.type) || 
1128
 
        sess->auth_type == PJ_STUN_AUTH_NONE)
1129
 
    {
1130
 
        return PJ_SUCCESS;
1131
 
    }
1132
 
 
1133
 
    status = pj_stun_authenticate_request(pkt, pkt_len, rdata->msg, 
1134
 
                                          &sess->cred, tmp_pool, &rdata->info,
1135
 
                                          &response);
1136
 
    if (status != PJ_SUCCESS && response != NULL) {
1137
 
        PJ_LOG(5,(SNAME(sess), "Message authentication failed"));
1138
 
        send_response(sess, token, tmp_pool, response, &rdata->info, 
1139
 
                      PJ_FALSE, src_addr, src_addr_len);
1140
 
    }
1141
 
 
1142
 
    return status;
1143
 
}
1144
 
 
1145
 
 
1146
 
/* Handle incoming response */
1147
 
static pj_status_t on_incoming_response(pj_stun_session *sess,
1148
 
                                        unsigned options,
1149
 
                                        const pj_uint8_t *pkt,
1150
 
                                        unsigned pkt_len,
1151
 
                                        pj_stun_msg *msg,
1152
 
                                        const pj_sockaddr_t *src_addr,
1153
 
                                        unsigned src_addr_len)
1154
 
{
1155
 
    pj_stun_tx_data *tdata;
1156
 
    pj_status_t status;
1157
 
 
1158
 
    /* Lookup pending client transaction */
1159
 
    tdata = tsx_lookup(sess, msg);
1160
 
    if (tdata == NULL) {
1161
 
        PJ_LOG(5,(SNAME(sess), 
1162
 
                  "Transaction not found, response silently discarded"));
1163
 
        return PJ_SUCCESS;
1164
 
    }
1165
 
 
1166
 
    if (sess->auth_type == PJ_STUN_AUTH_NONE)
1167
 
        options |= PJ_STUN_NO_AUTHENTICATE;
1168
 
 
1169
 
    /* Authenticate the message, unless PJ_STUN_NO_AUTHENTICATE
1170
 
     * is specified in the option.
1171
 
     */
1172
 
    if ((options & PJ_STUN_NO_AUTHENTICATE) == 0 && 
1173
 
        tdata->auth_info.auth_key.slen != 0 && 
1174
 
        pj_stun_auth_valid_for_msg(msg))
1175
 
    {
1176
 
        status = pj_stun_authenticate_response(pkt, pkt_len, msg, 
1177
 
                                               &tdata->auth_info.auth_key);
1178
 
        if (status != PJ_SUCCESS) {
1179
 
            PJ_LOG(5,(SNAME(sess), 
1180
 
                      "Response authentication failed"));
1181
 
            return status;
1182
 
        }
1183
 
    }
1184
 
 
1185
 
    /* Pass the response to the transaction. 
1186
 
     * If the message is accepted, transaction callback will be called,
1187
 
     * and this will call the session callback too.
1188
 
     */
1189
 
    status = pj_stun_client_tsx_on_rx_msg(tdata->client_tsx, msg, 
1190
 
                                          src_addr, src_addr_len);
1191
 
    if (status != PJ_SUCCESS) {
1192
 
        return status;
1193
 
    }
1194
 
 
1195
 
    return PJ_SUCCESS;
1196
 
}
1197
 
 
1198
 
 
1199
 
/* For requests, check if we cache the response */
1200
 
static pj_status_t check_cached_response(pj_stun_session *sess,
1201
 
                                         pj_pool_t *tmp_pool,
1202
 
                                         const pj_stun_msg *msg,
1203
 
                                         const pj_sockaddr_t *src_addr,
1204
 
                                         unsigned src_addr_len)
1205
 
{
1206
 
    pj_stun_tx_data *t;
1207
 
 
1208
 
    /* First lookup response in response cache */
1209
 
    t = sess->cached_response_list.next;
1210
 
    while (t != &sess->cached_response_list) {
1211
 
        if (t->msg_magic == msg->hdr.magic &&
1212
 
            t->msg->hdr.type == msg->hdr.type &&
1213
 
            pj_memcmp(t->msg_key, msg->hdr.tsx_id, 
1214
 
                      sizeof(msg->hdr.tsx_id))==0)
1215
 
        {
1216
 
            break;
1217
 
        }
1218
 
        t = t->next;
1219
 
    }
1220
 
 
1221
 
    if (t != &sess->cached_response_list) {
1222
 
        /* Found response in the cache */
1223
 
 
1224
 
        PJ_LOG(5,(SNAME(sess), 
1225
 
                 "Request retransmission, sending cached response"));
1226
 
 
1227
 
        send_response(sess, t->token, tmp_pool, t->msg, &t->auth_info, 
1228
 
                      PJ_TRUE, src_addr, src_addr_len);
1229
 
        return PJ_SUCCESS;
1230
 
    }
1231
 
 
1232
 
    return PJ_ENOTFOUND;
1233
 
}
1234
 
 
1235
 
/* Handle incoming request */
1236
 
static pj_status_t on_incoming_request(pj_stun_session *sess,
1237
 
                                       unsigned options,
1238
 
                                       void *token,
1239
 
                                       pj_pool_t *tmp_pool,
1240
 
                                       const pj_uint8_t *in_pkt,
1241
 
                                       unsigned in_pkt_len,
1242
 
                                       pj_stun_msg *msg,
1243
 
                                       const pj_sockaddr_t *src_addr,
1244
 
                                       unsigned src_addr_len)
1245
 
{
1246
 
    pj_stun_rx_data rdata;
1247
 
    pj_status_t status;
1248
 
 
1249
 
    /* Init rdata */
1250
 
    rdata.msg = msg;
1251
 
    pj_bzero(&rdata.info, sizeof(rdata.info));
1252
 
 
1253
 
    if (sess->auth_type == PJ_STUN_AUTH_NONE)
1254
 
        options |= PJ_STUN_NO_AUTHENTICATE;
1255
 
 
1256
 
    /* Authenticate the message, unless PJ_STUN_NO_AUTHENTICATE
1257
 
     * is specified in the option.
1258
 
     */
1259
 
    if ((options & PJ_STUN_NO_AUTHENTICATE) == 0) {
1260
 
        status = authenticate_req(sess, token, (const pj_uint8_t*) in_pkt, 
1261
 
                                  in_pkt_len,&rdata, tmp_pool, src_addr, 
1262
 
                                  src_addr_len);
1263
 
        if (status != PJ_SUCCESS) {
1264
 
            return status;
1265
 
        }
1266
 
    }
1267
 
 
1268
 
    /* Distribute to handler, or respond with Bad Request */
1269
 
    if (sess->cb.on_rx_request) {
1270
 
        status = (*sess->cb.on_rx_request)(sess, in_pkt, in_pkt_len, &rdata,
1271
 
                                           token, src_addr, src_addr_len);
1272
 
    } else {
1273
 
        pj_str_t err_text;
1274
 
        pj_stun_msg *response;
1275
 
 
1276
 
        err_text = pj_str("Callback is not set to handle request");
1277
 
        status = pj_stun_msg_create_response(tmp_pool, msg, 
1278
 
                                             PJ_STUN_SC_BAD_REQUEST, 
1279
 
                                             &err_text, &response);
1280
 
        if (status == PJ_SUCCESS && response) {
1281
 
            status = send_response(sess, token, tmp_pool, response, 
1282
 
                                   NULL, PJ_FALSE, src_addr, src_addr_len);
1283
 
        }
1284
 
    }
1285
 
 
1286
 
    return status;
1287
 
}
1288
 
 
1289
 
 
1290
 
/* Handle incoming indication */
1291
 
static pj_status_t on_incoming_indication(pj_stun_session *sess,
1292
 
                                          void *token,
1293
 
                                          pj_pool_t *tmp_pool,
1294
 
                                          const pj_uint8_t *in_pkt,
1295
 
                                          unsigned in_pkt_len,
1296
 
                                          const pj_stun_msg *msg,
1297
 
                                          const pj_sockaddr_t *src_addr,
1298
 
                                          unsigned src_addr_len)
1299
 
{
1300
 
    PJ_UNUSED_ARG(tmp_pool);
1301
 
 
1302
 
    /* Distribute to handler */
1303
 
    if (sess->cb.on_rx_indication) {
1304
 
        return (*sess->cb.on_rx_indication)(sess, in_pkt, in_pkt_len, msg,
1305
 
                                            token, src_addr, src_addr_len);
1306
 
    } else {
1307
 
        return PJ_SUCCESS;
1308
 
    }
1309
 
}
1310
 
 
1311
 
 
1312
 
/* Print outgoing message to log */
1313
 
static void dump_rx_msg(pj_stun_session *sess, const pj_stun_msg *msg,
1314
 
                        unsigned pkt_size, const pj_sockaddr_t *addr)
1315
 
{
1316
 
    char src_info[PJ_INET6_ADDRSTRLEN+10];
1317
 
    
1318
 
    if ((PJ_STUN_IS_REQUEST(msg->hdr.type) && 
1319
 
         (sess->log_flag & PJ_STUN_SESS_LOG_RX_REQ)==0) ||
1320
 
        (PJ_STUN_IS_RESPONSE(msg->hdr.type) &&
1321
 
         (sess->log_flag & PJ_STUN_SESS_LOG_RX_RES)==0) ||
1322
 
        (PJ_STUN_IS_INDICATION(msg->hdr.type) &&
1323
 
         (sess->log_flag & PJ_STUN_SESS_LOG_RX_IND)==0))
1324
 
    {
1325
 
        return;
1326
 
    }
1327
 
 
1328
 
    pj_sockaddr_print(addr, src_info, sizeof(src_info), 3);
1329
 
 
1330
 
    PJ_LOG(5,(SNAME(sess),
1331
 
              "RX %d bytes STUN message from %s:\n"
1332
 
              "--- begin STUN message ---\n"
1333
 
              "%s"
1334
 
              "--- end of STUN message ---\n",
1335
 
              pkt_size, src_info,
1336
 
              pj_stun_msg_dump(msg, sess->dump_buf, sizeof(sess->dump_buf), 
1337
 
                               NULL)));
1338
 
 
1339
 
}
1340
 
 
1341
 
/* Incoming packet */
1342
 
PJ_DEF(pj_status_t) pj_stun_session_on_rx_pkt(pj_stun_session *sess,
1343
 
                                              const void *packet,
1344
 
                                              pj_size_t pkt_size,
1345
 
                                              unsigned options,
1346
 
                                              void *token,
1347
 
                                              pj_size_t *parsed_len,
1348
 
                                              const pj_sockaddr_t *src_addr,
1349
 
                                              unsigned src_addr_len)
1350
 
{
1351
 
    pj_stun_msg *msg, *response;
1352
 
    pj_status_t status;
1353
 
 
1354
 
    PJ_ASSERT_RETURN(sess && packet && pkt_size, PJ_EINVAL);
1355
 
 
1356
 
    /* Lock the session and prevent user from destroying us in the callback */
1357
 
    pj_atomic_inc(sess->busy);
1358
 
    pj_lock_acquire(sess->lock);
1359
 
 
1360
 
    /* Reset pool */
1361
 
    pj_pool_reset(sess->rx_pool);
1362
 
 
1363
 
    /* Try to parse the message */
1364
 
    status = pj_stun_msg_decode(sess->rx_pool, (const pj_uint8_t*)packet,
1365
 
                                pkt_size, options, 
1366
 
                                &msg, parsed_len, &response);
1367
 
    if (status != PJ_SUCCESS) {
1368
 
        LOG_ERR_(sess, "STUN msg_decode() error", status);
1369
 
        if (response) {
1370
 
            send_response(sess, token, sess->rx_pool, response, NULL,
1371
 
                          PJ_FALSE, src_addr, src_addr_len);
1372
 
        }
1373
 
        goto on_return;
1374
 
    }
1375
 
 
1376
 
    dump_rx_msg(sess, msg, pkt_size, src_addr);
1377
 
 
1378
 
    /* For requests, check if we have cached response */
1379
 
    status = check_cached_response(sess, sess->rx_pool, msg, 
1380
 
                                   src_addr, src_addr_len);
1381
 
    if (status == PJ_SUCCESS) {
1382
 
        goto on_return;
1383
 
    }
1384
 
 
1385
 
    /* Handle message */
1386
 
    if (PJ_STUN_IS_SUCCESS_RESPONSE(msg->hdr.type) ||
1387
 
        PJ_STUN_IS_ERROR_RESPONSE(msg->hdr.type))
1388
 
    {
1389
 
        status = on_incoming_response(sess, options, 
1390
 
                                      (const pj_uint8_t*) packet, pkt_size, 
1391
 
                                      msg, src_addr, src_addr_len);
1392
 
 
1393
 
    } else if (PJ_STUN_IS_REQUEST(msg->hdr.type)) {
1394
 
 
1395
 
        status = on_incoming_request(sess, options, token, sess->rx_pool, 
1396
 
                                     (const pj_uint8_t*) packet, pkt_size, 
1397
 
                                     msg, src_addr, src_addr_len);
1398
 
 
1399
 
    } else if (PJ_STUN_IS_INDICATION(msg->hdr.type)) {
1400
 
 
1401
 
        status = on_incoming_indication(sess, token, sess->rx_pool, 
1402
 
                                        (const pj_uint8_t*) packet, pkt_size,
1403
 
                                        msg, src_addr, src_addr_len);
1404
 
 
1405
 
    } else {
1406
 
        pj_assert(!"Unexpected!");
1407
 
        status = PJ_EBUG;
1408
 
    }
1409
 
 
1410
 
on_return:
1411
 
    pj_lock_release(sess->lock);
1412
 
 
1413
 
    /* If we've received destroy request while we're on the callback,
1414
 
     * destroy the session now.
1415
 
     */
1416
 
    if (pj_atomic_dec_and_get(sess->busy)==0 && sess->destroy_request) {
1417
 
        pj_stun_session_destroy(sess);
1418
 
        return PJNATH_ESTUNDESTROYED;
1419
 
    }
1420
 
 
1421
 
    return status;
1422
 
}
1423