blob: 541175c6d5f24b9e704118b1c7f897aef3ea9a49 [file] [log] [blame]
Alexandre Lision8af73cb2013-12-10 14:11:20 -05001/* $Id$ */
2/*
3 * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
4 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20#include <pjnath/turn_sock.h>
21#include <pj/activesock.h>
22#include <pj/assert.h>
23#include <pj/errno.h>
24#include <pj/lock.h>
25#include <pj/log.h>
26#include <pj/pool.h>
27#include <pj/ioqueue.h>
28
29enum
30{
31 TIMER_NONE,
32 TIMER_DESTROY
33};
34
35
36enum { MAX_BIND_RETRY = 100 };
37
38
39#define INIT 0x1FFFFFFF
40
41struct pj_turn_sock
42{
43 pj_pool_t *pool;
44 const char *obj_name;
45 pj_turn_session *sess;
46 pj_turn_sock_cb cb;
47 void *user_data;
48
49 pj_bool_t is_destroying;
50 pj_grp_lock_t *grp_lock;
51
52 pj_turn_alloc_param alloc_param;
53 pj_stun_config cfg;
54 pj_turn_sock_cfg setting;
55
56 pj_timer_entry timer;
57
58 int af;
59 pj_turn_tp_type conn_type;
60 pj_activesock_t *active_sock;
61 pj_ioqueue_op_key_t send_key;
62};
63
64
65/*
66 * Callback prototypes.
67 */
68static pj_status_t turn_on_send_pkt(pj_turn_session *sess,
69 const pj_uint8_t *pkt,
70 unsigned pkt_len,
71 const pj_sockaddr_t *dst_addr,
72 unsigned dst_addr_len);
73static void turn_on_channel_bound(pj_turn_session *sess,
74 const pj_sockaddr_t *peer_addr,
75 unsigned addr_len,
76 unsigned ch_num);
77static void turn_on_rx_data(pj_turn_session *sess,
78 void *pkt,
79 unsigned pkt_len,
80 const pj_sockaddr_t *peer_addr,
81 unsigned addr_len);
82static void turn_on_state(pj_turn_session *sess,
83 pj_turn_state_t old_state,
84 pj_turn_state_t new_state);
85
86static pj_bool_t on_data_read(pj_activesock_t *asock,
87 void *data,
88 pj_size_t size,
89 pj_status_t status,
90 pj_size_t *remainder);
91static pj_bool_t on_connect_complete(pj_activesock_t *asock,
92 pj_status_t status);
93
94
95
96static void turn_sock_on_destroy(void *comp);
97static void destroy(pj_turn_sock *turn_sock);
98static void timer_cb(pj_timer_heap_t *th, pj_timer_entry *e);
99
100
101/* Init config */
102PJ_DEF(void) pj_turn_sock_cfg_default(pj_turn_sock_cfg *cfg)
103{
104 pj_bzero(cfg, sizeof(*cfg));
105 cfg->max_pkt_size = PJ_TURN_MAX_PKT_LEN;
106 cfg->qos_type = PJ_QOS_TYPE_BEST_EFFORT;
107 cfg->qos_ignore_error = PJ_TRUE;
108}
109
110
111/*
112 * Create.
113 */
114PJ_DEF(pj_status_t) pj_turn_sock_create(pj_stun_config *cfg,
115 int af,
116 pj_turn_tp_type conn_type,
117 const pj_turn_sock_cb *cb,
118 const pj_turn_sock_cfg *setting,
119 void *user_data,
120 pj_turn_sock **p_turn_sock)
121{
122 pj_turn_sock *turn_sock;
123 pj_turn_session_cb sess_cb;
124 pj_turn_sock_cfg default_setting;
125 pj_pool_t *pool;
126 const char *name_tmpl;
127 pj_status_t status;
128
129 PJ_ASSERT_RETURN(cfg && p_turn_sock, PJ_EINVAL);
130 PJ_ASSERT_RETURN(af==pj_AF_INET() || af==pj_AF_INET6(), PJ_EINVAL);
131 PJ_ASSERT_RETURN(conn_type!=PJ_TURN_TP_TCP || PJ_HAS_TCP, PJ_EINVAL);
132
133 if (!setting) {
134 pj_turn_sock_cfg_default(&default_setting);
135 setting = &default_setting;
136 }
137
138 switch (conn_type) {
139 case PJ_TURN_TP_UDP:
140 name_tmpl = "udprel%p";
141 break;
142 case PJ_TURN_TP_TCP:
143 name_tmpl = "tcprel%p";
144 break;
145 default:
146 PJ_ASSERT_RETURN(!"Invalid TURN conn_type", PJ_EINVAL);
147 name_tmpl = "tcprel%p";
148 break;
149 }
150
151 /* Create and init basic data structure */
152 pool = pj_pool_create(cfg->pf, name_tmpl, PJNATH_POOL_LEN_TURN_SOCK,
153 PJNATH_POOL_INC_TURN_SOCK, NULL);
154 turn_sock = PJ_POOL_ZALLOC_T(pool, pj_turn_sock);
155 turn_sock->pool = pool;
156 turn_sock->obj_name = pool->obj_name;
157 turn_sock->user_data = user_data;
158 turn_sock->af = af;
159 turn_sock->conn_type = conn_type;
160
161 /* Copy STUN config (this contains ioqueue, timer heap, etc.) */
162 pj_memcpy(&turn_sock->cfg, cfg, sizeof(*cfg));
163
164 /* Copy setting (QoS parameters etc */
165 pj_memcpy(&turn_sock->setting, setting, sizeof(*setting));
166
167 /* Set callback */
168 if (cb) {
169 pj_memcpy(&turn_sock->cb, cb, sizeof(*cb));
170 }
171
172 /* Session lock */
173 if (setting && setting->grp_lock) {
174 turn_sock->grp_lock = setting->grp_lock;
175 } else {
176 status = pj_grp_lock_create(pool, NULL, &turn_sock->grp_lock);
177 if (status != PJ_SUCCESS) {
178 pj_pool_release(pool);
179 return status;
180 }
181 }
182
183 pj_grp_lock_add_ref(turn_sock->grp_lock);
184 pj_grp_lock_add_handler(turn_sock->grp_lock, pool, turn_sock,
185 &turn_sock_on_destroy);
186
187 /* Init timer */
188 pj_timer_entry_init(&turn_sock->timer, TIMER_NONE, turn_sock, &timer_cb);
189
190 /* Init TURN session */
191 pj_bzero(&sess_cb, sizeof(sess_cb));
192 sess_cb.on_send_pkt = &turn_on_send_pkt;
193 sess_cb.on_channel_bound = &turn_on_channel_bound;
194 sess_cb.on_rx_data = &turn_on_rx_data;
195 sess_cb.on_state = &turn_on_state;
196 status = pj_turn_session_create(cfg, pool->obj_name, af, conn_type,
197 turn_sock->grp_lock, &sess_cb, 0,
198 turn_sock, &turn_sock->sess);
199 if (status != PJ_SUCCESS) {
200 destroy(turn_sock);
201 return status;
202 }
203
204 /* Note: socket and ioqueue will be created later once the TURN server
205 * has been resolved.
206 */
207
208 *p_turn_sock = turn_sock;
209 return PJ_SUCCESS;
210}
211
212/*
213 * Destroy.
214 */
215static void turn_sock_on_destroy(void *comp)
216{
217 pj_turn_sock *turn_sock = (pj_turn_sock*) comp;
218
219 if (turn_sock->pool) {
220 pj_pool_t *pool = turn_sock->pool;
221 PJ_LOG(4,(turn_sock->obj_name, "TURN socket destroyed"));
222 turn_sock->pool = NULL;
223 pj_pool_release(pool);
224 }
225}
226
227static void destroy(pj_turn_sock *turn_sock)
228{
229 PJ_LOG(4,(turn_sock->obj_name, "TURN socket destroy request, ref_cnt=%d",
230 pj_grp_lock_get_ref(turn_sock->grp_lock)));
231
232 pj_grp_lock_acquire(turn_sock->grp_lock);
233 if (turn_sock->is_destroying) {
234 pj_grp_lock_release(turn_sock->grp_lock);
235 return;
236 }
237
238 turn_sock->is_destroying = PJ_TRUE;
239 if (turn_sock->sess)
240 pj_turn_session_shutdown(turn_sock->sess);
241 if (turn_sock->active_sock)
242 pj_activesock_close(turn_sock->active_sock);
243 pj_grp_lock_dec_ref(turn_sock->grp_lock);
244 pj_grp_lock_release(turn_sock->grp_lock);
245}
246
247PJ_DEF(void) pj_turn_sock_destroy(pj_turn_sock *turn_sock)
248{
249 pj_grp_lock_acquire(turn_sock->grp_lock);
250 if (turn_sock->is_destroying) {
251 pj_grp_lock_release(turn_sock->grp_lock);
252 return;
253 }
254
255 if (turn_sock->sess) {
256 pj_turn_session_shutdown(turn_sock->sess);
257 /* This will ultimately call our state callback, and when
258 * session state is DESTROYING we will schedule a timer to
259 * destroy ourselves.
260 */
261 } else {
262 destroy(turn_sock);
263 }
264
265 pj_grp_lock_release(turn_sock->grp_lock);
266}
267
268
269/* Timer callback */
270static void timer_cb(pj_timer_heap_t *th, pj_timer_entry *e)
271{
272 pj_turn_sock *turn_sock = (pj_turn_sock*)e->user_data;
273 int eid = e->id;
274
275 PJ_UNUSED_ARG(th);
276
277 e->id = TIMER_NONE;
278
279 switch (eid) {
280 case TIMER_DESTROY:
281 destroy(turn_sock);
282 break;
283 default:
284 pj_assert(!"Invalid timer id");
285 break;
286 }
287}
288
289
290/* Display error */
291static void show_err(pj_turn_sock *turn_sock, const char *title,
292 pj_status_t status)
293{
294 PJ_PERROR(4,(turn_sock->obj_name, status, title));
295}
296
297/* On error, terminate session */
298static void sess_fail(pj_turn_sock *turn_sock, const char *title,
299 pj_status_t status)
300{
301 show_err(turn_sock, title, status);
302 if (turn_sock->sess) {
303 pj_turn_session_destroy(turn_sock->sess, status);
304 }
305}
306
307/*
308 * Set user data.
309 */
310PJ_DEF(pj_status_t) pj_turn_sock_set_user_data( pj_turn_sock *turn_sock,
311 void *user_data)
312{
313 PJ_ASSERT_RETURN(turn_sock, PJ_EINVAL);
314 turn_sock->user_data = user_data;
315 return PJ_SUCCESS;
316}
317
318/*
319 * Get user data.
320 */
321PJ_DEF(void*) pj_turn_sock_get_user_data(pj_turn_sock *turn_sock)
322{
323 PJ_ASSERT_RETURN(turn_sock, NULL);
324 return turn_sock->user_data;
325}
326
327/*
328 * Get group lock.
329 */
330PJ_DEF(pj_grp_lock_t *) pj_turn_sock_get_grp_lock(pj_turn_sock *turn_sock)
331{
332 PJ_ASSERT_RETURN(turn_sock, NULL);
333 return turn_sock->grp_lock;
334}
335
336/**
337 * Get info.
338 */
339PJ_DEF(pj_status_t) pj_turn_sock_get_info(pj_turn_sock *turn_sock,
340 pj_turn_session_info *info)
341{
342 PJ_ASSERT_RETURN(turn_sock && info, PJ_EINVAL);
343
344 if (turn_sock->sess) {
345 return pj_turn_session_get_info(turn_sock->sess, info);
346 } else {
347 pj_bzero(info, sizeof(*info));
348 info->state = PJ_TURN_STATE_NULL;
349 return PJ_SUCCESS;
350 }
351}
352
353/**
354 * Lock the TURN socket. Application may need to call this function to
355 * synchronize access to other objects to avoid deadlock.
356 */
357PJ_DEF(pj_status_t) pj_turn_sock_lock(pj_turn_sock *turn_sock)
358{
359 return pj_grp_lock_acquire(turn_sock->grp_lock);
360}
361
362/**
363 * Unlock the TURN socket.
364 */
365PJ_DEF(pj_status_t) pj_turn_sock_unlock(pj_turn_sock *turn_sock)
366{
367 return pj_grp_lock_release(turn_sock->grp_lock);
368}
369
370/*
371 * Set STUN message logging for this TURN session.
372 */
373PJ_DEF(void) pj_turn_sock_set_log( pj_turn_sock *turn_sock,
374 unsigned flags)
375{
376 pj_turn_session_set_log(turn_sock->sess, flags);
377}
378
379/*
380 * Set software name
381 */
382PJ_DEF(pj_status_t) pj_turn_sock_set_software_name( pj_turn_sock *turn_sock,
383 const pj_str_t *sw)
384{
385 return pj_turn_session_set_software_name(turn_sock->sess, sw);
386}
387
388/*
389 * Initialize.
390 */
391PJ_DEF(pj_status_t) pj_turn_sock_alloc(pj_turn_sock *turn_sock,
392 const pj_str_t *domain,
393 int default_port,
394 pj_dns_resolver *resolver,
395 const pj_stun_auth_cred *cred,
396 const pj_turn_alloc_param *param)
397{
398 pj_status_t status;
399
400 PJ_ASSERT_RETURN(turn_sock && domain, PJ_EINVAL);
401 PJ_ASSERT_RETURN(turn_sock->sess, PJ_EINVALIDOP);
402
403 pj_grp_lock_acquire(turn_sock->grp_lock);
404
405 /* Copy alloc param. We will call session_alloc() only after the
406 * server address has been resolved.
407 */
408 if (param) {
409 pj_turn_alloc_param_copy(turn_sock->pool, &turn_sock->alloc_param, param);
410 } else {
411 pj_turn_alloc_param_default(&turn_sock->alloc_param);
412 }
413
414 /* Set credental */
415 if (cred) {
416 status = pj_turn_session_set_credential(turn_sock->sess, cred);
417 if (status != PJ_SUCCESS) {
418 sess_fail(turn_sock, "Error setting credential", status);
419 pj_grp_lock_release(turn_sock->grp_lock);
420 return status;
421 }
422 }
423
424 /* Resolve server */
425 status = pj_turn_session_set_server(turn_sock->sess, domain, default_port,
426 resolver);
427 if (status != PJ_SUCCESS) {
428 sess_fail(turn_sock, "Error setting TURN server", status);
429 pj_grp_lock_release(turn_sock->grp_lock);
430 return status;
431 }
432
433 /* Done for now. The next work will be done when session state moved
434 * to RESOLVED state.
435 */
436 pj_grp_lock_release(turn_sock->grp_lock);
437 return PJ_SUCCESS;
438}
439
440/*
441 * Install permission
442 */
443PJ_DEF(pj_status_t) pj_turn_sock_set_perm( pj_turn_sock *turn_sock,
444 unsigned addr_cnt,
445 const pj_sockaddr addr[],
446 unsigned options)
447{
448 if (turn_sock->sess == NULL)
449 return PJ_EINVALIDOP;
450
451 return pj_turn_session_set_perm(turn_sock->sess, addr_cnt, addr, options);
452}
453
454/*
455 * Send packet.
456 */
457PJ_DEF(pj_status_t) pj_turn_sock_sendto( pj_turn_sock *turn_sock,
458 const pj_uint8_t *pkt,
459 unsigned pkt_len,
460 const pj_sockaddr_t *addr,
461 unsigned addr_len)
462{
463 PJ_ASSERT_RETURN(turn_sock && addr && addr_len, PJ_EINVAL);
464
465 if (turn_sock->sess == NULL)
466 return PJ_EINVALIDOP;
467
468 return pj_turn_session_sendto(turn_sock->sess, pkt, pkt_len,
469 addr, addr_len);
470}
471
472/*
473 * Bind a peer address to a channel number.
474 */
475PJ_DEF(pj_status_t) pj_turn_sock_bind_channel( pj_turn_sock *turn_sock,
476 const pj_sockaddr_t *peer,
477 unsigned addr_len)
478{
479 PJ_ASSERT_RETURN(turn_sock && peer && addr_len, PJ_EINVAL);
480 PJ_ASSERT_RETURN(turn_sock->sess != NULL, PJ_EINVALIDOP);
481
482 return pj_turn_session_bind_channel(turn_sock->sess, peer, addr_len);
483}
484
485
486/*
487 * Notification when outgoing TCP socket has been connected.
488 */
489static pj_bool_t on_connect_complete(pj_activesock_t *asock,
490 pj_status_t status)
491{
492 pj_turn_sock *turn_sock;
493
494 turn_sock = (pj_turn_sock*) pj_activesock_get_user_data(asock);
495 if (!turn_sock)
496 return PJ_FALSE;
497
498 pj_grp_lock_acquire(turn_sock->grp_lock);
499
500 /* TURN session may have already been destroyed here.
501 * See ticket #1557 (http://trac.pjsip.org/repos/ticket/1557).
502 */
503 if (!turn_sock->sess) {
504 sess_fail(turn_sock, "TURN session already destroyed", status);
505 pj_grp_lock_release(turn_sock->grp_lock);
506 return PJ_FALSE;
507 }
508
509 if (status != PJ_SUCCESS) {
510 sess_fail(turn_sock, "TCP connect() error", status);
511 pj_grp_lock_release(turn_sock->grp_lock);
512 return PJ_FALSE;
513 }
514
515 if (turn_sock->conn_type != PJ_TURN_TP_UDP) {
516 PJ_LOG(5,(turn_sock->obj_name, "TCP connected"));
517 }
518
519 /* Kick start pending read operation */
520 status = pj_activesock_start_read(asock, turn_sock->pool,
521 turn_sock->setting.max_pkt_size, 0);
522
523 /* Init send_key */
524 pj_ioqueue_op_key_init(&turn_sock->send_key, sizeof(turn_sock->send_key));
525
526 /* Send Allocate request */
527 status = pj_turn_session_alloc(turn_sock->sess, &turn_sock->alloc_param);
528 if (status != PJ_SUCCESS) {
529 sess_fail(turn_sock, "Error sending ALLOCATE", status);
530 pj_grp_lock_release(turn_sock->grp_lock);
531 return PJ_FALSE;
532 }
533
534 pj_grp_lock_release(turn_sock->grp_lock);
535 return PJ_TRUE;
536}
537
538static pj_uint16_t GETVAL16H(const pj_uint8_t *buf, unsigned pos)
539{
540 return (pj_uint16_t) ((buf[pos + 0] << 8) | \
541 (buf[pos + 1] << 0));
542}
543
544/* Quick check to determine if there is enough packet to process in the
545 * incoming buffer. Return the packet length, or zero if there's no packet.
546 */
547static unsigned has_packet(pj_turn_sock *turn_sock, const void *buf, pj_size_t bufsize)
548{
549 pj_bool_t is_stun;
550
551 if (turn_sock->conn_type == PJ_TURN_TP_UDP)
552 return (unsigned)bufsize;
553
554 /* Quickly check if this is STUN message, by checking the first two bits and
555 * size field which must be multiple of 4 bytes
556 */
557 is_stun = ((((pj_uint8_t*)buf)[0] & 0xC0) == 0) &&
558 ((GETVAL16H((const pj_uint8_t*)buf, 2) & 0x03)==0);
559
560 if (is_stun) {
561 pj_size_t msg_len = GETVAL16H((const pj_uint8_t*)buf, 2);
562 return (unsigned)((msg_len+20 <= bufsize) ? msg_len+20 : 0);
563 } else {
564 /* This must be ChannelData. */
565 pj_turn_channel_data cd;
566
567 if (bufsize < 4)
568 return 0;
569
570 /* Decode ChannelData packet */
571 pj_memcpy(&cd, buf, sizeof(pj_turn_channel_data));
572 cd.length = pj_ntohs(cd.length);
573
574 if (bufsize >= cd.length+sizeof(cd))
575 return (cd.length+sizeof(cd)+3) & (~3);
576 else
577 return 0;
578 }
579}
580
581/*
582 * Notification from ioqueue when incoming UDP packet is received.
583 */
584static pj_bool_t on_data_read(pj_activesock_t *asock,
585 void *data,
586 pj_size_t size,
587 pj_status_t status,
588 pj_size_t *remainder)
589{
590 pj_turn_sock *turn_sock;
591 pj_bool_t ret = PJ_TRUE;
592
593 turn_sock = (pj_turn_sock*) pj_activesock_get_user_data(asock);
594 pj_grp_lock_acquire(turn_sock->grp_lock);
595
596 if (status == PJ_SUCCESS && turn_sock->sess && !turn_sock->is_destroying) {
597 /* Report incoming packet to TURN session, repeat while we have
598 * "packet" in the buffer (required for stream-oriented transports)
599 */
600 unsigned pkt_len;
601
602 //PJ_LOG(5,(turn_sock->pool->obj_name,
603 // "Incoming data, %lu bytes total buffer", size));
604
605 while ((pkt_len=has_packet(turn_sock, data, size)) != 0) {
606 pj_size_t parsed_len;
607 //const pj_uint8_t *pkt = (const pj_uint8_t*)data;
608
609 //PJ_LOG(5,(turn_sock->pool->obj_name,
610 // "Packet start: %02X %02X %02X %02X",
611 // pkt[0], pkt[1], pkt[2], pkt[3]));
612
613 //PJ_LOG(5,(turn_sock->pool->obj_name,
614 // "Processing %lu bytes packet of %lu bytes total buffer",
615 // pkt_len, size));
616
617 parsed_len = (unsigned)size;
618 pj_turn_session_on_rx_pkt(turn_sock->sess, data, size, &parsed_len);
619
620 /* parsed_len may be zero if we have parsing error, so use our
621 * previous calculation to exhaust the bad packet.
622 */
623 if (parsed_len == 0)
624 parsed_len = pkt_len;
625
626 if (parsed_len < (unsigned)size) {
627 *remainder = size - parsed_len;
628 pj_memmove(data, ((char*)data)+parsed_len, *remainder);
629 } else {
630 *remainder = 0;
631 }
632 size = *remainder;
633
634 //PJ_LOG(5,(turn_sock->pool->obj_name,
635 // "Buffer size now %lu bytes", size));
636 }
637 } else if (status != PJ_SUCCESS &&
638 turn_sock->conn_type != PJ_TURN_TP_UDP)
639 {
640 sess_fail(turn_sock, "TCP connection closed", status);
641 ret = PJ_FALSE;
642 goto on_return;
643 }
644
645on_return:
646 pj_grp_lock_release(turn_sock->grp_lock);
647
648 return ret;
649}
650
651
652/*
653 * Callback from TURN session to send outgoing packet.
654 */
655static pj_status_t turn_on_send_pkt(pj_turn_session *sess,
656 const pj_uint8_t *pkt,
657 unsigned pkt_len,
658 const pj_sockaddr_t *dst_addr,
659 unsigned dst_addr_len)
660{
661 pj_turn_sock *turn_sock = (pj_turn_sock*)
662 pj_turn_session_get_user_data(sess);
663 pj_ssize_t len = pkt_len;
664 pj_status_t status;
665
666 if (turn_sock == NULL || turn_sock->is_destroying) {
667 /* We've been destroyed */
668 // https://trac.pjsip.org/repos/ticket/1316
669 //pj_assert(!"We should shutdown gracefully");
670 return PJ_EINVALIDOP;
671 }
672
673 PJ_UNUSED_ARG(dst_addr);
674 PJ_UNUSED_ARG(dst_addr_len);
675
676 status = pj_activesock_send(turn_sock->active_sock, &turn_sock->send_key,
677 pkt, &len, 0);
678 if (status != PJ_SUCCESS && status != PJ_EPENDING) {
679 show_err(turn_sock, "socket send()", status);
680 }
681
682 return status;
683}
684
685
686/*
687 * Callback from TURN session when a channel is successfully bound.
688 */
689static void turn_on_channel_bound(pj_turn_session *sess,
690 const pj_sockaddr_t *peer_addr,
691 unsigned addr_len,
692 unsigned ch_num)
693{
694 PJ_UNUSED_ARG(sess);
695 PJ_UNUSED_ARG(peer_addr);
696 PJ_UNUSED_ARG(addr_len);
697 PJ_UNUSED_ARG(ch_num);
698}
699
700
701/*
702 * Callback from TURN session upon incoming data.
703 */
704static void turn_on_rx_data(pj_turn_session *sess,
705 void *pkt,
706 unsigned pkt_len,
707 const pj_sockaddr_t *peer_addr,
708 unsigned addr_len)
709{
710 pj_turn_sock *turn_sock = (pj_turn_sock*)
711 pj_turn_session_get_user_data(sess);
712 if (turn_sock == NULL || turn_sock->is_destroying) {
713 /* We've been destroyed */
714 return;
715 }
716
717 if (turn_sock->cb.on_rx_data) {
718 (*turn_sock->cb.on_rx_data)(turn_sock, pkt, pkt_len,
719 peer_addr, addr_len);
720 }
721}
722
723
724/*
725 * Callback from TURN session when state has changed
726 */
727static void turn_on_state(pj_turn_session *sess,
728 pj_turn_state_t old_state,
729 pj_turn_state_t new_state)
730{
731 pj_turn_sock *turn_sock = (pj_turn_sock*)
732 pj_turn_session_get_user_data(sess);
733 pj_status_t status;
734
735 if (turn_sock == NULL) {
736 /* We've been destroyed */
737 return;
738 }
739
740 /* Notify app first */
741 if (turn_sock->cb.on_state) {
742 (*turn_sock->cb.on_state)(turn_sock, old_state, new_state);
743 }
744
745 /* Make sure user hasn't destroyed us in the callback */
746 if (turn_sock->sess && new_state == PJ_TURN_STATE_RESOLVED) {
747 pj_turn_session_info info;
748 pj_turn_session_get_info(turn_sock->sess, &info);
749 new_state = info.state;
750 }
751
752 if (turn_sock->sess && new_state == PJ_TURN_STATE_RESOLVED) {
753 /*
754 * Once server has been resolved, initiate outgoing TCP
755 * connection to the server.
756 */
757 pj_turn_session_info info;
758 char addrtxt[PJ_INET6_ADDRSTRLEN+8];
759 int sock_type;
760 pj_sock_t sock;
761 pj_activesock_cfg asock_cfg;
762 pj_activesock_cb asock_cb;
763 pj_sockaddr bound_addr, *cfg_bind_addr;
764 pj_uint16_t max_bind_retry;
765
766 /* Close existing connection, if any. This happens when
767 * we're switching to alternate TURN server when either TCP
768 * connection or ALLOCATE request failed.
769 */
770 if (turn_sock->active_sock) {
771 pj_activesock_close(turn_sock->active_sock);
772 turn_sock->active_sock = NULL;
773 }
774
775 /* Get server address from session info */
776 pj_turn_session_get_info(sess, &info);
777
778 if (turn_sock->conn_type == PJ_TURN_TP_UDP)
779 sock_type = pj_SOCK_DGRAM();
780 else
781 sock_type = pj_SOCK_STREAM();
782
783 /* Init socket */
784 status = pj_sock_socket(turn_sock->af, sock_type, 0, &sock);
785 if (status != PJ_SUCCESS) {
786 pj_turn_sock_destroy(turn_sock);
787 return;
788 }
789
790 /* Bind socket */
791 cfg_bind_addr = &turn_sock->setting.bound_addr;
792 max_bind_retry = MAX_BIND_RETRY;
793 if (turn_sock->setting.port_range &&
794 turn_sock->setting.port_range < max_bind_retry)
795 {
796 max_bind_retry = turn_sock->setting.port_range;
797 }
798 pj_sockaddr_init(turn_sock->af, &bound_addr, NULL, 0);
799 if (cfg_bind_addr->addr.sa_family == pj_AF_INET() ||
800 cfg_bind_addr->addr.sa_family == pj_AF_INET6())
801 {
802 pj_sockaddr_cp(&bound_addr, cfg_bind_addr);
803 }
804 status = pj_sock_bind_random(sock, &bound_addr,
805 turn_sock->setting.port_range,
806 max_bind_retry);
807 if (status != PJ_SUCCESS) {
808 pj_turn_sock_destroy(turn_sock);
809 return;
810 }
811
812 /* Apply QoS, if specified */
813 status = pj_sock_apply_qos2(sock, turn_sock->setting.qos_type,
814 &turn_sock->setting.qos_params,
815 (turn_sock->setting.qos_ignore_error?2:1),
816 turn_sock->pool->obj_name, NULL);
817 if (status != PJ_SUCCESS && !turn_sock->setting.qos_ignore_error) {
818 pj_turn_sock_destroy(turn_sock);
819 return;
820 }
821
822 /* Apply socket buffer size */
823 if (turn_sock->setting.so_rcvbuf_size > 0) {
824 unsigned sobuf_size = turn_sock->setting.so_rcvbuf_size;
825 status = pj_sock_setsockopt_sobuf(sock, pj_SO_RCVBUF(),
826 PJ_TRUE, &sobuf_size);
827 if (status != PJ_SUCCESS) {
828 pj_perror(3, turn_sock->obj_name, status,
829 "Failed setting SO_RCVBUF");
830 } else {
831 if (sobuf_size < turn_sock->setting.so_rcvbuf_size) {
832 PJ_LOG(4, (turn_sock->obj_name,
833 "Warning! Cannot set SO_RCVBUF as configured,"
834 " now=%d, configured=%d", sobuf_size,
835 turn_sock->setting.so_rcvbuf_size));
836 } else {
837 PJ_LOG(5, (turn_sock->obj_name, "SO_RCVBUF set to %d",
838 sobuf_size));
839 }
840 }
841 }
842 if (turn_sock->setting.so_sndbuf_size > 0) {
843 unsigned sobuf_size = turn_sock->setting.so_sndbuf_size;
844 status = pj_sock_setsockopt_sobuf(sock, pj_SO_SNDBUF(),
845 PJ_TRUE, &sobuf_size);
846 if (status != PJ_SUCCESS) {
847 pj_perror(3, turn_sock->obj_name, status,
848 "Failed setting SO_SNDBUF");
849 } else {
850 if (sobuf_size < turn_sock->setting.so_sndbuf_size) {
851 PJ_LOG(4, (turn_sock->obj_name,
852 "Warning! Cannot set SO_SNDBUF as configured,"
853 " now=%d, configured=%d", sobuf_size,
854 turn_sock->setting.so_sndbuf_size));
855 } else {
856 PJ_LOG(5, (turn_sock->obj_name, "SO_SNDBUF set to %d",
857 sobuf_size));
858 }
859 }
860 }
861
862 /* Create active socket */
863 pj_activesock_cfg_default(&asock_cfg);
864 asock_cfg.grp_lock = turn_sock->grp_lock;
865
866 pj_bzero(&asock_cb, sizeof(asock_cb));
867 asock_cb.on_data_read = &on_data_read;
868 asock_cb.on_connect_complete = &on_connect_complete;
869 status = pj_activesock_create(turn_sock->pool, sock,
870 sock_type, &asock_cfg,
871 turn_sock->cfg.ioqueue, &asock_cb,
872 turn_sock,
873 &turn_sock->active_sock);
874 if (status != PJ_SUCCESS) {
875 pj_turn_sock_destroy(turn_sock);
876 return;
877 }
878
879 PJ_LOG(5,(turn_sock->pool->obj_name,
880 "Connecting to %s",
881 pj_sockaddr_print(&info.server, addrtxt,
882 sizeof(addrtxt), 3)));
883
884 /* Initiate non-blocking connect */
885#if PJ_HAS_TCP
886 status=pj_activesock_start_connect(turn_sock->active_sock,
887 turn_sock->pool,
888 &info.server,
889 pj_sockaddr_get_len(&info.server));
890 if (status == PJ_SUCCESS) {
891 on_connect_complete(turn_sock->active_sock, PJ_SUCCESS);
892 } else if (status != PJ_EPENDING) {
893 pj_turn_sock_destroy(turn_sock);
894 return;
895 }
896#else
897 on_connect_complete(turn_sock->active_sock, PJ_SUCCESS);
898#endif
899
900 /* Done for now. Subsequent work will be done in
901 * on_connect_complete() callback.
902 */
903 }
904
905 if (new_state >= PJ_TURN_STATE_DESTROYING && turn_sock->sess) {
906 pj_time_val delay = {0, 0};
907
908 turn_sock->sess = NULL;
909 pj_turn_session_set_user_data(sess, NULL);
910
911 pj_timer_heap_cancel_if_active(turn_sock->cfg.timer_heap,
912 &turn_sock->timer, 0);
913 pj_timer_heap_schedule_w_grp_lock(turn_sock->cfg.timer_heap,
914 &turn_sock->timer,
915 &delay, TIMER_DESTROY,
916 turn_sock->grp_lock);
917 }
918}
919
920