blob: 96959738b88ad1e64d2e2e892b09b459e96a2b9a [file] [log] [blame]
Alexandre Lision8af73cb2013-12-10 14:11:20 -05001/* $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_grp_lock_t *) pj_stun_session_get_grp_lock(pj_stun_session *sess)
625{
626 PJ_ASSERT_RETURN(sess, NULL);
627 return sess->grp_lock;
628}
629
630PJ_DEF(pj_status_t) pj_stun_session_set_software_name(pj_stun_session *sess,
631 const pj_str_t *sw)
632{
633 PJ_ASSERT_RETURN(sess, PJ_EINVAL);
634 pj_grp_lock_acquire(sess->grp_lock);
635 if (sw && sw->slen)
636 pj_strdup(sess->pool, &sess->srv_name, sw);
637 else
638 sess->srv_name.slen = 0;
639 pj_grp_lock_release(sess->grp_lock);
640 return PJ_SUCCESS;
641}
642
643PJ_DEF(pj_status_t) pj_stun_session_set_credential(pj_stun_session *sess,
644 pj_stun_auth_type auth_type,
645 const pj_stun_auth_cred *cred)
646{
647 PJ_ASSERT_RETURN(sess, PJ_EINVAL);
648
649 pj_grp_lock_acquire(sess->grp_lock);
650 sess->auth_type = auth_type;
651 if (cred) {
652 pj_stun_auth_cred_dup(sess->pool, &sess->cred, cred);
653 } else {
654 sess->auth_type = PJ_STUN_AUTH_NONE;
655 pj_bzero(&sess->cred, sizeof(sess->cred));
656 }
657 pj_grp_lock_release(sess->grp_lock);
658
659 return PJ_SUCCESS;
660}
661
662PJ_DEF(void) pj_stun_session_set_log( pj_stun_session *sess,
663 unsigned flags)
664{
665 PJ_ASSERT_ON_FAIL(sess, return);
666 sess->log_flag = flags;
667}
668
669PJ_DEF(pj_bool_t) pj_stun_session_use_fingerprint(pj_stun_session *sess,
670 pj_bool_t use)
671{
672 pj_bool_t old_use;
673
674 PJ_ASSERT_RETURN(sess, PJ_FALSE);
675
676 old_use = sess->use_fingerprint;
677 sess->use_fingerprint = use;
678 return old_use;
679}
680
681static pj_status_t get_auth(pj_stun_session *sess,
682 pj_stun_tx_data *tdata)
683{
684 if (sess->cred.type == PJ_STUN_AUTH_CRED_STATIC) {
685 //tdata->auth_info.realm = sess->cred.data.static_cred.realm;
686 tdata->auth_info.realm = sess->server_realm;
687 tdata->auth_info.username = sess->cred.data.static_cred.username;
688 tdata->auth_info.nonce = sess->cred.data.static_cred.nonce;
689
690 pj_stun_create_key(tdata->pool, &tdata->auth_info.auth_key,
691 &tdata->auth_info.realm,
692 &tdata->auth_info.username,
693 sess->cred.data.static_cred.data_type,
694 &sess->cred.data.static_cred.data);
695
696 } else if (sess->cred.type == PJ_STUN_AUTH_CRED_DYNAMIC) {
697 pj_str_t password;
698 void *user_data = sess->cred.data.dyn_cred.user_data;
699 pj_stun_passwd_type data_type = PJ_STUN_PASSWD_PLAIN;
700 pj_status_t rc;
701
702 rc = (*sess->cred.data.dyn_cred.get_cred)(tdata->msg, user_data,
703 tdata->pool,
704 &tdata->auth_info.realm,
705 &tdata->auth_info.username,
706 &tdata->auth_info.nonce,
707 &data_type, &password);
708 if (rc != PJ_SUCCESS)
709 return rc;
710
711 pj_stun_create_key(tdata->pool, &tdata->auth_info.auth_key,
712 &tdata->auth_info.realm, &tdata->auth_info.username,
713 data_type, &password);
714
715 } else {
716 pj_assert(!"Unknown credential type");
717 return PJ_EBUG;
718 }
719
720 return PJ_SUCCESS;
721}
722
723PJ_DEF(pj_status_t) pj_stun_session_create_req(pj_stun_session *sess,
724 int method,
725 pj_uint32_t magic,
726 const pj_uint8_t tsx_id[12],
727 pj_stun_tx_data **p_tdata)
728{
729 pj_stun_tx_data *tdata = NULL;
730 pj_status_t status;
731
732 PJ_ASSERT_RETURN(sess && p_tdata, PJ_EINVAL);
733
734 pj_grp_lock_acquire(sess->grp_lock);
735 if (sess->is_destroying) {
736 pj_grp_lock_release(sess->grp_lock);
737 return PJ_EINVALIDOP;
738 }
739
740 status = create_tdata(sess, &tdata);
741 if (status != PJ_SUCCESS)
742 goto on_error;
743
744 /* Create STUN message */
745 status = pj_stun_msg_create(tdata->pool, method, magic,
746 tsx_id, &tdata->msg);
747 if (status != PJ_SUCCESS)
748 goto on_error;
749
750 /* copy the request's transaction ID as the transaction key. */
751 pj_assert(sizeof(tdata->msg_key)==sizeof(tdata->msg->hdr.tsx_id));
752 tdata->msg_magic = tdata->msg->hdr.magic;
753 pj_memcpy(tdata->msg_key, tdata->msg->hdr.tsx_id,
754 sizeof(tdata->msg->hdr.tsx_id));
755
756
757 /* Get authentication information for the request */
758 if (sess->auth_type == PJ_STUN_AUTH_NONE) {
759 /* No authentication */
760
761 } else if (sess->auth_type == PJ_STUN_AUTH_SHORT_TERM) {
762 /* MUST put authentication in request */
763 status = get_auth(sess, tdata);
764 if (status != PJ_SUCCESS)
765 goto on_error;
766
767 } else if (sess->auth_type == PJ_STUN_AUTH_LONG_TERM) {
768 /* Only put authentication information if we've received
769 * response from server.
770 */
771 if (sess->next_nonce.slen != 0) {
772 status = get_auth(sess, tdata);
773 if (status != PJ_SUCCESS)
774 goto on_error;
775 tdata->auth_info.nonce = sess->next_nonce;
776 tdata->auth_info.realm = sess->server_realm;
777 }
778
779 } else {
780 pj_assert(!"Invalid authentication type");
781 status = PJ_EBUG;
782 goto on_error;
783 }
784
785 *p_tdata = tdata;
786 pj_grp_lock_release(sess->grp_lock);
787 return PJ_SUCCESS;
788
789on_error:
790 if (tdata)
791 pj_pool_release(tdata->pool);
792 pj_grp_lock_release(sess->grp_lock);
793 return status;
794}
795
796PJ_DEF(pj_status_t) pj_stun_session_create_ind(pj_stun_session *sess,
797 int msg_type,
798 pj_stun_tx_data **p_tdata)
799{
800 pj_stun_tx_data *tdata = NULL;
801 pj_status_t status;
802
803 PJ_ASSERT_RETURN(sess && p_tdata, PJ_EINVAL);
804
805 pj_grp_lock_acquire(sess->grp_lock);
806 if (sess->is_destroying) {
807 pj_grp_lock_release(sess->grp_lock);
808 return PJ_EINVALIDOP;
809 }
810
811 status = create_tdata(sess, &tdata);
812 if (status != PJ_SUCCESS) {
813 pj_grp_lock_release(sess->grp_lock);
814 return status;
815 }
816
817 /* Create STUN message */
818 msg_type |= PJ_STUN_INDICATION_BIT;
819 status = pj_stun_msg_create(tdata->pool, msg_type, PJ_STUN_MAGIC,
820 NULL, &tdata->msg);
821 if (status != PJ_SUCCESS) {
822 pj_pool_release(tdata->pool);
823 pj_grp_lock_release(sess->grp_lock);
824 return status;
825 }
826
827 *p_tdata = tdata;
828
829 pj_grp_lock_release(sess->grp_lock);
830 return PJ_SUCCESS;
831}
832
833/*
834 * Create a STUN response message.
835 */
836PJ_DEF(pj_status_t) pj_stun_session_create_res( pj_stun_session *sess,
837 const pj_stun_rx_data *rdata,
838 unsigned err_code,
839 const pj_str_t *err_msg,
840 pj_stun_tx_data **p_tdata)
841{
842 pj_status_t status;
843 pj_stun_tx_data *tdata = NULL;
844
845 pj_grp_lock_acquire(sess->grp_lock);
846 if (sess->is_destroying) {
847 pj_grp_lock_release(sess->grp_lock);
848 return PJ_EINVALIDOP;
849 }
850
851 status = create_tdata(sess, &tdata);
852 if (status != PJ_SUCCESS) {
853 pj_grp_lock_release(sess->grp_lock);
854 return status;
855 }
856
857 /* Create STUN response message */
858 status = pj_stun_msg_create_response(tdata->pool, rdata->msg,
859 err_code, err_msg, &tdata->msg);
860 if (status != PJ_SUCCESS) {
861 pj_pool_release(tdata->pool);
862 pj_grp_lock_release(sess->grp_lock);
863 return status;
864 }
865
866 /* copy the request's transaction ID as the transaction key. */
867 pj_assert(sizeof(tdata->msg_key)==sizeof(rdata->msg->hdr.tsx_id));
868 tdata->msg_magic = rdata->msg->hdr.magic;
869 pj_memcpy(tdata->msg_key, rdata->msg->hdr.tsx_id,
870 sizeof(rdata->msg->hdr.tsx_id));
871
872 /* copy the credential found in the request */
873 pj_stun_req_cred_info_dup(tdata->pool, &tdata->auth_info, &rdata->info);
874
875 *p_tdata = tdata;
876
877 pj_grp_lock_release(sess->grp_lock);
878
879 return PJ_SUCCESS;
880}
881
882
883/* Print outgoing message to log */
884static void dump_tx_msg(pj_stun_session *sess, const pj_stun_msg *msg,
885 unsigned pkt_size, const pj_sockaddr_t *addr)
886{
887 char dst_name[PJ_INET6_ADDRSTRLEN+10];
888
889 if ((PJ_STUN_IS_REQUEST(msg->hdr.type) &&
890 (sess->log_flag & PJ_STUN_SESS_LOG_TX_REQ)==0) ||
891 (PJ_STUN_IS_RESPONSE(msg->hdr.type) &&
892 (sess->log_flag & PJ_STUN_SESS_LOG_TX_RES)==0) ||
893 (PJ_STUN_IS_INDICATION(msg->hdr.type) &&
894 (sess->log_flag & PJ_STUN_SESS_LOG_TX_IND)==0))
895 {
896 return;
897 }
898
899 pj_sockaddr_print(addr, dst_name, sizeof(dst_name), 3);
900
901 PJ_LOG(5,(SNAME(sess),
902 "TX %d bytes STUN message to %s:\n"
903 "--- begin STUN message ---\n"
904 "%s"
905 "--- end of STUN message ---\n",
906 pkt_size, dst_name,
907 pj_stun_msg_dump(msg, sess->dump_buf, sizeof(sess->dump_buf),
908 NULL)));
909
910}
911
912
913PJ_DEF(pj_status_t) pj_stun_session_send_msg( pj_stun_session *sess,
914 void *token,
915 pj_bool_t cache_res,
916 pj_bool_t retransmit,
917 const pj_sockaddr_t *server,
918 unsigned addr_len,
919 pj_stun_tx_data *tdata)
920{
921 pj_status_t status;
922
923 PJ_ASSERT_RETURN(sess && addr_len && server && tdata, PJ_EINVAL);
924
925 /* Lock the session and prevent user from destroying us in the callback */
926 pj_grp_lock_acquire(sess->grp_lock);
927 if (sess->is_destroying) {
928 pj_grp_lock_release(sess->grp_lock);
929 return PJ_EINVALIDOP;
930 }
931
932 pj_log_push_indent();
933
934 /* Allocate packet */
935 tdata->max_len = PJ_STUN_MAX_PKT_LEN;
936 tdata->pkt = pj_pool_alloc(tdata->pool, tdata->max_len);
937
938 tdata->token = token;
939 tdata->retransmit = retransmit;
940
941 /* Apply options */
942 status = apply_msg_options(sess, tdata->pool, &tdata->auth_info,
943 tdata->msg);
944 if (status != PJ_SUCCESS) {
945 pj_stun_msg_destroy_tdata(sess, tdata);
946 LOG_ERR_(sess, "Error applying options", status);
947 goto on_return;
948 }
949
950 /* Encode message */
951 status = pj_stun_msg_encode(tdata->msg, (pj_uint8_t*)tdata->pkt,
952 tdata->max_len, 0,
953 &tdata->auth_info.auth_key,
954 &tdata->pkt_size);
955 if (status != PJ_SUCCESS) {
956 pj_stun_msg_destroy_tdata(sess, tdata);
957 LOG_ERR_(sess, "STUN encode() error", status);
958 goto on_return;
959 }
960
961 /* Dump packet */
962 dump_tx_msg(sess, tdata->msg, (unsigned)tdata->pkt_size, server);
963
964 /* If this is a STUN request message, then send the request with
965 * a new STUN client transaction.
966 */
967 if (PJ_STUN_IS_REQUEST(tdata->msg->hdr.type)) {
968
969 /* Create STUN client transaction */
970 status = pj_stun_client_tsx_create(sess->cfg, tdata->pool,
971 sess->grp_lock,
972 &tsx_cb, &tdata->client_tsx);
973 PJ_ASSERT_RETURN(status==PJ_SUCCESS, status);
974 pj_stun_client_tsx_set_data(tdata->client_tsx, (void*)tdata);
975
976 /* Save the remote address */
977 tdata->addr_len = addr_len;
978 tdata->dst_addr = server;
979
980 /* Send the request! */
981 status = pj_stun_client_tsx_send_msg(tdata->client_tsx, retransmit,
982 tdata->pkt,
983 (unsigned)tdata->pkt_size);
984 if (status != PJ_SUCCESS && status != PJ_EPENDING) {
985 pj_stun_msg_destroy_tdata(sess, tdata);
986 LOG_ERR_(sess, "Error sending STUN request", status);
987 goto on_return;
988 }
989
990 /* Add to pending request list */
991 tsx_add(sess, tdata);
992
993 } else {
994 if (cache_res &&
995 (PJ_STUN_IS_SUCCESS_RESPONSE(tdata->msg->hdr.type) ||
996 PJ_STUN_IS_ERROR_RESPONSE(tdata->msg->hdr.type)))
997 {
998 /* Requested to keep the response in the cache */
999 pj_time_val timeout;
1000
1001 pj_memset(&tdata->res_timer, 0, sizeof(tdata->res_timer));
1002 pj_timer_entry_init(&tdata->res_timer, PJ_FALSE, tdata,
1003 &on_cache_timeout);
1004
1005 timeout.sec = sess->cfg->res_cache_msec / 1000;
1006 timeout.msec = sess->cfg->res_cache_msec % 1000;
1007
1008 status = pj_timer_heap_schedule_w_grp_lock(sess->cfg->timer_heap,
1009 &tdata->res_timer,
1010 &timeout, PJ_TRUE,
1011 sess->grp_lock);
1012 if (status != PJ_SUCCESS) {
1013 pj_stun_msg_destroy_tdata(sess, tdata);
1014 LOG_ERR_(sess, "Error scheduling response timer", status);
1015 goto on_return;
1016 }
1017
1018 pj_list_push_back(&sess->cached_response_list, tdata);
1019 }
1020
1021 /* Otherwise for non-request message, send directly to transport. */
1022 status = sess->cb.on_send_msg(sess, token, tdata->pkt,
1023 tdata->pkt_size, server, addr_len);
1024
1025 if (status != PJ_SUCCESS && status != PJ_EPENDING) {
1026 pj_stun_msg_destroy_tdata(sess, tdata);
1027 LOG_ERR_(sess, "Error sending STUN request", status);
1028 goto on_return;
1029 }
1030
1031 /* Destroy only when response is not cached*/
1032 if (tdata->res_timer.id == 0) {
1033 pj_stun_msg_destroy_tdata(sess, tdata);
1034 }
1035 }
1036
1037on_return:
1038 pj_log_pop_indent();
1039
1040 if (pj_grp_lock_release(sess->grp_lock))
1041 return PJ_EGONE;
1042
1043 return status;
1044}
1045
1046
1047/*
1048 * Create and send STUN response message.
1049 */
1050PJ_DEF(pj_status_t) pj_stun_session_respond( pj_stun_session *sess,
1051 const pj_stun_rx_data *rdata,
1052 unsigned code,
1053 const char *errmsg,
1054 void *token,
1055 pj_bool_t cache,
1056 const pj_sockaddr_t *dst_addr,
1057 unsigned addr_len)
1058{
1059 pj_status_t status;
1060 pj_str_t reason;
1061 pj_stun_tx_data *tdata;
1062
1063 pj_grp_lock_acquire(sess->grp_lock);
1064 if (sess->is_destroying) {
1065 pj_grp_lock_release(sess->grp_lock);
1066 return PJ_EINVALIDOP;
1067 }
1068
1069 status = pj_stun_session_create_res(sess, rdata, code,
1070 (errmsg?pj_cstr(&reason,errmsg):NULL),
1071 &tdata);
1072 if (status != PJ_SUCCESS) {
1073 pj_grp_lock_release(sess->grp_lock);
1074 return status;
1075 }
1076
1077 status = pj_stun_session_send_msg(sess, token, cache, PJ_FALSE,
1078 dst_addr, addr_len, tdata);
1079
1080 pj_grp_lock_release(sess->grp_lock);
1081 return status;
1082}
1083
1084
1085/*
1086 * Cancel outgoing STUN transaction.
1087 */
1088PJ_DEF(pj_status_t) pj_stun_session_cancel_req( pj_stun_session *sess,
1089 pj_stun_tx_data *tdata,
1090 pj_bool_t notify,
1091 pj_status_t notify_status)
1092{
1093 PJ_ASSERT_RETURN(sess && tdata, PJ_EINVAL);
1094 PJ_ASSERT_RETURN(!notify || notify_status!=PJ_SUCCESS, PJ_EINVAL);
1095 PJ_ASSERT_RETURN(PJ_STUN_IS_REQUEST(tdata->msg->hdr.type), PJ_EINVAL);
1096
1097 /* Lock the session and prevent user from destroying us in the callback */
1098 pj_grp_lock_acquire(sess->grp_lock);
1099 if (sess->is_destroying) {
1100 pj_grp_lock_release(sess->grp_lock);
1101 return PJ_EINVALIDOP;
1102 }
1103
1104 if (notify) {
1105 (sess->cb.on_request_complete)(sess, notify_status, tdata->token,
1106 tdata, NULL, NULL, 0);
1107 }
1108
1109 /* Just destroy tdata. This will destroy the transaction as well */
1110 pj_stun_msg_destroy_tdata(sess, tdata);
1111
1112 pj_grp_lock_release(sess->grp_lock);
1113
1114 return PJ_SUCCESS;
1115}
1116
1117/*
1118 * Explicitly request retransmission of the request.
1119 */
1120PJ_DEF(pj_status_t) pj_stun_session_retransmit_req(pj_stun_session *sess,
1121 pj_stun_tx_data *tdata,
1122 pj_bool_t mod_count)
1123{
1124 pj_status_t status;
1125
1126 PJ_ASSERT_RETURN(sess && tdata, PJ_EINVAL);
1127 PJ_ASSERT_RETURN(PJ_STUN_IS_REQUEST(tdata->msg->hdr.type), PJ_EINVAL);
1128
1129 /* Lock the session and prevent user from destroying us in the callback */
1130 pj_grp_lock_acquire(sess->grp_lock);
1131 if (sess->is_destroying) {
1132 pj_grp_lock_release(sess->grp_lock);
1133 return PJ_EINVALIDOP;
1134 }
1135
1136 status = pj_stun_client_tsx_retransmit(tdata->client_tsx, mod_count);
1137
1138 pj_grp_lock_release(sess->grp_lock);
1139
1140 return status;
1141}
1142
1143
1144/* Send response */
1145static pj_status_t send_response(pj_stun_session *sess, void *token,
1146 pj_pool_t *pool, pj_stun_msg *response,
1147 const pj_stun_req_cred_info *auth_info,
1148 pj_bool_t retransmission,
1149 const pj_sockaddr_t *addr, unsigned addr_len)
1150{
1151 pj_uint8_t *out_pkt;
1152 pj_size_t out_max_len, out_len;
1153 pj_status_t status;
1154
1155 /* Apply options */
1156 if (!retransmission) {
1157 status = apply_msg_options(sess, pool, auth_info, response);
1158 if (status != PJ_SUCCESS)
1159 return status;
1160 }
1161
1162 /* Alloc packet buffer */
1163 out_max_len = PJ_STUN_MAX_PKT_LEN;
1164 out_pkt = (pj_uint8_t*) pj_pool_alloc(pool, out_max_len);
1165
1166 /* Encode */
1167 status = pj_stun_msg_encode(response, out_pkt, out_max_len, 0,
1168 &auth_info->auth_key, &out_len);
1169 if (status != PJ_SUCCESS) {
1170 LOG_ERR_(sess, "Error encoding message", status);
1171 return status;
1172 }
1173
1174 /* Print log */
1175 dump_tx_msg(sess, response, (unsigned)out_len, addr);
1176
1177 /* Send packet */
1178 status = sess->cb.on_send_msg(sess, token, out_pkt, (unsigned)out_len,
1179 addr, addr_len);
1180
1181 return status;
1182}
1183
1184/* Authenticate incoming message */
1185static pj_status_t authenticate_req(pj_stun_session *sess,
1186 void *token,
1187 const pj_uint8_t *pkt,
1188 unsigned pkt_len,
1189 pj_stun_rx_data *rdata,
1190 pj_pool_t *tmp_pool,
1191 const pj_sockaddr_t *src_addr,
1192 unsigned src_addr_len)
1193{
1194 pj_stun_msg *response;
1195 pj_status_t status;
1196
1197 if (PJ_STUN_IS_ERROR_RESPONSE(rdata->msg->hdr.type) ||
1198 sess->auth_type == PJ_STUN_AUTH_NONE)
1199 {
1200 return PJ_SUCCESS;
1201 }
1202
1203 status = pj_stun_authenticate_request(pkt, pkt_len, rdata->msg,
1204 &sess->cred, tmp_pool, &rdata->info,
1205 &response);
1206 if (status != PJ_SUCCESS && response != NULL) {
1207 PJ_LOG(5,(SNAME(sess), "Message authentication failed"));
1208 send_response(sess, token, tmp_pool, response, &rdata->info,
1209 PJ_FALSE, src_addr, src_addr_len);
1210 }
1211
1212 return status;
1213}
1214
1215
1216/* Handle incoming response */
1217static pj_status_t on_incoming_response(pj_stun_session *sess,
1218 unsigned options,
1219 const pj_uint8_t *pkt,
1220 unsigned pkt_len,
1221 pj_stun_msg *msg,
1222 const pj_sockaddr_t *src_addr,
1223 unsigned src_addr_len)
1224{
1225 pj_stun_tx_data *tdata;
1226 pj_status_t status;
1227
1228 /* Lookup pending client transaction */
1229 tdata = tsx_lookup(sess, msg);
1230 if (tdata == NULL) {
1231 PJ_LOG(5,(SNAME(sess),
1232 "Transaction not found, response silently discarded"));
1233 return PJ_SUCCESS;
1234 }
1235
1236 if (sess->auth_type == PJ_STUN_AUTH_NONE)
1237 options |= PJ_STUN_NO_AUTHENTICATE;
1238
1239 /* Authenticate the message, unless PJ_STUN_NO_AUTHENTICATE
1240 * is specified in the option.
1241 */
1242 if ((options & PJ_STUN_NO_AUTHENTICATE) == 0 &&
1243 tdata->auth_info.auth_key.slen != 0 &&
1244 pj_stun_auth_valid_for_msg(msg))
1245 {
1246 status = pj_stun_authenticate_response(pkt, pkt_len, msg,
1247 &tdata->auth_info.auth_key);
1248 if (status != PJ_SUCCESS) {
1249 PJ_LOG(5,(SNAME(sess),
1250 "Response authentication failed"));
1251 return status;
1252 }
1253 }
1254
1255 /* Pass the response to the transaction.
1256 * If the message is accepted, transaction callback will be called,
1257 * and this will call the session callback too.
1258 */
1259 status = pj_stun_client_tsx_on_rx_msg(tdata->client_tsx, msg,
1260 src_addr, src_addr_len);
1261 if (status != PJ_SUCCESS) {
1262 return status;
1263 }
1264
1265 return PJ_SUCCESS;
1266}
1267
1268
1269/* For requests, check if we cache the response */
1270static pj_status_t check_cached_response(pj_stun_session *sess,
1271 pj_pool_t *tmp_pool,
1272 const pj_stun_msg *msg,
1273 const pj_sockaddr_t *src_addr,
1274 unsigned src_addr_len)
1275{
1276 pj_stun_tx_data *t;
1277
1278 /* First lookup response in response cache */
1279 t = sess->cached_response_list.next;
1280 while (t != &sess->cached_response_list) {
1281 if (t->msg_magic == msg->hdr.magic &&
1282 t->msg->hdr.type == msg->hdr.type &&
1283 pj_memcmp(t->msg_key, msg->hdr.tsx_id,
1284 sizeof(msg->hdr.tsx_id))==0)
1285 {
1286 break;
1287 }
1288 t = t->next;
1289 }
1290
1291 if (t != &sess->cached_response_list) {
1292 /* Found response in the cache */
1293
1294 PJ_LOG(5,(SNAME(sess),
1295 "Request retransmission, sending cached response"));
1296
1297 send_response(sess, t->token, tmp_pool, t->msg, &t->auth_info,
1298 PJ_TRUE, src_addr, src_addr_len);
1299 return PJ_SUCCESS;
1300 }
1301
1302 return PJ_ENOTFOUND;
1303}
1304
1305/* Handle incoming request */
1306static pj_status_t on_incoming_request(pj_stun_session *sess,
1307 unsigned options,
1308 void *token,
1309 pj_pool_t *tmp_pool,
1310 const pj_uint8_t *in_pkt,
1311 unsigned in_pkt_len,
1312 pj_stun_msg *msg,
1313 const pj_sockaddr_t *src_addr,
1314 unsigned src_addr_len)
1315{
1316 pj_stun_rx_data rdata;
1317 pj_status_t status;
1318
1319 /* Init rdata */
1320 rdata.msg = msg;
1321 pj_bzero(&rdata.info, sizeof(rdata.info));
1322
1323 if (sess->auth_type == PJ_STUN_AUTH_NONE)
1324 options |= PJ_STUN_NO_AUTHENTICATE;
1325
1326 /* Authenticate the message, unless PJ_STUN_NO_AUTHENTICATE
1327 * is specified in the option.
1328 */
1329 if ((options & PJ_STUN_NO_AUTHENTICATE) == 0) {
1330 status = authenticate_req(sess, token, (const pj_uint8_t*) in_pkt,
1331 in_pkt_len,&rdata, tmp_pool, src_addr,
1332 src_addr_len);
1333 if (status != PJ_SUCCESS) {
1334 return status;
1335 }
1336 }
1337
1338 /* Distribute to handler, or respond with Bad Request */
1339 if (sess->cb.on_rx_request) {
1340 status = (*sess->cb.on_rx_request)(sess, in_pkt, in_pkt_len, &rdata,
1341 token, src_addr, src_addr_len);
1342 } else {
1343 pj_str_t err_text;
1344 pj_stun_msg *response;
1345
1346 err_text = pj_str("Callback is not set to handle request");
1347 status = pj_stun_msg_create_response(tmp_pool, msg,
1348 PJ_STUN_SC_BAD_REQUEST,
1349 &err_text, &response);
1350 if (status == PJ_SUCCESS && response) {
1351 status = send_response(sess, token, tmp_pool, response,
1352 NULL, PJ_FALSE, src_addr, src_addr_len);
1353 }
1354 }
1355
1356 return status;
1357}
1358
1359
1360/* Handle incoming indication */
1361static pj_status_t on_incoming_indication(pj_stun_session *sess,
1362 void *token,
1363 pj_pool_t *tmp_pool,
1364 const pj_uint8_t *in_pkt,
1365 unsigned in_pkt_len,
1366 const pj_stun_msg *msg,
1367 const pj_sockaddr_t *src_addr,
1368 unsigned src_addr_len)
1369{
1370 PJ_UNUSED_ARG(tmp_pool);
1371
1372 /* Distribute to handler */
1373 if (sess->cb.on_rx_indication) {
1374 return (*sess->cb.on_rx_indication)(sess, in_pkt, in_pkt_len, msg,
1375 token, src_addr, src_addr_len);
1376 } else {
1377 return PJ_SUCCESS;
1378 }
1379}
1380
1381
1382/* Print outgoing message to log */
1383static void dump_rx_msg(pj_stun_session *sess, const pj_stun_msg *msg,
1384 unsigned pkt_size, const pj_sockaddr_t *addr)
1385{
1386 char src_info[PJ_INET6_ADDRSTRLEN+10];
1387
1388 if ((PJ_STUN_IS_REQUEST(msg->hdr.type) &&
1389 (sess->log_flag & PJ_STUN_SESS_LOG_RX_REQ)==0) ||
1390 (PJ_STUN_IS_RESPONSE(msg->hdr.type) &&
1391 (sess->log_flag & PJ_STUN_SESS_LOG_RX_RES)==0) ||
1392 (PJ_STUN_IS_INDICATION(msg->hdr.type) &&
1393 (sess->log_flag & PJ_STUN_SESS_LOG_RX_IND)==0))
1394 {
1395 return;
1396 }
1397
1398 pj_sockaddr_print(addr, src_info, sizeof(src_info), 3);
1399
1400 PJ_LOG(5,(SNAME(sess),
1401 "RX %d bytes STUN message from %s:\n"
1402 "--- begin STUN message ---\n"
1403 "%s"
1404 "--- end of STUN message ---\n",
1405 pkt_size, src_info,
1406 pj_stun_msg_dump(msg, sess->dump_buf, sizeof(sess->dump_buf),
1407 NULL)));
1408
1409}
1410
1411/* Incoming packet */
1412PJ_DEF(pj_status_t) pj_stun_session_on_rx_pkt(pj_stun_session *sess,
1413 const void *packet,
1414 pj_size_t pkt_size,
1415 unsigned options,
1416 void *token,
1417 pj_size_t *parsed_len,
1418 const pj_sockaddr_t *src_addr,
1419 unsigned src_addr_len)
1420{
1421 pj_stun_msg *msg, *response;
1422 pj_status_t status;
1423
1424 PJ_ASSERT_RETURN(sess && packet && pkt_size, PJ_EINVAL);
1425
1426 /* Lock the session and prevent user from destroying us in the callback */
1427 pj_grp_lock_acquire(sess->grp_lock);
1428
1429 if (sess->is_destroying) {
1430 pj_grp_lock_release(sess->grp_lock);
1431 return PJ_EINVALIDOP;
1432 }
1433
1434 pj_log_push_indent();
1435
1436 /* Reset pool */
1437 pj_pool_reset(sess->rx_pool);
1438
1439 /* Try to parse the message */
1440 status = pj_stun_msg_decode(sess->rx_pool, (const pj_uint8_t*)packet,
1441 pkt_size, options,
1442 &msg, parsed_len, &response);
1443 if (status != PJ_SUCCESS) {
1444 LOG_ERR_(sess, "STUN msg_decode() error", status);
1445 if (response) {
1446 send_response(sess, token, sess->rx_pool, response, NULL,
1447 PJ_FALSE, src_addr, src_addr_len);
1448 }
1449 goto on_return;
1450 }
1451
1452 dump_rx_msg(sess, msg, (unsigned)pkt_size, src_addr);
1453
1454 /* For requests, check if we have cached response */
1455 status = check_cached_response(sess, sess->rx_pool, msg,
1456 src_addr, src_addr_len);
1457 if (status == PJ_SUCCESS) {
1458 goto on_return;
1459 }
1460
1461 /* Handle message */
1462 if (PJ_STUN_IS_SUCCESS_RESPONSE(msg->hdr.type) ||
1463 PJ_STUN_IS_ERROR_RESPONSE(msg->hdr.type))
1464 {
1465 status = on_incoming_response(sess, options,
1466 (const pj_uint8_t*) packet,
1467 (unsigned)pkt_size, msg,
1468 src_addr, src_addr_len);
1469
1470 } else if (PJ_STUN_IS_REQUEST(msg->hdr.type)) {
1471
1472 status = on_incoming_request(sess, options, token, sess->rx_pool,
1473 (const pj_uint8_t*) packet,
1474 (unsigned)pkt_size,
1475 msg, src_addr, src_addr_len);
1476
1477 } else if (PJ_STUN_IS_INDICATION(msg->hdr.type)) {
1478
1479 status = on_incoming_indication(sess, token, sess->rx_pool,
1480 (const pj_uint8_t*) packet,
1481 (unsigned)pkt_size, msg, src_addr,
1482 src_addr_len);
1483
1484 } else {
1485 pj_assert(!"Unexpected!");
1486 status = PJ_EBUG;
1487 }
1488
1489on_return:
1490 pj_log_pop_indent();
1491
1492 if (pj_grp_lock_release(sess->grp_lock))
1493 return PJ_EGONE;
1494
1495 return status;
1496}
1497