~ubuntu-branches/ubuntu/wily/sflphone/wily

« back to all changes in this revision

Viewing changes to daemon/libs/pjproject-2.1.0/pjnath/src/pjnath/nat_detect.c

  • Committer: Package Import Robot
  • Author(s): Jonathan Riddell
  • Date: 2015-01-07 14:51:16 UTC
  • mfrom: (4.3.5 sid)
  • Revision ID: package-import@ubuntu.com-20150107145116-yxnafinf4lrdvrmx
Tags: 1.4.1-0.1ubuntu1
* Merge with Debian, remaining changes:
 - Drop soprano, nepomuk build-dep
* Drop ubuntu patches, now upstream

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* $Id: nat_detect.c 4360 2013-02-21 11:26:35Z bennylp $ */
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_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
 
 
112
 
static void on_read_complete(pj_ioqueue_key_t *key, 
113
 
                             pj_ioqueue_op_key_t *op_key, 
114
 
                             pj_ssize_t bytes_read);
115
 
static 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);
122
 
static 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
 
 
129
 
static 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);
133
 
static void on_sess_timer(pj_timer_heap_t *th,
134
 
                             pj_timer_entry *te);
135
 
static void sess_destroy(nat_detect_session *sess);
136
 
 
137
 
 
138
 
/*
139
 
 * Get the NAT name from the specified NAT type.
140
 
 */
141
 
PJ_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
 
 
149
 
static 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
 
 
159
 
static 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
 
 
169
 
static 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
 
 
207
 
PJ_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
 
 
331
 
on_error:
332
 
    sess_destroy(sess);
333
 
    return status;
334
 
}
335
 
 
336
 
 
337
 
static 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
 
 
359
 
static 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
 
 */
395
 
static 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, -bytes_read, PJ_STUN_NAT_TYPE_ERR_UNKNOWN);
414
 
            goto on_return;
415
 
        }
416
 
 
417
 
    } else if (bytes_read > 0) {
418
 
        pj_stun_session_on_rx_pkt(sess->stun_sess, sess->rx_pkt, bytes_read,
419
 
                                  PJ_STUN_IS_DATAGRAM|PJ_STUN_CHECK_PACKET, 
420
 
                                  NULL, NULL, 
421
 
                                  &sess->src_addr, sess->src_addr_len);
422
 
    }
423
 
 
424
 
 
425
 
    sess->rx_pkt_len = sizeof(sess->rx_pkt);
426
 
    sess->src_addr_len = sizeof(sess->src_addr);
427
 
    status = pj_ioqueue_recvfrom(key, op_key, sess->rx_pkt, &sess->rx_pkt_len,
428
 
                                 PJ_IOQUEUE_ALWAYS_ASYNC, 
429
 
                                 &sess->src_addr, &sess->src_addr_len);
430
 
 
431
 
    if (status != PJ_EPENDING) {
432
 
        pj_assert(status != PJ_SUCCESS);
433
 
        end_session(sess, status, PJ_STUN_NAT_TYPE_ERR_UNKNOWN);
434
 
    }
435
 
 
436
 
on_return:
437
 
    pj_mutex_unlock(sess->mutex);
438
 
}
439
 
 
440
 
 
441
 
/*
442
 
 * Callback to send outgoing packet from STUN session.
443
 
 */
444
 
static pj_status_t on_send_msg(pj_stun_session *stun_sess,
445
 
                               void *token,
446
 
                               const void *pkt,
447
 
                               pj_size_t pkt_size,
448
 
                               const pj_sockaddr_t *dst_addr,
449
 
                               unsigned addr_len)
450
 
{
451
 
    nat_detect_session *sess;
452
 
    pj_ssize_t pkt_len;
453
 
    pj_status_t status;
454
 
 
455
 
    PJ_UNUSED_ARG(token);
456
 
 
457
 
    sess = (nat_detect_session*) pj_stun_session_get_user_data(stun_sess);
458
 
 
459
 
    pkt_len = pkt_size;
460
 
    status = pj_ioqueue_sendto(sess->key, &sess->write_op, pkt, &pkt_len, 0,
461
 
                               dst_addr, addr_len);
462
 
 
463
 
    return status;
464
 
 
465
 
}
466
 
 
467
 
/*
468
 
 * Callback upon request completion.
469
 
 */
470
 
static void on_request_complete(pj_stun_session *stun_sess,
471
 
                                pj_status_t status,
472
 
                                void *token,
473
 
                                pj_stun_tx_data *tdata,
474
 
                                const pj_stun_msg *response,
475
 
                                const pj_sockaddr_t *src_addr,
476
 
                                unsigned src_addr_len)
477
 
{
478
 
    nat_detect_session *sess;
479
 
    pj_stun_sockaddr_attr *mattr = NULL;
480
 
    pj_stun_changed_addr_attr *ca = NULL;
481
 
    pj_uint32_t *tsx_id;
482
 
    int cmp;
483
 
    unsigned test_id;
484
 
 
485
 
    PJ_UNUSED_ARG(token);
486
 
    PJ_UNUSED_ARG(tdata);
487
 
    PJ_UNUSED_ARG(src_addr);
488
 
    PJ_UNUSED_ARG(src_addr_len);
489
 
 
490
 
    sess = (nat_detect_session*) pj_stun_session_get_user_data(stun_sess);
491
 
 
492
 
    pj_mutex_lock(sess->mutex);
493
 
 
494
 
    /* Find errors in the response */
495
 
    if (status == PJ_SUCCESS) {
496
 
 
497
 
        /* Check error message */
498
 
        if (PJ_STUN_IS_ERROR_RESPONSE(response->hdr.type)) {
499
 
            pj_stun_errcode_attr *eattr;
500
 
            int err_code;
501
 
 
502
 
            eattr = (pj_stun_errcode_attr*)
503
 
                    pj_stun_msg_find_attr(response, PJ_STUN_ATTR_ERROR_CODE, 0);
504
 
 
505
 
            if (eattr != NULL)
506
 
                err_code = eattr->err_code;
507
 
            else
508
 
                err_code = PJ_STUN_SC_SERVER_ERROR;
509
 
 
510
 
            status = PJ_STATUS_FROM_STUN_CODE(err_code);
511
 
 
512
 
 
513
 
        } else {
514
 
 
515
 
            /* Get MAPPED-ADDRESS or XOR-MAPPED-ADDRESS */
516
 
            mattr = (pj_stun_sockaddr_attr*)
517
 
                    pj_stun_msg_find_attr(response, PJ_STUN_ATTR_XOR_MAPPED_ADDR, 0);
518
 
            if (mattr == NULL) {
519
 
                mattr = (pj_stun_sockaddr_attr*)
520
 
                        pj_stun_msg_find_attr(response, PJ_STUN_ATTR_MAPPED_ADDR, 0);
521
 
            }
522
 
 
523
 
            if (mattr == NULL) {
524
 
                status = PJNATH_ESTUNNOMAPPEDADDR;
525
 
            }
526
 
 
527
 
            /* Get CHANGED-ADDRESS attribute */
528
 
            ca = (pj_stun_changed_addr_attr*)
529
 
                 pj_stun_msg_find_attr(response, PJ_STUN_ATTR_CHANGED_ADDR, 0);
530
 
 
531
 
            if (ca == NULL) {
532
 
                status = PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_SERVER_ERROR);
533
 
            }
534
 
 
535
 
        }
536
 
    }
537
 
 
538
 
    /* Save the result */
539
 
    tsx_id = (pj_uint32_t*) tdata->msg->hdr.tsx_id;
540
 
    test_id = tsx_id[2];
541
 
 
542
 
    if (test_id >= ST_MAX) {
543
 
        PJ_LOG(4,(sess->pool->obj_name, "Invalid transaction ID %u in response",
544
 
                  test_id));
545
 
        end_session(sess, PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_SERVER_ERROR),
546
 
                    PJ_STUN_NAT_TYPE_ERR_UNKNOWN);
547
 
        goto on_return;
548
 
    }
549
 
 
550
 
    PJ_LOG(5,(sess->pool->obj_name, "Completed %s, status=%d",
551
 
              test_names[test_id], status));
552
 
 
553
 
    sess->result[test_id].complete = PJ_TRUE;
554
 
    sess->result[test_id].status = status;
555
 
    if (status == PJ_SUCCESS) {
556
 
        pj_memcpy(&sess->result[test_id].ma, &mattr->sockaddr.ipv4,
557
 
                  sizeof(pj_sockaddr_in));
558
 
        pj_memcpy(&sess->result[test_id].ca, &ca->sockaddr.ipv4,
559
 
                  sizeof(pj_sockaddr_in));
560
 
    }
561
 
 
562
 
    /* Send Test 1B only when Test 2 completes. Must not send Test 1B
563
 
     * before Test 2 completes to avoid creating mapping on the NAT.
564
 
     */
565
 
    if (!sess->result[ST_TEST_1B].executed && 
566
 
        sess->result[ST_TEST_2].complete &&
567
 
        sess->result[ST_TEST_2].status != PJ_SUCCESS &&
568
 
        sess->result[ST_TEST_1].complete &&
569
 
        sess->result[ST_TEST_1].status == PJ_SUCCESS) 
570
 
    {
571
 
        cmp = pj_memcmp(&sess->local_addr, &sess->result[ST_TEST_1].ma,
572
 
                        sizeof(pj_sockaddr_in));
573
 
        if (cmp != 0)
574
 
            send_test(sess, ST_TEST_1B, &sess->result[ST_TEST_1].ca, 0);
575
 
    }
576
 
 
577
 
    if (test_completed(sess)<3 || test_completed(sess)!=test_executed(sess))
578
 
        goto on_return;
579
 
 
580
 
    /* Handle the test result according to RFC 3489 page 22:
581
 
 
582
 
 
583
 
                        +--------+
584
 
                        |  Test  |
585
 
                        |   1    |
586
 
                        +--------+
587
 
                             |
588
 
                             |
589
 
                             V
590
 
                            /\              /\
591
 
                         N /  \ Y          /  \ Y             +--------+
592
 
          UDP     <-------/Resp\--------->/ IP \------------->|  Test  |
593
 
          Blocked         \ ?  /          \Same/              |   2    |
594
 
                           \  /            \? /               +--------+
595
 
                            \/              \/                    |
596
 
                                             | N                  |
597
 
                                             |                    V
598
 
                                             V                    /\
599
 
                                         +--------+  Sym.      N /  \
600
 
                                         |  Test  |  UDP    <---/Resp\
601
 
                                         |   2    |  Firewall   \ ?  /
602
 
                                         +--------+              \  /
603
 
                                             |                    \/
604
 
                                             V                     |Y
605
 
                  /\                         /\                    |
606
 
   Symmetric  N  /  \       +--------+   N  /  \                   V
607
 
      NAT  <--- / IP \<-----|  Test  |<--- /Resp\               Open
608
 
                \Same/      |   1B   |     \ ?  /               Internet
609
 
                 \? /       +--------+      \  /
610
 
                  \/                         \/
611
 
                  |                           |Y
612
 
                  |                           |
613
 
                  |                           V
614
 
                  |                           Full
615
 
                  |                           Cone
616
 
                  V              /\
617
 
              +--------+        /  \ Y
618
 
              |  Test  |------>/Resp\---->Restricted
619
 
              |   3    |       \ ?  /
620
 
              +--------+        \  /
621
 
                                 \/
622
 
                                  |N
623
 
                                  |       Port
624
 
                                  +------>Restricted
625
 
 
626
 
                 Figure 2: Flow for type discovery process
627
 
     */
628
 
 
629
 
    switch (sess->result[ST_TEST_1].status) {
630
 
    case PJNATH_ESTUNTIMEDOUT:
631
 
        /*
632
 
         * Test 1 has timed-out. Conclude with NAT_TYPE_BLOCKED. 
633
 
         */
634
 
        end_session(sess, PJ_SUCCESS, PJ_STUN_NAT_TYPE_BLOCKED);
635
 
        break;
636
 
    case PJ_SUCCESS:
637
 
        /*
638
 
         * Test 1 is successful. Further tests are needed to detect
639
 
         * NAT type. Compare the MAPPED-ADDRESS with the local address.
640
 
         */
641
 
        cmp = pj_memcmp(&sess->local_addr, &sess->result[ST_TEST_1].ma,
642
 
                        sizeof(pj_sockaddr_in));
643
 
        if (cmp==0) {
644
 
            /*
645
 
             * MAPPED-ADDRESS and local address is equal. Need one more
646
 
             * test to determine NAT type.
647
 
             */
648
 
            switch (sess->result[ST_TEST_2].status) {
649
 
            case PJ_SUCCESS:
650
 
                /*
651
 
                 * Test 2 is also successful. We're in the open.
652
 
                 */
653
 
                end_session(sess, PJ_SUCCESS, PJ_STUN_NAT_TYPE_OPEN);
654
 
                break;
655
 
            case PJNATH_ESTUNTIMEDOUT:
656
 
                /*
657
 
                 * Test 2 has timed out. We're behind somekind of UDP
658
 
                 * firewall.
659
 
                 */
660
 
                end_session(sess, PJ_SUCCESS, PJ_STUN_NAT_TYPE_SYMMETRIC_UDP);
661
 
                break;
662
 
            default:
663
 
                /*
664
 
                 * We've got other error with Test 2.
665
 
                 */
666
 
                end_session(sess, sess->result[ST_TEST_2].status, 
667
 
                            PJ_STUN_NAT_TYPE_ERR_UNKNOWN);
668
 
                break;
669
 
            }
670
 
        } else {
671
 
            /*
672
 
             * MAPPED-ADDRESS is different than local address.
673
 
             * We're behind NAT.
674
 
             */
675
 
            switch (sess->result[ST_TEST_2].status) {
676
 
            case PJ_SUCCESS:
677
 
                /*
678
 
                 * Test 2 is successful. We're behind a full-cone NAT.
679
 
                 */
680
 
                end_session(sess, PJ_SUCCESS, PJ_STUN_NAT_TYPE_FULL_CONE);
681
 
                break;
682
 
            case PJNATH_ESTUNTIMEDOUT:
683
 
                /*
684
 
                 * Test 2 has timed-out Check result of test 1B..
685
 
                 */
686
 
                switch (sess->result[ST_TEST_1B].status) {
687
 
                case PJ_SUCCESS:
688
 
                    /*
689
 
                     * Compare the MAPPED-ADDRESS of test 1B with the
690
 
                     * MAPPED-ADDRESS returned in test 1..
691
 
                     */
692
 
                    cmp = pj_memcmp(&sess->result[ST_TEST_1].ma,
693
 
                                    &sess->result[ST_TEST_1B].ma,
694
 
                                    sizeof(pj_sockaddr_in));
695
 
                    if (cmp != 0) {
696
 
                        /*
697
 
                         * MAPPED-ADDRESS is different, we're behind a
698
 
                         * symmetric NAT.
699
 
                         */
700
 
                        end_session(sess, PJ_SUCCESS,
701
 
                                    PJ_STUN_NAT_TYPE_SYMMETRIC);
702
 
                    } else {
703
 
                        /*
704
 
                         * MAPPED-ADDRESS is equal. We're behind a restricted
705
 
                         * or port-restricted NAT, depending on the result of
706
 
                         * test 3.
707
 
                         */
708
 
                        switch (sess->result[ST_TEST_3].status) {
709
 
                        case PJ_SUCCESS:
710
 
                            /*
711
 
                             * Test 3 is successful, we're behind a restricted
712
 
                             * NAT.
713
 
                             */
714
 
                            end_session(sess, PJ_SUCCESS,
715
 
                                        PJ_STUN_NAT_TYPE_RESTRICTED);
716
 
                            break;
717
 
                        case PJNATH_ESTUNTIMEDOUT:
718
 
                            /*
719
 
                             * Test 3 failed, we're behind a port restricted
720
 
                             * NAT.
721
 
                             */
722
 
                            end_session(sess, PJ_SUCCESS,
723
 
                                        PJ_STUN_NAT_TYPE_PORT_RESTRICTED);
724
 
                            break;
725
 
                        default:
726
 
                            /*
727
 
                             * Got other error with test 3.
728
 
                             */
729
 
                            end_session(sess, sess->result[ST_TEST_3].status,
730
 
                                        PJ_STUN_NAT_TYPE_ERR_UNKNOWN);
731
 
                            break;
732
 
                        }
733
 
                    }
734
 
                    break;
735
 
                case PJNATH_ESTUNTIMEDOUT:
736
 
                    /*
737
 
                     * Strangely test 1B has failed. Maybe connectivity was
738
 
                     * lost? Or perhaps port 3489 (the usual port number in
739
 
                     * CHANGED-ADDRESS) is blocked?
740
 
                     */
741
 
                    switch (sess->result[ST_TEST_3].status) {
742
 
                    case PJ_SUCCESS:
743
 
                        /* Although test 1B failed, test 3 was successful.
744
 
                         * It could be that port 3489 is blocked, while the
745
 
                         * NAT itself looks to be a Restricted one.
746
 
                         */
747
 
                        end_session(sess, PJ_SUCCESS, 
748
 
                                    PJ_STUN_NAT_TYPE_RESTRICTED);
749
 
                        break;
750
 
                    default:
751
 
                        /* Can't distinguish between Symmetric and Port
752
 
                         * Restricted, so set the type to Unknown
753
 
                         */
754
 
                        end_session(sess, PJ_SUCCESS, 
755
 
                                    PJ_STUN_NAT_TYPE_ERR_UNKNOWN);
756
 
                        break;
757
 
                    }
758
 
                    break;
759
 
                default:
760
 
                    /*
761
 
                     * Got other error with test 1B.
762
 
                     */
763
 
                    end_session(sess, sess->result[ST_TEST_1B].status,
764
 
                                PJ_STUN_NAT_TYPE_ERR_UNKNOWN);
765
 
                    break;
766
 
                }
767
 
                break;
768
 
            default:
769
 
                /*
770
 
                 * We've got other error with Test 2.
771
 
                 */
772
 
                end_session(sess, sess->result[ST_TEST_2].status, 
773
 
                            PJ_STUN_NAT_TYPE_ERR_UNKNOWN);
774
 
                break;
775
 
            }
776
 
        }
777
 
        break;
778
 
    default:
779
 
        /*
780
 
         * We've got other error with Test 1.
781
 
         */
782
 
        end_session(sess, sess->result[ST_TEST_1].status, 
783
 
                    PJ_STUN_NAT_TYPE_ERR_UNKNOWN);
784
 
        break;
785
 
    }
786
 
 
787
 
on_return:
788
 
    pj_mutex_unlock(sess->mutex);
789
 
}
790
 
 
791
 
 
792
 
/* Perform test */
793
 
static pj_status_t send_test(nat_detect_session *sess,
794
 
                             enum test_type test_id,
795
 
                             const pj_sockaddr_in *alt_addr,
796
 
                             pj_uint32_t change_flag)
797
 
{
798
 
    pj_uint32_t magic, tsx_id[3];
799
 
    pj_status_t status;
800
 
 
801
 
    sess->result[test_id].executed = PJ_TRUE;
802
 
 
803
 
    /* Randomize tsx id */
804
 
    do {
805
 
        magic = pj_rand();
806
 
    } while (magic == PJ_STUN_MAGIC);
807
 
 
808
 
    tsx_id[0] = pj_rand();
809
 
    tsx_id[1] = pj_rand();
810
 
    tsx_id[2] = test_id;
811
 
 
812
 
    /* Create BIND request */
813
 
    status = pj_stun_session_create_req(sess->stun_sess, 
814
 
                                        PJ_STUN_BINDING_REQUEST, magic,
815
 
                                        (pj_uint8_t*)tsx_id, 
816
 
                                        &sess->result[test_id].tdata);
817
 
    if (status != PJ_SUCCESS)
818
 
        goto on_error;
819
 
 
820
 
    /* Add CHANGE-REQUEST attribute */
821
 
    status = pj_stun_msg_add_uint_attr(sess->pool, 
822
 
                                       sess->result[test_id].tdata->msg,
823
 
                                       PJ_STUN_ATTR_CHANGE_REQUEST,
824
 
                                       change_flag);
825
 
    if (status != PJ_SUCCESS)
826
 
        goto on_error;
827
 
 
828
 
    /* Configure alternate address */
829
 
    if (alt_addr)
830
 
        sess->cur_server = (pj_sockaddr_in*) alt_addr;
831
 
    else
832
 
        sess->cur_server = &sess->server;
833
 
 
834
 
    PJ_LOG(5,(sess->pool->obj_name, 
835
 
              "Performing %s to %s:%d", 
836
 
              test_names[test_id],
837
 
              pj_inet_ntoa(sess->cur_server->sin_addr),
838
 
              pj_ntohs(sess->cur_server->sin_port)));
839
 
 
840
 
    /* Send the request */
841
 
    status = pj_stun_session_send_msg(sess->stun_sess, NULL, PJ_TRUE,
842
 
                                      PJ_TRUE, sess->cur_server, 
843
 
                                      sizeof(pj_sockaddr_in),
844
 
                                      sess->result[test_id].tdata);
845
 
    if (status != PJ_SUCCESS)
846
 
        goto on_error;
847
 
 
848
 
    return PJ_SUCCESS;
849
 
 
850
 
on_error:
851
 
    sess->result[test_id].complete = PJ_TRUE;
852
 
    sess->result[test_id].status = status;
853
 
 
854
 
    return status;
855
 
}
856
 
 
857
 
 
858
 
/* Timer callback */
859
 
static void on_sess_timer(pj_timer_heap_t *th,
860
 
                             pj_timer_entry *te)
861
 
{
862
 
    nat_detect_session *sess;
863
 
 
864
 
    sess = (nat_detect_session*) te->user_data;
865
 
 
866
 
    if (te->id == TIMER_DESTROY) {
867
 
        pj_mutex_lock(sess->mutex);
868
 
        pj_ioqueue_unregister(sess->key);
869
 
        sess->key = NULL;
870
 
        sess->sock = PJ_INVALID_SOCKET;
871
 
        te->id = 0;
872
 
        pj_mutex_unlock(sess->mutex);
873
 
 
874
 
        sess_destroy(sess);
875
 
 
876
 
    } else if (te->id == TIMER_TEST) {
877
 
 
878
 
        pj_bool_t next_timer;
879
 
 
880
 
        pj_mutex_lock(sess->mutex);
881
 
 
882
 
        next_timer = PJ_FALSE;
883
 
 
884
 
        if (sess->timer_executed == 0) {
885
 
            send_test(sess, ST_TEST_1, NULL, 0);
886
 
            next_timer = PJ_TRUE;
887
 
        } else if (sess->timer_executed == 1) {
888
 
            send_test(sess, ST_TEST_2, NULL, CHANGE_IP_PORT_FLAG);
889
 
            next_timer = PJ_TRUE;
890
 
        } else if (sess->timer_executed == 2) {
891
 
            send_test(sess, ST_TEST_3, NULL, CHANGE_PORT_FLAG);
892
 
        } else {
893
 
            pj_assert(!"Shouldn't have timer at this state");
894
 
        }
895
 
 
896
 
        ++sess->timer_executed;
897
 
 
898
 
        if (next_timer) {
899
 
            pj_time_val delay = {0, TEST_INTERVAL};
900
 
            pj_timer_heap_schedule(th, te, &delay);
901
 
        } else {
902
 
            te->id = 0;
903
 
        }
904
 
 
905
 
        pj_mutex_unlock(sess->mutex);
906
 
 
907
 
    } else {
908
 
        pj_assert(!"Invalid timer ID");
909
 
    }
910
 
}
911