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