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