blob: a4fa588ebc3e612679c87eb84b5cb57aeb620ed3 [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/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_grp_lock_t *grp_lock;
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);
136static void sess_on_destroy(void *member);
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_grp_lock_create(pool, NULL, &sess->grp_lock);
237 if (status != PJ_SUCCESS) {
238 /* Group lock not created yet, just destroy pool and return */
239 pj_pool_release(pool);
240 return status;
241 }
242
243 pj_grp_lock_add_ref(sess->grp_lock);
244 pj_grp_lock_add_handler(sess->grp_lock, pool, sess, &sess_on_destroy);
245
246 pj_memcpy(&sess->server, server, sizeof(pj_sockaddr_in));
247
248 /*
249 * Init timer to self-destroy.
250 */
251 sess->timer_heap = stun_cfg->timer_heap;
252 sess->timer.cb = &on_sess_timer;
253 sess->timer.user_data = sess;
254
255
256 /*
257 * Initialize socket.
258 */
259 status = pj_sock_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, &sess->sock);
260 if (status != PJ_SUCCESS)
261 goto on_error;
262
263 /*
264 * Bind to any.
265 */
266 pj_bzero(&sess->local_addr, sizeof(pj_sockaddr_in));
267 sess->local_addr.sin_family = pj_AF_INET();
268 status = pj_sock_bind(sess->sock, &sess->local_addr,
269 sizeof(pj_sockaddr_in));
270 if (status != PJ_SUCCESS)
271 goto on_error;
272
273 /*
274 * Get local/bound address.
275 */
276 addr_len = sizeof(sess->local_addr);
277 status = pj_sock_getsockname(sess->sock, &sess->local_addr, &addr_len);
278 if (status != PJ_SUCCESS)
279 goto on_error;
280
281 /*
282 * Find out which interface is used to send to the server.
283 */
284 status = get_local_interface(server, &sess->local_addr.sin_addr);
285 if (status != PJ_SUCCESS)
286 goto on_error;
287
288 PJ_LOG(5,(sess->pool->obj_name, "Local address is %s:%d",
289 pj_inet_ntoa(sess->local_addr.sin_addr),
290 pj_ntohs(sess->local_addr.sin_port)));
291
292 PJ_LOG(5,(sess->pool->obj_name, "Server set to %s:%d",
293 pj_inet_ntoa(server->sin_addr),
294 pj_ntohs(server->sin_port)));
295
296 /*
297 * Register socket to ioqueue to receive asynchronous input
298 * notification.
299 */
300 pj_bzero(&ioqueue_cb, sizeof(ioqueue_cb));
301 ioqueue_cb.on_read_complete = &on_read_complete;
302
303 status = pj_ioqueue_register_sock2(sess->pool, stun_cfg->ioqueue,
304 sess->sock, sess->grp_lock, sess,
305 &ioqueue_cb, &sess->key);
306 if (status != PJ_SUCCESS)
307 goto on_error;
308
309 /*
310 * Create STUN session.
311 */
312 pj_bzero(&sess_cb, sizeof(sess_cb));
313 sess_cb.on_request_complete = &on_request_complete;
314 sess_cb.on_send_msg = &on_send_msg;
315 status = pj_stun_session_create(stun_cfg, pool->obj_name, &sess_cb,
316 PJ_FALSE, sess->grp_lock, &sess->stun_sess);
317 if (status != PJ_SUCCESS)
318 goto on_error;
319
320 pj_stun_session_set_user_data(sess->stun_sess, sess);
321
322 /*
323 * Kick-off ioqueue reading.
324 */
325 pj_ioqueue_op_key_init(&sess->read_op, sizeof(sess->read_op));
326 pj_ioqueue_op_key_init(&sess->write_op, sizeof(sess->write_op));
327 on_read_complete(sess->key, &sess->read_op, 0);
328
329 /*
330 * Start TEST_1
331 */
332 sess->timer.id = TIMER_TEST;
333 on_sess_timer(stun_cfg->timer_heap, &sess->timer);
334
335 return PJ_SUCCESS;
336
337on_error:
338 sess_destroy(sess);
339 return status;
340}
341
342
343static void sess_destroy(nat_detect_session *sess)
344{
345 if (sess->stun_sess) {
346 pj_stun_session_destroy(sess->stun_sess);
347 sess->stun_sess = NULL;
348 }
349
350 if (sess->key) {
351 pj_ioqueue_unregister(sess->key);
352 sess->key = NULL;
353 sess->sock = PJ_INVALID_SOCKET;
354 } else if (sess->sock && sess->sock != PJ_INVALID_SOCKET) {
355 pj_sock_close(sess->sock);
356 sess->sock = PJ_INVALID_SOCKET;
357 }
358
359 if (sess->grp_lock) {
360 pj_grp_lock_dec_ref(sess->grp_lock);
361 }
362}
363
364static void sess_on_destroy(void *member)
365{
366 nat_detect_session *sess = (nat_detect_session*)member;
367 if (sess->pool) {
368 pj_pool_release(sess->pool);
369 }
370}
371
372static void end_session(nat_detect_session *sess,
373 pj_status_t status,
374 pj_stun_nat_type nat_type)
375{
376 pj_stun_nat_detect_result result;
377 char errmsg[PJ_ERR_MSG_SIZE];
378 pj_time_val delay;
379
380 if (sess->timer.id != 0) {
381 pj_timer_heap_cancel(sess->timer_heap, &sess->timer);
382 sess->timer.id = 0;
383 }
384
385 pj_bzero(&result, sizeof(result));
386 errmsg[0] = '\0';
387 result.status_text = errmsg;
388
389 result.status = status;
390 pj_strerror(status, errmsg, sizeof(errmsg));
391 result.nat_type = nat_type;
392 result.nat_type_name = nat_type_names[result.nat_type];
393
394 if (sess->cb)
395 (*sess->cb)(sess->user_data, &result);
396
397 delay.sec = 0;
398 delay.msec = 0;
399
400 sess->timer.id = TIMER_DESTROY;
401 pj_timer_heap_schedule(sess->timer_heap, &sess->timer, &delay);
402}
403
404
405/*
406 * Callback upon receiving packet from network.
407 */
408static void on_read_complete(pj_ioqueue_key_t *key,
409 pj_ioqueue_op_key_t *op_key,
410 pj_ssize_t bytes_read)
411{
412 nat_detect_session *sess;
413 pj_status_t status;
414
415 sess = (nat_detect_session *) pj_ioqueue_get_user_data(key);
416 pj_assert(sess != NULL);
417
418 pj_grp_lock_acquire(sess->grp_lock);
419
420 /* Ignore packet when STUN session has been destroyed */
421 if (!sess->stun_sess)
422 goto on_return;
423
424 if (bytes_read < 0) {
425 if (-bytes_read != PJ_STATUS_FROM_OS(OSERR_EWOULDBLOCK) &&
426 -bytes_read != PJ_STATUS_FROM_OS(OSERR_EINPROGRESS) &&
427 -bytes_read != PJ_STATUS_FROM_OS(OSERR_ECONNRESET))
428 {
429 /* Permanent error */
430 end_session(sess, (pj_status_t)-bytes_read,
431 PJ_STUN_NAT_TYPE_ERR_UNKNOWN);
432 goto on_return;
433 }
434
435 } else if (bytes_read > 0) {
436 pj_stun_session_on_rx_pkt(sess->stun_sess, sess->rx_pkt, bytes_read,
437 PJ_STUN_IS_DATAGRAM|PJ_STUN_CHECK_PACKET,
438 NULL, NULL,
439 &sess->src_addr, sess->src_addr_len);
440 }
441
442
443 sess->rx_pkt_len = sizeof(sess->rx_pkt);
444 sess->src_addr_len = sizeof(sess->src_addr);
445 status = pj_ioqueue_recvfrom(key, op_key, sess->rx_pkt, &sess->rx_pkt_len,
446 PJ_IOQUEUE_ALWAYS_ASYNC,
447 &sess->src_addr, &sess->src_addr_len);
448
449 if (status != PJ_EPENDING) {
450 pj_assert(status != PJ_SUCCESS);
451 end_session(sess, status, PJ_STUN_NAT_TYPE_ERR_UNKNOWN);
452 }
453
454on_return:
455 pj_grp_lock_release(sess->grp_lock);
456}
457
458
459/*
460 * Callback to send outgoing packet from STUN session.
461 */
462static pj_status_t on_send_msg(pj_stun_session *stun_sess,
463 void *token,
464 const void *pkt,
465 pj_size_t pkt_size,
466 const pj_sockaddr_t *dst_addr,
467 unsigned addr_len)
468{
469 nat_detect_session *sess;
470 pj_ssize_t pkt_len;
471 pj_status_t status;
472
473 PJ_UNUSED_ARG(token);
474
475 sess = (nat_detect_session*) pj_stun_session_get_user_data(stun_sess);
476
477 pkt_len = pkt_size;
478 status = pj_ioqueue_sendto(sess->key, &sess->write_op, pkt, &pkt_len, 0,
479 dst_addr, addr_len);
480
481 return status;
482
483}
484
485/*
486 * Callback upon request completion.
487 */
488static void on_request_complete(pj_stun_session *stun_sess,
489 pj_status_t status,
490 void *token,
491 pj_stun_tx_data *tdata,
492 const pj_stun_msg *response,
493 const pj_sockaddr_t *src_addr,
494 unsigned src_addr_len)
495{
496 nat_detect_session *sess;
497 pj_stun_sockaddr_attr *mattr = NULL;
498 pj_stun_changed_addr_attr *ca = NULL;
499 pj_uint32_t *tsx_id;
500 int cmp;
501 unsigned test_id;
502
503 PJ_UNUSED_ARG(token);
504 PJ_UNUSED_ARG(tdata);
505 PJ_UNUSED_ARG(src_addr);
506 PJ_UNUSED_ARG(src_addr_len);
507
508 sess = (nat_detect_session*) pj_stun_session_get_user_data(stun_sess);
509
510 pj_grp_lock_acquire(sess->grp_lock);
511
512 /* Find errors in the response */
513 if (status == PJ_SUCCESS) {
514
515 /* Check error message */
516 if (PJ_STUN_IS_ERROR_RESPONSE(response->hdr.type)) {
517 pj_stun_errcode_attr *eattr;
518 int err_code;
519
520 eattr = (pj_stun_errcode_attr*)
521 pj_stun_msg_find_attr(response, PJ_STUN_ATTR_ERROR_CODE, 0);
522
523 if (eattr != NULL)
524 err_code = eattr->err_code;
525 else
526 err_code = PJ_STUN_SC_SERVER_ERROR;
527
528 status = PJ_STATUS_FROM_STUN_CODE(err_code);
529
530
531 } else {
532
533 /* Get MAPPED-ADDRESS or XOR-MAPPED-ADDRESS */
534 mattr = (pj_stun_sockaddr_attr*)
535 pj_stun_msg_find_attr(response, PJ_STUN_ATTR_XOR_MAPPED_ADDR, 0);
536 if (mattr == NULL) {
537 mattr = (pj_stun_sockaddr_attr*)
538 pj_stun_msg_find_attr(response, PJ_STUN_ATTR_MAPPED_ADDR, 0);
539 }
540
541 if (mattr == NULL) {
542 status = PJNATH_ESTUNNOMAPPEDADDR;
543 }
544
545 /* Get CHANGED-ADDRESS attribute */
546 ca = (pj_stun_changed_addr_attr*)
547 pj_stun_msg_find_attr(response, PJ_STUN_ATTR_CHANGED_ADDR, 0);
548
549 if (ca == NULL) {
550 status = PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_SERVER_ERROR);
551 }
552
553 }
554 }
555
556 /* Save the result */
557 tsx_id = (pj_uint32_t*) tdata->msg->hdr.tsx_id;
558 test_id = tsx_id[2];
559
560 if (test_id >= ST_MAX) {
561 PJ_LOG(4,(sess->pool->obj_name, "Invalid transaction ID %u in response",
562 test_id));
563 end_session(sess, PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_SERVER_ERROR),
564 PJ_STUN_NAT_TYPE_ERR_UNKNOWN);
565 goto on_return;
566 }
567
568 PJ_LOG(5,(sess->pool->obj_name, "Completed %s, status=%d",
569 test_names[test_id], status));
570
571 sess->result[test_id].complete = PJ_TRUE;
572 sess->result[test_id].status = status;
573 if (status == PJ_SUCCESS) {
574 pj_memcpy(&sess->result[test_id].ma, &mattr->sockaddr.ipv4,
575 sizeof(pj_sockaddr_in));
576 pj_memcpy(&sess->result[test_id].ca, &ca->sockaddr.ipv4,
577 sizeof(pj_sockaddr_in));
578 }
579
580 /* Send Test 1B only when Test 2 completes. Must not send Test 1B
581 * before Test 2 completes to avoid creating mapping on the NAT.
582 */
583 if (!sess->result[ST_TEST_1B].executed &&
584 sess->result[ST_TEST_2].complete &&
585 sess->result[ST_TEST_2].status != PJ_SUCCESS &&
586 sess->result[ST_TEST_1].complete &&
587 sess->result[ST_TEST_1].status == PJ_SUCCESS)
588 {
589 cmp = pj_memcmp(&sess->local_addr, &sess->result[ST_TEST_1].ma,
590 sizeof(pj_sockaddr_in));
591 if (cmp != 0)
592 send_test(sess, ST_TEST_1B, &sess->result[ST_TEST_1].ca, 0);
593 }
594
595 if (test_completed(sess)<3 || test_completed(sess)!=test_executed(sess))
596 goto on_return;
597
598 /* Handle the test result according to RFC 3489 page 22:
599
600
601 +--------+
602 | Test |
603 | 1 |
604 +--------+
605 |
606 |
607 V
608 /\ /\
609 N / \ Y / \ Y +--------+
610 UDP <-------/Resp\--------->/ IP \------------->| Test |
611 Blocked \ ? / \Same/ | 2 |
612 \ / \? / +--------+
613 \/ \/ |
614 | N |
615 | V
616 V /\
617 +--------+ Sym. N / \
618 | Test | UDP <---/Resp\
619 | 2 | Firewall \ ? /
620 +--------+ \ /
621 | \/
622 V |Y
623 /\ /\ |
624 Symmetric N / \ +--------+ N / \ V
625 NAT <--- / IP \<-----| Test |<--- /Resp\ Open
626 \Same/ | 1B | \ ? / Internet
627 \? / +--------+ \ /
628 \/ \/
629 | |Y
630 | |
631 | V
632 | Full
633 | Cone
634 V /\
635 +--------+ / \ Y
636 | Test |------>/Resp\---->Restricted
637 | 3 | \ ? /
638 +--------+ \ /
639 \/
640 |N
641 | Port
642 +------>Restricted
643
644 Figure 2: Flow for type discovery process
645 */
646
647 switch (sess->result[ST_TEST_1].status) {
648 case PJNATH_ESTUNTIMEDOUT:
649 /*
650 * Test 1 has timed-out. Conclude with NAT_TYPE_BLOCKED.
651 */
652 end_session(sess, PJ_SUCCESS, PJ_STUN_NAT_TYPE_BLOCKED);
653 break;
654 case PJ_SUCCESS:
655 /*
656 * Test 1 is successful. Further tests are needed to detect
657 * NAT type. Compare the MAPPED-ADDRESS with the local address.
658 */
659 cmp = pj_memcmp(&sess->local_addr, &sess->result[ST_TEST_1].ma,
660 sizeof(pj_sockaddr_in));
661 if (cmp==0) {
662 /*
663 * MAPPED-ADDRESS and local address is equal. Need one more
664 * test to determine NAT type.
665 */
666 switch (sess->result[ST_TEST_2].status) {
667 case PJ_SUCCESS:
668 /*
669 * Test 2 is also successful. We're in the open.
670 */
671 end_session(sess, PJ_SUCCESS, PJ_STUN_NAT_TYPE_OPEN);
672 break;
673 case PJNATH_ESTUNTIMEDOUT:
674 /*
675 * Test 2 has timed out. We're behind somekind of UDP
676 * firewall.
677 */
678 end_session(sess, PJ_SUCCESS, PJ_STUN_NAT_TYPE_SYMMETRIC_UDP);
679 break;
680 default:
681 /*
682 * We've got other error with Test 2.
683 */
684 end_session(sess, sess->result[ST_TEST_2].status,
685 PJ_STUN_NAT_TYPE_ERR_UNKNOWN);
686 break;
687 }
688 } else {
689 /*
690 * MAPPED-ADDRESS is different than local address.
691 * We're behind NAT.
692 */
693 switch (sess->result[ST_TEST_2].status) {
694 case PJ_SUCCESS:
695 /*
696 * Test 2 is successful. We're behind a full-cone NAT.
697 */
698 end_session(sess, PJ_SUCCESS, PJ_STUN_NAT_TYPE_FULL_CONE);
699 break;
700 case PJNATH_ESTUNTIMEDOUT:
701 /*
702 * Test 2 has timed-out Check result of test 1B..
703 */
704 switch (sess->result[ST_TEST_1B].status) {
705 case PJ_SUCCESS:
706 /*
707 * Compare the MAPPED-ADDRESS of test 1B with the
708 * MAPPED-ADDRESS returned in test 1..
709 */
710 cmp = pj_memcmp(&sess->result[ST_TEST_1].ma,
711 &sess->result[ST_TEST_1B].ma,
712 sizeof(pj_sockaddr_in));
713 if (cmp != 0) {
714 /*
715 * MAPPED-ADDRESS is different, we're behind a
716 * symmetric NAT.
717 */
718 end_session(sess, PJ_SUCCESS,
719 PJ_STUN_NAT_TYPE_SYMMETRIC);
720 } else {
721 /*
722 * MAPPED-ADDRESS is equal. We're behind a restricted
723 * or port-restricted NAT, depending on the result of
724 * test 3.
725 */
726 switch (sess->result[ST_TEST_3].status) {
727 case PJ_SUCCESS:
728 /*
729 * Test 3 is successful, we're behind a restricted
730 * NAT.
731 */
732 end_session(sess, PJ_SUCCESS,
733 PJ_STUN_NAT_TYPE_RESTRICTED);
734 break;
735 case PJNATH_ESTUNTIMEDOUT:
736 /*
737 * Test 3 failed, we're behind a port restricted
738 * NAT.
739 */
740 end_session(sess, PJ_SUCCESS,
741 PJ_STUN_NAT_TYPE_PORT_RESTRICTED);
742 break;
743 default:
744 /*
745 * Got other error with test 3.
746 */
747 end_session(sess, sess->result[ST_TEST_3].status,
748 PJ_STUN_NAT_TYPE_ERR_UNKNOWN);
749 break;
750 }
751 }
752 break;
753 case PJNATH_ESTUNTIMEDOUT:
754 /*
755 * Strangely test 1B has failed. Maybe connectivity was
756 * lost? Or perhaps port 3489 (the usual port number in
757 * CHANGED-ADDRESS) is blocked?
758 */
759 switch (sess->result[ST_TEST_3].status) {
760 case PJ_SUCCESS:
761 /* Although test 1B failed, test 3 was successful.
762 * It could be that port 3489 is blocked, while the
763 * NAT itself looks to be a Restricted one.
764 */
765 end_session(sess, PJ_SUCCESS,
766 PJ_STUN_NAT_TYPE_RESTRICTED);
767 break;
768 default:
769 /* Can't distinguish between Symmetric and Port
770 * Restricted, so set the type to Unknown
771 */
772 end_session(sess, PJ_SUCCESS,
773 PJ_STUN_NAT_TYPE_ERR_UNKNOWN);
774 break;
775 }
776 break;
777 default:
778 /*
779 * Got other error with test 1B.
780 */
781 end_session(sess, sess->result[ST_TEST_1B].status,
782 PJ_STUN_NAT_TYPE_ERR_UNKNOWN);
783 break;
784 }
785 break;
786 default:
787 /*
788 * We've got other error with Test 2.
789 */
790 end_session(sess, sess->result[ST_TEST_2].status,
791 PJ_STUN_NAT_TYPE_ERR_UNKNOWN);
792 break;
793 }
794 }
795 break;
796 default:
797 /*
798 * We've got other error with Test 1.
799 */
800 end_session(sess, sess->result[ST_TEST_1].status,
801 PJ_STUN_NAT_TYPE_ERR_UNKNOWN);
802 break;
803 }
804
805on_return:
806 pj_grp_lock_release(sess->grp_lock);
807}
808
809
810/* Perform test */
811static pj_status_t send_test(nat_detect_session *sess,
812 enum test_type test_id,
813 const pj_sockaddr_in *alt_addr,
814 pj_uint32_t change_flag)
815{
816 pj_uint32_t magic, tsx_id[3];
817 pj_status_t status;
818
819 sess->result[test_id].executed = PJ_TRUE;
820
821 /* Randomize tsx id */
822 do {
823 magic = pj_rand();
824 } while (magic == PJ_STUN_MAGIC);
825
826 tsx_id[0] = pj_rand();
827 tsx_id[1] = pj_rand();
828 tsx_id[2] = test_id;
829
830 /* Create BIND request */
831 status = pj_stun_session_create_req(sess->stun_sess,
832 PJ_STUN_BINDING_REQUEST, magic,
833 (pj_uint8_t*)tsx_id,
834 &sess->result[test_id].tdata);
835 if (status != PJ_SUCCESS)
836 goto on_error;
837
838 /* Add CHANGE-REQUEST attribute */
839 status = pj_stun_msg_add_uint_attr(sess->pool,
840 sess->result[test_id].tdata->msg,
841 PJ_STUN_ATTR_CHANGE_REQUEST,
842 change_flag);
843 if (status != PJ_SUCCESS)
844 goto on_error;
845
846 /* Configure alternate address */
847 if (alt_addr)
848 sess->cur_server = (pj_sockaddr_in*) alt_addr;
849 else
850 sess->cur_server = &sess->server;
851
852 PJ_LOG(5,(sess->pool->obj_name,
853 "Performing %s to %s:%d",
854 test_names[test_id],
855 pj_inet_ntoa(sess->cur_server->sin_addr),
856 pj_ntohs(sess->cur_server->sin_port)));
857
858 /* Send the request */
859 status = pj_stun_session_send_msg(sess->stun_sess, NULL, PJ_TRUE,
860 PJ_TRUE, sess->cur_server,
861 sizeof(pj_sockaddr_in),
862 sess->result[test_id].tdata);
863 if (status != PJ_SUCCESS)
864 goto on_error;
865
866 return PJ_SUCCESS;
867
868on_error:
869 sess->result[test_id].complete = PJ_TRUE;
870 sess->result[test_id].status = status;
871
872 return status;
873}
874
875
876/* Timer callback */
877static void on_sess_timer(pj_timer_heap_t *th,
878 pj_timer_entry *te)
879{
880 nat_detect_session *sess;
881
882 sess = (nat_detect_session*) te->user_data;
883
884 if (te->id == TIMER_DESTROY) {
885 pj_grp_lock_acquire(sess->grp_lock);
886 pj_ioqueue_unregister(sess->key);
887 sess->key = NULL;
888 sess->sock = PJ_INVALID_SOCKET;
889 te->id = 0;
890 pj_grp_lock_release(sess->grp_lock);
891
892 sess_destroy(sess);
893
894 } else if (te->id == TIMER_TEST) {
895
896 pj_bool_t next_timer;
897
898 pj_grp_lock_acquire(sess->grp_lock);
899
900 next_timer = PJ_FALSE;
901
902 if (sess->timer_executed == 0) {
903 send_test(sess, ST_TEST_1, NULL, 0);
904 next_timer = PJ_TRUE;
905 } else if (sess->timer_executed == 1) {
906 send_test(sess, ST_TEST_2, NULL, CHANGE_IP_PORT_FLAG);
907 next_timer = PJ_TRUE;
908 } else if (sess->timer_executed == 2) {
909 send_test(sess, ST_TEST_3, NULL, CHANGE_PORT_FLAG);
910 } else {
911 pj_assert(!"Shouldn't have timer at this state");
912 }
913
914 ++sess->timer_executed;
915
916 if (next_timer) {
917 pj_time_val delay = {0, TEST_INTERVAL};
918 pj_timer_heap_schedule(th, te, &delay);
919 } else {
920 te->id = 0;
921 }
922
923 pj_grp_lock_release(sess->grp_lock);
924
925 } else {
926 pj_assert(!"Invalid timer ID");
927 }
928}
929