~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-test/ice_test.c

  • Committer: Package Import Robot
  • Author(s): Mark Purcell
  • Date: 2014-01-28 18:23:36 UTC
  • mfrom: (1.1.11)
  • mto: This revision was merged to the branch mainline in revision 24.
  • Revision ID: package-import@ubuntu.com-20140128182336-3xenud1kbnwmf3mz
* New upstream release 
  - Fixes "New Upstream Release" (Closes: #735846)
  - Fixes "Ringtone does not stop" (Closes: #727164)
  - Fixes "[sflphone-kde] crash on startup" (Closes: #718178)
  - Fixes "sflphone GUI crashes when call is hung up" (Closes: #736583)
* Build-Depends: ensure GnuTLS 2.6
  - libucommon-dev (>= 6.0.7-1.1), libccrtp-dev (>= 2.0.6-3)
  - Fixes "FTBFS Build-Depends libgnutls{26,28}-dev" (Closes: #722040)
* Fix "boost 1.49 is going away" unversioned Build-Depends: (Closes: #736746)
* Add Build-Depends: libsndfile-dev, nepomuk-core-dev

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* $Id: ice_test.c 4412 2013-03-05 03:12:32Z riza $ */
 
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 "test.h"
 
21
#include "server.h"
 
22
 
 
23
enum
 
24
{
 
25
    NO  = 0,
 
26
    YES = 1,
 
27
    SRV = 3,
 
28
};
 
29
 
 
30
#define NODELAY         0xFFFFFFFF
 
31
#define SRV_DOMAIN      "pjsip.lab.domain"
 
32
#define MAX_THREADS     16
 
33
 
 
34
#define THIS_FILE       "ice_test.c"
 
35
#define INDENT          "    "
 
36
 
 
37
/* Client flags */
 
38
enum
 
39
{
 
40
    WRONG_TURN  = 1,
 
41
    DEL_ON_ERR  = 2,
 
42
};
 
43
 
 
44
 
 
45
/* Test results */
 
46
struct test_result
 
47
{
 
48
    pj_status_t init_status;    /* init successful?             */
 
49
    pj_status_t nego_status;    /* negotiation successful?      */
 
50
    unsigned    rx_cnt[4];      /* Number of data received      */
 
51
};
 
52
 
 
53
/*  Role    comp#   host?   stun?   turn?   flag?  ans_del snd_del des_del */
 
54
/* Test session configuration */
 
55
struct test_cfg
 
56
{
 
57
    pj_ice_sess_role role;      /* Role.                        */
 
58
    unsigned    comp_cnt;       /* Component count              */
 
59
    unsigned    enable_host;    /* Enable host candidates       */
 
60
    unsigned    enable_stun;    /* Enable srflx candidates      */
 
61
    unsigned    enable_turn;    /* Enable turn candidates       */
 
62
    unsigned    client_flag;    /* Client flags                 */
 
63
 
 
64
    unsigned    answer_delay;   /* Delay before sending SDP     */
 
65
    unsigned    send_delay;     /* unused */
 
66
    unsigned    destroy_delay;  /* unused */
 
67
 
 
68
    struct test_result expected;/* Expected result              */
 
69
 
 
70
    pj_bool_t   nom_regular;    /* Use regular nomination?      */
 
71
};
 
72
 
 
73
/* ICE endpoint state */
 
74
struct ice_ept
 
75
{
 
76
    struct test_cfg      cfg;   /* Configuratino.               */
 
77
    pj_ice_strans       *ice;   /* ICE stream transport         */
 
78
    struct test_result   result;/* Test result.                 */
 
79
 
 
80
    pj_str_t             ufrag; /* username fragment.           */
 
81
    pj_str_t             pass;  /* password                     */
 
82
};
 
83
 
 
84
/* Session param */
 
85
struct sess_param
 
86
{
 
87
    unsigned             worker_cnt;
 
88
    unsigned             worker_timeout;
 
89
    pj_bool_t            worker_quit;
 
90
 
 
91
    pj_bool_t            destroy_after_create;
 
92
    pj_bool_t            destroy_after_one_done;
 
93
};
 
94
 
 
95
/* The test session */
 
96
struct test_sess
 
97
{
 
98
    pj_pool_t           *pool;
 
99
    pj_stun_config      *stun_cfg;
 
100
    pj_dns_resolver     *resolver;
 
101
 
 
102
    struct sess_param   *param;
 
103
 
 
104
    test_server         *server;
 
105
 
 
106
    pj_thread_t         *worker_threads[MAX_THREADS];
 
107
 
 
108
    unsigned             server_flag;
 
109
    struct ice_ept       caller;
 
110
    struct ice_ept       callee;
 
111
};
 
112
 
 
113
 
 
114
static void ice_on_rx_data(pj_ice_strans *ice_st,
 
115
                           unsigned comp_id, 
 
116
                           void *pkt, pj_size_t size,
 
117
                           const pj_sockaddr_t *src_addr,
 
118
                           unsigned src_addr_len);
 
119
static void ice_on_ice_complete(pj_ice_strans *ice_st, 
 
120
                                pj_ice_strans_op op,
 
121
                                pj_status_t status);
 
122
static void destroy_sess(struct test_sess *sess, unsigned wait_msec);
 
123
 
 
124
/* Create ICE stream transport */
 
125
static int create_ice_strans(struct test_sess *test_sess,
 
126
                             struct ice_ept *ept,
 
127
                             pj_ice_strans **p_ice)
 
128
{
 
129
    pj_ice_strans *ice;
 
130
    pj_ice_strans_cb ice_cb;
 
131
    pj_ice_strans_cfg ice_cfg;
 
132
    pj_sockaddr hostip;
 
133
    char serverip[PJ_INET6_ADDRSTRLEN];
 
134
    pj_status_t status;
 
135
 
 
136
    status = pj_gethostip(pj_AF_INET(), &hostip);
 
137
    if (status != PJ_SUCCESS)
 
138
        return -1030;
 
139
 
 
140
    pj_sockaddr_print(&hostip, serverip, sizeof(serverip), 0);
 
141
 
 
142
    /* Init callback structure */
 
143
    pj_bzero(&ice_cb, sizeof(ice_cb));
 
144
    ice_cb.on_rx_data = &ice_on_rx_data;
 
145
    ice_cb.on_ice_complete = &ice_on_ice_complete;
 
146
 
 
147
    /* Init ICE stream transport configuration structure */
 
148
    pj_ice_strans_cfg_default(&ice_cfg);
 
149
    pj_memcpy(&ice_cfg.stun_cfg, test_sess->stun_cfg, sizeof(pj_stun_config));
 
150
    if ((ept->cfg.enable_stun & SRV)==SRV || (ept->cfg.enable_turn & SRV)==SRV)
 
151
        ice_cfg.resolver = test_sess->resolver;
 
152
 
 
153
    if (ept->cfg.enable_stun & YES) {
 
154
        if ((ept->cfg.enable_stun & SRV) == SRV) {
 
155
            ice_cfg.stun.server = pj_str(SRV_DOMAIN);
 
156
        } else {
 
157
            ice_cfg.stun.server = pj_str(serverip);
 
158
        }
 
159
        ice_cfg.stun.port = STUN_SERVER_PORT;
 
160
    }
 
161
 
 
162
    if (ept->cfg.enable_host == 0) {
 
163
        ice_cfg.stun.max_host_cands = 0;
 
164
    } else {
 
165
        //ice_cfg.stun.no_host_cands = PJ_FALSE;
 
166
        ice_cfg.stun.loop_addr = PJ_TRUE;
 
167
    }
 
168
 
 
169
 
 
170
    if (ept->cfg.enable_turn & YES) {
 
171
        if ((ept->cfg.enable_turn & SRV) == SRV) {
 
172
            ice_cfg.turn.server = pj_str(SRV_DOMAIN);
 
173
        } else {
 
174
            ice_cfg.turn.server = pj_str(serverip);
 
175
        }
 
176
        ice_cfg.turn.port = TURN_SERVER_PORT;
 
177
        ice_cfg.turn.conn_type = PJ_TURN_TP_UDP;
 
178
        ice_cfg.turn.auth_cred.type = PJ_STUN_AUTH_CRED_STATIC;
 
179
        ice_cfg.turn.auth_cred.data.static_cred.realm = pj_str(SRV_DOMAIN);
 
180
        if (ept->cfg.client_flag & WRONG_TURN)
 
181
            ice_cfg.turn.auth_cred.data.static_cred.username = pj_str("xxx");
 
182
        else
 
183
            ice_cfg.turn.auth_cred.data.static_cred.username = pj_str(TURN_USERNAME);
 
184
        ice_cfg.turn.auth_cred.data.static_cred.data_type = PJ_STUN_PASSWD_PLAIN;
 
185
        ice_cfg.turn.auth_cred.data.static_cred.data = pj_str(TURN_PASSWD);
 
186
    }
 
187
 
 
188
    /* Create ICE stream transport */
 
189
    status = pj_ice_strans_create(NULL, &ice_cfg, ept->cfg.comp_cnt,
 
190
                                  (void*)ept, &ice_cb,
 
191
                                  &ice);
 
192
    if (status != PJ_SUCCESS) {
 
193
        app_perror(INDENT "err: pj_ice_strans_create()", status);
 
194
        return status;
 
195
    }
 
196
 
 
197
    pj_create_unique_string(test_sess->pool, &ept->ufrag);
 
198
    pj_create_unique_string(test_sess->pool, &ept->pass);
 
199
 
 
200
    /* Looks alright */
 
201
    *p_ice = ice;
 
202
    return PJ_SUCCESS;
 
203
}
 
204
                             
 
205
/* Create test session */
 
206
static int create_sess(pj_stun_config *stun_cfg,
 
207
                       unsigned server_flag,
 
208
                       struct test_cfg *caller_cfg,
 
209
                       struct test_cfg *callee_cfg,
 
210
                       struct sess_param *test_param,
 
211
                       struct test_sess **p_sess)
 
212
{
 
213
    pj_pool_t *pool;
 
214
    struct test_sess *sess;
 
215
    pj_str_t ns_ip;
 
216
    pj_uint16_t ns_port;
 
217
    unsigned flags;
 
218
    pj_status_t status;
 
219
 
 
220
    /* Create session structure */
 
221
    pool = pj_pool_create(mem, "testsess", 512, 512, NULL);
 
222
    sess = PJ_POOL_ZALLOC_T(pool, struct test_sess);
 
223
    sess->pool = pool;
 
224
    sess->stun_cfg = stun_cfg;
 
225
    sess->param = test_param;
 
226
 
 
227
    pj_memcpy(&sess->caller.cfg, caller_cfg, sizeof(*caller_cfg));
 
228
    sess->caller.result.init_status = sess->caller.result.nego_status = PJ_EPENDING;
 
229
 
 
230
    pj_memcpy(&sess->callee.cfg, callee_cfg, sizeof(*callee_cfg));
 
231
    sess->callee.result.init_status = sess->callee.result.nego_status = PJ_EPENDING;
 
232
 
 
233
    /* Create server */
 
234
    flags = server_flag;
 
235
    status = create_test_server(stun_cfg, flags, SRV_DOMAIN, &sess->server);
 
236
    if (status != PJ_SUCCESS) {
 
237
        app_perror(INDENT "error: create_test_server()", status);
 
238
        destroy_sess(sess, 500);
 
239
        return -10;
 
240
    }
 
241
    sess->server->turn_respond_allocate = 
 
242
        sess->server->turn_respond_refresh = PJ_TRUE;
 
243
 
 
244
    /* Create resolver */
 
245
    status = pj_dns_resolver_create(mem, NULL, 0, stun_cfg->timer_heap,
 
246
                                    stun_cfg->ioqueue, &sess->resolver);
 
247
    if (status != PJ_SUCCESS) {
 
248
        app_perror(INDENT "error: pj_dns_resolver_create()", status);
 
249
        destroy_sess(sess, 500);
 
250
        return -20;
 
251
    }
 
252
 
 
253
    ns_ip = pj_str("127.0.0.1");
 
254
    ns_port = (pj_uint16_t)DNS_SERVER_PORT;
 
255
    status = pj_dns_resolver_set_ns(sess->resolver, 1, &ns_ip, &ns_port);
 
256
    if (status != PJ_SUCCESS) {
 
257
        app_perror( INDENT "error: pj_dns_resolver_set_ns()", status);
 
258
        destroy_sess(sess, 500);
 
259
        return -21;
 
260
    }
 
261
 
 
262
    /* Create caller ICE stream transport */
 
263
    status = create_ice_strans(sess, &sess->caller, &sess->caller.ice);
 
264
    if (status != PJ_SUCCESS) {
 
265
        destroy_sess(sess, 500);
 
266
        return -30;
 
267
    }
 
268
 
 
269
    /* Create callee ICE stream transport */
 
270
    status = create_ice_strans(sess, &sess->callee, &sess->callee.ice);
 
271
    if (status != PJ_SUCCESS) {
 
272
        destroy_sess(sess, 500);
 
273
        return -40;
 
274
    }
 
275
 
 
276
    *p_sess = sess;
 
277
    return 0;
 
278
}
 
279
 
 
280
/* Destroy test session */
 
281
static void destroy_sess(struct test_sess *sess, unsigned wait_msec)
 
282
{
 
283
    unsigned i;
 
284
 
 
285
    if (sess->caller.ice) {
 
286
        pj_ice_strans_destroy(sess->caller.ice);
 
287
        sess->caller.ice = NULL;
 
288
    }
 
289
 
 
290
    if (sess->callee.ice) {
 
291
        pj_ice_strans_destroy(sess->callee.ice);
 
292
        sess->callee.ice = NULL;
 
293
    }
 
294
 
 
295
    sess->param->worker_quit = PJ_TRUE;
 
296
    for (i=0; i<sess->param->worker_cnt; ++i) {
 
297
        if (sess->worker_threads[i])
 
298
            pj_thread_join(sess->worker_threads[i]);
 
299
    }
 
300
 
 
301
    poll_events(sess->stun_cfg, wait_msec, PJ_FALSE);
 
302
 
 
303
    if (sess->resolver) {
 
304
        pj_dns_resolver_destroy(sess->resolver, PJ_FALSE);
 
305
        sess->resolver = NULL;
 
306
    }
 
307
 
 
308
    if (sess->server) {
 
309
        destroy_test_server(sess->server);
 
310
        sess->server = NULL;
 
311
    }
 
312
 
 
313
    if (sess->pool) {
 
314
        pj_pool_t *pool = sess->pool;
 
315
        sess->pool = NULL;
 
316
        pj_pool_release(pool);
 
317
    }
 
318
}
 
319
 
 
320
static void ice_on_rx_data(pj_ice_strans *ice_st,
 
321
                           unsigned comp_id, 
 
322
                           void *pkt, pj_size_t size,
 
323
                           const pj_sockaddr_t *src_addr,
 
324
                           unsigned src_addr_len)
 
325
{
 
326
    struct ice_ept *ept;
 
327
 
 
328
    PJ_UNUSED_ARG(pkt);
 
329
    PJ_UNUSED_ARG(size);
 
330
    PJ_UNUSED_ARG(src_addr);
 
331
    PJ_UNUSED_ARG(src_addr_len);
 
332
 
 
333
    ept = (struct ice_ept*) pj_ice_strans_get_user_data(ice_st);
 
334
    ept->result.rx_cnt[comp_id]++;
 
335
}
 
336
 
 
337
 
 
338
static void ice_on_ice_complete(pj_ice_strans *ice_st, 
 
339
                                pj_ice_strans_op op,
 
340
                                pj_status_t status)
 
341
{
 
342
    struct ice_ept *ept;
 
343
 
 
344
    ept = (struct ice_ept*) pj_ice_strans_get_user_data(ice_st);
 
345
    switch (op) {
 
346
    case PJ_ICE_STRANS_OP_INIT:
 
347
        ept->result.init_status = status;
 
348
        if (status != PJ_SUCCESS && (ept->cfg.client_flag & DEL_ON_ERR)) {
 
349
            pj_ice_strans_destroy(ice_st);
 
350
            ept->ice = NULL;
 
351
        }
 
352
        break;
 
353
    case PJ_ICE_STRANS_OP_NEGOTIATION:
 
354
        ept->result.nego_status = status;
 
355
        break;
 
356
    case PJ_ICE_STRANS_OP_KEEP_ALIVE:
 
357
        /* keep alive failed? */
 
358
        break;
 
359
    default:
 
360
        pj_assert(!"Unknown op");
 
361
    }
 
362
}
 
363
 
 
364
 
 
365
/* Start ICE negotiation on the endpoint, based on parameter from
 
366
 * the other endpoint.
 
367
 */
 
368
static pj_status_t start_ice(struct ice_ept *ept, const struct ice_ept *remote)
 
369
{
 
370
    pj_ice_sess_cand rcand[32];
 
371
    unsigned i, rcand_cnt = 0;
 
372
    pj_status_t status;
 
373
 
 
374
    /* Enum remote candidates */
 
375
    for (i=0; i<remote->cfg.comp_cnt; ++i) {
 
376
        unsigned cnt = PJ_ARRAY_SIZE(rcand) - rcand_cnt;
 
377
        status = pj_ice_strans_enum_cands(remote->ice, i+1, &cnt, rcand+rcand_cnt);
 
378
        if (status != PJ_SUCCESS) {
 
379
            app_perror(INDENT "err: pj_ice_strans_enum_cands()", status);
 
380
            return status;
 
381
        }
 
382
        rcand_cnt += cnt;
 
383
    }
 
384
 
 
385
    status = pj_ice_strans_start_ice(ept->ice, &remote->ufrag, &remote->pass,
 
386
                                     rcand_cnt, rcand);
 
387
    if (status != PJ_SUCCESS) {
 
388
        app_perror(INDENT "err: pj_ice_strans_start_ice()", status);
 
389
        return status;
 
390
    }
 
391
 
 
392
    return PJ_SUCCESS;
 
393
}
 
394
 
 
395
 
 
396
/* Check that the pair in both agents are matched */
 
397
static int check_pair(const struct ice_ept *ept1, const struct ice_ept *ept2,
 
398
                      int start_err)
 
399
{
 
400
    unsigned i, min_cnt, max_cnt;
 
401
 
 
402
    if (ept1->cfg.comp_cnt < ept2->cfg.comp_cnt) {
 
403
        min_cnt = ept1->cfg.comp_cnt;
 
404
        max_cnt = ept2->cfg.comp_cnt;
 
405
    } else {
 
406
        min_cnt = ept2->cfg.comp_cnt;
 
407
        max_cnt = ept1->cfg.comp_cnt;
 
408
    }
 
409
 
 
410
    /* Must have valid pair for common components */
 
411
    for (i=0; i<min_cnt; ++i) {
 
412
        const pj_ice_sess_check *c1;
 
413
        const pj_ice_sess_check *c2;
 
414
 
 
415
        c1 = pj_ice_strans_get_valid_pair(ept1->ice, i+1);
 
416
        if (c1 == NULL) {
 
417
            PJ_LOG(3,(THIS_FILE, INDENT "err: unable to get valid pair for ice1 "
 
418
                          "component %d", i+1));
 
419
            return start_err - 2;
 
420
        }
 
421
 
 
422
        c2 = pj_ice_strans_get_valid_pair(ept2->ice, i+1);
 
423
        if (c2 == NULL) {
 
424
            PJ_LOG(3,(THIS_FILE, INDENT "err: unable to get valid pair for ice2 "
 
425
                          "component %d", i+1));
 
426
            return start_err - 4;
 
427
        }
 
428
 
 
429
        if (pj_sockaddr_cmp(&c1->rcand->addr, &c2->lcand->addr) != 0) {
 
430
            PJ_LOG(3,(THIS_FILE, INDENT "err: candidate pair does not match "
 
431
                          "for component %d", i+1));
 
432
            return start_err - 6;
 
433
        }
 
434
    }
 
435
 
 
436
    /* Extra components must not have valid pair */
 
437
    for (; i<max_cnt; ++i) {
 
438
        if (ept1->cfg.comp_cnt>i &&
 
439
            pj_ice_strans_get_valid_pair(ept1->ice, i+1) != NULL) 
 
440
        {
 
441
            PJ_LOG(3,(THIS_FILE, INDENT "err: ice1 shouldn't have valid pair "
 
442
                          "for component %d", i+1));
 
443
            return start_err - 8;
 
444
        }
 
445
        if (ept2->cfg.comp_cnt>i &&
 
446
            pj_ice_strans_get_valid_pair(ept2->ice, i+1) != NULL) 
 
447
        {
 
448
            PJ_LOG(3,(THIS_FILE, INDENT "err: ice2 shouldn't have valid pair "
 
449
                          "for component %d", i+1));
 
450
            return start_err - 9;
 
451
        }
 
452
    }
 
453
 
 
454
    return 0;
 
455
}
 
456
 
 
457
 
 
458
#define WAIT_UNTIL(timeout,expr, RC)  { \
 
459
                                pj_time_val t0, t; \
 
460
                                pj_gettimeofday(&t0); \
 
461
                                RC = -1; \
 
462
                                for (;;) { \
 
463
                                    poll_events(stun_cfg, 10, PJ_FALSE); \
 
464
                                    pj_gettimeofday(&t); \
 
465
                                    if (expr) { \
 
466
                                        rc = PJ_SUCCESS; \
 
467
                                        break; \
 
468
                                    } \
 
469
                                    PJ_TIME_VAL_SUB(t, t0); \
 
470
                                    if ((unsigned)PJ_TIME_VAL_MSEC(t) >= (timeout)) \
 
471
                                        break; \
 
472
                                } \
 
473
                            }
 
474
 
 
475
int worker_thread_proc(void *data)
 
476
{
 
477
    pj_status_t rc;
 
478
    struct test_sess *sess = (struct test_sess *) data;
 
479
    pj_stun_config *stun_cfg = sess->stun_cfg;
 
480
    
 
481
    /* Wait until negotiation is complete on both endpoints */
 
482
#define ALL_DONE    (sess->param->worker_quit || \
 
483
                        (sess->caller.result.nego_status!=PJ_EPENDING && \
 
484
                         sess->callee.result.nego_status!=PJ_EPENDING))
 
485
    WAIT_UNTIL(sess->param->worker_timeout, ALL_DONE, rc);
 
486
    
 
487
    return 0;
 
488
}
 
489
 
 
490
static int perform_test2(const char *title,
 
491
                         pj_stun_config *stun_cfg,
 
492
                         unsigned server_flag,
 
493
                         struct test_cfg *caller_cfg,
 
494
                         struct test_cfg *callee_cfg,
 
495
                         struct sess_param *test_param)
 
496
{
 
497
    pjlib_state pjlib_state;
 
498
    struct test_sess *sess;
 
499
    unsigned i;
 
500
    int rc;
 
501
 
 
502
    PJ_LOG(3,(THIS_FILE, INDENT "%s", title));
 
503
 
 
504
    capture_pjlib_state(stun_cfg, &pjlib_state);
 
505
 
 
506
    rc = create_sess(stun_cfg, server_flag, caller_cfg, callee_cfg, test_param, &sess);
 
507
    if (rc != 0)
 
508
        return rc;
 
509
 
 
510
#define ALL_READY   (sess->caller.result.init_status!=PJ_EPENDING && \
 
511
                     sess->callee.result.init_status!=PJ_EPENDING)
 
512
 
 
513
    /* Wait until both ICE transports are initialized */
 
514
    WAIT_UNTIL(30000, ALL_READY, rc);
 
515
 
 
516
    if (!ALL_READY) {
 
517
        PJ_LOG(3,(THIS_FILE, INDENT "err: init timed-out"));
 
518
        destroy_sess(sess, 500);
 
519
        return -100;
 
520
    }
 
521
 
 
522
    if (sess->caller.result.init_status != sess->caller.cfg.expected.init_status) {
 
523
        app_perror(INDENT "err: caller init", sess->caller.result.init_status);
 
524
        destroy_sess(sess, 500);
 
525
        return -102;
 
526
    }
 
527
    if (sess->callee.result.init_status != sess->callee.cfg.expected.init_status) {
 
528
        app_perror(INDENT "err: callee init", sess->callee.result.init_status);
 
529
        destroy_sess(sess, 500);
 
530
        return -104;
 
531
    }
 
532
 
 
533
    /* Failure condition */
 
534
    if (sess->caller.result.init_status != PJ_SUCCESS ||
 
535
        sess->callee.result.init_status != PJ_SUCCESS)
 
536
    {
 
537
        rc = 0;
 
538
        goto on_return;
 
539
    }
 
540
    /* Init ICE on caller */
 
541
    rc = pj_ice_strans_init_ice(sess->caller.ice, sess->caller.cfg.role, 
 
542
                                &sess->caller.ufrag, &sess->caller.pass);
 
543
    if (rc != PJ_SUCCESS) {
 
544
        app_perror(INDENT "err: caller pj_ice_strans_init_ice()", rc);
 
545
        destroy_sess(sess, 500);
 
546
        return -100;
 
547
    }
 
548
 
 
549
    /* Init ICE on callee */
 
550
    rc = pj_ice_strans_init_ice(sess->callee.ice, sess->callee.cfg.role, 
 
551
                                &sess->callee.ufrag, &sess->callee.pass);
 
552
    if (rc != PJ_SUCCESS) {
 
553
        app_perror(INDENT "err: callee pj_ice_strans_init_ice()", rc);
 
554
        destroy_sess(sess, 500);
 
555
        return -110;
 
556
    }
 
557
    /* Start ICE on callee */
 
558
    rc = start_ice(&sess->callee, &sess->caller);
 
559
    if (rc != PJ_SUCCESS) {
 
560
        destroy_sess(sess, 500);
 
561
        return -120;
 
562
    }
 
563
    /* Wait for callee's answer_delay */
 
564
    poll_events(stun_cfg, sess->callee.cfg.answer_delay, PJ_FALSE);
 
565
    /* Start ICE on caller */
 
566
    rc = start_ice(&sess->caller, &sess->callee);
 
567
    if (rc != PJ_SUCCESS) {
 
568
        destroy_sess(sess, 500);
 
569
        return -130;
 
570
    }
 
571
 
 
572
    for (i=0; i<sess->param->worker_cnt; ++i) {
 
573
        pj_status_t status;
 
574
 
 
575
        status = pj_thread_create(sess->pool, "worker_thread",
 
576
                                  worker_thread_proc, sess, 0, 0,
 
577
                                  &sess->worker_threads[i]);
 
578
        if (status != PJ_SUCCESS) {
 
579
            PJ_LOG(3,(THIS_FILE, INDENT "err: create thread"));
 
580
            destroy_sess(sess, 500);
 
581
            return -135;
 
582
        }
 
583
    }
 
584
 
 
585
    if (sess->param->destroy_after_create)
 
586
        goto on_destroy;
 
587
 
 
588
    if (sess->param->destroy_after_one_done) {
 
589
        while (sess->caller.result.init_status==PJ_EPENDING &&
 
590
               sess->callee.result.init_status==PJ_EPENDING)
 
591
        {
 
592
            if (sess->param->worker_cnt)
 
593
                pj_thread_sleep(0);
 
594
            else
 
595
                poll_events(stun_cfg, 0, PJ_FALSE);
 
596
        }
 
597
        goto on_destroy;
 
598
    }
 
599
    
 
600
    WAIT_UNTIL(30000, ALL_DONE, rc);
 
601
    if (!ALL_DONE) {
 
602
        PJ_LOG(3,(THIS_FILE, INDENT "err: negotiation timed-out"));
 
603
        destroy_sess(sess, 500);
 
604
        return -140;
 
605
    }
 
606
 
 
607
    if (sess->caller.result.nego_status != sess->caller.cfg.expected.nego_status) {
 
608
        app_perror(INDENT "err: caller negotiation failed", sess->caller.result.nego_status);
 
609
        destroy_sess(sess, 500);
 
610
        return -150;
 
611
    }
 
612
 
 
613
    if (sess->callee.result.nego_status != sess->callee.cfg.expected.nego_status) {
 
614
        app_perror(INDENT "err: callee negotiation failed", sess->callee.result.nego_status);
 
615
        destroy_sess(sess, 500);
 
616
        return -160;
 
617
    }
 
618
 
 
619
    /* Verify that both agents have agreed on the same pair */
 
620
    rc = check_pair(&sess->caller, &sess->callee, -170);
 
621
    if (rc != 0) {
 
622
        destroy_sess(sess, 500);
 
623
        return rc;
 
624
    }
 
625
    rc = check_pair(&sess->callee, &sess->caller, -180);
 
626
    if (rc != 0) {
 
627
        destroy_sess(sess, 500);
 
628
        return rc;
 
629
    }
 
630
 
 
631
    /* Looks like everything is okay */
 
632
on_destroy:
 
633
 
 
634
    /* Destroy ICE stream transports first to let it de-allocate
 
635
     * TURN relay (otherwise there'll be timer/memory leak, unless
 
636
     * we wait for long time in the last poll_events() below).
 
637
     */
 
638
    if (sess->caller.ice) {
 
639
        pj_ice_strans_destroy(sess->caller.ice);
 
640
        sess->caller.ice = NULL;
 
641
    }
 
642
 
 
643
    if (sess->callee.ice) {
 
644
        pj_ice_strans_destroy(sess->callee.ice);
 
645
        sess->callee.ice = NULL;
 
646
    }
 
647
 
 
648
on_return:
 
649
    /* Wait.. */
 
650
    poll_events(stun_cfg, 200, PJ_FALSE);
 
651
 
 
652
    /* Now destroy everything */
 
653
    destroy_sess(sess, 500);
 
654
 
 
655
    /* Flush events */
 
656
    poll_events(stun_cfg, 100, PJ_FALSE);
 
657
 
 
658
    rc = check_pjlib_state(stun_cfg, &pjlib_state);
 
659
    if (rc != 0) {
 
660
        return rc;
 
661
    }
 
662
 
 
663
    return rc;
 
664
}
 
665
 
 
666
static int perform_test(const char *title,
 
667
                        pj_stun_config *stun_cfg,
 
668
                        unsigned server_flag,
 
669
                        struct test_cfg *caller_cfg,
 
670
                        struct test_cfg *callee_cfg)
 
671
{
 
672
    struct sess_param test_param;
 
673
 
 
674
    pj_bzero(&test_param, sizeof(test_param));
 
675
    return perform_test2(title, stun_cfg, server_flag, caller_cfg,
 
676
                         callee_cfg, &test_param);
 
677
}
 
678
 
 
679
#define ROLE1   PJ_ICE_SESS_ROLE_CONTROLLED
 
680
#define ROLE2   PJ_ICE_SESS_ROLE_CONTROLLING
 
681
 
 
682
int ice_test(void)
 
683
{
 
684
    pj_pool_t *pool;
 
685
    pj_stun_config stun_cfg;
 
686
    unsigned i;
 
687
    int rc;
 
688
    struct sess_cfg_t {
 
689
        const char      *title;
 
690
        unsigned         server_flag;
 
691
        struct test_cfg  ua1;
 
692
        struct test_cfg  ua2;
 
693
    } sess_cfg[] = 
 
694
    {
 
695
        /*  Role    comp#   host?   stun?   turn?   flag?  ans_del snd_del des_del */
 
696
        {
 
697
            "hosts candidates only",
 
698
            0xFFFF,
 
699
            {ROLE1, 1,      YES,    NO,     NO,     NO,     0,      0,      0, {PJ_SUCCESS, PJ_SUCCESS}},
 
700
            {ROLE2, 1,      YES,    NO,     NO,     NO,     0,      0,      0, {PJ_SUCCESS, PJ_SUCCESS}}
 
701
        },
 
702
        {
 
703
            "host and srflxes",
 
704
            0xFFFF,
 
705
            {ROLE1, 1,      YES,    YES,    NO,     NO,     0,      0,      0, {PJ_SUCCESS, PJ_SUCCESS}},
 
706
            {ROLE2, 1,      YES,    YES,    NO,     NO,     0,      0,      0, {PJ_SUCCESS, PJ_SUCCESS}}
 
707
        },
 
708
        {
 
709
            "host vs relay",
 
710
            0xFFFF,
 
711
            {ROLE1, 1,      YES,    NO,    NO,      NO,     0,      0,      0, {PJ_SUCCESS, PJ_SUCCESS}},
 
712
            {ROLE2, 1,      NO,     NO,    YES,     NO,     0,      0,      0, {PJ_SUCCESS, PJ_SUCCESS}}
 
713
        },
 
714
        {
 
715
            "relay vs host",
 
716
            0xFFFF,
 
717
            {ROLE1, 1,      NO,     NO,   YES,      NO,     0,      0,      0, {PJ_SUCCESS, PJ_SUCCESS}},
 
718
            {ROLE2, 1,     YES,     NO,    NO,      NO,     0,      0,      0, {PJ_SUCCESS, PJ_SUCCESS}}
 
719
        },
 
720
        {
 
721
            "relay vs relay",
 
722
            0xFFFF,
 
723
            {ROLE1, 1,      NO,     NO,   YES,      NO,     0,      0,      0, {PJ_SUCCESS, PJ_SUCCESS}},
 
724
            {ROLE2, 1,      NO,     NO,   YES,      NO,     0,      0,      0, {PJ_SUCCESS, PJ_SUCCESS}}
 
725
        },
 
726
        {
 
727
            "all candidates",
 
728
            0xFFFF,
 
729
            {ROLE1, 1,     YES,    YES,   YES,      NO,     0,      0,      0, {PJ_SUCCESS, PJ_SUCCESS}},
 
730
            {ROLE2, 1,     YES,    YES,   YES,      NO,     0,      0,      0, {PJ_SUCCESS, PJ_SUCCESS}}
 
731
        },
 
732
    };
 
733
 
 
734
    pool = pj_pool_create(mem, NULL, 512, 512, NULL);
 
735
    rc = create_stun_config(pool, &stun_cfg);
 
736
    if (rc != PJ_SUCCESS) {
 
737
        pj_pool_release(pool);
 
738
        return -7;
 
739
    }
 
740
 
 
741
    /* Simple test first with host candidate */
 
742
    if (1) {
 
743
        struct sess_cfg_t cfg = 
 
744
        {
 
745
            "Basic with host candidates",
 
746
            0x0,
 
747
            /*  Role    comp#   host?   stun?   turn?   flag?  ans_del snd_del des_del */
 
748
            {ROLE1,     1,      YES,     NO,        NO,     0,      0,      0,      0, {PJ_SUCCESS, PJ_SUCCESS}},
 
749
            {ROLE2,     1,      YES,     NO,        NO,     0,      0,      0,      0, {PJ_SUCCESS, PJ_SUCCESS}}
 
750
        };
 
751
 
 
752
        rc = perform_test(cfg.title, &stun_cfg, cfg.server_flag, 
 
753
                          &cfg.ua1, &cfg.ua2);
 
754
        if (rc != 0)
 
755
            goto on_return;
 
756
 
 
757
        cfg.ua1.comp_cnt = 2;
 
758
        cfg.ua2.comp_cnt = 2;
 
759
        rc = perform_test("Basic with host candidates, 2 components", 
 
760
                          &stun_cfg, cfg.server_flag, 
 
761
                          &cfg.ua1, &cfg.ua2);
 
762
        if (rc != 0)
 
763
            goto on_return;
 
764
    }
 
765
    
 
766
    /* Simple test first with srflx candidate */
 
767
    if (1) {
 
768
        struct sess_cfg_t cfg = 
 
769
        {
 
770
            "Basic with srflx candidates",
 
771
            0xFFFF,
 
772
            /*  Role    comp#   host?   stun?   turn?   flag?  ans_del snd_del des_del */
 
773
            {ROLE1,     1,      YES,    YES,        NO,     0,      0,      0,      0, {PJ_SUCCESS, PJ_SUCCESS}},
 
774
            {ROLE2,     1,      YES,    YES,        NO,     0,      0,      0,      0, {PJ_SUCCESS, PJ_SUCCESS}}
 
775
        };
 
776
 
 
777
        rc = perform_test(cfg.title, &stun_cfg, cfg.server_flag, 
 
778
                          &cfg.ua1, &cfg.ua2);
 
779
        if (rc != 0)
 
780
            goto on_return;
 
781
 
 
782
        cfg.ua1.comp_cnt = 2;
 
783
        cfg.ua2.comp_cnt = 2;
 
784
 
 
785
        rc = perform_test("Basic with srflx candidates, 2 components", 
 
786
                          &stun_cfg, cfg.server_flag, 
 
787
                          &cfg.ua1, &cfg.ua2);
 
788
        if (rc != 0)
 
789
            goto on_return;
 
790
    }
 
791
 
 
792
    /* Simple test with relay candidate */
 
793
    if (1) {
 
794
        struct sess_cfg_t cfg = 
 
795
        {
 
796
            "Basic with relay candidates",
 
797
            0xFFFF,
 
798
            /*  Role    comp#   host?   stun?   turn?   flag?  ans_del snd_del des_del */
 
799
            {ROLE1,     1,       NO,     NO,      YES,      0,      0,      0,      0, {PJ_SUCCESS, PJ_SUCCESS}},
 
800
            {ROLE2,     1,       NO,     NO,      YES,      0,      0,      0,      0, {PJ_SUCCESS, PJ_SUCCESS}}
 
801
        };
 
802
 
 
803
        rc = perform_test(cfg.title, &stun_cfg, cfg.server_flag, 
 
804
                          &cfg.ua1, &cfg.ua2);
 
805
        if (rc != 0)
 
806
            goto on_return;
 
807
 
 
808
        cfg.ua1.comp_cnt = 2;
 
809
        cfg.ua2.comp_cnt = 2;
 
810
 
 
811
        rc = perform_test("Basic with relay candidates, 2 components", 
 
812
                          &stun_cfg, cfg.server_flag, 
 
813
                          &cfg.ua1, &cfg.ua2);
 
814
        if (rc != 0)
 
815
            goto on_return;
 
816
    }
 
817
 
 
818
    /* Failure test with STUN resolution */
 
819
    if (1) {
 
820
        struct sess_cfg_t cfg = 
 
821
        {
 
822
            "STUN resolution failure",
 
823
            0x0,
 
824
            /*  Role    comp#   host?   stun?   turn?   flag?  ans_del snd_del des_del */
 
825
            {ROLE1,     2,       NO,    YES,        NO,     0,      0,      0,      0, {PJNATH_ESTUNTIMEDOUT, -1}},
 
826
            {ROLE2,     2,       NO,    YES,        NO,     0,      0,      0,      0, {PJNATH_ESTUNTIMEDOUT, -1}}
 
827
        };
 
828
 
 
829
        rc = perform_test(cfg.title, &stun_cfg, cfg.server_flag,
 
830
                          &cfg.ua1, &cfg.ua2);
 
831
        if (rc != 0)
 
832
            goto on_return;
 
833
 
 
834
        cfg.ua1.client_flag |= DEL_ON_ERR;
 
835
        cfg.ua2.client_flag |= DEL_ON_ERR;
 
836
 
 
837
        rc = perform_test("STUN resolution failure with destroy on callback", 
 
838
                          &stun_cfg, cfg.server_flag, 
 
839
                          &cfg.ua1, &cfg.ua2);
 
840
        if (rc != 0)
 
841
            goto on_return;
 
842
    }
 
843
 
 
844
    /* Failure test with TURN resolution */
 
845
    if (1) {
 
846
        struct sess_cfg_t cfg = 
 
847
        {
 
848
            "TURN allocation failure",
 
849
            0xFFFF,
 
850
            /*  Role    comp#   host?   stun?   turn?   flag?  ans_del snd_del des_del */
 
851
            {ROLE1,     2,       NO,    NO,     YES, WRONG_TURN,    0,      0,      0, {PJ_STATUS_FROM_STUN_CODE(401), -1}},
 
852
            {ROLE2,     2,       NO,    NO,     YES, WRONG_TURN,    0,      0,      0, {PJ_STATUS_FROM_STUN_CODE(401), -1}}
 
853
        };
 
854
 
 
855
        rc = perform_test(cfg.title, &stun_cfg, cfg.server_flag, 
 
856
                          &cfg.ua1, &cfg.ua2);
 
857
        if (rc != 0)
 
858
            goto on_return;
 
859
 
 
860
        cfg.ua1.client_flag |= DEL_ON_ERR;
 
861
        cfg.ua2.client_flag |= DEL_ON_ERR;
 
862
 
 
863
        rc = perform_test("TURN allocation failure with destroy on callback", 
 
864
                          &stun_cfg, cfg.server_flag, 
 
865
                          &cfg.ua1, &cfg.ua2);
 
866
        if (rc != 0)
 
867
            goto on_return;
 
868
    }
 
869
 
 
870
 
 
871
    /* STUN failure, testing TURN deallocation */
 
872
    if (1) {
 
873
        struct sess_cfg_t cfg = 
 
874
        {
 
875
            "STUN failure, testing TURN deallocation",
 
876
            0xFFFF & (~(CREATE_STUN_SERVER)),
 
877
            /*  Role    comp#   host?   stun?   turn?   flag?  ans_del snd_del des_del */
 
878
            {ROLE1,     1,       YES,    YES,   YES,    0,    0,            0,      0, {PJNATH_ESTUNTIMEDOUT, -1}},
 
879
            {ROLE2,     1,       YES,    YES,   YES,    0,    0,            0,      0, {PJNATH_ESTUNTIMEDOUT, -1}}
 
880
        };
 
881
 
 
882
        rc = perform_test(cfg.title, &stun_cfg, cfg.server_flag,
 
883
                          &cfg.ua1, &cfg.ua2);
 
884
        if (rc != 0)
 
885
            goto on_return;
 
886
 
 
887
        cfg.ua1.client_flag |= DEL_ON_ERR;
 
888
        cfg.ua2.client_flag |= DEL_ON_ERR;
 
889
 
 
890
        rc = perform_test("STUN failure, testing TURN deallocation (cb)", 
 
891
                          &stun_cfg, cfg.server_flag, 
 
892
                          &cfg.ua1, &cfg.ua2);
 
893
        if (rc != 0)
 
894
            goto on_return;
 
895
    }
 
896
 
 
897
    rc = 0;
 
898
    /* Iterate each test item */
 
899
    for (i=0; i<PJ_ARRAY_SIZE(sess_cfg); ++i) {
 
900
        struct sess_cfg_t *cfg = &sess_cfg[i];
 
901
        unsigned delay[] = { 50, 2000 };
 
902
        unsigned d;
 
903
 
 
904
        PJ_LOG(3,(THIS_FILE, "  %s", cfg->title));
 
905
 
 
906
        /* For each test item, test with various answer delay */
 
907
        for (d=0; d<PJ_ARRAY_SIZE(delay); ++d) {
 
908
            struct role_t {
 
909
                pj_ice_sess_role        ua1;
 
910
                pj_ice_sess_role        ua2;
 
911
            } role[] = 
 
912
            {
 
913
                { ROLE1, ROLE2},
 
914
                { ROLE2, ROLE1},
 
915
                { ROLE1, ROLE1},
 
916
                { ROLE2, ROLE2}
 
917
            };
 
918
            unsigned j;
 
919
 
 
920
            cfg->ua1.answer_delay = delay[d];
 
921
            cfg->ua2.answer_delay = delay[d];
 
922
 
 
923
            /* For each test item, test with role conflict scenarios */
 
924
            for (j=0; j<PJ_ARRAY_SIZE(role); ++j) {
 
925
                unsigned k1;
 
926
 
 
927
                cfg->ua1.role = role[j].ua1;
 
928
                cfg->ua2.role = role[j].ua2;
 
929
 
 
930
                /* For each test item, test with different number of components */
 
931
                for (k1=1; k1<=2; ++k1) {
 
932
                    unsigned k2;
 
933
 
 
934
                    cfg->ua1.comp_cnt = k1;
 
935
 
 
936
                    for (k2=1; k2<=2; ++k2) {
 
937
                        char title[120];
 
938
 
 
939
                        sprintf(title, 
 
940
                                "%s/%s, %dms answer delay, %d vs %d components", 
 
941
                                pj_ice_sess_role_name(role[j].ua1),
 
942
                                pj_ice_sess_role_name(role[j].ua2),
 
943
                                delay[d], k1, k2);
 
944
 
 
945
                        cfg->ua2.comp_cnt = k2;
 
946
                        rc = perform_test(title, &stun_cfg, cfg->server_flag, 
 
947
                                          &cfg->ua1, &cfg->ua2);
 
948
                        if (rc != 0)
 
949
                            goto on_return;
 
950
                    }
 
951
                }
 
952
            }
 
953
        }
 
954
    }
 
955
 
 
956
on_return:
 
957
    destroy_stun_config(&stun_cfg);
 
958
    pj_pool_release(pool);
 
959
    return rc;
 
960
}
 
961
 
 
962
int ice_one_conc_test(pj_stun_config *stun_cfg, int err_quit)
 
963
{
 
964
    struct sess_cfg_t {
 
965
        const char      *title;
 
966
        unsigned         server_flag;
 
967
        struct test_cfg  ua1;
 
968
        struct test_cfg  ua2;
 
969
    } cfg =
 
970
    {
 
971
        "Concurrency test",
 
972
        0xFFFF,
 
973
        /*  Role    comp#   host?   stun?   turn?   flag?  ans_del snd_del des_del */
 
974
        {ROLE1, 1,      YES,     YES,       YES,    0,      0,      0,      0, {PJ_SUCCESS, PJ_SUCCESS}},
 
975
        {ROLE2, 1,      YES,     YES,       YES,    0,      0,      0,      0, {PJ_SUCCESS, PJ_SUCCESS}}
 
976
    };
 
977
    struct sess_param test_param;
 
978
    int rc;
 
979
 
 
980
 
 
981
    /* test a: destroy as soon as nego starts */
 
982
    cfg.title = "    ice test a: immediate destroy";
 
983
    pj_bzero(&test_param, sizeof(test_param));
 
984
    test_param.worker_cnt = 4;
 
985
    test_param.worker_timeout = 1000;
 
986
    test_param.destroy_after_create = PJ_TRUE;
 
987
 
 
988
    rc = perform_test2(cfg.title, stun_cfg, cfg.server_flag,
 
989
                       &cfg.ua1, &cfg.ua2, &test_param);
 
990
    if (rc != 0 && err_quit)
 
991
        return rc;
 
992
 
 
993
    /* test b: destroy as soon as one is done */
 
994
    cfg.title = "    ice test b: destroy after 1 success";
 
995
    test_param.destroy_after_create = PJ_FALSE;
 
996
    test_param.destroy_after_one_done = PJ_TRUE;
 
997
 
 
998
    rc = perform_test2(cfg.title, stun_cfg, cfg.server_flag,
 
999
                       &cfg.ua1, &cfg.ua2, &test_param);
 
1000
    if (rc != 0 && err_quit)
 
1001
        return rc;
 
1002
 
 
1003
    /* test c: normal */
 
1004
    cfg.title = "    ice test c: normal flow";
 
1005
    pj_bzero(&test_param, sizeof(test_param));
 
1006
    test_param.worker_cnt = 4;
 
1007
    test_param.worker_timeout = 1000;
 
1008
 
 
1009
    rc = perform_test2(cfg.title, stun_cfg, cfg.server_flag,
 
1010
                       &cfg.ua1, &cfg.ua2, &test_param);
 
1011
    if (rc != 0 && err_quit)
 
1012
        return rc;
 
1013
 
 
1014
    return 0;
 
1015
}
 
1016
 
 
1017
int ice_conc_test(void)
 
1018
{
 
1019
    const unsigned LOOP = 100;
 
1020
    pj_pool_t *pool;
 
1021
    pj_stun_config stun_cfg;
 
1022
    unsigned i;
 
1023
    int rc;
 
1024
    
 
1025
    pool = pj_pool_create(mem, NULL, 512, 512, NULL);
 
1026
    rc = create_stun_config(pool, &stun_cfg);
 
1027
    if (rc != PJ_SUCCESS) {
 
1028
        pj_pool_release(pool);
 
1029
        return -7;
 
1030
    }
 
1031
    
 
1032
    for (i = 0; i < LOOP; i++) {
 
1033
        PJ_LOG(3,(THIS_FILE, INDENT "Test %d of %d", i+1, LOOP));
 
1034
        rc = ice_one_conc_test(&stun_cfg, PJ_TRUE);
 
1035
        if (rc)
 
1036
            break;
 
1037
    }
 
1038
 
 
1039
    /* Avoid compiler warning */
 
1040
    goto on_return;
 
1041
    
 
1042
on_return:
 
1043
    destroy_stun_config(&stun_cfg);
 
1044
    pj_pool_release(pool);
 
1045
 
 
1046
    return rc;
 
1047
}