3 * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
4 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
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.
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.
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
20 #include <pjnath/turn_session.h>
21 #include <pjnath/errno.h>
22 #include <pjlib-util/srv_resolver.h>
23 #include <pj/addr_resolv.h>
24 #include <pj/assert.h>
34 #define PJ_TURN_CHANNEL_MIN 0x4000
35 #define PJ_TURN_CHANNEL_MAX 0x7FFF /* inclusive */
36 #define PJ_TURN_CHANNEL_HTABLE_SIZE 8
37 #define PJ_TURN_PERM_HTABLE_SIZE 8
39 static const char *state_names[] =
58 /* This structure describes a channel binding. A channel binding is index by
59 * the channel number or IP address and port number of the peer.
63 /* The channel number */
66 /* PJ_TRUE if we've received successful response to ChannelBind request
71 /* The peer IP address and port */
74 /* The channel binding expiration */
79 /* This structure describes a permission. A permission is identified by the
84 /* Cache of hash value to speed-up lookup */
87 /* The permission IP address. The port number MUST be zero */
90 /* Number of peers that uses this permission. */
93 /* Automatically renew this permission once it expires? */
96 /* The permission expiration */
99 /* Arbitrary/random pointer value (token) to map this perm with the
100 * request to create it. It is used to invalidate this perm when the
107 /* The TURN client session structure */
108 struct pj_turn_session
111 const char *obj_name;
112 pj_turn_session_cb cb;
114 pj_stun_config stun_cfg;
119 pj_turn_state_t state;
120 pj_status_t last_status;
121 pj_bool_t pending_destroy;
122 pj_bool_t destroy_notified;
124 pj_stun_session *stun;
130 pj_timer_heap_t *timer_heap;
131 pj_timer_entry timer;
133 pj_dns_srv_async_query *dns_async;
134 pj_uint16_t default_port;
137 pj_turn_tp_type conn_type;
138 pj_uint16_t srv_addr_cnt;
139 pj_sockaddr *srv_addr_list;
140 pj_sockaddr *srv_addr;
142 pj_bool_t pending_alloc;
143 pj_turn_alloc_param alloc_param;
145 pj_sockaddr mapped_addr;
146 pj_sockaddr relay_addr;
148 pj_hash_table_t *ch_table;
149 pj_hash_table_t *perm_table;
151 pj_uint32_t send_ind_tsx_id[3];
152 /* tx_pkt must be 16bit aligned */
153 pj_uint8_t tx_pkt[PJ_TURN_MAX_PKT_LEN];
162 static void sess_shutdown(pj_turn_session *sess,
164 static void do_destroy(pj_turn_session *sess);
165 static void send_refresh(pj_turn_session *sess, int lifetime);
166 static pj_status_t stun_on_send_msg(pj_stun_session *sess,
170 const pj_sockaddr_t *dst_addr,
172 static void stun_on_request_complete(pj_stun_session *sess,
175 pj_stun_tx_data *tdata,
176 const pj_stun_msg *response,
177 const pj_sockaddr_t *src_addr,
178 unsigned src_addr_len);
179 static pj_status_t stun_on_rx_indication(pj_stun_session *sess,
180 const pj_uint8_t *pkt,
182 const pj_stun_msg *msg,
184 const pj_sockaddr_t *src_addr,
185 unsigned src_addr_len);
186 static void dns_srv_resolver_cb(void *user_data,
188 const pj_dns_srv_record *rec);
189 static struct ch_t *lookup_ch_by_addr(pj_turn_session *sess,
190 const pj_sockaddr_t *addr,
193 pj_bool_t bind_channel);
194 static struct ch_t *lookup_ch_by_chnum(pj_turn_session *sess,
196 static struct perm_t *lookup_perm(pj_turn_session *sess,
197 const pj_sockaddr_t *addr,
200 static void invalidate_perm(pj_turn_session *sess,
201 struct perm_t *perm);
202 static void on_timer_event(pj_timer_heap_t *th, pj_timer_entry *e);
206 * Create default pj_turn_alloc_param.
208 PJ_DEF(void) pj_turn_alloc_param_default(pj_turn_alloc_param *prm)
210 pj_bzero(prm, sizeof(*prm));
214 * Duplicate pj_turn_alloc_param.
216 PJ_DEF(void) pj_turn_alloc_param_copy( pj_pool_t *pool,
217 pj_turn_alloc_param *dst,
218 const pj_turn_alloc_param *src)
221 pj_memcpy(dst, src, sizeof(*dst));
225 * Get TURN state name.
227 PJ_DEF(const char*) pj_turn_state_name(pj_turn_state_t state)
229 return state_names[state];
233 * Create TURN client session.
235 PJ_DEF(pj_status_t) pj_turn_session_create( const pj_stun_config *cfg,
238 pj_turn_tp_type conn_type,
239 const pj_turn_session_cb *cb,
242 pj_turn_session **p_sess)
245 pj_turn_session *sess;
246 pj_stun_session_cb stun_cb;
247 pj_lock_t *null_lock;
250 PJ_ASSERT_RETURN(cfg && cfg->pf && cb && p_sess, PJ_EINVAL);
251 PJ_ASSERT_RETURN(cb->on_send_pkt, PJ_EINVAL);
253 PJ_UNUSED_ARG(options);
258 /* Allocate and create TURN session */
259 pool = pj_pool_create(cfg->pf, name, PJNATH_POOL_LEN_TURN_SESS,
260 PJNATH_POOL_INC_TURN_SESS, NULL);
261 sess = PJ_POOL_ZALLOC_T(pool, pj_turn_session);
263 sess->obj_name = pool->obj_name;
264 sess->timer_heap = cfg->timer_heap;
265 sess->af = (pj_uint16_t)af;
266 sess->conn_type = conn_type;
267 sess->ka_interval = PJ_TURN_KEEP_ALIVE_SEC;
268 sess->user_data = user_data;
269 sess->next_ch = PJ_TURN_CHANNEL_MIN;
271 /* Copy STUN session */
272 pj_memcpy(&sess->stun_cfg, cfg, sizeof(pj_stun_config));
275 pj_memcpy(&sess->cb, cb, sizeof(*cb));
277 /* Peer hash table */
278 sess->ch_table = pj_hash_create(pool, PJ_TURN_CHANNEL_HTABLE_SIZE);
280 /* Permission hash table */
281 sess->perm_table = pj_hash_create(pool, PJ_TURN_PERM_HTABLE_SIZE);
284 status = pj_lock_create_recursive_mutex(pool, sess->obj_name,
286 if (status != PJ_SUCCESS) {
292 pj_timer_entry_init(&sess->timer, TIMER_NONE, sess, &on_timer_event);
294 /* Create STUN session */
295 pj_bzero(&stun_cb, sizeof(stun_cb));
296 stun_cb.on_send_msg = &stun_on_send_msg;
297 stun_cb.on_request_complete = &stun_on_request_complete;
298 stun_cb.on_rx_indication = &stun_on_rx_indication;
299 status = pj_stun_session_create(&sess->stun_cfg, sess->obj_name, &stun_cb,
300 PJ_FALSE, &sess->stun);
301 if (status != PJ_SUCCESS) {
306 /* Attach ourself to STUN session */
307 pj_stun_session_set_user_data(sess->stun, sess);
309 /* Replace mutex in STUN session with a NULL mutex, since access to
310 * STUN session is serialized.
312 status = pj_lock_create_null_mutex(pool, name, &null_lock);
313 if (status != PJ_SUCCESS) {
317 pj_stun_session_set_lock(sess->stun, null_lock, PJ_TRUE);
321 PJ_LOG(4,(sess->obj_name, "TURN client session created"));
329 static void do_destroy(pj_turn_session *sess)
333 pj_lock_acquire(sess->lock);
336 /* Cancel pending timer, if any */
337 if (sess->timer.id != TIMER_NONE) {
338 pj_timer_heap_cancel(sess->timer_heap, &sess->timer);
339 sess->timer.id = TIMER_NONE;
342 /* Destroy STUN session */
344 pj_stun_session_destroy(sess->stun);
350 pj_lock_release(sess->lock);
351 pj_lock_destroy(sess->lock);
357 pj_pool_t *pool = sess->pool;
359 PJ_LOG(4,(sess->obj_name, "TURN client session destroyed"));
362 pj_pool_release(pool);
367 /* Set session state */
368 static void set_state(pj_turn_session *sess, enum pj_turn_state_t state)
370 pj_turn_state_t old_state = sess->state;
372 if (state==sess->state)
375 PJ_LOG(4,(sess->obj_name, "State changed %s --> %s",
376 state_names[old_state], state_names[state]));
379 if (sess->cb.on_state) {
380 (*sess->cb.on_state)(sess, old_state, state);
385 * Notify application and shutdown the TURN session.
387 static void sess_shutdown(pj_turn_session *sess,
390 pj_bool_t can_destroy = PJ_TRUE;
392 PJ_LOG(4,(sess->obj_name, "Request to shutdown in state %s, cause:%d",
393 state_names[sess->state], status));
395 if (sess->last_status == PJ_SUCCESS && status != PJ_SUCCESS)
396 sess->last_status = status;
398 switch (sess->state) {
399 case PJ_TURN_STATE_NULL:
401 case PJ_TURN_STATE_RESOLVING:
402 if (sess->dns_async != NULL) {
403 pj_dns_srv_cancel_query(sess->dns_async, PJ_FALSE);
404 sess->dns_async = NULL;
407 case PJ_TURN_STATE_RESOLVED:
409 case PJ_TURN_STATE_ALLOCATING:
410 /* We need to wait until allocation complete */
411 sess->pending_destroy = PJ_TRUE;
412 can_destroy = PJ_FALSE;
414 case PJ_TURN_STATE_READY:
415 /* Send REFRESH with LIFETIME=0 */
416 can_destroy = PJ_FALSE;
417 send_refresh(sess, 0);
419 case PJ_TURN_STATE_DEALLOCATING:
420 can_destroy = PJ_FALSE;
421 /* This may recursively call this function again with
422 * state==PJ_TURN_STATE_DEALLOCATED.
424 send_refresh(sess, 0);
426 case PJ_TURN_STATE_DEALLOCATED:
427 case PJ_TURN_STATE_DESTROYING:
432 /* Schedule destroy */
433 pj_time_val delay = {0, 0};
435 set_state(sess, PJ_TURN_STATE_DESTROYING);
437 if (sess->timer.id != TIMER_NONE) {
438 pj_timer_heap_cancel(sess->timer_heap, &sess->timer);
439 sess->timer.id = TIMER_NONE;
442 sess->timer.id = TIMER_DESTROY;
443 pj_timer_heap_schedule(sess->timer_heap, &sess->timer, &delay);
449 * Public API to destroy TURN client session.
451 PJ_DEF(pj_status_t) pj_turn_session_shutdown(pj_turn_session *sess)
453 PJ_ASSERT_RETURN(sess, PJ_EINVAL);
455 pj_lock_acquire(sess->lock);
457 sess_shutdown(sess, PJ_SUCCESS);
459 pj_lock_release(sess->lock);
466 * Forcefully destroy the TURN session.
468 PJ_DEF(pj_status_t) pj_turn_session_destroy( pj_turn_session *sess,
469 pj_status_t last_err)
471 PJ_ASSERT_RETURN(sess, PJ_EINVAL);
473 if (last_err != PJ_SUCCESS && sess->last_status == PJ_SUCCESS)
474 sess->last_status = last_err;
475 set_state(sess, PJ_TURN_STATE_DEALLOCATED);
476 sess_shutdown(sess, PJ_SUCCESS);
482 * Get TURN session info.
484 PJ_DEF(pj_status_t) pj_turn_session_get_info( pj_turn_session *sess,
485 pj_turn_session_info *info)
489 PJ_ASSERT_RETURN(sess && info, PJ_EINVAL);
491 pj_gettimeofday(&now);
493 info->state = sess->state;
494 info->conn_type = sess->conn_type;
495 info->lifetime = sess->expiry.sec - now.sec;
496 info->last_status = sess->last_status;
499 pj_memcpy(&info->server, sess->srv_addr, sizeof(info->server));
501 pj_bzero(&info->server, sizeof(info->server));
503 pj_memcpy(&info->mapped_addr, &sess->mapped_addr,
504 sizeof(sess->mapped_addr));
505 pj_memcpy(&info->relay_addr, &sess->relay_addr,
506 sizeof(sess->relay_addr));
513 * Re-assign user data.
515 PJ_DEF(pj_status_t) pj_turn_session_set_user_data( pj_turn_session *sess,
518 sess->user_data = user_data;
524 * Retrieve user data.
526 PJ_DEF(void*) pj_turn_session_get_user_data(pj_turn_session *sess)
528 return sess->user_data;
533 * Configure message logging. By default all flags are enabled.
535 * @param sess The TURN client session.
536 * @param flags Bitmask combination of #pj_stun_sess_msg_log_flag
538 PJ_DEF(void) pj_turn_session_set_log( pj_turn_session *sess,
541 pj_stun_session_set_log(sess->stun, flags);
548 PJ_DEF(pj_status_t) pj_turn_session_set_software_name( pj_turn_session *sess,
553 pj_lock_acquire(sess->lock);
554 status = pj_stun_session_set_software_name(sess->stun, sw);
555 pj_lock_release(sess->lock);
562 * Set the server or domain name of the server.
564 PJ_DEF(pj_status_t) pj_turn_session_set_server( pj_turn_session *sess,
565 const pj_str_t *domain,
567 pj_dns_resolver *resolver)
569 pj_sockaddr tmp_addr;
570 pj_bool_t is_ip_addr;
573 PJ_ASSERT_RETURN(sess && domain, PJ_EINVAL);
574 PJ_ASSERT_RETURN(sess->state == PJ_TURN_STATE_NULL, PJ_EINVALIDOP);
576 pj_lock_acquire(sess->lock);
578 /* See if "domain" contains just IP address */
579 tmp_addr.addr.sa_family = sess->af;
580 status = pj_inet_pton(sess->af, domain,
581 pj_sockaddr_get_addr(&tmp_addr));
582 is_ip_addr = (status == PJ_SUCCESS);
584 if (!is_ip_addr && resolver) {
585 /* Resolve with DNS SRV resolution, and fallback to DNS A resolution
586 * if default_port is specified.
591 switch (sess->conn_type) {
593 res_name = pj_str("_turn._udp.");
596 res_name = pj_str("_turn._tcp.");
599 res_name = pj_str("_turns._tcp.");
602 status = PJNATH_ETURNINTP;
606 /* Fallback to DNS A only if default port is specified */
607 if (default_port>0 && default_port<65536) {
608 opt = PJ_DNS_SRV_FALLBACK_A;
609 sess->default_port = (pj_uint16_t)default_port;
612 PJ_LOG(5,(sess->obj_name, "Resolving %.*s%.*s with DNS SRV",
613 (int)res_name.slen, res_name.ptr,
614 (int)domain->slen, domain->ptr));
615 set_state(sess, PJ_TURN_STATE_RESOLVING);
617 /* User may have destroyed us in the callback */
618 if (sess->state != PJ_TURN_STATE_RESOLVING) {
619 status = PJ_ECANCELLED;
623 status = pj_dns_srv_resolve(domain, &res_name, default_port,
624 sess->pool, resolver, opt, sess,
625 &dns_srv_resolver_cb, &sess->dns_async);
626 if (status != PJ_SUCCESS) {
627 set_state(sess, PJ_TURN_STATE_NULL);
632 /* Resolver is not specified, resolve with standard gethostbyname().
633 * The default_port MUST be specified in this case.
638 /* Default port must be specified */
639 PJ_ASSERT_RETURN(default_port>0 && default_port<65536, PJ_EINVAL);
640 sess->default_port = (pj_uint16_t)default_port;
642 cnt = PJ_TURN_MAX_DNS_SRV_CNT;
644 pj_pool_calloc(sess->pool, cnt, sizeof(pj_addrinfo));
646 PJ_LOG(5,(sess->obj_name, "Resolving %.*s with DNS A",
647 (int)domain->slen, domain->ptr));
648 set_state(sess, PJ_TURN_STATE_RESOLVING);
650 /* User may have destroyed us in the callback */
651 if (sess->state != PJ_TURN_STATE_RESOLVING) {
652 status = PJ_ECANCELLED;
656 status = pj_getaddrinfo(sess->af, domain, &cnt, ai);
657 if (status != PJ_SUCCESS)
660 sess->srv_addr_cnt = (pj_uint16_t)cnt;
661 sess->srv_addr_list = (pj_sockaddr*)
662 pj_pool_calloc(sess->pool, cnt,
663 sizeof(pj_sockaddr));
664 for (i=0; i<cnt; ++i) {
665 pj_sockaddr *addr = &sess->srv_addr_list[i];
666 pj_memcpy(addr, &ai[i].ai_addr, sizeof(pj_sockaddr));
667 addr->addr.sa_family = sess->af;
668 addr->ipv4.sin_port = pj_htons(sess->default_port);
671 sess->srv_addr = &sess->srv_addr_list[0];
672 set_state(sess, PJ_TURN_STATE_RESOLVED);
676 pj_lock_release(sess->lock);
682 * Set credential to be used by the session.
684 PJ_DEF(pj_status_t) pj_turn_session_set_credential(pj_turn_session *sess,
685 const pj_stun_auth_cred *cred)
687 PJ_ASSERT_RETURN(sess && cred, PJ_EINVAL);
688 PJ_ASSERT_RETURN(sess->stun, PJ_EINVALIDOP);
690 pj_lock_acquire(sess->lock);
692 pj_stun_session_set_credential(sess->stun, PJ_STUN_AUTH_LONG_TERM, cred);
694 pj_lock_release(sess->lock);
701 * Create TURN allocation.
703 PJ_DEF(pj_status_t) pj_turn_session_alloc(pj_turn_session *sess,
704 const pj_turn_alloc_param *param)
706 pj_stun_tx_data *tdata;
707 pj_bool_t retransmit;
710 PJ_ASSERT_RETURN(sess, PJ_EINVAL);
711 PJ_ASSERT_RETURN(sess->state>PJ_TURN_STATE_NULL &&
712 sess->state<=PJ_TURN_STATE_RESOLVED,
715 pj_lock_acquire(sess->lock);
717 if (param && param != &sess->alloc_param)
718 pj_turn_alloc_param_copy(sess->pool, &sess->alloc_param, param);
720 if (sess->state < PJ_TURN_STATE_RESOLVED) {
721 sess->pending_alloc = PJ_TRUE;
723 PJ_LOG(4,(sess->obj_name, "Pending ALLOCATE in state %s",
724 state_names[sess->state]));
726 pj_lock_release(sess->lock);
731 /* Ready to allocate */
732 pj_assert(sess->state == PJ_TURN_STATE_RESOLVED);
734 /* Create a bare request */
735 status = pj_stun_session_create_req(sess->stun, PJ_STUN_ALLOCATE_REQUEST,
736 PJ_STUN_MAGIC, NULL, &tdata);
737 if (status != PJ_SUCCESS) {
738 pj_lock_release(sess->lock);
742 /* MUST include REQUESTED-TRANSPORT attribute */
743 pj_stun_msg_add_uint_attr(tdata->pool, tdata->msg,
744 PJ_STUN_ATTR_REQ_TRANSPORT,
745 PJ_STUN_SET_RT_PROTO(PJ_TURN_TP_UDP));
747 /* Include BANDWIDTH if requested */
748 if (sess->alloc_param.bandwidth > 0) {
749 pj_stun_msg_add_uint_attr(tdata->pool, tdata->msg,
750 PJ_STUN_ATTR_BANDWIDTH,
751 sess->alloc_param.bandwidth);
754 /* Include LIFETIME if requested */
755 if (sess->alloc_param.lifetime > 0) {
756 pj_stun_msg_add_uint_attr(tdata->pool, tdata->msg,
757 PJ_STUN_ATTR_LIFETIME,
758 sess->alloc_param.lifetime);
761 /* Server address must be set */
762 pj_assert(sess->srv_addr != NULL);
765 set_state(sess, PJ_TURN_STATE_ALLOCATING);
766 retransmit = (sess->conn_type == PJ_TURN_TP_UDP);
767 status = pj_stun_session_send_msg(sess->stun, NULL, PJ_FALSE,
768 retransmit, sess->srv_addr,
769 pj_sockaddr_get_len(sess->srv_addr),
771 if (status != PJ_SUCCESS) {
772 /* Set state back to RESOLVED. We don't want to destroy session now,
773 * let the application do it if it wants to.
775 set_state(sess, PJ_TURN_STATE_RESOLVED);
778 pj_lock_release(sess->lock);
784 * Install or renew permissions
786 PJ_DEF(pj_status_t) pj_turn_session_set_perm( pj_turn_session *sess,
788 const pj_sockaddr addr[],
791 pj_stun_tx_data *tdata;
792 pj_hash_iterator_t it_buf, *it;
794 unsigned i, attr_added=0;
797 PJ_ASSERT_RETURN(sess && addr_cnt && addr, PJ_EINVAL);
799 pj_lock_acquire(sess->lock);
801 /* Create a bare CreatePermission request */
802 status = pj_stun_session_create_req(sess->stun,
803 PJ_STUN_CREATE_PERM_REQUEST,
804 PJ_STUN_MAGIC, NULL, &tdata);
805 if (status != PJ_SUCCESS) {
806 pj_lock_release(sess->lock);
810 /* Create request token to map the request to the perm structures
811 * which the request belongs.
813 req_token = (void*)(long)pj_rand();
815 /* Process the addresses */
816 for (i=0; i<addr_cnt; ++i) {
819 /* Lookup the perm structure and create if it doesn't exist */
820 perm = lookup_perm(sess, &addr[i], pj_sockaddr_get_len(&addr[i]),
822 perm->renew = (options & 0x01);
824 /* Only add to the request if the request doesn't contain this
827 if (perm->req_token != req_token) {
828 perm->req_token = req_token;
830 /* Add XOR-PEER-ADDRESS */
831 status = pj_stun_msg_add_sockaddr_attr(tdata->pool, tdata->msg,
832 PJ_STUN_ATTR_XOR_PEER_ADDR,
836 if (status != PJ_SUCCESS)
843 pj_assert(attr_added != 0);
845 /* Send the request */
846 status = pj_stun_session_send_msg(sess->stun, req_token, PJ_FALSE,
847 (sess->conn_type==PJ_TURN_TP_UDP),
849 pj_sockaddr_get_len(sess->srv_addr),
851 if (status != PJ_SUCCESS) {
852 /* tdata is already destroyed */
857 pj_lock_release(sess->lock);
863 pj_stun_msg_destroy_tdata(sess->stun, tdata);
865 /* invalidate perm structures associated with this request */
866 it = pj_hash_first(sess->perm_table, &it_buf);
868 struct perm_t *perm = (struct perm_t*)
869 pj_hash_this(sess->perm_table, it);
870 it = pj_hash_next(sess->perm_table, it);
871 if (perm->req_token == req_token)
872 invalidate_perm(sess, perm);
874 pj_lock_release(sess->lock);
881 static void send_refresh(pj_turn_session *sess, int lifetime)
883 pj_stun_tx_data *tdata;
886 PJ_ASSERT_ON_FAIL(sess->state==PJ_TURN_STATE_READY, return);
888 /* Create a bare REFRESH request */
889 status = pj_stun_session_create_req(sess->stun, PJ_STUN_REFRESH_REQUEST,
890 PJ_STUN_MAGIC, NULL, &tdata);
891 if (status != PJ_SUCCESS)
896 pj_stun_msg_add_uint_attr(tdata->pool, tdata->msg,
897 PJ_STUN_ATTR_LIFETIME, lifetime);
902 set_state(sess, PJ_TURN_STATE_DEALLOCATING);
905 status = pj_stun_session_send_msg(sess->stun, NULL, PJ_FALSE,
906 (sess->conn_type==PJ_TURN_TP_UDP),
908 pj_sockaddr_get_len(sess->srv_addr),
910 if (status != PJ_SUCCESS)
917 set_state(sess, PJ_TURN_STATE_DEALLOCATED);
918 sess_shutdown(sess, status);
924 * Relay data to the specified peer through the session.
926 PJ_DEF(pj_status_t) pj_turn_session_sendto( pj_turn_session *sess,
927 const pj_uint8_t *pkt,
929 const pj_sockaddr_t *addr,
936 PJ_ASSERT_RETURN(sess && pkt && pkt_len && addr && addr_len,
939 /* Return error if we're not ready */
940 if (sess->state != PJ_TURN_STATE_READY) {
944 /* Lock session now */
945 pj_lock_acquire(sess->lock);
947 /* Lookup permission first */
948 perm = lookup_perm(sess, addr, pj_sockaddr_get_len(addr), PJ_FALSE);
950 /* Permission doesn't exist, install it first */
951 char ipstr[PJ_INET6_ADDRSTRLEN+2];
953 PJ_LOG(4,(sess->obj_name,
954 "sendto(): IP %s has no permission, requesting it first..",
955 pj_sockaddr_print(addr, ipstr, sizeof(ipstr), 2)));
957 status = pj_turn_session_set_perm(sess, 1, (const pj_sockaddr*)addr,
959 if (status != PJ_SUCCESS) {
960 pj_lock_release(sess->lock);
965 /* See if the peer is bound to a channel number */
966 ch = lookup_ch_by_addr(sess, addr, pj_sockaddr_get_len(addr),
968 if (ch && ch->num != PJ_TURN_INVALID_CHANNEL && ch->bound) {
971 /* Peer is assigned a channel number, we can use ChannelData */
972 pj_turn_channel_data *cd = (pj_turn_channel_data*)sess->tx_pkt;
974 pj_assert(sizeof(*cd)==4);
976 /* Calculate total length, including paddings */
977 total_len = (pkt_len + sizeof(*cd) + 3) & (~3);
978 if (total_len > sizeof(sess->tx_pkt)) {
983 cd->ch_number = pj_htons((pj_uint16_t)ch->num);
984 cd->length = pj_htons((pj_uint16_t)pkt_len);
985 pj_memcpy(cd+1, pkt, pkt_len);
987 pj_assert(sess->srv_addr != NULL);
989 status = sess->cb.on_send_pkt(sess, sess->tx_pkt, total_len,
991 pj_sockaddr_get_len(sess->srv_addr));
994 /* Use Send Indication. */
995 pj_stun_sockaddr_attr peer_attr;
996 pj_stun_binary_attr data_attr;
997 pj_stun_msg send_ind;
998 pj_size_t send_ind_len;
1000 /* Increment counter */
1001 ++sess->send_ind_tsx_id[2];
1003 /* Create blank SEND-INDICATION */
1004 status = pj_stun_msg_init(&send_ind, PJ_STUN_SEND_INDICATION,
1006 (const pj_uint8_t*)sess->send_ind_tsx_id);
1007 if (status != PJ_SUCCESS)
1010 /* Add XOR-PEER-ADDRESS */
1011 pj_stun_sockaddr_attr_init(&peer_attr, PJ_STUN_ATTR_XOR_PEER_ADDR,
1012 PJ_TRUE, addr, addr_len);
1013 pj_stun_msg_add_attr(&send_ind, (pj_stun_attr_hdr*)&peer_attr);
1015 /* Add DATA attribute */
1016 pj_stun_binary_attr_init(&data_attr, NULL, PJ_STUN_ATTR_DATA, NULL, 0);
1017 data_attr.data = (pj_uint8_t*)pkt;
1018 data_attr.length = pkt_len;
1019 pj_stun_msg_add_attr(&send_ind, (pj_stun_attr_hdr*)&data_attr);
1021 /* Encode the message */
1022 status = pj_stun_msg_encode(&send_ind, sess->tx_pkt,
1023 sizeof(sess->tx_pkt), 0,
1024 NULL, &send_ind_len);
1025 if (status != PJ_SUCCESS)
1028 /* Send the Send Indication */
1029 status = sess->cb.on_send_pkt(sess, sess->tx_pkt, send_ind_len,
1031 pj_sockaddr_get_len(sess->srv_addr));
1035 pj_lock_release(sess->lock);
1041 * Bind a peer address to a channel number.
1043 PJ_DEF(pj_status_t) pj_turn_session_bind_channel(pj_turn_session *sess,
1044 const pj_sockaddr_t *peer_adr,
1048 pj_stun_tx_data *tdata;
1052 PJ_ASSERT_RETURN(sess && peer_adr && addr_len, PJ_EINVAL);
1053 PJ_ASSERT_RETURN(sess->state == PJ_TURN_STATE_READY, PJ_EINVALIDOP);
1055 pj_lock_acquire(sess->lock);
1057 /* Create blank ChannelBind request */
1058 status = pj_stun_session_create_req(sess->stun,
1059 PJ_STUN_CHANNEL_BIND_REQUEST,
1060 PJ_STUN_MAGIC, NULL, &tdata);
1061 if (status != PJ_SUCCESS)
1064 /* Lookup if this peer has already been assigned a number */
1065 ch = lookup_ch_by_addr(sess, peer_adr, pj_sockaddr_get_len(peer_adr),
1069 if (ch->num != PJ_TURN_INVALID_CHANNEL) {
1070 /* Channel is already bound. This is a refresh request. */
1073 PJ_ASSERT_ON_FAIL(sess->next_ch <= PJ_TURN_CHANNEL_MAX,
1074 {status=PJ_ETOOMANY; goto on_return;});
1075 ch->num = ch_num = sess->next_ch++;
1078 /* Add CHANNEL-NUMBER attribute */
1079 pj_stun_msg_add_uint_attr(tdata->pool, tdata->msg,
1080 PJ_STUN_ATTR_CHANNEL_NUMBER,
1081 PJ_STUN_SET_CH_NB(ch_num));
1083 /* Add XOR-PEER-ADDRESS attribute */
1084 pj_stun_msg_add_sockaddr_attr(tdata->pool, tdata->msg,
1085 PJ_STUN_ATTR_XOR_PEER_ADDR, PJ_TRUE,
1086 peer_adr, addr_len);
1088 /* Send the request, associate peer data structure with tdata
1089 * for future reference when we receive the ChannelBind response.
1091 status = pj_stun_session_send_msg(sess->stun, ch, PJ_FALSE,
1092 (sess->conn_type==PJ_TURN_TP_UDP),
1094 pj_sockaddr_get_len(sess->srv_addr),
1098 pj_lock_release(sess->lock);
1104 * Notify TURN client session upon receiving a packet from server.
1105 * The packet maybe a STUN packet or ChannelData packet.
1107 PJ_DEF(pj_status_t) pj_turn_session_on_rx_pkt(pj_turn_session *sess,
1110 pj_size_t *parsed_len)
1114 pj_bool_t is_datagram;
1116 /* Packet could be ChannelData or STUN message (response or
1120 /* Start locking the session */
1121 pj_lock_acquire(sess->lock);
1123 is_datagram = (sess->conn_type==PJ_TURN_TP_UDP);
1125 /* Quickly check if this is STUN message */
1126 is_stun = ((((pj_uint8_t*)pkt)[0] & 0xC0) == 0);
1129 /* This looks like STUN, give it to the STUN session */
1132 options = PJ_STUN_CHECK_PACKET | PJ_STUN_NO_FINGERPRINT_CHECK;
1134 options |= PJ_STUN_IS_DATAGRAM;
1135 status=pj_stun_session_on_rx_pkt(sess->stun, pkt, pkt_len,
1136 options, NULL, parsed_len,
1138 pj_sockaddr_get_len(sess->srv_addr));
1141 /* This must be ChannelData. */
1142 pj_turn_channel_data cd;
1146 if (parsed_len) *parsed_len = 0;
1147 return PJ_ETOOSMALL;
1150 /* Decode ChannelData packet */
1151 pj_memcpy(&cd, pkt, sizeof(pj_turn_channel_data));
1152 cd.ch_number = pj_ntohs(cd.ch_number);
1153 cd.length = pj_ntohs(cd.length);
1155 /* Check that size is sane */
1156 if (pkt_len < cd.length+sizeof(cd)) {
1159 /* Discard the datagram */
1160 *parsed_len = pkt_len;
1162 /* Insufficient fragment */
1166 status = PJ_ETOOSMALL;
1170 /* Apply padding too */
1171 *parsed_len = ((cd.length + 3) & (~3)) + sizeof(cd);
1175 /* Lookup channel */
1176 ch = lookup_ch_by_chnum(sess, cd.ch_number);
1177 if (!ch || !ch->bound) {
1178 status = PJ_ENOTFOUND;
1182 /* Notify application */
1183 if (sess->cb.on_rx_data) {
1184 (*sess->cb.on_rx_data)(sess, ((pj_uint8_t*)pkt)+sizeof(cd),
1185 cd.length, &ch->addr,
1186 pj_sockaddr_get_len(&ch->addr));
1189 status = PJ_SUCCESS;
1193 pj_lock_release(sess->lock);
1199 * This is a callback from STUN session to send outgoing packet.
1201 static pj_status_t stun_on_send_msg(pj_stun_session *stun,
1205 const pj_sockaddr_t *dst_addr,
1208 pj_turn_session *sess;
1210 PJ_UNUSED_ARG(token);
1212 sess = (pj_turn_session*) pj_stun_session_get_user_data(stun);
1213 return (*sess->cb.on_send_pkt)(sess, (const pj_uint8_t*)pkt, pkt_size,
1214 dst_addr, addr_len);
1219 * Handle failed ALLOCATE or REFRESH request. This may switch to alternate
1220 * server if we have one.
1222 static void on_session_fail( pj_turn_session *sess,
1223 enum pj_stun_method_e method,
1225 const pj_str_t *reason)
1227 sess->last_status = status;
1231 char err_msg[PJ_ERR_MSG_SIZE];
1233 if (reason == NULL) {
1234 pj_strerror(status, err_msg, sizeof(err_msg));
1235 reason1 = pj_str(err_msg);
1239 PJ_LOG(4,(sess->obj_name, "%s error: %.*s",
1240 pj_stun_get_method_name(method),
1241 (int)reason->slen, reason->ptr));
1243 /* If this is ALLOCATE response and we don't have more server
1244 * addresses to try, notify application and destroy the TURN
1247 if (method==PJ_STUN_ALLOCATE_METHOD &&
1248 sess->srv_addr == &sess->srv_addr_list[sess->srv_addr_cnt-1])
1251 set_state(sess, PJ_TURN_STATE_DEALLOCATED);
1252 sess_shutdown(sess, status);
1256 /* Otherwise if this is not ALLOCATE response, notify application
1257 * that session has been TERMINATED.
1259 if (method!=PJ_STUN_ALLOCATE_METHOD) {
1260 set_state(sess, PJ_TURN_STATE_DEALLOCATED);
1261 sess_shutdown(sess, status);
1265 /* Try next server */
1269 PJ_LOG(4,(sess->obj_name, "Trying next server"));
1270 set_state(sess, PJ_TURN_STATE_RESOLVED);
1277 * Handle successful response to ALLOCATE or REFRESH request.
1279 static void on_allocate_success(pj_turn_session *sess,
1280 enum pj_stun_method_e method,
1281 const pj_stun_msg *msg)
1283 const pj_stun_lifetime_attr *lf_attr;
1284 const pj_stun_xor_relayed_addr_attr *raddr_attr;
1285 const pj_stun_sockaddr_attr *mapped_attr;
1287 pj_time_val timeout;
1289 /* Must have LIFETIME attribute */
1290 lf_attr = (const pj_stun_lifetime_attr*)
1291 pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_LIFETIME, 0);
1292 if (lf_attr == NULL) {
1293 on_session_fail(sess, method, PJNATH_EINSTUNMSG,
1294 pj_cstr(&s, "Error: Missing LIFETIME attribute"));
1298 /* If LIFETIME is zero, this is a deallocation */
1299 if (lf_attr->value == 0) {
1300 set_state(sess, PJ_TURN_STATE_DEALLOCATED);
1301 sess_shutdown(sess, PJ_SUCCESS);
1305 /* Update lifetime and keep-alive interval */
1306 sess->lifetime = lf_attr->value;
1307 pj_gettimeofday(&sess->expiry);
1309 if (sess->lifetime < PJ_TURN_KEEP_ALIVE_SEC) {
1310 if (sess->lifetime <= 2) {
1311 on_session_fail(sess, method, PJ_ETOOSMALL,
1312 pj_cstr(&s, "Error: LIFETIME too small"));
1315 sess->ka_interval = sess->lifetime - 2;
1316 sess->expiry.sec += (sess->ka_interval-1);
1320 sess->ka_interval = PJ_TURN_KEEP_ALIVE_SEC;
1322 timeout = sess->lifetime - PJ_TURN_REFRESH_SEC_BEFORE;
1323 if (timeout < sess->ka_interval)
1324 timeout = sess->ka_interval - 1;
1326 sess->expiry.sec += timeout;
1329 /* Check that relayed transport address contains correct
1332 raddr_attr = (const pj_stun_xor_relayed_addr_attr*)
1333 pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_XOR_RELAYED_ADDR, 0);
1334 if (raddr_attr == NULL && method==PJ_STUN_ALLOCATE_METHOD) {
1335 on_session_fail(sess, method, PJNATH_EINSTUNMSG,
1336 pj_cstr(&s, "Error: Received ALLOCATE without "
1337 "RELAY-ADDRESS attribute"));
1340 if (raddr_attr && raddr_attr->sockaddr.addr.sa_family != sess->af) {
1341 on_session_fail(sess, method, PJNATH_EINSTUNMSG,
1342 pj_cstr(&s, "Error: RELAY-ADDRESS with non IPv4"
1343 " address family is not supported "
1347 if (raddr_attr && !pj_sockaddr_has_addr(&raddr_attr->sockaddr)) {
1348 on_session_fail(sess, method, PJNATH_EINSTUNMSG,
1349 pj_cstr(&s, "Error: Invalid IP address in "
1350 "RELAY-ADDRESS attribute"));
1354 /* Save relayed address */
1356 /* If we already have relay address, check if the relay address
1357 * in the response matches our relay address.
1359 if (pj_sockaddr_has_addr(&sess->relay_addr)) {
1360 if (pj_sockaddr_cmp(&sess->relay_addr, &raddr_attr->sockaddr)) {
1361 on_session_fail(sess, method, PJNATH_EINSTUNMSG,
1362 pj_cstr(&s, "Error: different RELAY-ADDRESS is"
1363 "returned by server"));
1367 /* Otherwise save the relayed address */
1368 pj_memcpy(&sess->relay_addr, &raddr_attr->sockaddr,
1369 sizeof(pj_sockaddr));
1373 /* Get mapped address */
1374 mapped_attr = (const pj_stun_sockaddr_attr*)
1375 pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_XOR_MAPPED_ADDR, 0);
1377 pj_memcpy(&sess->mapped_addr, &mapped_attr->sockaddr,
1378 sizeof(mapped_attr->sockaddr));
1383 /* Cancel existing keep-alive timer, if any */
1384 pj_assert(sess->timer.id != TIMER_DESTROY);
1386 if (sess->timer.id != TIMER_NONE) {
1387 pj_timer_heap_cancel(sess->timer_heap, &sess->timer);
1388 sess->timer.id = TIMER_NONE;
1391 /* Start keep-alive timer once allocation succeeds */
1392 timeout.sec = sess->ka_interval;
1395 sess->timer.id = TIMER_KEEP_ALIVE;
1396 pj_timer_heap_schedule(sess->timer_heap, &sess->timer, &timeout);
1398 set_state(sess, PJ_TURN_STATE_READY);
1402 * Notification from STUN session on request completion.
1404 static void stun_on_request_complete(pj_stun_session *stun,
1407 pj_stun_tx_data *tdata,
1408 const pj_stun_msg *response,
1409 const pj_sockaddr_t *src_addr,
1410 unsigned src_addr_len)
1412 pj_turn_session *sess;
1413 enum pj_stun_method_e method = (enum pj_stun_method_e)
1414 PJ_STUN_GET_METHOD(tdata->msg->hdr.type);
1416 PJ_UNUSED_ARG(src_addr);
1417 PJ_UNUSED_ARG(src_addr_len);
1419 sess = (pj_turn_session*)pj_stun_session_get_user_data(stun);
1421 if (method == PJ_STUN_ALLOCATE_METHOD) {
1423 /* Destroy if we have pending destroy request */
1424 if (sess->pending_destroy) {
1425 if (status == PJ_SUCCESS)
1426 sess->state = PJ_TURN_STATE_READY;
1428 sess->state = PJ_TURN_STATE_DEALLOCATED;
1429 sess_shutdown(sess, PJ_SUCCESS);
1433 /* Handle ALLOCATE response */
1434 if (status==PJ_SUCCESS &&
1435 PJ_STUN_IS_SUCCESS_RESPONSE(response->hdr.type))
1438 /* Successful Allocate response */
1439 on_allocate_success(sess, method, response);
1442 /* Failed Allocate request */
1443 const pj_str_t *err_msg = NULL;
1445 if (status == PJ_SUCCESS) {
1446 const pj_stun_errcode_attr *err_attr;
1447 err_attr = (const pj_stun_errcode_attr*)
1448 pj_stun_msg_find_attr(response,
1449 PJ_STUN_ATTR_ERROR_CODE, 0);
1451 status = PJ_STATUS_FROM_STUN_CODE(err_attr->err_code);
1452 err_msg = &err_attr->reason;
1454 status = PJNATH_EINSTUNMSG;
1458 on_session_fail(sess, method, status, err_msg);
1461 } else if (method == PJ_STUN_REFRESH_METHOD) {
1462 /* Handle Refresh response */
1463 if (status==PJ_SUCCESS &&
1464 PJ_STUN_IS_SUCCESS_RESPONSE(response->hdr.type))
1466 /* Success, schedule next refresh. */
1467 on_allocate_success(sess, method, response);
1470 /* Failed Refresh request */
1471 const pj_str_t *err_msg = NULL;
1473 pj_assert(status != PJ_SUCCESS);
1476 const pj_stun_errcode_attr *err_attr;
1477 err_attr = (const pj_stun_errcode_attr*)
1478 pj_stun_msg_find_attr(response,
1479 PJ_STUN_ATTR_ERROR_CODE, 0);
1481 status = PJ_STATUS_FROM_STUN_CODE(err_attr->err_code);
1482 err_msg = &err_attr->reason;
1486 /* Notify and destroy */
1487 on_session_fail(sess, method, status, err_msg);
1490 } else if (method == PJ_STUN_CHANNEL_BIND_METHOD) {
1491 /* Handle ChannelBind response */
1492 if (status==PJ_SUCCESS &&
1493 PJ_STUN_IS_SUCCESS_RESPONSE(response->hdr.type))
1495 /* Successful ChannelBind response */
1496 struct ch_t *ch = (struct ch_t*)token;
1498 pj_assert(ch->num != PJ_TURN_INVALID_CHANNEL);
1499 ch->bound = PJ_TRUE;
1501 /* Update hash table */
1502 lookup_ch_by_addr(sess, &ch->addr,
1503 pj_sockaddr_get_len(&ch->addr),
1507 /* Failed ChannelBind response */
1508 pj_str_t reason = {"", 0};
1510 char errbuf[PJ_ERR_MSG_SIZE];
1512 pj_assert(status != PJ_SUCCESS);
1515 const pj_stun_errcode_attr *err_attr;
1516 err_attr = (const pj_stun_errcode_attr*)
1517 pj_stun_msg_find_attr(response,
1518 PJ_STUN_ATTR_ERROR_CODE, 0);
1520 err_code = err_attr->err_code;
1521 status = PJ_STATUS_FROM_STUN_CODE(err_attr->err_code);
1522 reason = err_attr->reason;
1526 reason = pj_strerror(status, errbuf, sizeof(errbuf));
1529 PJ_LOG(1,(sess->obj_name, "ChannelBind failed: %d/%.*s",
1530 err_code, (int)reason.slen, reason.ptr));
1532 if (err_code == PJ_STUN_SC_ALLOCATION_MISMATCH) {
1533 /* Allocation mismatch means allocation no longer exists */
1534 on_session_fail(sess, PJ_STUN_CHANNEL_BIND_METHOD,
1540 } else if (method == PJ_STUN_CREATE_PERM_METHOD) {
1541 /* Handle CreatePermission response */
1542 if (status==PJ_SUCCESS &&
1543 PJ_STUN_IS_SUCCESS_RESPONSE(response->hdr.type))
1545 /* No special handling when the request is successful. */
1547 /* Iterate the permission table and invalidate all permissions
1548 * that are related to this request.
1550 pj_hash_iterator_t it_buf, *it;
1551 char ipstr[PJ_INET6_ADDRSTRLEN+10];
1553 char errbuf[PJ_ERR_MSG_SIZE];
1556 pj_assert(status != PJ_SUCCESS);
1559 const pj_stun_errcode_attr *eattr;
1561 eattr = (const pj_stun_errcode_attr*)
1562 pj_stun_msg_find_attr(response,
1563 PJ_STUN_ATTR_ERROR_CODE, 0);
1565 err_code = eattr->err_code;
1566 reason = eattr->reason;
1569 reason = pj_str("?");
1573 reason = pj_strerror(status, errbuf, sizeof(errbuf));
1576 it = pj_hash_first(sess->perm_table, &it_buf);
1578 struct perm_t *perm = (struct perm_t*)
1579 pj_hash_this(sess->perm_table, it);
1580 it = pj_hash_next(sess->perm_table, it);
1582 if (perm->req_token == token) {
1583 PJ_LOG(1,(sess->obj_name,
1584 "CreatePermission failed for IP %s: %d/%.*s",
1585 pj_sockaddr_print(&perm->addr, ipstr,
1587 err_code, (int)reason.slen, reason.ptr));
1589 invalidate_perm(sess, perm);
1593 if (err_code == PJ_STUN_SC_ALLOCATION_MISMATCH) {
1594 /* Allocation mismatch means allocation no longer exists */
1595 on_session_fail(sess, PJ_STUN_CREATE_PERM_METHOD,
1602 PJ_LOG(4,(sess->obj_name, "Unexpected STUN %s response",
1603 pj_stun_get_method_name(response->hdr.type)));
1609 * Notification from STUN session on incoming STUN Indication
1612 static pj_status_t stun_on_rx_indication(pj_stun_session *stun,
1613 const pj_uint8_t *pkt,
1615 const pj_stun_msg *msg,
1617 const pj_sockaddr_t *src_addr,
1618 unsigned src_addr_len)
1620 pj_turn_session *sess;
1621 pj_stun_xor_peer_addr_attr *peer_attr;
1622 pj_stun_icmp_attr *icmp;
1623 pj_stun_data_attr *data_attr;
1625 PJ_UNUSED_ARG(token);
1627 PJ_UNUSED_ARG(pkt_len);
1628 PJ_UNUSED_ARG(src_addr);
1629 PJ_UNUSED_ARG(src_addr_len);
1631 sess = (pj_turn_session*)pj_stun_session_get_user_data(stun);
1633 /* Expecting Data Indication only */
1634 if (msg->hdr.type != PJ_STUN_DATA_INDICATION) {
1635 PJ_LOG(4,(sess->obj_name, "Unexpected STUN %s indication",
1636 pj_stun_get_method_name(msg->hdr.type)));
1637 return PJ_EINVALIDOP;
1640 /* Check if there is ICMP attribute in the message */
1641 icmp = (pj_stun_icmp_attr*)
1642 pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_ICMP, 0);
1644 /* This is a forwarded ICMP packet. Ignore it for now */
1648 /* Get XOR-PEER-ADDRESS attribute */
1649 peer_attr = (pj_stun_xor_peer_addr_attr*)
1650 pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_XOR_PEER_ADDR, 0);
1652 /* Get DATA attribute */
1653 data_attr = (pj_stun_data_attr*)
1654 pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_DATA, 0);
1656 /* Must have both XOR-PEER-ADDRESS and DATA attributes */
1657 if (!peer_attr || !data_attr) {
1658 PJ_LOG(4,(sess->obj_name,
1659 "Received Data indication with missing attributes"));
1660 return PJ_EINVALIDOP;
1663 /* Notify application */
1664 if (sess->cb.on_rx_data) {
1665 (*sess->cb.on_rx_data)(sess, data_attr->data, data_attr->length,
1666 &peer_attr->sockaddr,
1667 pj_sockaddr_get_len(&peer_attr->sockaddr));
1675 * Notification on completion of DNS SRV resolution.
1677 static void dns_srv_resolver_cb(void *user_data,
1679 const pj_dns_srv_record *rec)
1681 pj_turn_session *sess = (pj_turn_session*) user_data;
1682 unsigned i, cnt, tot_cnt;
1684 /* Clear async resolver */
1685 sess->dns_async = NULL;
1688 if (status != PJ_SUCCESS) {
1689 sess_shutdown(sess, status);
1693 /* Calculate total number of server entries in the response */
1695 for (i=0; i<rec->count; ++i) {
1696 tot_cnt += rec->entry[i].server.addr_count;
1699 if (tot_cnt > PJ_TURN_MAX_DNS_SRV_CNT)
1700 tot_cnt = PJ_TURN_MAX_DNS_SRV_CNT;
1702 /* Allocate server entries */
1703 sess->srv_addr_list = (pj_sockaddr*)
1704 pj_pool_calloc(sess->pool, tot_cnt,
1705 sizeof(pj_sockaddr));
1707 /* Copy results to server entries */
1708 for (i=0, cnt=0; i<rec->count && cnt<PJ_TURN_MAX_DNS_SRV_CNT; ++i) {
1711 for (j=0; j<rec->entry[i].server.addr_count &&
1712 cnt<PJ_TURN_MAX_DNS_SRV_CNT; ++j)
1714 pj_sockaddr_in *addr = &sess->srv_addr_list[cnt].ipv4;
1716 addr->sin_family = sess->af;
1717 addr->sin_port = pj_htons(rec->entry[i].port);
1718 addr->sin_addr.s_addr = rec->entry[i].server.addr[j].s_addr;
1723 sess->srv_addr_cnt = (pj_uint16_t)cnt;
1725 /* Set current server */
1726 sess->srv_addr = &sess->srv_addr_list[0];
1728 /* Set state to PJ_TURN_STATE_RESOLVED */
1729 set_state(sess, PJ_TURN_STATE_RESOLVED);
1731 /* Run pending allocation */
1732 if (sess->pending_alloc) {
1733 pj_turn_session_alloc(sess, NULL);
1739 * Lookup peer descriptor from its address.
1741 static struct ch_t *lookup_ch_by_addr(pj_turn_session *sess,
1742 const pj_sockaddr_t *addr,
1745 pj_bool_t bind_channel)
1747 pj_uint32_t hval = 0;
1751 pj_hash_get(sess->ch_table, addr, addr_len, &hval);
1752 if (ch == NULL && update) {
1753 ch = PJ_POOL_ZALLOC_T(sess->pool, struct ch_t);
1754 ch->num = PJ_TURN_INVALID_CHANNEL;
1755 pj_memcpy(&ch->addr, addr, addr_len);
1757 /* Register by peer address */
1758 pj_hash_set(sess->pool, sess->ch_table, &ch->addr, addr_len,
1763 pj_gettimeofday(&ch->expiry);
1764 ch->expiry.sec += PJ_TURN_PERM_TIMEOUT - sess->ka_interval - 1;
1767 pj_uint32_t hval = 0;
1768 /* Register by channel number */
1769 pj_assert(ch->num != PJ_TURN_INVALID_CHANNEL && ch->bound);
1771 if (pj_hash_get(sess->ch_table, &ch->num,
1772 sizeof(ch->num), &hval)==0) {
1773 pj_hash_set(sess->pool, sess->ch_table, &ch->num,
1774 sizeof(ch->num), hval, ch);
1779 /* Also create/update permission for this destination. Ideally we
1780 * should update this when we receive the successful response,
1781 * but that would cause duplicate CreatePermission to be sent
1782 * during refreshing.
1785 lookup_perm(sess, &ch->addr, pj_sockaddr_get_len(&ch->addr), PJ_TRUE);
1793 * Lookup channel descriptor from its channel number.
1795 static struct ch_t *lookup_ch_by_chnum(pj_turn_session *sess,
1798 return (struct ch_t*) pj_hash_get(sess->ch_table, &chnum,
1799 sizeof(chnum), NULL);
1804 * Lookup permission and optionally create if it doesn't exist.
1806 static struct perm_t *lookup_perm(pj_turn_session *sess,
1807 const pj_sockaddr_t *addr,
1811 pj_uint32_t hval = 0;
1812 pj_sockaddr perm_addr;
1813 struct perm_t *perm;
1815 /* make sure port number if zero */
1816 if (pj_sockaddr_get_port(addr) != 0) {
1817 pj_memcpy(&perm_addr, addr, addr_len);
1818 pj_sockaddr_set_port(&perm_addr, 0);
1822 /* lookup and create if it doesn't exist and wanted */
1823 perm = (struct perm_t*)
1824 pj_hash_get(sess->perm_table, addr, addr_len, &hval);
1825 if (perm == NULL && update) {
1826 perm = PJ_POOL_ZALLOC_T(sess->pool, struct perm_t);
1827 pj_memcpy(&perm->addr, addr, addr_len);
1830 pj_hash_set(sess->pool, sess->perm_table, &perm->addr, addr_len,
1834 if (perm && update) {
1835 pj_gettimeofday(&perm->expiry);
1836 perm->expiry.sec += PJ_TURN_PERM_TIMEOUT - sess->ka_interval - 1;
1846 static void invalidate_perm(pj_turn_session *sess,
1847 struct perm_t *perm)
1849 pj_hash_set(NULL, sess->perm_table, &perm->addr,
1850 pj_sockaddr_get_len(&perm->addr), perm->hval, NULL);
1854 * Scan permission's hash table to refresh the permission.
1856 static unsigned refresh_permissions(pj_turn_session *sess,
1857 const pj_time_val *now)
1859 pj_stun_tx_data *tdata = NULL;
1861 void *req_token = NULL;
1862 pj_hash_iterator_t *it, itbuf;
1865 it = pj_hash_first(sess->perm_table, &itbuf);
1867 struct perm_t *perm = (struct perm_t*)
1868 pj_hash_this(sess->perm_table, it);
1870 it = pj_hash_next(sess->perm_table, it);
1872 if (perm->expiry.sec-1 <= now->sec) {
1874 /* Renew this permission */
1875 if (tdata == NULL) {
1876 /* Create a bare CreatePermission request */
1877 status = pj_stun_session_create_req(
1879 PJ_STUN_CREATE_PERM_REQUEST,
1880 PJ_STUN_MAGIC, NULL, &tdata);
1881 if (status != PJ_SUCCESS) {
1882 PJ_LOG(1,(sess->obj_name,
1883 "Error creating CreatePermission request: %d",
1888 /* Create request token to map the request to the perm
1889 * structures which the request belongs.
1891 req_token = (void*)(long)pj_rand();
1894 status = pj_stun_msg_add_sockaddr_attr(
1897 PJ_STUN_ATTR_XOR_PEER_ADDR,
1900 sizeof(perm->addr));
1901 if (status != PJ_SUCCESS) {
1902 pj_stun_msg_destroy_tdata(sess->stun, tdata);
1906 perm->expiry = *now;
1907 perm->expiry.sec += PJ_TURN_PERM_TIMEOUT-sess->ka_interval-1;
1908 perm->req_token = req_token;
1912 /* This permission has expired and app doesn't want
1913 * us to renew, so delete it from the hash table.
1915 invalidate_perm(sess, perm);
1921 status = pj_stun_session_send_msg(sess->stun, req_token, PJ_FALSE,
1922 (sess->conn_type==PJ_TURN_TP_UDP),
1924 pj_sockaddr_get_len(sess->srv_addr),
1926 if (status != PJ_SUCCESS) {
1927 PJ_LOG(1,(sess->obj_name,
1928 "Error sending CreatePermission request: %d",
1941 static void on_timer_event(pj_timer_heap_t *th, pj_timer_entry *e)
1943 pj_turn_session *sess = (pj_turn_session*)e->user_data;
1944 enum timer_id_t eid;
1948 pj_lock_acquire(sess->lock);
1950 eid = (enum timer_id_t) e->id;
1953 if (eid == TIMER_KEEP_ALIVE) {
1955 pj_hash_iterator_t itbuf, *it;
1956 pj_bool_t resched = PJ_TRUE;
1957 pj_bool_t pkt_sent = PJ_FALSE;
1959 pj_gettimeofday(&now);
1961 /* Refresh allocation if it's time to do so */
1962 if (PJ_TIME_VAL_LTE(sess->expiry, now)) {
1963 int lifetime = sess->alloc_param.lifetime;
1968 send_refresh(sess, lifetime);
1973 /* Scan hash table to refresh bound channels */
1974 it = pj_hash_first(sess->ch_table, &itbuf);
1976 struct ch_t *ch = (struct ch_t*)
1977 pj_hash_this(sess->ch_table, it);
1978 if (ch->bound && PJ_TIME_VAL_LTE(ch->expiry, now)) {
1980 /* Send ChannelBind to refresh channel binding and
1983 pj_turn_session_bind_channel(sess, &ch->addr,
1984 pj_sockaddr_get_len(&ch->addr));
1988 it = pj_hash_next(sess->ch_table, it);
1991 /* Scan permission table to refresh permissions */
1992 if (refresh_permissions(sess, &now))
1995 /* If no packet is sent, send a blank Send indication to
1996 * refresh local NAT.
1998 if (!pkt_sent && sess->alloc_param.ka_interval > 0) {
1999 pj_stun_tx_data *tdata;
2002 /* Create blank SEND-INDICATION */
2003 rc = pj_stun_session_create_ind(sess->stun,
2004 PJ_STUN_SEND_INDICATION, &tdata);
2005 if (rc == PJ_SUCCESS) {
2006 /* Add DATA attribute with zero length */
2007 pj_stun_msg_add_binary_attr(tdata->pool, tdata->msg,
2008 PJ_STUN_ATTR_DATA, NULL, 0);
2010 /* Send the indication */
2011 pj_stun_session_send_msg(sess->stun, NULL, PJ_FALSE,
2012 PJ_FALSE, sess->srv_addr,
2013 pj_sockaddr_get_len(sess->srv_addr),
2018 /* Reshcedule timer */
2022 delay.sec = sess->ka_interval;
2025 sess->timer.id = TIMER_KEEP_ALIVE;
2026 pj_timer_heap_schedule(sess->timer_heap, &sess->timer, &delay);
2029 pj_lock_release(sess->lock);
2031 } else if (eid == TIMER_DESTROY) {
2032 /* Time to destroy */
2033 pj_lock_release(sess->lock);
2036 pj_assert(!"Unknown timer event");
2037 pj_lock_release(sess->lock);