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