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