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