1
/* $Id: stun_session.c 3553 2011-05-05 06:14:19Z 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_session.h>
21
#include <pjnath/errno.h>
24
struct pj_stun_session
29
pj_bool_t delete_lock;
30
pj_stun_session_cb cb;
34
pj_bool_t destroy_request;
36
pj_bool_t use_fingerprint;
40
#if PJ_LOG_MAX_LEVEL >= 5
45
pj_stun_auth_type auth_type;
46
pj_stun_auth_cred cred;
49
pj_str_t server_realm;
53
pj_stun_tx_data pending_request_list;
54
pj_stun_tx_data cached_response_list;
57
#define SNAME(s_) ((s_)->pool->obj_name)
59
#if PJ_LOG_MAX_LEVEL >= 5
60
# define TRACE_(expr) PJ_LOG(5,expr)
65
#define LOG_ERR_(sess,title,rc) pjnath_perror(sess->pool->obj_name,title,rc)
67
#define TDATA_POOL_SIZE PJNATH_POOL_LEN_STUN_TDATA
68
#define TDATA_POOL_INC PJNATH_POOL_INC_STUN_TDATA
71
static void stun_tsx_on_complete(pj_stun_client_tsx *tsx,
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,
79
static void stun_tsx_on_destroy(pj_stun_client_tsx *tsx);
81
static pj_stun_tsx_cb tsx_cb =
83
&stun_tsx_on_complete,
84
&stun_tsx_on_send_msg,
89
static pj_status_t tsx_add(pj_stun_session *sess,
90
pj_stun_tx_data *tdata)
92
pj_list_push_front(&sess->pending_request_list, tdata);
96
static pj_status_t tsx_erase(pj_stun_session *sess,
97
pj_stun_tx_data *tdata)
100
pj_list_erase(tdata);
104
static pj_stun_tx_data* tsx_lookup(pj_stun_session *sess,
105
const pj_stun_msg *msg)
107
pj_stun_tx_data *tdata;
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)
124
static pj_status_t create_tdata(pj_stun_session *sess,
125
pj_stun_tx_data **p_tdata)
128
pj_stun_tx_data *tdata;
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);
135
tdata = PJ_POOL_ZALLOC_T(pool, pj_stun_tx_data);
146
static void stun_tsx_on_destroy(pj_stun_client_tsx *tsx)
148
pj_stun_tx_data *tdata;
150
tdata = (pj_stun_tx_data*) pj_stun_client_tsx_get_data(tsx);
151
tsx_erase(tdata->sess, tdata);
153
pj_stun_client_tsx_destroy(tsx);
154
pj_pool_release(tdata->pool);
157
static void destroy_tdata(pj_stun_tx_data *tdata, pj_bool_t force)
159
if (tdata->res_timer.id != PJ_FALSE) {
160
pj_timer_heap_cancel(tdata->sess->cfg->timer_heap,
162
tdata->res_timer.id = PJ_FALSE;
163
pj_list_erase(tdata);
167
if (tdata->client_tsx) {
168
tsx_erase(tdata->sess, tdata);
169
pj_stun_client_tsx_destroy(tdata->client_tsx);
171
pj_pool_release(tdata->pool);
174
if (tdata->client_tsx) {
175
pj_time_val delay = {2, 0};
176
pj_stun_client_tsx_schedule_destroy(tdata->client_tsx, &delay);
179
pj_pool_release(tdata->pool);
185
* Destroy the transmit data.
187
PJ_DEF(void) pj_stun_msg_destroy_tdata( pj_stun_session *sess,
188
pj_stun_tx_data *tdata)
191
destroy_tdata(tdata, PJ_FALSE);
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)
199
pj_stun_tx_data *tdata;
201
PJ_UNUSED_ARG(timer_heap);
203
entry->id = PJ_FALSE;
204
tdata = (pj_stun_tx_data*) entry->user_data;
206
PJ_LOG(5,(SNAME(tdata->sess), "Response cache deleted"));
208
pj_list_erase(tdata);
209
pj_stun_msg_destroy_tdata(tdata->sess, tdata);
212
static pj_status_t apply_msg_options(pj_stun_session *sess,
214
const pj_stun_req_cred_info *auth_info,
217
pj_status_t status = 0;
218
pj_str_t realm, username, nonce, auth_key;
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
224
* If magic value is not PJ_STUN_MAGIC, only apply the attribute for
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)))
232
pj_stun_msg_add_string_attr(pool, msg, PJ_STUN_ATTR_SOFTWARE,
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;
242
realm.slen = username.slen = nonce.slen = auth_key.slen = 0;
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,
250
PJ_ASSERT_RETURN(status==PJ_SUCCESS, status);
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,
258
PJ_ASSERT_RETURN(status==PJ_SUCCESS, status);
261
/* Add NONCE when desired */
263
(PJ_STUN_IS_REQUEST(msg->hdr.type) ||
264
PJ_STUN_IS_ERROR_RESPONSE(msg->hdr.type)))
266
status = pj_stun_msg_add_string_attr(pool, msg,
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);
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);
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)
295
const pj_stun_errcode_attr *ea;
297
*notify_user = PJ_TRUE;
302
if (sess->auth_type != PJ_STUN_AUTH_LONG_TERM)
305
if (!PJ_STUN_IS_ERROR_RESPONSE(response->hdr.type)) {
306
sess->auth_retry = 0;
310
ea = (const pj_stun_errcode_attr*)
311
pj_stun_msg_find_attr(response, PJ_STUN_ATTR_ERROR_CODE, 0);
313
PJ_LOG(4,(SNAME(sess), "Invalid error response: no ERROR-CODE"
315
*notify_user = PJ_FALSE;
316
return PJNATH_EINSTUNMSG;
319
if (ea->err_code == PJ_STUN_SC_UNAUTHORIZED ||
320
ea->err_code == PJ_STUN_SC_STALE_NONCE)
322
const pj_stun_nonce_attr *anonce;
323
const pj_stun_realm_attr *arealm;
324
pj_stun_tx_data *tdata;
328
anonce = (const pj_stun_nonce_attr*)
329
pj_stun_msg_find_attr(response, PJ_STUN_ATTR_NONCE, 0);
331
PJ_LOG(4,(SNAME(sess), "Invalid response: missing NONCE"));
332
*notify_user = PJ_FALSE;
333
return PJNATH_EINSTUNMSG;
336
/* Bail out if we've supplied the correct nonce */
337
if (pj_strcmp(&anonce->value, &sess->next_nonce)==0) {
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 "
345
return PJ_STATUS_FROM_STUN_CODE(401);
348
/* Save next_nonce */
349
pj_strdup(sess->pool, &sess->next_nonce, &anonce->value);
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);
355
pj_strdup(sess->pool, &sess->server_realm, &arealm->value);
358
/* Create new request */
359
status = pj_stun_session_create_req(sess, request->msg->hdr.type,
360
request->msg->hdr.magic,
362
if (status != PJ_SUCCESS)
365
/* Duplicate all the attributes in the old request, except
366
* USERNAME, REALM, M-I, and NONCE, which will be filled in
369
for (i=0; i<request->msg->attr_count; ++i) {
370
const pj_stun_attr_hdr *asrc = request->msg->attr[i];
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)
380
tdata->msg->attr[tdata->msg->attr_count++] =
381
pj_stun_attr_clone(tdata->pool, asrc);
384
/* Will retry the request with authentication, no need to
387
*notify_user = PJ_FALSE;
389
PJ_LOG(4,(SNAME(sess), "Retrying request with new authentication"));
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);
397
sess->auth_retry = 0;
403
static void stun_tsx_on_complete(pj_stun_client_tsx *tsx,
405
const pj_stun_msg *response,
406
const pj_sockaddr_t *src_addr,
407
unsigned src_addr_len)
409
pj_stun_session *sess;
410
pj_bool_t notify_user = PJ_TRUE;
411
pj_stun_tx_data *tdata;
413
tdata = (pj_stun_tx_data*) pj_stun_client_tsx_get_data(tsx);
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);
420
/* Handle authentication challenge */
421
handle_auth_challenge(sess, tdata, response, src_addr,
422
src_addr_len, ¬ify_user);
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);
429
/* Destroy the transmit data. This will remove the transaction
430
* from the pending list too.
432
pj_stun_msg_destroy_tdata(sess, tdata);
435
pj_lock_release(sess->lock);
437
if (pj_atomic_dec_and_get(sess->busy)==0 && sess->destroy_request) {
438
pj_stun_session_destroy(sess);
443
static pj_status_t stun_tsx_on_send_msg(pj_stun_client_tsx *tsx,
444
const void *stun_pkt,
447
pj_stun_tx_data *tdata;
448
pj_stun_session *sess;
451
tdata = (pj_stun_tx_data*) pj_stun_client_tsx_get_data(tsx);
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);
458
status = sess->cb.on_send_msg(tdata->sess, tdata->token, stun_pkt,
459
pkt_size, tdata->dst_addr,
461
pj_lock_release(sess->lock);
463
if (pj_atomic_dec_and_get(sess->busy)==0 && sess->destroy_request) {
464
pj_stun_session_destroy(sess);
465
return PJNATH_ESTUNDESTROYED;
471
/* **************************************************************************/
473
PJ_DEF(pj_status_t) pj_stun_session_create( pj_stun_config *cfg,
475
const pj_stun_session_cb *cb,
476
pj_bool_t fingerprint,
477
pj_stun_session **p_sess)
480
pj_stun_session *sess;
483
PJ_ASSERT_RETURN(cfg && cb && p_sess, PJ_EINVAL);
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);
492
sess = PJ_POOL_ZALLOC_T(pool, pj_stun_session);
495
pj_memcpy(&sess->cb, cb, sizeof(*cb));
496
sess->use_fingerprint = fingerprint;
497
sess->log_flag = 0xFFFF;
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());
503
sess->rx_pool = pj_pool_create(sess->cfg->pf, name,
504
PJNATH_POOL_LEN_STUN_TDATA,
505
PJNATH_POOL_INC_STUN_TDATA, NULL);
507
pj_list_init(&sess->pending_request_list);
508
pj_list_init(&sess->cached_response_list);
510
status = pj_lock_create_recursive_mutex(pool, name, &sess->lock);
511
if (status != PJ_SUCCESS) {
512
pj_pool_release(pool);
515
sess->delete_lock = PJ_TRUE;
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);
529
PJ_DEF(pj_status_t) pj_stun_session_destroy(pj_stun_session *sess)
531
PJ_ASSERT_RETURN(sess, PJ_EINVAL);
533
pj_lock_acquire(sess->lock);
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);
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);
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);
551
pj_lock_release(sess->lock);
553
if (sess->delete_lock) {
554
pj_lock_destroy(sess->lock);
558
pj_pool_release(sess->rx_pool);
559
sess->rx_pool = NULL;
562
pj_pool_release(sess->pool);
568
PJ_DEF(pj_status_t) pj_stun_session_set_user_data( pj_stun_session *sess,
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);
578
PJ_DEF(void*) pj_stun_session_get_user_data(pj_stun_session *sess)
580
PJ_ASSERT_RETURN(sess, NULL);
581
return sess->user_data;
584
PJ_DEF(pj_status_t) pj_stun_session_set_lock( pj_stun_session *sess,
588
pj_lock_t *old_lock = sess->lock;
591
PJ_ASSERT_RETURN(sess && lock, PJ_EINVAL);
593
pj_lock_acquire(old_lock);
595
old_del = sess->delete_lock;
596
sess->delete_lock = auto_del;
597
pj_lock_release(old_lock);
600
pj_lock_destroy(old_lock);
605
PJ_DEF(pj_status_t) pj_stun_session_set_software_name(pj_stun_session *sess,
608
PJ_ASSERT_RETURN(sess, PJ_EINVAL);
610
pj_strdup(sess->pool, &sess->srv_name, sw);
612
sess->srv_name.slen = 0;
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)
620
PJ_ASSERT_RETURN(sess, PJ_EINVAL);
622
sess->auth_type = auth_type;
624
pj_stun_auth_cred_dup(sess->pool, &sess->cred, cred);
626
sess->auth_type = PJ_STUN_AUTH_NONE;
627
pj_bzero(&sess->cred, sizeof(sess->cred));
633
PJ_DEF(void) pj_stun_session_set_log( pj_stun_session *sess,
636
PJ_ASSERT_ON_FAIL(sess, return);
637
sess->log_flag = flags;
640
PJ_DEF(pj_bool_t) pj_stun_session_use_fingerprint(pj_stun_session *sess,
645
PJ_ASSERT_RETURN(sess, PJ_FALSE);
647
old_use = sess->use_fingerprint;
648
sess->use_fingerprint = use;
652
static pj_status_t get_auth(pj_stun_session *sess,
653
pj_stun_tx_data *tdata)
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;
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);
667
} else if (sess->cred.type == PJ_STUN_AUTH_CRED_DYNAMIC) {
669
void *user_data = sess->cred.data.dyn_cred.user_data;
670
pj_stun_passwd_type data_type = PJ_STUN_PASSWD_PLAIN;
673
rc = (*sess->cred.data.dyn_cred.get_cred)(tdata->msg, user_data,
675
&tdata->auth_info.realm,
676
&tdata->auth_info.username,
677
&tdata->auth_info.nonce,
678
&data_type, &password);
679
if (rc != PJ_SUCCESS)
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);
687
pj_assert(!"Unknown credential type");
694
PJ_DEF(pj_status_t) pj_stun_session_create_req(pj_stun_session *sess,
697
const pj_uint8_t tsx_id[12],
698
pj_stun_tx_data **p_tdata)
700
pj_stun_tx_data *tdata = NULL;
703
PJ_ASSERT_RETURN(sess && p_tdata, PJ_EINVAL);
705
status = create_tdata(sess, &tdata);
706
if (status != PJ_SUCCESS)
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);
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));
724
/* Get authentication information for the request */
725
if (sess->auth_type == PJ_STUN_AUTH_NONE) {
726
/* No authentication */
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);
736
} else if (sess->auth_type == PJ_STUN_AUTH_LONG_TERM) {
737
/* Only put authentication information if we've received
738
* response from server.
740
if (sess->next_nonce.slen != 0) {
741
status = get_auth(sess, tdata);
742
if (status != PJ_SUCCESS) {
743
pj_pool_release(tdata->pool);
746
tdata->auth_info.nonce = sess->next_nonce;
747
tdata->auth_info.realm = sess->server_realm;
751
pj_assert(!"Invalid authentication type");
752
pj_pool_release(tdata->pool);
760
PJ_DEF(pj_status_t) pj_stun_session_create_ind(pj_stun_session *sess,
762
pj_stun_tx_data **p_tdata)
764
pj_stun_tx_data *tdata = NULL;
767
PJ_ASSERT_RETURN(sess && p_tdata, PJ_EINVAL);
769
status = create_tdata(sess, &tdata);
770
if (status != PJ_SUCCESS)
773
/* Create STUN message */
774
msg_type |= PJ_STUN_INDICATION_BIT;
775
status = pj_stun_msg_create(tdata->pool, msg_type, PJ_STUN_MAGIC,
777
if (status != PJ_SUCCESS) {
778
pj_pool_release(tdata->pool);
787
* Create a STUN response message.
789
PJ_DEF(pj_status_t) pj_stun_session_create_res( pj_stun_session *sess,
790
const pj_stun_rx_data *rdata,
792
const pj_str_t *err_msg,
793
pj_stun_tx_data **p_tdata)
796
pj_stun_tx_data *tdata = NULL;
798
status = create_tdata(sess, &tdata);
799
if (status != PJ_SUCCESS)
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);
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));
816
/* copy the credential found in the request */
817
pj_stun_req_cred_info_dup(tdata->pool, &tdata->auth_info, &rdata->info);
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)
829
char dst_name[PJ_INET6_ADDRSTRLEN+10];
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))
841
pj_sockaddr_print(addr, dst_name, sizeof(dst_name), 3);
843
PJ_LOG(5,(SNAME(sess),
844
"TX %d bytes STUN message to %s:\n"
845
"--- begin STUN message ---\n"
847
"--- end of STUN message ---\n",
849
pj_stun_msg_dump(msg, sess->dump_buf, sizeof(sess->dump_buf),
855
PJ_DEF(pj_status_t) pj_stun_session_send_msg( pj_stun_session *sess,
858
pj_bool_t retransmit,
859
const pj_sockaddr_t *server,
861
pj_stun_tx_data *tdata)
865
PJ_ASSERT_RETURN(sess && addr_len && server && tdata, PJ_EINVAL);
867
/* Allocate packet */
868
tdata->max_len = PJ_STUN_MAX_PKT_LEN;
869
tdata->pkt = pj_pool_alloc(tdata->pool, tdata->max_len);
871
tdata->token = token;
872
tdata->retransmit = retransmit;
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);
879
status = apply_msg_options(sess, tdata->pool, &tdata->auth_info,
881
if (status != PJ_SUCCESS) {
882
pj_stun_msg_destroy_tdata(sess, tdata);
883
LOG_ERR_(sess, "Error applying options", status);
888
status = pj_stun_msg_encode(tdata->msg, (pj_uint8_t*)tdata->pkt,
890
&tdata->auth_info.auth_key,
892
if (status != PJ_SUCCESS) {
893
pj_stun_msg_destroy_tdata(sess, tdata);
894
LOG_ERR_(sess, "STUN encode() error", status);
899
dump_tx_msg(sess, tdata->msg, tdata->pkt_size, server);
901
/* If this is a STUN request message, then send the request with
902
* a new STUN client transaction.
904
if (PJ_STUN_IS_REQUEST(tdata->msg->hdr.type)) {
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);
912
/* Save the remote address */
913
tdata->addr_len = addr_len;
914
tdata->dst_addr = server;
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);
925
/* Add to pending request list */
926
tsx_add(sess, tdata);
930
(PJ_STUN_IS_SUCCESS_RESPONSE(tdata->msg->hdr.type) ||
931
PJ_STUN_IS_ERROR_RESPONSE(tdata->msg->hdr.type)))
933
/* Requested to keep the response in the cache */
936
pj_memset(&tdata->res_timer, 0, sizeof(tdata->res_timer));
937
pj_timer_entry_init(&tdata->res_timer, PJ_TRUE, tdata,
940
timeout.sec = sess->cfg->res_cache_msec / 1000;
941
timeout.msec = sess->cfg->res_cache_msec % 1000;
943
status = pj_timer_heap_schedule(sess->cfg->timer_heap,
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);
953
pj_list_push_back(&sess->cached_response_list, tdata);
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);
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);
966
/* Destroy only when response is not cached*/
967
if (tdata->res_timer.id == 0) {
968
pj_stun_msg_destroy_tdata(sess, tdata);
973
pj_lock_release(sess->lock);
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;
986
* Create and send STUN response message.
988
PJ_DEF(pj_status_t) pj_stun_session_respond( pj_stun_session *sess,
989
const pj_stun_rx_data *rdata,
994
const pj_sockaddr_t *dst_addr,
999
pj_stun_tx_data *tdata;
1001
status = pj_stun_session_create_res(sess, rdata, code,
1002
(errmsg?pj_cstr(&reason,errmsg):NULL),
1004
if (status != PJ_SUCCESS)
1007
return pj_stun_session_send_msg(sess, token, cache, PJ_FALSE,
1008
dst_addr, addr_len, tdata);
1013
* Cancel outgoing STUN transaction.
1015
PJ_DEF(pj_status_t) pj_stun_session_cancel_req( pj_stun_session *sess,
1016
pj_stun_tx_data *tdata,
1018
pj_status_t notify_status)
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);
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);
1029
(sess->cb.on_request_complete)(sess, notify_status, tdata->token,
1030
tdata, NULL, NULL, 0);
1033
/* Just destroy tdata. This will destroy the transaction as well */
1034
pj_stun_msg_destroy_tdata(sess, tdata);
1036
pj_lock_release(sess->lock);
1038
if (pj_atomic_dec_and_get(sess->busy)==0 && sess->destroy_request) {
1039
pj_stun_session_destroy(sess);
1040
return PJNATH_ESTUNDESTROYED;
1047
* Explicitly request retransmission of the request.
1049
PJ_DEF(pj_status_t) pj_stun_session_retransmit_req(pj_stun_session *sess,
1050
pj_stun_tx_data *tdata)
1054
PJ_ASSERT_RETURN(sess && tdata, PJ_EINVAL);
1055
PJ_ASSERT_RETURN(PJ_STUN_IS_REQUEST(tdata->msg->hdr.type), PJ_EINVAL);
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);
1061
status = pj_stun_client_tsx_retransmit(tdata->client_tsx);
1063
pj_lock_release(sess->lock);
1065
if (pj_atomic_dec_and_get(sess->busy)==0 && sess->destroy_request) {
1066
pj_stun_session_destroy(sess);
1067
return PJNATH_ESTUNDESTROYED;
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)
1081
pj_uint8_t *out_pkt;
1082
pj_size_t out_max_len, out_len;
1086
if (!retransmission) {
1087
status = apply_msg_options(sess, pool, auth_info, response);
1088
if (status != PJ_SUCCESS)
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);
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);
1105
dump_tx_msg(sess, response, out_len, addr);
1108
status = sess->cb.on_send_msg(sess, token, out_pkt, out_len,
1114
/* Authenticate incoming message */
1115
static pj_status_t authenticate_req(pj_stun_session *sess,
1117
const pj_uint8_t *pkt,
1119
pj_stun_rx_data *rdata,
1120
pj_pool_t *tmp_pool,
1121
const pj_sockaddr_t *src_addr,
1122
unsigned src_addr_len)
1124
pj_stun_msg *response;
1127
if (PJ_STUN_IS_ERROR_RESPONSE(rdata->msg->hdr.type) ||
1128
sess->auth_type == PJ_STUN_AUTH_NONE)
1133
status = pj_stun_authenticate_request(pkt, pkt_len, rdata->msg,
1134
&sess->cred, tmp_pool, &rdata->info,
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);
1146
/* Handle incoming response */
1147
static pj_status_t on_incoming_response(pj_stun_session *sess,
1149
const pj_uint8_t *pkt,
1152
const pj_sockaddr_t *src_addr,
1153
unsigned src_addr_len)
1155
pj_stun_tx_data *tdata;
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"));
1166
if (sess->auth_type == PJ_STUN_AUTH_NONE)
1167
options |= PJ_STUN_NO_AUTHENTICATE;
1169
/* Authenticate the message, unless PJ_STUN_NO_AUTHENTICATE
1170
* is specified in the option.
1172
if ((options & PJ_STUN_NO_AUTHENTICATE) == 0 &&
1173
tdata->auth_info.auth_key.slen != 0 &&
1174
pj_stun_auth_valid_for_msg(msg))
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"));
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.
1189
status = pj_stun_client_tsx_on_rx_msg(tdata->client_tsx, msg,
1190
src_addr, src_addr_len);
1191
if (status != PJ_SUCCESS) {
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)
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)
1221
if (t != &sess->cached_response_list) {
1222
/* Found response in the cache */
1224
PJ_LOG(5,(SNAME(sess),
1225
"Request retransmission, sending cached response"));
1227
send_response(sess, t->token, tmp_pool, t->msg, &t->auth_info,
1228
PJ_TRUE, src_addr, src_addr_len);
1232
return PJ_ENOTFOUND;
1235
/* Handle incoming request */
1236
static pj_status_t on_incoming_request(pj_stun_session *sess,
1239
pj_pool_t *tmp_pool,
1240
const pj_uint8_t *in_pkt,
1241
unsigned in_pkt_len,
1243
const pj_sockaddr_t *src_addr,
1244
unsigned src_addr_len)
1246
pj_stun_rx_data rdata;
1251
pj_bzero(&rdata.info, sizeof(rdata.info));
1253
if (sess->auth_type == PJ_STUN_AUTH_NONE)
1254
options |= PJ_STUN_NO_AUTHENTICATE;
1256
/* Authenticate the message, unless PJ_STUN_NO_AUTHENTICATE
1257
* is specified in the option.
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,
1263
if (status != PJ_SUCCESS) {
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);
1274
pj_stun_msg *response;
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);
1290
/* Handle incoming indication */
1291
static pj_status_t on_incoming_indication(pj_stun_session *sess,
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)
1300
PJ_UNUSED_ARG(tmp_pool);
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);
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)
1316
char src_info[PJ_INET6_ADDRSTRLEN+10];
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))
1328
pj_sockaddr_print(addr, src_info, sizeof(src_info), 3);
1330
PJ_LOG(5,(SNAME(sess),
1331
"RX %d bytes STUN message from %s:\n"
1332
"--- begin STUN message ---\n"
1334
"--- end of STUN message ---\n",
1336
pj_stun_msg_dump(msg, sess->dump_buf, sizeof(sess->dump_buf),
1341
/* Incoming packet */
1342
PJ_DEF(pj_status_t) pj_stun_session_on_rx_pkt(pj_stun_session *sess,
1347
pj_size_t *parsed_len,
1348
const pj_sockaddr_t *src_addr,
1349
unsigned src_addr_len)
1351
pj_stun_msg *msg, *response;
1354
PJ_ASSERT_RETURN(sess && packet && pkt_size, PJ_EINVAL);
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);
1361
pj_pool_reset(sess->rx_pool);
1363
/* Try to parse the message */
1364
status = pj_stun_msg_decode(sess->rx_pool, (const pj_uint8_t*)packet,
1366
&msg, parsed_len, &response);
1367
if (status != PJ_SUCCESS) {
1368
LOG_ERR_(sess, "STUN msg_decode() error", status);
1370
send_response(sess, token, sess->rx_pool, response, NULL,
1371
PJ_FALSE, src_addr, src_addr_len);
1376
dump_rx_msg(sess, msg, pkt_size, src_addr);
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) {
1385
/* Handle message */
1386
if (PJ_STUN_IS_SUCCESS_RESPONSE(msg->hdr.type) ||
1387
PJ_STUN_IS_ERROR_RESPONSE(msg->hdr.type))
1389
status = on_incoming_response(sess, options,
1390
(const pj_uint8_t*) packet, pkt_size,
1391
msg, src_addr, src_addr_len);
1393
} else if (PJ_STUN_IS_REQUEST(msg->hdr.type)) {
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);
1399
} else if (PJ_STUN_IS_INDICATION(msg->hdr.type)) {
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);
1406
pj_assert(!"Unexpected!");
1411
pj_lock_release(sess->lock);
1413
/* If we've received destroy request while we're on the callback,
1414
* destroy the session now.
1416
if (pj_atomic_dec_and_get(sess->busy)==0 && sess->destroy_request) {
1417
pj_stun_session_destroy(sess);
1418
return PJNATH_ESTUNDESTROYED;