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, &notify_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