blob: 546f63d410918dc46ac8009180fa9875b4f71ef8 [file] [log] [blame]
Tristan Matthews0a329cc2013-07-17 13:20:14 -04001/* $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/nat_detect.h>
21#include <pjnath/errno.h>
22#include <pj/assert.h>
23#include <pj/ioqueue.h>
24#include <pj/log.h>
25#include <pj/os.h>
26#include <pj/pool.h>
27#include <pj/rand.h>
28#include <pj/string.h>
29#include <pj/timer.h>
30#include <pj/compat/socket.h>
31
32
33static const char *nat_type_names[] =
34{
35 "Unknown",
36 "ErrUnknown",
37 "Open",
38 "Blocked",
39 "Symmetric UDP",
40 "Full Cone",
41 "Symmetric",
42 "Restricted",
43 "Port Restricted"
44};
45
46
47#define CHANGE_IP_FLAG 4
48#define CHANGE_PORT_FLAG 2
49#define CHANGE_IP_PORT_FLAG (CHANGE_IP_FLAG | CHANGE_PORT_FLAG)
50#define TEST_INTERVAL 50
51
52enum test_type
53{
54 ST_TEST_1,
55 ST_TEST_2,
56 ST_TEST_3,
57 ST_TEST_1B,
58 ST_MAX
59};
60
61static const char *test_names[] =
62{
63 "Test I: Binding request",
64 "Test II: Binding request with change address and port request",
65 "Test III: Binding request with change port request",
66 "Test IB: Binding request to alternate address"
67};
68
69enum timer_type
70{
71 TIMER_TEST = 1,
72 TIMER_DESTROY = 2
73};
74
75typedef struct nat_detect_session
76{
77 pj_pool_t *pool;
78 pj_mutex_t *mutex;
79
80 pj_timer_heap_t *timer_heap;
81 pj_timer_entry timer;
82 unsigned timer_executed;
83
84 void *user_data;
85 pj_stun_nat_detect_cb *cb;
86 pj_sock_t sock;
87 pj_sockaddr_in local_addr;
88 pj_ioqueue_key_t *key;
89 pj_sockaddr_in server;
90 pj_sockaddr_in *cur_server;
91 pj_stun_session *stun_sess;
92
93 pj_ioqueue_op_key_t read_op, write_op;
94 pj_uint8_t rx_pkt[PJ_STUN_MAX_PKT_LEN];
95 pj_ssize_t rx_pkt_len;
96 pj_sockaddr_in src_addr;
97 int src_addr_len;
98
99 struct result
100 {
101 pj_bool_t executed;
102 pj_bool_t complete;
103 pj_status_t status;
104 pj_sockaddr_in ma;
105 pj_sockaddr_in ca;
106 pj_stun_tx_data *tdata;
107 } result[ST_MAX];
108
109} nat_detect_session;
110
111
112static void on_read_complete(pj_ioqueue_key_t *key,
113 pj_ioqueue_op_key_t *op_key,
114 pj_ssize_t bytes_read);
115static void on_request_complete(pj_stun_session *sess,
116 pj_status_t status,
117 void *token,
118 pj_stun_tx_data *tdata,
119 const pj_stun_msg *response,
120 const pj_sockaddr_t *src_addr,
121 unsigned src_addr_len);
122static pj_status_t on_send_msg(pj_stun_session *sess,
123 void *token,
124 const void *pkt,
125 pj_size_t pkt_size,
126 const pj_sockaddr_t *dst_addr,
127 unsigned addr_len);
128
129static pj_status_t send_test(nat_detect_session *sess,
130 enum test_type test_id,
131 const pj_sockaddr_in *alt_addr,
132 pj_uint32_t change_flag);
133static void on_sess_timer(pj_timer_heap_t *th,
134 pj_timer_entry *te);
135static void sess_destroy(nat_detect_session *sess);
136
137
138/*
139 * Get the NAT name from the specified NAT type.
140 */
141PJ_DEF(const char*) pj_stun_get_nat_name(pj_stun_nat_type type)
142{
143 PJ_ASSERT_RETURN(type >= 0 && type <= PJ_STUN_NAT_TYPE_PORT_RESTRICTED,
144 "*Invalid*");
145
146 return nat_type_names[type];
147}
148
149static int test_executed(nat_detect_session *sess)
150{
151 unsigned i, count;
152 for (i=0, count=0; i<PJ_ARRAY_SIZE(sess->result); ++i) {
153 if (sess->result[i].executed)
154 ++count;
155 }
156 return count;
157}
158
159static int test_completed(nat_detect_session *sess)
160{
161 unsigned i, count;
162 for (i=0, count=0; i<PJ_ARRAY_SIZE(sess->result); ++i) {
163 if (sess->result[i].complete)
164 ++count;
165 }
166 return count;
167}
168
169static pj_status_t get_local_interface(const pj_sockaddr_in *server,
170 pj_in_addr *local_addr)
171{
172 pj_sock_t sock;
173 pj_sockaddr_in tmp;
174 int addr_len;
175 pj_status_t status;
176
177 status = pj_sock_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, &sock);
178 if (status != PJ_SUCCESS)
179 return status;
180
181 status = pj_sock_bind_in(sock, 0, 0);
182 if (status != PJ_SUCCESS) {
183 pj_sock_close(sock);
184 return status;
185 }
186
187 status = pj_sock_connect(sock, server, sizeof(pj_sockaddr_in));
188 if (status != PJ_SUCCESS) {
189 pj_sock_close(sock);
190 return status;
191 }
192
193 addr_len = sizeof(pj_sockaddr_in);
194 status = pj_sock_getsockname(sock, &tmp, &addr_len);
195 if (status != PJ_SUCCESS) {
196 pj_sock_close(sock);
197 return status;
198 }
199
200 local_addr->s_addr = tmp.sin_addr.s_addr;
201
202 pj_sock_close(sock);
203 return PJ_SUCCESS;
204}
205
206
207PJ_DEF(pj_status_t) pj_stun_detect_nat_type(const pj_sockaddr_in *server,
208 pj_stun_config *stun_cfg,
209 void *user_data,
210 pj_stun_nat_detect_cb *cb)
211{
212 pj_pool_t *pool;
213 nat_detect_session *sess;
214 pj_stun_session_cb sess_cb;
215 pj_ioqueue_callback ioqueue_cb;
216 int addr_len;
217 pj_status_t status;
218
219 PJ_ASSERT_RETURN(server && stun_cfg, PJ_EINVAL);
220 PJ_ASSERT_RETURN(stun_cfg->pf && stun_cfg->ioqueue && stun_cfg->timer_heap,
221 PJ_EINVAL);
222
223 /*
224 * Init NAT detection session.
225 */
226 pool = pj_pool_create(stun_cfg->pf, "natck%p", PJNATH_POOL_LEN_NATCK,
227 PJNATH_POOL_INC_NATCK, NULL);
228 if (!pool)
229 return PJ_ENOMEM;
230
231 sess = PJ_POOL_ZALLOC_T(pool, nat_detect_session);
232 sess->pool = pool;
233 sess->user_data = user_data;
234 sess->cb = cb;
235
236 status = pj_mutex_create_recursive(pool, pool->obj_name, &sess->mutex);
237 if (status != PJ_SUCCESS)
238 goto on_error;
239
240 pj_memcpy(&sess->server, server, sizeof(pj_sockaddr_in));
241
242 /*
243 * Init timer to self-destroy.
244 */
245 sess->timer_heap = stun_cfg->timer_heap;
246 sess->timer.cb = &on_sess_timer;
247 sess->timer.user_data = sess;
248
249
250 /*
251 * Initialize socket.
252 */
253 status = pj_sock_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, &sess->sock);
254 if (status != PJ_SUCCESS)
255 goto on_error;
256
257 /*
258 * Bind to any.
259 */
260 pj_bzero(&sess->local_addr, sizeof(pj_sockaddr_in));
261 sess->local_addr.sin_family = pj_AF_INET();
262 status = pj_sock_bind(sess->sock, &sess->local_addr,
263 sizeof(pj_sockaddr_in));
264 if (status != PJ_SUCCESS)
265 goto on_error;
266
267 /*
268 * Get local/bound address.
269 */
270 addr_len = sizeof(sess->local_addr);
271 status = pj_sock_getsockname(sess->sock, &sess->local_addr, &addr_len);
272 if (status != PJ_SUCCESS)
273 goto on_error;
274
275 /*
276 * Find out which interface is used to send to the server.
277 */
278 status = get_local_interface(server, &sess->local_addr.sin_addr);
279 if (status != PJ_SUCCESS)
280 goto on_error;
281
282 PJ_LOG(5,(sess->pool->obj_name, "Local address is %s:%d",
283 pj_inet_ntoa(sess->local_addr.sin_addr),
284 pj_ntohs(sess->local_addr.sin_port)));
285
286 PJ_LOG(5,(sess->pool->obj_name, "Server set to %s:%d",
287 pj_inet_ntoa(server->sin_addr),
288 pj_ntohs(server->sin_port)));
289
290 /*
291 * Register socket to ioqueue to receive asynchronous input
292 * notification.
293 */
294 pj_bzero(&ioqueue_cb, sizeof(ioqueue_cb));
295 ioqueue_cb.on_read_complete = &on_read_complete;
296
297 status = pj_ioqueue_register_sock(sess->pool, stun_cfg->ioqueue,
298 sess->sock, sess, &ioqueue_cb,
299 &sess->key);
300 if (status != PJ_SUCCESS)
301 goto on_error;
302
303 /*
304 * Create STUN session.
305 */
306 pj_bzero(&sess_cb, sizeof(sess_cb));
307 sess_cb.on_request_complete = &on_request_complete;
308 sess_cb.on_send_msg = &on_send_msg;
309 status = pj_stun_session_create(stun_cfg, pool->obj_name, &sess_cb,
310 PJ_FALSE, NULL, &sess->stun_sess);
311 if (status != PJ_SUCCESS)
312 goto on_error;
313
314 pj_stun_session_set_user_data(sess->stun_sess, sess);
315
316 /*
317 * Kick-off ioqueue reading.
318 */
319 pj_ioqueue_op_key_init(&sess->read_op, sizeof(sess->read_op));
320 pj_ioqueue_op_key_init(&sess->write_op, sizeof(sess->write_op));
321 on_read_complete(sess->key, &sess->read_op, 0);
322
323 /*
324 * Start TEST_1
325 */
326 sess->timer.id = TIMER_TEST;
327 on_sess_timer(stun_cfg->timer_heap, &sess->timer);
328
329 return PJ_SUCCESS;
330
331on_error:
332 sess_destroy(sess);
333 return status;
334}
335
336
337static void sess_destroy(nat_detect_session *sess)
338{
339 if (sess->stun_sess) {
340 pj_stun_session_destroy(sess->stun_sess);
341 }
342
343 if (sess->key) {
344 pj_ioqueue_unregister(sess->key);
345 } else if (sess->sock && sess->sock != PJ_INVALID_SOCKET) {
346 pj_sock_close(sess->sock);
347 }
348
349 if (sess->mutex) {
350 pj_mutex_destroy(sess->mutex);
351 }
352
353 if (sess->pool) {
354 pj_pool_release(sess->pool);
355 }
356}
357
358
359static void end_session(nat_detect_session *sess,
360 pj_status_t status,
361 pj_stun_nat_type nat_type)
362{
363 pj_stun_nat_detect_result result;
364 char errmsg[PJ_ERR_MSG_SIZE];
365 pj_time_val delay;
366
367 if (sess->timer.id != 0) {
368 pj_timer_heap_cancel(sess->timer_heap, &sess->timer);
369 sess->timer.id = 0;
370 }
371
372 pj_bzero(&result, sizeof(result));
373 errmsg[0] = '\0';
374 result.status_text = errmsg;
375
376 result.status = status;
377 pj_strerror(status, errmsg, sizeof(errmsg));
378 result.nat_type = nat_type;
379 result.nat_type_name = nat_type_names[result.nat_type];
380
381 if (sess->cb)
382 (*sess->cb)(sess->user_data, &result);
383
384 delay.sec = 0;
385 delay.msec = 0;
386
387 sess->timer.id = TIMER_DESTROY;
388 pj_timer_heap_schedule(sess->timer_heap, &sess->timer, &delay);
389}
390
391
392/*
393 * Callback upon receiving packet from network.
394 */
395static void on_read_complete(pj_ioqueue_key_t *key,
396 pj_ioqueue_op_key_t *op_key,
397 pj_ssize_t bytes_read)
398{
399 nat_detect_session *sess;
400 pj_status_t status;
401
402 sess = (nat_detect_session *) pj_ioqueue_get_user_data(key);
403 pj_assert(sess != NULL);
404
405 pj_mutex_lock(sess->mutex);
406
407 if (bytes_read < 0) {
408 if (-bytes_read != PJ_STATUS_FROM_OS(OSERR_EWOULDBLOCK) &&
409 -bytes_read != PJ_STATUS_FROM_OS(OSERR_EINPROGRESS) &&
410 -bytes_read != PJ_STATUS_FROM_OS(OSERR_ECONNRESET))
411 {
412 /* Permanent error */
413 end_session(sess, (pj_status_t)-bytes_read,
414 PJ_STUN_NAT_TYPE_ERR_UNKNOWN);
415 goto on_return;
416 }
417
418 } else if (bytes_read > 0) {
419 pj_stun_session_on_rx_pkt(sess->stun_sess, sess->rx_pkt, bytes_read,
420 PJ_STUN_IS_DATAGRAM|PJ_STUN_CHECK_PACKET,
421 NULL, NULL,
422 &sess->src_addr, sess->src_addr_len);
423 }
424
425
426 sess->rx_pkt_len = sizeof(sess->rx_pkt);
427 sess->src_addr_len = sizeof(sess->src_addr);
428 status = pj_ioqueue_recvfrom(key, op_key, sess->rx_pkt, &sess->rx_pkt_len,
429 PJ_IOQUEUE_ALWAYS_ASYNC,
430 &sess->src_addr, &sess->src_addr_len);
431
432 if (status != PJ_EPENDING) {
433 pj_assert(status != PJ_SUCCESS);
434 end_session(sess, status, PJ_STUN_NAT_TYPE_ERR_UNKNOWN);
435 }
436
437on_return:
438 pj_mutex_unlock(sess->mutex);
439}
440
441
442/*
443 * Callback to send outgoing packet from STUN session.
444 */
445static pj_status_t on_send_msg(pj_stun_session *stun_sess,
446 void *token,
447 const void *pkt,
448 pj_size_t pkt_size,
449 const pj_sockaddr_t *dst_addr,
450 unsigned addr_len)
451{
452 nat_detect_session *sess;
453 pj_ssize_t pkt_len;
454 pj_status_t status;
455
456 PJ_UNUSED_ARG(token);
457
458 sess = (nat_detect_session*) pj_stun_session_get_user_data(stun_sess);
459
460 pkt_len = pkt_size;
461 status = pj_ioqueue_sendto(sess->key, &sess->write_op, pkt, &pkt_len, 0,
462 dst_addr, addr_len);
463
464 return status;
465
466}
467
468/*
469 * Callback upon request completion.
470 */
471static void on_request_complete(pj_stun_session *stun_sess,
472 pj_status_t status,
473 void *token,
474 pj_stun_tx_data *tdata,
475 const pj_stun_msg *response,
476 const pj_sockaddr_t *src_addr,
477 unsigned src_addr_len)
478{
479 nat_detect_session *sess;
480 pj_stun_sockaddr_attr *mattr = NULL;
481 pj_stun_changed_addr_attr *ca = NULL;
482 pj_uint32_t *tsx_id;
483 int cmp;
484 unsigned test_id;
485
486 PJ_UNUSED_ARG(token);
487 PJ_UNUSED_ARG(tdata);
488 PJ_UNUSED_ARG(src_addr);
489 PJ_UNUSED_ARG(src_addr_len);
490
491 sess = (nat_detect_session*) pj_stun_session_get_user_data(stun_sess);
492
493 pj_mutex_lock(sess->mutex);
494
495 /* Find errors in the response */
496 if (status == PJ_SUCCESS) {
497
498 /* Check error message */
499 if (PJ_STUN_IS_ERROR_RESPONSE(response->hdr.type)) {
500 pj_stun_errcode_attr *eattr;
501 int err_code;
502
503 eattr = (pj_stun_errcode_attr*)
504 pj_stun_msg_find_attr(response, PJ_STUN_ATTR_ERROR_CODE, 0);
505
506 if (eattr != NULL)
507 err_code = eattr->err_code;
508 else
509 err_code = PJ_STUN_SC_SERVER_ERROR;
510
511 status = PJ_STATUS_FROM_STUN_CODE(err_code);
512
513
514 } else {
515
516 /* Get MAPPED-ADDRESS or XOR-MAPPED-ADDRESS */
517 mattr = (pj_stun_sockaddr_attr*)
518 pj_stun_msg_find_attr(response, PJ_STUN_ATTR_XOR_MAPPED_ADDR, 0);
519 if (mattr == NULL) {
520 mattr = (pj_stun_sockaddr_attr*)
521 pj_stun_msg_find_attr(response, PJ_STUN_ATTR_MAPPED_ADDR, 0);
522 }
523
524 if (mattr == NULL) {
525 status = PJNATH_ESTUNNOMAPPEDADDR;
526 }
527
528 /* Get CHANGED-ADDRESS attribute */
529 ca = (pj_stun_changed_addr_attr*)
530 pj_stun_msg_find_attr(response, PJ_STUN_ATTR_CHANGED_ADDR, 0);
531
532 if (ca == NULL) {
533 status = PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_SERVER_ERROR);
534 }
535
536 }
537 }
538
539 /* Save the result */
540 tsx_id = (pj_uint32_t*) tdata->msg->hdr.tsx_id;
541 test_id = tsx_id[2];
542
543 if (test_id >= ST_MAX) {
544 PJ_LOG(4,(sess->pool->obj_name, "Invalid transaction ID %u in response",
545 test_id));
546 end_session(sess, PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_SERVER_ERROR),
547 PJ_STUN_NAT_TYPE_ERR_UNKNOWN);
548 goto on_return;
549 }
550
551 PJ_LOG(5,(sess->pool->obj_name, "Completed %s, status=%d",
552 test_names[test_id], status));
553
554 sess->result[test_id].complete = PJ_TRUE;
555 sess->result[test_id].status = status;
556 if (status == PJ_SUCCESS) {
557 pj_memcpy(&sess->result[test_id].ma, &mattr->sockaddr.ipv4,
558 sizeof(pj_sockaddr_in));
559 pj_memcpy(&sess->result[test_id].ca, &ca->sockaddr.ipv4,
560 sizeof(pj_sockaddr_in));
561 }
562
563 /* Send Test 1B only when Test 2 completes. Must not send Test 1B
564 * before Test 2 completes to avoid creating mapping on the NAT.
565 */
566 if (!sess->result[ST_TEST_1B].executed &&
567 sess->result[ST_TEST_2].complete &&
568 sess->result[ST_TEST_2].status != PJ_SUCCESS &&
569 sess->result[ST_TEST_1].complete &&
570 sess->result[ST_TEST_1].status == PJ_SUCCESS)
571 {
572 cmp = pj_memcmp(&sess->local_addr, &sess->result[ST_TEST_1].ma,
573 sizeof(pj_sockaddr_in));
574 if (cmp != 0)
575 send_test(sess, ST_TEST_1B, &sess->result[ST_TEST_1].ca, 0);
576 }
577
578 if (test_completed(sess)<3 || test_completed(sess)!=test_executed(sess))
579 goto on_return;
580
581 /* Handle the test result according to RFC 3489 page 22:
582
583
584 +--------+
585 | Test |
586 | 1 |
587 +--------+
588 |
589 |
590 V
591 /\ /\
592 N / \ Y / \ Y +--------+
593 UDP <-------/Resp\--------->/ IP \------------->| Test |
594 Blocked \ ? / \Same/ | 2 |
595 \ / \? / +--------+
596 \/ \/ |
597 | N |
598 | V
599 V /\
600 +--------+ Sym. N / \
601 | Test | UDP <---/Resp\
602 | 2 | Firewall \ ? /
603 +--------+ \ /
604 | \/
605 V |Y
606 /\ /\ |
607 Symmetric N / \ +--------+ N / \ V
608 NAT <--- / IP \<-----| Test |<--- /Resp\ Open
609 \Same/ | 1B | \ ? / Internet
610 \? / +--------+ \ /
611 \/ \/
612 | |Y
613 | |
614 | V
615 | Full
616 | Cone
617 V /\
618 +--------+ / \ Y
619 | Test |------>/Resp\---->Restricted
620 | 3 | \ ? /
621 +--------+ \ /
622 \/
623 |N
624 | Port
625 +------>Restricted
626
627 Figure 2: Flow for type discovery process
628 */
629
630 switch (sess->result[ST_TEST_1].status) {
631 case PJNATH_ESTUNTIMEDOUT:
632 /*
633 * Test 1 has timed-out. Conclude with NAT_TYPE_BLOCKED.
634 */
635 end_session(sess, PJ_SUCCESS, PJ_STUN_NAT_TYPE_BLOCKED);
636 break;
637 case PJ_SUCCESS:
638 /*
639 * Test 1 is successful. Further tests are needed to detect
640 * NAT type. Compare the MAPPED-ADDRESS with the local address.
641 */
642 cmp = pj_memcmp(&sess->local_addr, &sess->result[ST_TEST_1].ma,
643 sizeof(pj_sockaddr_in));
644 if (cmp==0) {
645 /*
646 * MAPPED-ADDRESS and local address is equal. Need one more
647 * test to determine NAT type.
648 */
649 switch (sess->result[ST_TEST_2].status) {
650 case PJ_SUCCESS:
651 /*
652 * Test 2 is also successful. We're in the open.
653 */
654 end_session(sess, PJ_SUCCESS, PJ_STUN_NAT_TYPE_OPEN);
655 break;
656 case PJNATH_ESTUNTIMEDOUT:
657 /*
658 * Test 2 has timed out. We're behind somekind of UDP
659 * firewall.
660 */
661 end_session(sess, PJ_SUCCESS, PJ_STUN_NAT_TYPE_SYMMETRIC_UDP);
662 break;
663 default:
664 /*
665 * We've got other error with Test 2.
666 */
667 end_session(sess, sess->result[ST_TEST_2].status,
668 PJ_STUN_NAT_TYPE_ERR_UNKNOWN);
669 break;
670 }
671 } else {
672 /*
673 * MAPPED-ADDRESS is different than local address.
674 * We're behind NAT.
675 */
676 switch (sess->result[ST_TEST_2].status) {
677 case PJ_SUCCESS:
678 /*
679 * Test 2 is successful. We're behind a full-cone NAT.
680 */
681 end_session(sess, PJ_SUCCESS, PJ_STUN_NAT_TYPE_FULL_CONE);
682 break;
683 case PJNATH_ESTUNTIMEDOUT:
684 /*
685 * Test 2 has timed-out Check result of test 1B..
686 */
687 switch (sess->result[ST_TEST_1B].status) {
688 case PJ_SUCCESS:
689 /*
690 * Compare the MAPPED-ADDRESS of test 1B with the
691 * MAPPED-ADDRESS returned in test 1..
692 */
693 cmp = pj_memcmp(&sess->result[ST_TEST_1].ma,
694 &sess->result[ST_TEST_1B].ma,
695 sizeof(pj_sockaddr_in));
696 if (cmp != 0) {
697 /*
698 * MAPPED-ADDRESS is different, we're behind a
699 * symmetric NAT.
700 */
701 end_session(sess, PJ_SUCCESS,
702 PJ_STUN_NAT_TYPE_SYMMETRIC);
703 } else {
704 /*
705 * MAPPED-ADDRESS is equal. We're behind a restricted
706 * or port-restricted NAT, depending on the result of
707 * test 3.
708 */
709 switch (sess->result[ST_TEST_3].status) {
710 case PJ_SUCCESS:
711 /*
712 * Test 3 is successful, we're behind a restricted
713 * NAT.
714 */
715 end_session(sess, PJ_SUCCESS,
716 PJ_STUN_NAT_TYPE_RESTRICTED);
717 break;
718 case PJNATH_ESTUNTIMEDOUT:
719 /*
720 * Test 3 failed, we're behind a port restricted
721 * NAT.
722 */
723 end_session(sess, PJ_SUCCESS,
724 PJ_STUN_NAT_TYPE_PORT_RESTRICTED);
725 break;
726 default:
727 /*
728 * Got other error with test 3.
729 */
730 end_session(sess, sess->result[ST_TEST_3].status,
731 PJ_STUN_NAT_TYPE_ERR_UNKNOWN);
732 break;
733 }
734 }
735 break;
736 case PJNATH_ESTUNTIMEDOUT:
737 /*
738 * Strangely test 1B has failed. Maybe connectivity was
739 * lost? Or perhaps port 3489 (the usual port number in
740 * CHANGED-ADDRESS) is blocked?
741 */
742 switch (sess->result[ST_TEST_3].status) {
743 case PJ_SUCCESS:
744 /* Although test 1B failed, test 3 was successful.
745 * It could be that port 3489 is blocked, while the
746 * NAT itself looks to be a Restricted one.
747 */
748 end_session(sess, PJ_SUCCESS,
749 PJ_STUN_NAT_TYPE_RESTRICTED);
750 break;
751 default:
752 /* Can't distinguish between Symmetric and Port
753 * Restricted, so set the type to Unknown
754 */
755 end_session(sess, PJ_SUCCESS,
756 PJ_STUN_NAT_TYPE_ERR_UNKNOWN);
757 break;
758 }
759 break;
760 default:
761 /*
762 * Got other error with test 1B.
763 */
764 end_session(sess, sess->result[ST_TEST_1B].status,
765 PJ_STUN_NAT_TYPE_ERR_UNKNOWN);
766 break;
767 }
768 break;
769 default:
770 /*
771 * We've got other error with Test 2.
772 */
773 end_session(sess, sess->result[ST_TEST_2].status,
774 PJ_STUN_NAT_TYPE_ERR_UNKNOWN);
775 break;
776 }
777 }
778 break;
779 default:
780 /*
781 * We've got other error with Test 1.
782 */
783 end_session(sess, sess->result[ST_TEST_1].status,
784 PJ_STUN_NAT_TYPE_ERR_UNKNOWN);
785 break;
786 }
787
788on_return:
789 pj_mutex_unlock(sess->mutex);
790}
791
792
793/* Perform test */
794static pj_status_t send_test(nat_detect_session *sess,
795 enum test_type test_id,
796 const pj_sockaddr_in *alt_addr,
797 pj_uint32_t change_flag)
798{
799 pj_uint32_t magic, tsx_id[3];
800 pj_status_t status;
801
802 sess->result[test_id].executed = PJ_TRUE;
803
804 /* Randomize tsx id */
805 do {
806 magic = pj_rand();
807 } while (magic == PJ_STUN_MAGIC);
808
809 tsx_id[0] = pj_rand();
810 tsx_id[1] = pj_rand();
811 tsx_id[2] = test_id;
812
813 /* Create BIND request */
814 status = pj_stun_session_create_req(sess->stun_sess,
815 PJ_STUN_BINDING_REQUEST, magic,
816 (pj_uint8_t*)tsx_id,
817 &sess->result[test_id].tdata);
818 if (status != PJ_SUCCESS)
819 goto on_error;
820
821 /* Add CHANGE-REQUEST attribute */
822 status = pj_stun_msg_add_uint_attr(sess->pool,
823 sess->result[test_id].tdata->msg,
824 PJ_STUN_ATTR_CHANGE_REQUEST,
825 change_flag);
826 if (status != PJ_SUCCESS)
827 goto on_error;
828
829 /* Configure alternate address */
830 if (alt_addr)
831 sess->cur_server = (pj_sockaddr_in*) alt_addr;
832 else
833 sess->cur_server = &sess->server;
834
835 PJ_LOG(5,(sess->pool->obj_name,
836 "Performing %s to %s:%d",
837 test_names[test_id],
838 pj_inet_ntoa(sess->cur_server->sin_addr),
839 pj_ntohs(sess->cur_server->sin_port)));
840
841 /* Send the request */
842 status = pj_stun_session_send_msg(sess->stun_sess, NULL, PJ_TRUE,
843 PJ_TRUE, sess->cur_server,
844 sizeof(pj_sockaddr_in),
845 sess->result[test_id].tdata);
846 if (status != PJ_SUCCESS)
847 goto on_error;
848
849 return PJ_SUCCESS;
850
851on_error:
852 sess->result[test_id].complete = PJ_TRUE;
853 sess->result[test_id].status = status;
854
855 return status;
856}
857
858
859/* Timer callback */
860static void on_sess_timer(pj_timer_heap_t *th,
861 pj_timer_entry *te)
862{
863 nat_detect_session *sess;
864
865 sess = (nat_detect_session*) te->user_data;
866
867 if (te->id == TIMER_DESTROY) {
868 pj_mutex_lock(sess->mutex);
869 pj_ioqueue_unregister(sess->key);
870 sess->key = NULL;
871 sess->sock = PJ_INVALID_SOCKET;
872 te->id = 0;
873 pj_mutex_unlock(sess->mutex);
874
875 sess_destroy(sess);
876
877 } else if (te->id == TIMER_TEST) {
878
879 pj_bool_t next_timer;
880
881 pj_mutex_lock(sess->mutex);
882
883 next_timer = PJ_FALSE;
884
885 if (sess->timer_executed == 0) {
886 send_test(sess, ST_TEST_1, NULL, 0);
887 next_timer = PJ_TRUE;
888 } else if (sess->timer_executed == 1) {
889 send_test(sess, ST_TEST_2, NULL, CHANGE_IP_PORT_FLAG);
890 next_timer = PJ_TRUE;
891 } else if (sess->timer_executed == 2) {
892 send_test(sess, ST_TEST_3, NULL, CHANGE_PORT_FLAG);
893 } else {
894 pj_assert(!"Shouldn't have timer at this state");
895 }
896
897 ++sess->timer_executed;
898
899 if (next_timer) {
900 pj_time_val delay = {0, TEST_INTERVAL};
901 pj_timer_heap_schedule(th, te, &delay);
902 } else {
903 te->id = 0;
904 }
905
906 pj_mutex_unlock(sess->mutex);
907
908 } else {
909 pj_assert(!"Invalid timer ID");
910 }
911}
912