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