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 #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 CLIENT_IPV4 = 4,
43 CLIENT_IPV6 = 8
44 };
45
46 /* Test results */
47 struct test_result
48 {
49 pj_status_t start_status; /* start ice successful? */
50 pj_status_t init_status; /* init successful? */
51 pj_status_t nego_status; /* negotiation successful? */
52 unsigned rx_cnt[4]; /* Number of data received */
53 };
54
55 /* Role comp# host? stun? turn? flag? ans_del snd_del des_del */
56 /* Test session configuration */
57 struct test_cfg
58 {
59 pj_ice_sess_role role; /* Role. */
60 unsigned comp_cnt; /* Component count */
61 unsigned enable_host; /* Enable host candidates */
62 unsigned enable_stun; /* Enable srflx candidates */
63 unsigned enable_turn; /* Enable turn candidates */
64 unsigned client_flag; /* Client flags */
65
66 unsigned answer_delay; /* Delay before sending SDP */
67 unsigned send_delay; /* unused */
68 unsigned destroy_delay; /* unused */
69
70 struct test_result expected;/* Expected result */
71
72 pj_bool_t nom_regular; /* Use regular nomination? */
73 pj_ice_sess_trickle trickle; /* Trickle ICE mode */
74 };
75
76 /* ICE endpoint state */
77 struct ice_ept
78 {
79 struct test_cfg cfg; /* Configuratino. */
80 pj_ice_strans *ice; /* ICE stream transport */
81 struct test_result result;/* Test result. */
82
83 pj_str_t ufrag; /* username fragment. */
84 pj_str_t pass; /* password */
85
86 /* Trickle ICE */
87 pj_bool_t last_cand; /* Got last candidate? */
88 };
89
90 /* Session param */
91 struct sess_param
92 {
93 unsigned worker_cnt;
94 unsigned worker_timeout;
95 pj_bool_t worker_quit;
96
97 pj_bool_t destroy_after_create;
98 pj_bool_t destroy_after_one_done;
99 };
100
101 /* The test session */
102 struct test_sess
103 {
104 pj_pool_t *pool;
105 pj_stun_config *stun_cfg;
106 pj_dns_resolver *resolver;
107
108 struct sess_param *param;
109
110 test_server *server1; /* Test server for IPv4. */
111 test_server *server2; /* Test server for IPv6. */
112
113 pj_thread_t *worker_threads[MAX_THREADS];
114
115 unsigned server_flag;
116 struct ice_ept caller;
117 struct ice_ept callee;
118 };
119
120
121 static void ice_on_rx_data(pj_ice_strans *ice_st,
122 unsigned comp_id,
123 void *pkt, pj_size_t size,
124 const pj_sockaddr_t *src_addr,
125 unsigned src_addr_len);
126 static void ice_on_ice_complete(pj_ice_strans *ice_st,
127 pj_ice_strans_op op,
128 pj_status_t status);
129 static void ice_on_new_candidate(pj_ice_strans *ice_st,
130 const pj_ice_sess_cand *cand,
131 pj_bool_t last);
132
133 static void destroy_sess(struct test_sess *sess, unsigned wait_msec);
134
135 #if USE_IPV6
136
enable_ipv6_test()137 static pj_bool_t enable_ipv6_test()
138 {
139 pj_sockaddr addr;
140 pj_bool_t retval = PJ_TRUE;
141 if (pj_gethostip(pj_AF_INET6(), &addr) == PJ_SUCCESS) {
142 const pj_in6_addr *a = &addr.ipv6.sin6_addr;
143 if (a->s6_addr[0] == 0xFE && (a->s6_addr[1] & 0xC0) == 0x80) {
144 retval = PJ_FALSE;
145 PJ_LOG(3,(THIS_FILE, INDENT "Skipping IPv6 test due to link-local "
146 "address"));
147 }
148 } else {
149 retval = PJ_FALSE;
150 PJ_LOG(3,(THIS_FILE, INDENT "Skipping IPv6 test due to fail getting "
151 "IPv6 address"));
152 }
153 return retval;
154 }
155
156 #endif
157
set_stun_turn_cfg(struct ice_ept * ept,pj_ice_strans_cfg * ice_cfg,char * serverip,pj_bool_t use_ipv6)158 static void set_stun_turn_cfg(struct ice_ept *ept,
159 pj_ice_strans_cfg *ice_cfg,
160 char *serverip,
161 pj_bool_t use_ipv6)
162 {
163 if (ept->cfg.enable_stun & YES) {
164 unsigned stun_idx = ice_cfg->stun_tp_cnt++;
165 pj_ice_strans_stun_cfg_default(&ice_cfg->stun_tp[stun_idx]);
166
167 if ((ept->cfg.enable_stun & SRV) == SRV) {
168 ice_cfg->stun_tp[stun_idx].server = pj_str(SRV_DOMAIN);
169 } else {
170 ice_cfg->stun_tp[stun_idx].server = pj_str(serverip);
171 }
172 ice_cfg->stun_tp[stun_idx].port = STUN_SERVER_PORT;
173
174 ice_cfg->stun_tp[stun_idx].af = GET_AF(use_ipv6);
175 }
176 ice_cfg->stun.af = GET_AF(use_ipv6);
177 if (ept->cfg.enable_host == 0) {
178 ice_cfg->stun.max_host_cands = 0;
179 } else {
180 //ice_cfg.stun.no_host_cands = PJ_FALSE;
181 ice_cfg->stun.loop_addr = PJ_TRUE;
182 }
183
184 if (ept->cfg.enable_turn & YES) {
185 unsigned turn_idx = ice_cfg->turn_tp_cnt++;
186 pj_ice_strans_turn_cfg_default(&ice_cfg->turn_tp[turn_idx]);
187
188 if ((ept->cfg.enable_turn & SRV) == SRV) {
189 ice_cfg->turn_tp[turn_idx].server = pj_str(SRV_DOMAIN);
190 } else {
191 ice_cfg->turn_tp[turn_idx].server = pj_str(serverip);
192 }
193 ice_cfg->turn_tp[turn_idx].port = TURN_SERVER_PORT;
194 ice_cfg->turn_tp[turn_idx].conn_type = PJ_TURN_TP_UDP;
195 ice_cfg->turn_tp[turn_idx].auth_cred.type = PJ_STUN_AUTH_CRED_STATIC;
196 ice_cfg->turn_tp[turn_idx].auth_cred.data.static_cred.realm =
197 pj_str(SRV_DOMAIN);
198 if (ept->cfg.client_flag & WRONG_TURN)
199 ice_cfg->turn_tp[turn_idx].auth_cred.data.static_cred.username =
200 pj_str("xxx");
201 else
202 ice_cfg->turn_tp[turn_idx].auth_cred.data.static_cred.username =
203 pj_str(TURN_USERNAME);
204
205 ice_cfg->turn_tp[turn_idx].auth_cred.data.static_cred.data_type =
206 PJ_STUN_PASSWD_PLAIN;
207 ice_cfg->turn_tp[turn_idx].auth_cred.data.static_cred.data =
208 pj_str(TURN_PASSWD);
209
210 ice_cfg->turn_tp[turn_idx].af = GET_AF(use_ipv6);
211 }
212 }
213
214 /* Create ICE stream transport */
create_ice_strans(struct test_sess * test_sess,struct ice_ept * ept,pj_ice_strans ** p_ice)215 static int create_ice_strans(struct test_sess *test_sess,
216 struct ice_ept *ept,
217 pj_ice_strans **p_ice)
218 {
219 pj_ice_strans *ice;
220 pj_ice_strans_cb ice_cb;
221 pj_ice_strans_cfg ice_cfg;
222 pj_sockaddr hostip;
223 char serveripv4[PJ_INET6_ADDRSTRLEN];
224 char serveripv6[PJ_INET6_ADDRSTRLEN];
225 pj_status_t status;
226 unsigned flag = (ept->cfg.client_flag)?ept->cfg.client_flag:CLIENT_IPV4;
227
228 status = pj_gethostip(pj_AF_INET(), &hostip);
229 if (status != PJ_SUCCESS)
230 return -1030;
231
232 pj_sockaddr_print(&hostip, serveripv4, sizeof(serveripv4), 0);
233
234 if (flag & CLIENT_IPV6) {
235 status = pj_gethostip(pj_AF_INET6(), &hostip);
236 if (status != PJ_SUCCESS)
237 return -1031;
238
239 pj_sockaddr_print(&hostip, serveripv6, sizeof(serveripv6), 0);
240 }
241
242 /* Init callback structure */
243 pj_bzero(&ice_cb, sizeof(ice_cb));
244 ice_cb.on_rx_data = &ice_on_rx_data;
245 ice_cb.on_ice_complete = &ice_on_ice_complete;
246 ice_cb.on_new_candidate = &ice_on_new_candidate;
247
248 /* Init ICE stream transport configuration structure */
249 pj_ice_strans_cfg_default(&ice_cfg);
250 ice_cfg.opt.trickle = ept->cfg.trickle;
251 pj_memcpy(&ice_cfg.stun_cfg, test_sess->stun_cfg, sizeof(pj_stun_config));
252 if ((ept->cfg.enable_stun & SRV)==SRV || (ept->cfg.enable_turn & SRV)==SRV)
253 ice_cfg.resolver = test_sess->resolver;
254
255 if (flag & CLIENT_IPV4) {
256 set_stun_turn_cfg(ept, &ice_cfg, serveripv4, PJ_FALSE);
257 }
258
259 if (flag & CLIENT_IPV6) {
260 set_stun_turn_cfg(ept, &ice_cfg, serveripv6, PJ_TRUE);
261 }
262
263 /* Create ICE stream transport */
264 status = pj_ice_strans_create(NULL, &ice_cfg, ept->cfg.comp_cnt,
265 (void*)ept, &ice_cb,
266 &ice);
267 if (status != PJ_SUCCESS) {
268 app_perror(INDENT "err: pj_ice_strans_create()", status);
269 return status;
270 }
271
272 pj_create_unique_string(test_sess->pool, &ept->ufrag);
273 pj_create_unique_string(test_sess->pool, &ept->pass);
274
275 /* Looks alright */
276 *p_ice = ice;
277 return PJ_SUCCESS;
278 }
279
280 /* Create test session */
create_sess(pj_stun_config * stun_cfg,unsigned server_flag,struct test_cfg * caller_cfg,struct test_cfg * callee_cfg,struct sess_param * test_param,struct test_sess ** p_sess)281 static int create_sess(pj_stun_config *stun_cfg,
282 unsigned server_flag,
283 struct test_cfg *caller_cfg,
284 struct test_cfg *callee_cfg,
285 struct sess_param *test_param,
286 struct test_sess **p_sess)
287 {
288 pj_pool_t *pool;
289 struct test_sess *sess;
290 pj_str_t ns_ip;
291 pj_uint16_t ns_port;
292 unsigned flags;
293 pj_status_t status = PJ_SUCCESS;
294
295 /* Create session structure */
296 pool = pj_pool_create(mem, "testsess", 512, 512, NULL);
297 sess = PJ_POOL_ZALLOC_T(pool, struct test_sess);
298 sess->pool = pool;
299 sess->stun_cfg = stun_cfg;
300 sess->param = test_param;
301
302 pj_memcpy(&sess->caller.cfg, caller_cfg, sizeof(*caller_cfg));
303 sess->caller.result.init_status = sess->caller.result.nego_status = PJ_EPENDING;
304
305 pj_memcpy(&sess->callee.cfg, callee_cfg, sizeof(*callee_cfg));
306 sess->callee.result.init_status = sess->callee.result.nego_status = PJ_EPENDING;
307
308 /* Create server */
309 flags = server_flag;
310 if (flags & SERVER_IPV4) {
311 status = create_test_server(stun_cfg, (flags & ~SERVER_IPV6),
312 SRV_DOMAIN, &sess->server1);
313 }
314
315 if ((status == PJ_SUCCESS) && (flags & SERVER_IPV6)) {
316 status = create_test_server(stun_cfg, (flags & ~SERVER_IPV4),
317 SRV_DOMAIN, &sess->server2);
318 }
319
320 if (status != PJ_SUCCESS) {
321 app_perror(INDENT "error: create_test_server()", status);
322 destroy_sess(sess, 500);
323 return -10;
324 }
325 if (flags & SERVER_IPV4) {
326 sess->server1->turn_respond_allocate =
327 sess->server1->turn_respond_refresh = PJ_TRUE;
328 }
329
330 if (flags & SERVER_IPV6) {
331 sess->server2->turn_respond_allocate =
332 sess->server2->turn_respond_refresh = PJ_TRUE;
333 }
334
335 /* Create resolver */
336 if ((sess->callee.cfg.enable_stun & SRV)==SRV ||
337 (sess->callee.cfg.enable_turn & SRV)==SRV ||
338 (sess->caller.cfg.enable_stun & SRV)==SRV ||
339 (sess->caller.cfg.enable_turn & SRV)==SRV)
340 {
341 status = pj_dns_resolver_create(mem, NULL, 0, stun_cfg->timer_heap,
342 stun_cfg->ioqueue, &sess->resolver);
343 if (status != PJ_SUCCESS) {
344 app_perror(INDENT "error: pj_dns_resolver_create()", status);
345 destroy_sess(sess, 500);
346 return -20;
347 }
348
349 ns_ip = (flags & SERVER_IPV6)?pj_str("::1"):pj_str("127.0.0.1");
350 ns_port = (pj_uint16_t)DNS_SERVER_PORT;
351 status = pj_dns_resolver_set_ns(sess->resolver, 1, &ns_ip, &ns_port);
352 if (status != PJ_SUCCESS) {
353 app_perror(INDENT "error: pj_dns_resolver_set_ns()", status);
354 destroy_sess(sess, 500);
355 return -21;
356 }
357 }
358
359 /* Create caller ICE stream transport */
360 status = create_ice_strans(sess, &sess->caller, &sess->caller.ice);
361 if (status != PJ_SUCCESS) {
362 destroy_sess(sess, 500);
363 return -30;
364 }
365
366 /* Create callee ICE stream transport */
367 status = create_ice_strans(sess, &sess->callee, &sess->callee.ice);
368 if (status != PJ_SUCCESS) {
369 destroy_sess(sess, 500);
370 return -40;
371 }
372
373 *p_sess = sess;
374 return 0;
375 }
376
377 /* Destroy test session */
destroy_sess(struct test_sess * sess,unsigned wait_msec)378 static void destroy_sess(struct test_sess *sess, unsigned wait_msec)
379 {
380 unsigned i;
381
382 if (sess->caller.ice) {
383 pj_ice_strans_destroy(sess->caller.ice);
384 sess->caller.ice = NULL;
385 }
386
387 if (sess->callee.ice) {
388 pj_ice_strans_destroy(sess->callee.ice);
389 sess->callee.ice = NULL;
390 }
391
392 sess->param->worker_quit = PJ_TRUE;
393 for (i=0; i<sess->param->worker_cnt; ++i) {
394 if (sess->worker_threads[i])
395 pj_thread_join(sess->worker_threads[i]);
396 }
397
398 poll_events(sess->stun_cfg, wait_msec, PJ_FALSE);
399
400 if (sess->resolver) {
401 pj_dns_resolver_destroy(sess->resolver, PJ_FALSE);
402 sess->resolver = NULL;
403 }
404
405 if (sess->server1) {
406 destroy_test_server(sess->server1);
407 sess->server1 = NULL;
408 }
409
410 if (sess->server2) {
411 destroy_test_server(sess->server2);
412 sess->server2 = NULL;
413 }
414
415 pj_pool_safe_release(&sess->pool);
416 }
417
ice_on_rx_data(pj_ice_strans * ice_st,unsigned comp_id,void * pkt,pj_size_t size,const pj_sockaddr_t * src_addr,unsigned src_addr_len)418 static void ice_on_rx_data(pj_ice_strans *ice_st,
419 unsigned comp_id,
420 void *pkt, pj_size_t size,
421 const pj_sockaddr_t *src_addr,
422 unsigned src_addr_len)
423 {
424 struct ice_ept *ept;
425
426 PJ_UNUSED_ARG(pkt);
427 PJ_UNUSED_ARG(size);
428 PJ_UNUSED_ARG(src_addr);
429 PJ_UNUSED_ARG(src_addr_len);
430
431 ept = (struct ice_ept*) pj_ice_strans_get_user_data(ice_st);
432 ept->result.rx_cnt[comp_id]++;
433 }
434
435
ice_on_ice_complete(pj_ice_strans * ice_st,pj_ice_strans_op op,pj_status_t status)436 static void ice_on_ice_complete(pj_ice_strans *ice_st,
437 pj_ice_strans_op op,
438 pj_status_t status)
439 {
440 struct ice_ept *ept;
441
442 ept = (struct ice_ept*) pj_ice_strans_get_user_data(ice_st);
443 if (!ept)
444 return;
445
446 switch (op) {
447 case PJ_ICE_STRANS_OP_INIT:
448 ept->result.init_status = status;
449 if (status != PJ_SUCCESS && (ept->cfg.client_flag & DEL_ON_ERR)) {
450 pj_ice_strans_destroy(ice_st);
451 ept->ice = NULL;
452 }
453 break;
454 case PJ_ICE_STRANS_OP_NEGOTIATION:
455 ept->result.nego_status = status;
456 break;
457 case PJ_ICE_STRANS_OP_KEEP_ALIVE:
458 /* keep alive failed? */
459 break;
460 default:
461 pj_assert(!"Unknown op");
462 }
463 }
464
ice_on_new_candidate(pj_ice_strans * ice_st,const pj_ice_sess_cand * cand,pj_bool_t last)465 static void ice_on_new_candidate(pj_ice_strans *ice_st,
466 const pj_ice_sess_cand *cand,
467 pj_bool_t last)
468 {
469 struct ice_ept *ept;
470 char buf1[PJ_INET6_ADDRSTRLEN+10];
471 char buf2[PJ_INET6_ADDRSTRLEN+10];
472
473 ept = (struct ice_ept*) pj_ice_strans_get_user_data(ice_st);
474 if (!ept)
475 return;
476
477 ept->last_cand = last;
478
479 if (cand) {
480 PJ_LOG(4,(THIS_FILE, INDENT "%p: discovered a new candidate "
481 "comp=%d, type=%s, addr=%s, baseaddr=%s, end=%d",
482 ept->ice, cand->comp_id,
483 pj_ice_get_cand_type_name(cand->type),
484 pj_sockaddr_print(&cand->addr, buf1, sizeof(buf1), 3),
485 pj_sockaddr_print(&cand->base_addr, buf2, sizeof(buf2), 3),
486 last));
487 } else if (ept->ice && last) {
488 PJ_LOG(4,(THIS_FILE, INDENT "%p: end of candidate", ept->ice));
489 }
490 }
491
492 /* Start ICE negotiation on the endpoint, based on parameter from
493 * the other endpoint.
494 */
start_ice(struct ice_ept * ept,const struct ice_ept * remote)495 static pj_status_t start_ice(struct ice_ept *ept, const struct ice_ept *remote)
496 {
497 pj_ice_sess_cand rcand[32];
498 unsigned rcand_cnt = 0;
499 pj_status_t status;
500
501 /* Enum remote candidates */
502 if (ept->cfg.trickle == PJ_ICE_SESS_TRICKLE_DISABLED) {
503 unsigned i;
504 for (i=0; i<remote->cfg.comp_cnt; ++i) {
505 unsigned cnt = PJ_ARRAY_SIZE(rcand) - rcand_cnt;
506 status = pj_ice_strans_enum_cands(remote->ice, i+1, &cnt, rcand+rcand_cnt);
507 if (status != PJ_SUCCESS) {
508 app_perror(INDENT "err: pj_ice_strans_enum_cands()", status);
509 return status;
510 }
511 rcand_cnt += cnt;
512 }
513 }
514
515 status = pj_ice_strans_start_ice(ept->ice, &remote->ufrag, &remote->pass,
516 rcand_cnt, rcand);
517
518 if (status != ept->cfg.expected.start_status) {
519 app_perror(INDENT "err: pj_ice_strans_start_ice()", status);
520 return status;
521 }
522
523 return status;
524 }
525
526
527 /* Check that the pair in both agents are matched */
check_pair(const struct ice_ept * ept1,const struct ice_ept * ept2,int start_err)528 static int check_pair(const struct ice_ept *ept1, const struct ice_ept *ept2,
529 int start_err)
530 {
531 unsigned i, min_cnt, max_cnt;
532
533 if (ept1->cfg.comp_cnt < ept2->cfg.comp_cnt) {
534 min_cnt = ept1->cfg.comp_cnt;
535 max_cnt = ept2->cfg.comp_cnt;
536 } else {
537 min_cnt = ept2->cfg.comp_cnt;
538 max_cnt = ept1->cfg.comp_cnt;
539 }
540
541 /* Must have valid pair for common components */
542 for (i=0; i<min_cnt; ++i) {
543 const pj_ice_sess_check *c1;
544 const pj_ice_sess_check *c2;
545
546 c1 = pj_ice_strans_get_valid_pair(ept1->ice, i+1);
547 if (c1 == NULL) {
548 PJ_LOG(3,(THIS_FILE, INDENT "err: unable to get valid pair for ice1 "
549 "component %d", i+1));
550 return start_err - 2;
551 }
552
553 c2 = pj_ice_strans_get_valid_pair(ept2->ice, i+1);
554 if (c2 == NULL) {
555 PJ_LOG(3,(THIS_FILE, INDENT "err: unable to get valid pair for ice2 "
556 "component %d", i+1));
557 return start_err - 4;
558 }
559
560 if (pj_sockaddr_cmp(&c1->rcand->addr, &c2->lcand->addr) != 0) {
561 PJ_LOG(3,(THIS_FILE, INDENT "err: candidate pair does not match "
562 "for component %d", i+1));
563 return start_err - 6;
564 }
565 }
566
567 /* Extra components must not have valid pair */
568 for (; i<max_cnt; ++i) {
569 if (ept1->cfg.comp_cnt>i &&
570 pj_ice_strans_get_valid_pair(ept1->ice, i+1) != NULL)
571 {
572 PJ_LOG(3,(THIS_FILE, INDENT "err: ice1 shouldn't have valid pair "
573 "for component %d", i+1));
574 return start_err - 8;
575 }
576 if (ept2->cfg.comp_cnt>i &&
577 pj_ice_strans_get_valid_pair(ept2->ice, i+1) != NULL)
578 {
579 PJ_LOG(3,(THIS_FILE, INDENT "err: ice2 shouldn't have valid pair "
580 "for component %d", i+1));
581 return start_err - 9;
582 }
583 }
584
585 return 0;
586 }
587
588
589 #define WAIT_UNTIL(timeout,expr, RC) { \
590 pj_time_val t0, t; \
591 pj_gettimeofday(&t0); \
592 RC = -1; \
593 for (;;) { \
594 poll_events(stun_cfg, 10, PJ_FALSE); \
595 pj_gettimeofday(&t); \
596 if (expr) { \
597 RC = PJ_SUCCESS; \
598 break; \
599 } \
600 PJ_TIME_VAL_SUB(t, t0); \
601 if ((unsigned)PJ_TIME_VAL_MSEC(t) >= (timeout)) \
602 break; \
603 } \
604 }
605
worker_thread_proc(void * data)606 int worker_thread_proc(void *data)
607 {
608 pj_status_t rc;
609 struct test_sess *sess = (struct test_sess *) data;
610 pj_stun_config *stun_cfg = sess->stun_cfg;
611
612 /* Wait until negotiation is complete on both endpoints */
613 #define ALL_DONE (sess->param->worker_quit || \
614 (sess->caller.result.nego_status!=PJ_EPENDING && \
615 sess->callee.result.nego_status!=PJ_EPENDING))
616 WAIT_UNTIL(sess->param->worker_timeout, ALL_DONE, rc);
617 PJ_UNUSED_ARG(rc);
618 return 0;
619 }
620
perform_test2(const char * title,pj_stun_config * stun_cfg,unsigned server_flag,struct test_cfg * caller_cfg,struct test_cfg * callee_cfg,struct sess_param * test_param)621 static int perform_test2(const char *title,
622 pj_stun_config *stun_cfg,
623 unsigned server_flag,
624 struct test_cfg *caller_cfg,
625 struct test_cfg *callee_cfg,
626 struct sess_param *test_param)
627 {
628 pjlib_state pjlib_state;
629 struct test_sess *sess;
630 unsigned i;
631 int rc;
632 char add_title1[16];
633 char add_title2[16];
634 pj_bool_t client_mix_test = ((callee_cfg->client_flag &
635 (CLIENT_IPV4+CLIENT_IPV6)) !=
636 (caller_cfg->client_flag &
637 (CLIENT_IPV4+CLIENT_IPV6)));
638
639 sprintf(add_title1, "%s%s%s", (server_flag & SERVER_IPV4)?"IPv4":"",
640 ((server_flag & SERVER_IPV4)&&(server_flag & SERVER_IPV6))?"+":"",
641 (server_flag & SERVER_IPV6)?"IPv6":"");
642
643 sprintf(add_title2, "%s", client_mix_test?"Mix test":"");
644
645 PJ_LOG(3,(THIS_FILE, INDENT "%s (%s) %s", title, add_title1, add_title2));
646
647 capture_pjlib_state(stun_cfg, &pjlib_state);
648
649 rc = create_sess(stun_cfg, server_flag, caller_cfg, callee_cfg, test_param,
650 &sess);
651 if (rc != 0)
652 return rc;
653
654 #define ALL_READY (sess->caller.result.init_status!=PJ_EPENDING && \
655 sess->callee.result.init_status!=PJ_EPENDING)
656
657 /* Wait until both ICE transports are initialized */
658 WAIT_UNTIL(30000, ALL_READY, rc);
659
660 if (!ALL_READY) {
661 PJ_LOG(3,(THIS_FILE, INDENT "err: init timed-out"));
662 destroy_sess(sess, 500);
663 return -100;
664 }
665
666 if (sess->caller.result.init_status != sess->caller.cfg.expected.init_status) {
667 app_perror(INDENT "err: caller init", sess->caller.result.init_status);
668 destroy_sess(sess, 500);
669 return -102;
670 }
671 if (sess->callee.result.init_status != sess->callee.cfg.expected.init_status) {
672 app_perror(INDENT "err: callee init", sess->callee.result.init_status);
673 destroy_sess(sess, 500);
674 return -104;
675 }
676
677 /* Failure condition */
678 if (sess->caller.result.init_status != PJ_SUCCESS ||
679 sess->callee.result.init_status != PJ_SUCCESS)
680 {
681 rc = 0;
682 goto on_return;
683 }
684 /* Init ICE on caller */
685 rc = pj_ice_strans_init_ice(sess->caller.ice, sess->caller.cfg.role,
686 &sess->caller.ufrag, &sess->caller.pass);
687 if (rc != PJ_SUCCESS) {
688 app_perror(INDENT "err: caller pj_ice_strans_init_ice()", rc);
689 destroy_sess(sess, 500);
690 return -100;
691 }
692
693 /* Init ICE on callee */
694 rc = pj_ice_strans_init_ice(sess->callee.ice, sess->callee.cfg.role,
695 &sess->callee.ufrag, &sess->callee.pass);
696 if (rc != PJ_SUCCESS) {
697 app_perror(INDENT "err: callee pj_ice_strans_init_ice()", rc);
698 destroy_sess(sess, 500);
699 return -110;
700 }
701 /* Start ICE on callee */
702 rc = start_ice(&sess->callee, &sess->caller);
703 if (rc != PJ_SUCCESS) {
704 int retval = (rc == sess->callee.cfg.expected.start_status)?0:-120;
705 destroy_sess(sess, 500);
706 return retval;
707 }
708 /* Wait for callee's answer_delay */
709 poll_events(stun_cfg, sess->callee.cfg.answer_delay, PJ_FALSE);
710 /* Start ICE on caller */
711 rc = start_ice(&sess->caller, &sess->callee);
712 if (rc != PJ_SUCCESS) {
713 int retval = (rc == sess->caller.cfg.expected.start_status)?0:-130;
714 destroy_sess(sess, 500);
715 return retval;
716 }
717
718 for (i=0; i<sess->param->worker_cnt; ++i) {
719 pj_status_t status;
720
721 status = pj_thread_create(sess->pool, "worker_thread",
722 worker_thread_proc, sess, 0, 0,
723 &sess->worker_threads[i]);
724 if (status != PJ_SUCCESS) {
725 PJ_LOG(3,(THIS_FILE, INDENT "err: create thread"));
726 destroy_sess(sess, 500);
727 return -135;
728 }
729 }
730
731 if (sess->param->destroy_after_create)
732 goto on_destroy;
733
734 if (sess->param->destroy_after_one_done) {
735 while (sess->caller.result.init_status==PJ_EPENDING &&
736 sess->callee.result.init_status==PJ_EPENDING)
737 {
738 if (sess->param->worker_cnt)
739 pj_thread_sleep(0);
740 else
741 poll_events(stun_cfg, 0, PJ_FALSE);
742 }
743 goto on_destroy;
744 }
745
746 WAIT_UNTIL(30000, ALL_DONE, rc);
747 if (!ALL_DONE) {
748 PJ_LOG(3,(THIS_FILE, INDENT "err: negotiation timed-out"));
749 destroy_sess(sess, 500);
750 return -140;
751 }
752
753 if (sess->caller.result.nego_status != sess->caller.cfg.expected.nego_status) {
754 app_perror(INDENT "err: caller negotiation failed", sess->caller.result.nego_status);
755 destroy_sess(sess, 500);
756 return -150;
757 }
758
759 if (sess->callee.result.nego_status != sess->callee.cfg.expected.nego_status) {
760 app_perror(INDENT "err: callee negotiation failed", sess->callee.result.nego_status);
761 destroy_sess(sess, 500);
762 return -160;
763 }
764
765 /* Verify that both agents have agreed on the same pair */
766 rc = check_pair(&sess->caller, &sess->callee, -170);
767 if (rc != 0) {
768 destroy_sess(sess, 500);
769 return rc;
770 }
771 rc = check_pair(&sess->callee, &sess->caller, -180);
772 if (rc != 0) {
773 destroy_sess(sess, 500);
774 return rc;
775 }
776
777 /* Looks like everything is okay */
778 on_destroy:
779
780 /* Destroy ICE stream transports first to let it de-allocate
781 * TURN relay (otherwise there'll be timer/memory leak, unless
782 * we wait for long time in the last poll_events() below).
783 */
784 if (sess->caller.ice) {
785 pj_ice_strans_destroy(sess->caller.ice);
786 sess->caller.ice = NULL;
787 }
788
789 if (sess->callee.ice) {
790 pj_ice_strans_destroy(sess->callee.ice);
791 sess->callee.ice = NULL;
792 }
793
794 on_return:
795 /* Wait.. */
796 poll_events(stun_cfg, 200, PJ_FALSE);
797
798 /* Now destroy everything */
799 destroy_sess(sess, 500);
800
801 /* Flush events */
802 poll_events(stun_cfg, 100, PJ_FALSE);
803
804 rc = check_pjlib_state(stun_cfg, &pjlib_state);
805 if (rc != 0) {
806 return rc;
807 }
808
809 return rc;
810 }
811
set_client_server_flag(unsigned server_flag,unsigned caller_flag,unsigned callee_flag,unsigned * res_server_flag,unsigned * res_caller_flag,unsigned * res_callee_flag)812 static void set_client_server_flag(unsigned server_flag,
813 unsigned caller_flag,
814 unsigned callee_flag,
815 unsigned *res_server_flag,
816 unsigned *res_caller_flag,
817 unsigned *res_callee_flag)
818 {
819 enum {
820 RST_CLT_FLAG = CLIENT_IPV4+CLIENT_IPV6,
821 RST_SRV_FLAG = SERVER_IPV4+SERVER_IPV6
822 };
823
824 *res_server_flag = (*res_server_flag & ~RST_SRV_FLAG) | server_flag;
825 *res_caller_flag = (*res_caller_flag & ~RST_CLT_FLAG) | caller_flag;
826 *res_callee_flag = (*res_callee_flag & ~RST_CLT_FLAG) | callee_flag;
827 }
828
perform_test(const char * title,pj_stun_config * stun_cfg,unsigned server_flag,struct test_cfg * caller_cfg,struct test_cfg * callee_cfg)829 static int perform_test(const char *title,
830 pj_stun_config *stun_cfg,
831 unsigned server_flag,
832 struct test_cfg *caller_cfg,
833 struct test_cfg *callee_cfg)
834 {
835 struct sess_param test_param;
836 int rc;
837 int expected_caller_start_ice = caller_cfg->expected.start_status;
838 int expected_callee_start_ice = callee_cfg->expected.start_status;
839
840 set_client_server_flag(SERVER_IPV4, CLIENT_IPV4, CLIENT_IPV4,
841 &server_flag, &caller_cfg->client_flag,
842 &callee_cfg->client_flag);
843
844
845 pj_bzero(&test_param, sizeof(test_param));
846
847 rc = perform_test2(title, stun_cfg, server_flag, caller_cfg,
848 callee_cfg, &test_param);
849
850 #if USE_IPV6
851 if (enable_ipv6_test()) {
852
853 /* Test for IPV6. */
854 if (rc == PJ_SUCCESS) {
855 pj_bzero(&test_param, sizeof(test_param));
856 set_client_server_flag(SERVER_IPV6, CLIENT_IPV6, CLIENT_IPV6,
857 &server_flag, &caller_cfg->client_flag,
858 &callee_cfg->client_flag);
859
860 rc = perform_test2(title, stun_cfg, server_flag, caller_cfg,
861 callee_cfg, &test_param);
862 }
863
864 /* Test for IPV4+IPV6. */
865 if (rc == PJ_SUCCESS) {
866 pj_bzero(&test_param, sizeof(test_param));
867 set_client_server_flag(SERVER_IPV4+SERVER_IPV6,
868 CLIENT_IPV4+CLIENT_IPV6,
869 CLIENT_IPV4+CLIENT_IPV6,
870 &server_flag,
871 &caller_cfg->client_flag,
872 &callee_cfg->client_flag);
873
874 rc = perform_test2(title, stun_cfg, server_flag, caller_cfg,
875 callee_cfg, &test_param);
876 }
877
878 /* Test controller(IPV4) vs controlled(IPV6). */
879 if (rc == PJ_SUCCESS) {
880 pj_bzero(&test_param, sizeof(test_param));
881 set_client_server_flag(SERVER_IPV4+SERVER_IPV6,
882 CLIENT_IPV4,
883 CLIENT_IPV6,
884 &server_flag,
885 &caller_cfg->client_flag,
886 &callee_cfg->client_flag);
887 caller_cfg->expected.start_status = PJ_ENOTFOUND;
888 callee_cfg->expected.start_status = PJ_ENOTFOUND;
889
890 rc = perform_test2(title, stun_cfg, server_flag, caller_cfg,
891 callee_cfg, &test_param);
892 }
893 }
894 #endif
895 callee_cfg->expected.start_status = expected_callee_start_ice;
896 caller_cfg->expected.start_status = expected_caller_start_ice;
897
898 return rc;
899 }
900
901 #define ROLE1 PJ_ICE_SESS_ROLE_CONTROLLED
902 #define ROLE2 PJ_ICE_SESS_ROLE_CONTROLLING
903
ice_test(void)904 int ice_test(void)
905 {
906 pj_pool_t *pool;
907 pj_stun_config stun_cfg;
908 unsigned i;
909 int rc;
910 struct sess_cfg_t {
911 const char *title;
912 unsigned server_flag;
913 struct test_cfg ua1;
914 struct test_cfg ua2;
915 } sess_cfg[] =
916 {
917 /* Role comp# host? stun? turn? flag? ans_del snd_del des_del */
918 {
919 "hosts candidates only",
920 0x1FFF,
921 {ROLE1, 1, YES, NO, NO, NO, 0, 0, 0, {PJ_SUCCESS, PJ_SUCCESS, PJ_SUCCESS}},
922 {ROLE2, 1, YES, NO, NO, NO, 0, 0, 0, {PJ_SUCCESS, PJ_SUCCESS, PJ_SUCCESS}}
923 },
924 {
925 "host and srflxes",
926 0x1FFF,
927 {ROLE1, 1, YES, YES, NO, NO, 0, 0, 0, {PJ_SUCCESS, PJ_SUCCESS, PJ_SUCCESS}},
928 {ROLE2, 1, YES, YES, NO, NO, 0, 0, 0, {PJ_SUCCESS, PJ_SUCCESS, PJ_SUCCESS}}
929 },
930 {
931 "host vs relay",
932 0x1FFF,
933 {ROLE1, 1, YES, NO, NO, NO, 0, 0, 0, {PJ_SUCCESS, PJ_SUCCESS, PJ_SUCCESS}},
934 {ROLE2, 1, NO, NO, YES, NO, 0, 0, 0, {PJ_SUCCESS, PJ_SUCCESS, PJ_SUCCESS}}
935 },
936 {
937 "relay vs host",
938 0x1FFF,
939 {ROLE1, 1, NO, NO, YES, NO, 0, 0, 0, {PJ_SUCCESS, PJ_SUCCESS, PJ_SUCCESS}},
940 {ROLE2, 1, YES, NO, NO, NO, 0, 0, 0, {PJ_SUCCESS, PJ_SUCCESS, PJ_SUCCESS}}
941 },
942 {
943 "relay vs relay",
944 0x1FFF,
945 {ROLE1, 1, NO, NO, YES, NO, 0, 0, 0, {PJ_SUCCESS, PJ_SUCCESS, PJ_SUCCESS}},
946 {ROLE2, 1, NO, NO, YES, NO, 0, 0, 0, {PJ_SUCCESS, PJ_SUCCESS, PJ_SUCCESS}}
947 },
948 {
949 "all candidates",
950 0x1FFF,
951 {ROLE1, 1, YES, YES, YES, NO, 0, 0, 0, {PJ_SUCCESS, PJ_SUCCESS, PJ_SUCCESS}},
952 {ROLE2, 1, YES, YES, YES, NO, 0, 0, 0, {PJ_SUCCESS, PJ_SUCCESS, PJ_SUCCESS}}
953 },
954 };
955
956 pool = pj_pool_create(mem, NULL, 512, 512, NULL);
957 rc = create_stun_config(pool, &stun_cfg);
958 if (rc != PJ_SUCCESS) {
959 pj_pool_release(pool);
960 return -7;
961 }
962
963 /* Simple test first with host candidate */
964 if (1) {
965 struct sess_cfg_t cfg =
966 {
967 "Basic with host candidates",
968 0x0,
969 /* Role comp# host? stun? turn? flag? ans_del snd_del des_del */
970 {ROLE1, 1, YES, NO, NO, 0, 0, 0, 0, {PJ_SUCCESS, PJ_SUCCESS, PJ_SUCCESS}},
971 {ROLE2, 1, YES, NO, NO, 0, 0, 0, 0, {PJ_SUCCESS, PJ_SUCCESS, PJ_SUCCESS}}
972 };
973
974 rc = perform_test(cfg.title, &stun_cfg, cfg.server_flag,
975 &cfg.ua1, &cfg.ua2);
976 if (rc != 0)
977 goto on_return;
978
979 cfg.ua1.comp_cnt = 2;
980 cfg.ua2.comp_cnt = 2;
981 rc = perform_test("Basic with host candidates, 2 components",
982 &stun_cfg, cfg.server_flag,
983 &cfg.ua1, &cfg.ua2);
984 if (rc != 0)
985 goto on_return;
986 }
987
988 /* Simple test first with srflx candidate */
989 if (1) {
990 struct sess_cfg_t cfg =
991 {
992 "Basic with srflx candidates",
993 0xFFFF,
994 /* Role comp# host? stun? turn? flag? ans_del snd_del des_del */
995 {ROLE1, 1, YES, YES, NO, 0, 0, 0, 0, {PJ_SUCCESS, PJ_SUCCESS, PJ_SUCCESS}},
996 {ROLE2, 1, YES, YES, NO, 0, 0, 0, 0, {PJ_SUCCESS, PJ_SUCCESS, PJ_SUCCESS}}
997 };
998
999 rc = perform_test(cfg.title, &stun_cfg, cfg.server_flag,
1000 &cfg.ua1, &cfg.ua2);
1001 if (rc != 0)
1002 goto on_return;
1003
1004 cfg.ua1.comp_cnt = 2;
1005 cfg.ua2.comp_cnt = 2;
1006
1007 rc = perform_test("Basic with srflx candidates, 2 components",
1008 &stun_cfg, cfg.server_flag,
1009 &cfg.ua1, &cfg.ua2);
1010 if (rc != 0)
1011 goto on_return;
1012 }
1013
1014 /* Simple test with relay candidate */
1015 if (1) {
1016 struct sess_cfg_t cfg =
1017 {
1018 "Basic with relay candidates",
1019 0xFFFF,
1020 /* Role comp# host? stun? turn? flag? ans_del snd_del des_del */
1021 {ROLE1, 1, NO, NO, YES, 0, 0, 0, 0, {PJ_SUCCESS, PJ_SUCCESS, PJ_SUCCESS}},
1022 {ROLE2, 1, NO, NO, YES, 0, 0, 0, 0, {PJ_SUCCESS, PJ_SUCCESS, PJ_SUCCESS}}
1023 };
1024
1025 rc = perform_test(cfg.title, &stun_cfg, cfg.server_flag,
1026 &cfg.ua1, &cfg.ua2);
1027 if (rc != 0)
1028 goto on_return;
1029
1030 cfg.ua1.comp_cnt = 2;
1031 cfg.ua2.comp_cnt = 2;
1032
1033 rc = perform_test("Basic with relay candidates, 2 components",
1034 &stun_cfg, cfg.server_flag,
1035 &cfg.ua1, &cfg.ua2);
1036 if (rc != 0)
1037 goto on_return;
1038 }
1039
1040 /* Failure test with STUN resolution */
1041 if (1) {
1042 struct sess_cfg_t cfg =
1043 {
1044 "STUN resolution failure",
1045 0x0,
1046 /* Role comp# host? stun? turn? flag? ans_del snd_del des_del */
1047 {ROLE1, 2, NO, YES, NO, 0, 0, 0, 0, {PJ_SUCCESS, PJNATH_ESTUNTIMEDOUT, -1}},
1048 {ROLE2, 2, NO, YES, NO, 0, 0, 0, 0, {PJ_SUCCESS, PJNATH_ESTUNTIMEDOUT, -1}}
1049 };
1050
1051 rc = perform_test(cfg.title, &stun_cfg, cfg.server_flag,
1052 &cfg.ua1, &cfg.ua2);
1053 if (rc != 0)
1054 goto on_return;
1055
1056 cfg.ua1.client_flag |= DEL_ON_ERR;
1057 cfg.ua2.client_flag |= DEL_ON_ERR;
1058
1059 rc = perform_test("STUN resolution failure with destroy on callback",
1060 &stun_cfg, cfg.server_flag,
1061 &cfg.ua1, &cfg.ua2);
1062 if (rc != 0)
1063 goto on_return;
1064 }
1065
1066 /* Failure test with TURN resolution */
1067 if (1) {
1068 struct sess_cfg_t cfg =
1069 {
1070 "TURN allocation failure",
1071 0xFFFF,
1072 /* Role comp# host? stun? turn? flag? ans_del snd_del des_del */
1073 {ROLE1, 2, NO, NO, YES, WRONG_TURN, 0, 0, 0, {PJ_SUCCESS, PJ_STATUS_FROM_STUN_CODE(401), -1}},
1074 {ROLE2, 2, NO, NO, YES, WRONG_TURN, 0, 0, 0, {PJ_SUCCESS, PJ_STATUS_FROM_STUN_CODE(401), -1}}
1075 };
1076
1077 rc = perform_test(cfg.title, &stun_cfg, cfg.server_flag,
1078 &cfg.ua1, &cfg.ua2);
1079 if (rc != 0)
1080 goto on_return;
1081
1082 cfg.ua1.client_flag |= DEL_ON_ERR;
1083 cfg.ua2.client_flag |= DEL_ON_ERR;
1084
1085 rc = perform_test("TURN allocation failure with destroy on callback",
1086 &stun_cfg, cfg.server_flag,
1087 &cfg.ua1, &cfg.ua2);
1088 if (rc != 0)
1089 goto on_return;
1090 }
1091
1092
1093 /* STUN failure, testing TURN deallocation */
1094 if (1) {
1095 struct sess_cfg_t cfg =
1096 {
1097 "STUN failure, testing TURN deallocation",
1098 0xFFFF & (~(CREATE_STUN_SERVER)),
1099 /* Role comp# host? stun? turn? flag? ans_del snd_del des_del */
1100 {ROLE1, 1, YES, YES, YES, 0, 0, 0, 0, {PJ_SUCCESS, PJNATH_ESTUNTIMEDOUT, -1}},
1101 {ROLE2, 1, YES, YES, YES, 0, 0, 0, 0, {PJ_SUCCESS, PJNATH_ESTUNTIMEDOUT, -1}}
1102 };
1103
1104 rc = perform_test(cfg.title, &stun_cfg, cfg.server_flag,
1105 &cfg.ua1, &cfg.ua2);
1106 if (rc != 0)
1107 goto on_return;
1108
1109 cfg.ua1.client_flag |= DEL_ON_ERR;
1110 cfg.ua2.client_flag |= DEL_ON_ERR;
1111
1112 rc = perform_test("STUN failure, testing TURN deallocation (cb)",
1113 &stun_cfg, cfg.server_flag,
1114 &cfg.ua1, &cfg.ua2);
1115 if (rc != 0)
1116 goto on_return;
1117 }
1118
1119 rc = 0;
1120 /* Iterate each test item */
1121 for (i=0; i<PJ_ARRAY_SIZE(sess_cfg); ++i) {
1122 struct sess_cfg_t *cfg = &sess_cfg[i];
1123 unsigned delay[] = { 50, 2000 };
1124 unsigned d;
1125
1126 PJ_LOG(3,(THIS_FILE, " %s", cfg->title));
1127
1128 /* For each test item, test with various answer delay */
1129 for (d=0; d<PJ_ARRAY_SIZE(delay); ++d) {
1130 struct role_t {
1131 pj_ice_sess_role ua1;
1132 pj_ice_sess_role ua2;
1133 } role[] =
1134 {
1135 { ROLE1, ROLE2},
1136 { ROLE2, ROLE1},
1137 { ROLE1, ROLE1},
1138 { ROLE2, ROLE2}
1139 };
1140 unsigned j;
1141
1142 cfg->ua1.answer_delay = delay[d];
1143 cfg->ua2.answer_delay = delay[d];
1144
1145 /* For each test item, test with role conflict scenarios */
1146 for (j=0; j<PJ_ARRAY_SIZE(role); ++j) {
1147 unsigned k1;
1148
1149 cfg->ua1.role = role[j].ua1;
1150 cfg->ua2.role = role[j].ua2;
1151
1152 /* For each test item, test with different number of components */
1153 for (k1=1; k1<=2; ++k1) {
1154 unsigned k2;
1155
1156 cfg->ua1.comp_cnt = k1;
1157
1158 for (k2=1; k2<=2; ++k2) {
1159 char title[120];
1160
1161 sprintf(title,
1162 "%s/%s, %dms answer delay, %d vs %d components",
1163 pj_ice_sess_role_name(role[j].ua1),
1164 pj_ice_sess_role_name(role[j].ua2),
1165 delay[d], k1, k2);
1166
1167 cfg->ua2.comp_cnt = k2;
1168 rc = perform_test(title, &stun_cfg, cfg->server_flag,
1169 &cfg->ua1, &cfg->ua2);
1170 if (rc != 0)
1171 goto on_return;
1172 }
1173 }
1174 }
1175 }
1176 }
1177
1178 on_return:
1179 destroy_stun_config(&stun_cfg);
1180 pj_pool_release(pool);
1181 return rc;
1182 }
1183
ice_one_conc_test(pj_stun_config * stun_cfg,int err_quit)1184 int ice_one_conc_test(pj_stun_config *stun_cfg, int err_quit)
1185 {
1186 struct sess_cfg_t {
1187 const char *title;
1188 unsigned server_flag;
1189 struct test_cfg ua1;
1190 struct test_cfg ua2;
1191 } cfg =
1192 {
1193 "Concurrency test",
1194 0x1FFF,
1195 /* Role comp# host? stun? turn? flag? ans_del snd_del des_del */
1196 {ROLE1, 1, YES, YES, YES, CLIENT_IPV4, 0, 0, 0, {PJ_SUCCESS, PJ_SUCCESS, PJ_SUCCESS}},
1197 {ROLE2, 1, YES, YES, YES, CLIENT_IPV4, 0, 0, 0, {PJ_SUCCESS, PJ_SUCCESS, PJ_SUCCESS}}
1198 };
1199 struct sess_param test_param;
1200 int rc;
1201
1202
1203 /* test a: destroy as soon as nego starts */
1204 cfg.title = " ice test a: immediate destroy";
1205 pj_bzero(&test_param, sizeof(test_param));
1206 test_param.worker_cnt = 4;
1207 test_param.worker_timeout = 1000;
1208 test_param.destroy_after_create = PJ_TRUE;
1209
1210 rc = perform_test2(cfg.title, stun_cfg, cfg.server_flag,
1211 &cfg.ua1, &cfg.ua2, &test_param);
1212 if (rc != 0 && err_quit)
1213 return rc;
1214
1215 /* test b: destroy as soon as one is done */
1216 cfg.title = " ice test b: destroy after 1 success";
1217 test_param.destroy_after_create = PJ_FALSE;
1218 test_param.destroy_after_one_done = PJ_TRUE;
1219
1220 rc = perform_test2(cfg.title, stun_cfg, cfg.server_flag,
1221 &cfg.ua1, &cfg.ua2, &test_param);
1222 if (rc != 0 && err_quit)
1223 return rc;
1224
1225 /* test c: normal */
1226 cfg.title = " ice test c: normal flow";
1227 pj_bzero(&test_param, sizeof(test_param));
1228 test_param.worker_cnt = 4;
1229 test_param.worker_timeout = 1000;
1230
1231 rc = perform_test2(cfg.title, stun_cfg, cfg.server_flag,
1232 &cfg.ua1, &cfg.ua2, &test_param);
1233 if (rc != 0 && err_quit)
1234 return rc;
1235
1236 return 0;
1237 }
1238
ice_conc_test(void)1239 int ice_conc_test(void)
1240 {
1241 const unsigned LOOP = 100;
1242 pj_pool_t *pool;
1243 pj_stun_config stun_cfg;
1244 unsigned i;
1245 int rc;
1246
1247 pool = pj_pool_create(mem, NULL, 512, 512, NULL);
1248 rc = create_stun_config(pool, &stun_cfg);
1249 if (rc != PJ_SUCCESS) {
1250 pj_pool_release(pool);
1251 return -7;
1252 }
1253
1254 for (i = 0; i < LOOP; i++) {
1255 PJ_LOG(3,(THIS_FILE, INDENT "Test %d of %d", i+1, LOOP));
1256 rc = ice_one_conc_test(&stun_cfg, PJ_TRUE);
1257 if (rc)
1258 break;
1259 }
1260
1261 /* Avoid compiler warning */
1262 goto on_return;
1263
1264 on_return:
1265 destroy_stun_config(&stun_cfg);
1266 pj_pool_release(pool);
1267
1268 return rc;
1269 }
1270
1271 struct timer_data
1272 {
1273 struct test_sess *sess;
1274 unsigned caller_last_cand_cnt[PJ_ICE_MAX_COMP];
1275 unsigned callee_last_cand_cnt[PJ_ICE_MAX_COMP];
1276 };
1277
1278
1279 /* Timer callback to check & signal new candidates */
timer_new_cand(pj_timer_heap_t * th,pj_timer_entry * te)1280 static void timer_new_cand(pj_timer_heap_t *th, pj_timer_entry *te)
1281 {
1282 struct timer_data *data = (struct timer_data*)te->user_data;
1283 struct test_sess *sess = data->sess;
1284 struct ice_ept *caller = &sess->caller;
1285 struct ice_ept *callee = &sess->callee;
1286 pj_bool_t caller_last_cand, callee_last_cand;
1287 unsigned i, ncomp;
1288 pj_status_t rc;
1289
1290 /* ICE transport may have been destroyed */
1291 if (!caller->ice || !callee->ice)
1292 return;
1293
1294 caller_last_cand = caller->last_cand;
1295 callee_last_cand = callee->last_cand;
1296 //PJ_LOG(3,(THIS_FILE, INDENT "End-of-cand status: caller=%d callee=%d",
1297 // caller_last_cand, callee_last_cand));
1298
1299 ncomp = PJ_MIN(caller->cfg.comp_cnt, callee->cfg.comp_cnt);
1300 for (i = 0; i < ncomp; ++i) {
1301 pj_ice_sess_cand cand[PJ_ICE_ST_MAX_CAND];
1302 unsigned j, cnt;
1303
1304 /* Check caller candidates */
1305 cnt = PJ_ICE_ST_MAX_CAND;
1306 rc = pj_ice_strans_enum_cands(caller->ice, i+1, &cnt, cand);
1307 if (rc != PJ_SUCCESS) {
1308 app_perror(INDENT "err: caller pj_ice_strans_enum_cands()", rc);
1309 continue;
1310 }
1311
1312 if (cnt > data->caller_last_cand_cnt[i]) {
1313 unsigned new_cnt = cnt - data->caller_last_cand_cnt[i];
1314
1315 /* Update remote with new candidates */
1316 rc = pj_ice_strans_update_check_list(callee->ice,
1317 &caller->ufrag,
1318 &caller->pass,
1319 new_cnt, &cand[cnt - new_cnt],
1320 caller_last_cand && (i==ncomp-1));
1321 if (rc != PJ_SUCCESS) {
1322 app_perror(INDENT "err: callee pj_ice_strans_update_check_list()", rc);
1323 continue;
1324 }
1325
1326 data->caller_last_cand_cnt[i] = cnt;
1327 PJ_LOG(4,(THIS_FILE, INDENT "Updated callee with %d new candidates %s",
1328 new_cnt, (caller_last_cand?"(last)":"")));
1329
1330 for (j = 0; j < new_cnt; ++j) {
1331 pj_ice_sess_cand *c = &cand[cnt - new_cnt + j];
1332 char buf1[PJ_INET6_ADDRSTRLEN+10];
1333 char buf2[PJ_INET6_ADDRSTRLEN+10];
1334 PJ_LOG(4,(THIS_FILE, INDENT
1335 "%d: comp=%d, type=%s, addr=%s, baseaddr=%s",
1336 j+1, c->comp_id,
1337 pj_ice_get_cand_type_name(c->type),
1338 pj_sockaddr_print(&c->addr, buf1, sizeof(buf1), 3),
1339 pj_sockaddr_print(&c->base_addr, buf2, sizeof(buf2), 3)
1340 ));
1341 }
1342 }
1343
1344 /* Check callee candidates */
1345 cnt = PJ_ICE_ST_MAX_CAND;
1346 rc = pj_ice_strans_enum_cands(callee->ice, i+1, &cnt, cand);
1347 if (rc != PJ_SUCCESS) {
1348 app_perror(INDENT "err: caller pj_ice_strans_enum_cands()", rc);
1349 continue;
1350 }
1351
1352 if (cnt > data->callee_last_cand_cnt[i]) {
1353 unsigned new_cnt = cnt - data->callee_last_cand_cnt[i];
1354
1355 /* Update remote with new candidates */
1356 rc = pj_ice_strans_update_check_list(caller->ice,
1357 &callee->ufrag,
1358 &callee->pass,
1359 new_cnt, &cand[cnt - new_cnt],
1360 callee_last_cand && (i==ncomp-1));
1361 if (rc != PJ_SUCCESS) {
1362 app_perror(INDENT "err: caller pj_ice_strans_update_check_list()", rc);
1363 continue;
1364 }
1365
1366 data->callee_last_cand_cnt[i] = cnt;
1367 PJ_LOG(4,(THIS_FILE, INDENT "Updated caller with %d new candidates %s",
1368 new_cnt, (callee_last_cand?"(last)":"")));
1369
1370 for (j = 0; j < new_cnt; ++j) {
1371 pj_ice_sess_cand *c = &cand[cnt - new_cnt + j];
1372 char buf1[PJ_INET6_ADDRSTRLEN+10];
1373 char buf2[PJ_INET6_ADDRSTRLEN+10];
1374 PJ_LOG(4,(THIS_FILE, INDENT
1375 "%d: comp=%d, type=%s, addr=%s, baseaddr=%s",
1376 j+1, c->comp_id,
1377 pj_ice_get_cand_type_name(c->type),
1378 pj_sockaddr_print(&c->addr, buf1, sizeof(buf1), 3),
1379 pj_sockaddr_print(&c->base_addr, buf2, sizeof(buf2), 3)
1380 ));
1381 }
1382 }
1383 }
1384
1385 if (!caller_last_cand || !callee_last_cand) {
1386 /* Reschedule until all candidates are gathered */
1387 pj_time_val timeout = {0, 10};
1388 pj_time_val_normalize(&timeout);
1389 pj_timer_heap_schedule(th, te, &timeout);
1390 //PJ_LOG(3,(THIS_FILE, INDENT "Rescheduled new candidate check"));
1391 }
1392 }
1393
1394
perform_trickle_test(const char * title,pj_stun_config * stun_cfg,unsigned server_flag,struct test_cfg * caller_cfg,struct test_cfg * callee_cfg,struct sess_param * test_param)1395 static int perform_trickle_test(const char *title,
1396 pj_stun_config *stun_cfg,
1397 unsigned server_flag,
1398 struct test_cfg *caller_cfg,
1399 struct test_cfg *callee_cfg,
1400 struct sess_param *test_param)
1401 {
1402 pjlib_state pjlib_state;
1403 struct test_sess *sess;
1404 struct timer_data timer_data;
1405 pj_timer_entry te_new_cand;
1406 int rc;
1407
1408 PJ_LOG(3,(THIS_FILE, "%s, %d vs %d components",
1409 title, caller_cfg->comp_cnt, callee_cfg->comp_cnt));
1410
1411 capture_pjlib_state(stun_cfg, &pjlib_state);
1412
1413 rc = create_sess(stun_cfg, server_flag, caller_cfg, callee_cfg,
1414 test_param, &sess);
1415 if (rc != 0)
1416 return rc;
1417
1418 /* Init ICE on caller */
1419 rc = pj_ice_strans_init_ice(sess->caller.ice, sess->caller.cfg.role,
1420 &sess->caller.ufrag, &sess->caller.pass);
1421 if (rc != PJ_SUCCESS) {
1422 app_perror(INDENT "err: caller pj_ice_strans_init_ice()", rc);
1423 rc = -100;
1424 goto on_return;
1425 }
1426
1427 /* Init ICE on callee */
1428 rc = pj_ice_strans_init_ice(sess->callee.ice, sess->callee.cfg.role,
1429 &sess->callee.ufrag, &sess->callee.pass);
1430 if (rc != PJ_SUCCESS) {
1431 app_perror(INDENT "err: callee pj_ice_strans_init_ice()", rc);
1432 rc = -110;
1433 goto on_return;
1434 }
1435
1436 /* Start ICE on callee */
1437 rc = start_ice(&sess->callee, &sess->caller);
1438 if (rc != PJ_SUCCESS) {
1439 int retval = (rc == sess->callee.cfg.expected.start_status)?0:-120;
1440 rc = retval;
1441 goto on_return;
1442 }
1443
1444 /* Start ICE on caller */
1445 rc = start_ice(&sess->caller, &sess->callee);
1446 if (rc != PJ_SUCCESS) {
1447 int retval = (rc == sess->caller.cfg.expected.start_status)?0:-130;
1448 rc = retval;
1449 goto on_return;
1450 }
1451
1452 /* Start polling new candidate */
1453 //if (!sess->caller.last_cand || !sess->callee.last_cand)
1454 {
1455 pj_time_val timeout = {0, 10};
1456
1457 pj_bzero(&timer_data, sizeof(timer_data));
1458 timer_data.sess = sess;
1459
1460 pj_time_val_normalize(&timeout);
1461 pj_timer_entry_init(&te_new_cand, 0, &timer_data, &timer_new_cand);
1462 pj_timer_heap_schedule(stun_cfg->timer_heap, &te_new_cand, &timeout);
1463 }
1464
1465 WAIT_UNTIL(30000, ALL_DONE, rc);
1466 if (!ALL_DONE) {
1467 PJ_LOG(3,(THIS_FILE, INDENT "err: negotiation timed-out"));
1468 rc = -140;
1469 goto on_return;
1470 }
1471
1472 if (rc != 0)
1473 goto on_return;
1474
1475 if (sess->caller.result.nego_status != sess->caller.cfg.expected.nego_status) {
1476 app_perror(INDENT "err: caller negotiation failed", sess->caller.result.nego_status);
1477 rc = -150;
1478 goto on_return;
1479 }
1480
1481 if (sess->callee.result.nego_status != sess->callee.cfg.expected.nego_status) {
1482 app_perror(INDENT "err: callee negotiation failed", sess->callee.result.nego_status);
1483 rc = -160;
1484 goto on_return;
1485 }
1486
1487 /* Verify that both agents have agreed on the same pair */
1488 rc = check_pair(&sess->caller, &sess->callee, -170);
1489 if (rc != 0) {
1490 goto on_return;
1491 }
1492 rc = check_pair(&sess->callee, &sess->caller, -180);
1493 if (rc != 0) {
1494 goto on_return;
1495 }
1496
1497 /* Looks like everything is okay */
1498
1499 /* Destroy ICE stream transports first to let it de-allocate
1500 * TURN relay (otherwise there'll be timer/memory leak, unless
1501 * we wait for long time in the last poll_events() below).
1502 */
1503 if (sess->caller.ice) {
1504 pj_ice_strans_destroy(sess->caller.ice);
1505 sess->caller.ice = NULL;
1506 }
1507
1508 if (sess->callee.ice) {
1509 pj_ice_strans_destroy(sess->callee.ice);
1510 sess->callee.ice = NULL;
1511 }
1512
1513 on_return:
1514 /* Wait.. */
1515 poll_events(stun_cfg, 200, PJ_FALSE);
1516
1517 /* Now destroy everything */
1518 destroy_sess(sess, 500);
1519
1520 /* Flush events */
1521 poll_events(stun_cfg, 100, PJ_FALSE);
1522
1523 if (rc == 0)
1524 rc = check_pjlib_state(stun_cfg, &pjlib_state);
1525
1526 return rc;
1527 }
1528
1529
1530 /* Simple trickle ICE test */
trickle_ice_test(void)1531 int trickle_ice_test(void)
1532 {
1533 pj_pool_t *pool;
1534 pj_stun_config stun_cfg;
1535 struct sess_param test_param;
1536 unsigned i;
1537 int rc;
1538
1539 struct sess_cfg_t {
1540 const char *title;
1541 unsigned server_flag;
1542 struct test_cfg ua1;
1543 struct test_cfg ua2;
1544 } cfg[] = {
1545 {
1546 "With host-only",
1547 0x1FFF,
1548 /*Role comp# host? stun? turn? flag? ans_del snd_del des_del */
1549 {ROLE1, 1, YES, NO, NO, CLIENT_IPV4, 0, 0, 0, {PJ_SUCCESS, PJ_SUCCESS, PJ_SUCCESS}},
1550 {ROLE2, 1, YES, NO, NO, CLIENT_IPV4, 0, 0, 0, {PJ_SUCCESS, PJ_SUCCESS, PJ_SUCCESS}}
1551 },
1552 {
1553 "With turn-only",
1554 0x1FFF,
1555 /*Role comp# host? stun? turn? flag? ans_del snd_del des_del */
1556 {ROLE1, 1, NO, NO, YES, CLIENT_IPV4, 0, 0, 0, {PJ_SUCCESS, PJ_SUCCESS, PJ_SUCCESS}},
1557 {ROLE2, 1, NO, NO, YES, CLIENT_IPV4, 0, 0, 0, {PJ_SUCCESS, PJ_SUCCESS, PJ_SUCCESS}}
1558 },
1559 {
1560 /* STUN candidates will be pruned */
1561 "With host+turn",
1562 0x1FFF,
1563 /*Role comp# host? stun? turn? flag? ans_del snd_del des_del */
1564 {ROLE1, 1, YES, YES, YES, CLIENT_IPV4, 0, 0, 0, {PJ_SUCCESS, PJ_SUCCESS, PJ_SUCCESS}},
1565 {ROLE2, 1, YES, YES, YES, CLIENT_IPV4, 0, 0, 0, {PJ_SUCCESS, PJ_SUCCESS, PJ_SUCCESS}}
1566 }};
1567
1568 PJ_LOG(3,(THIS_FILE, "Trickle ICE"));
1569 pj_log_push_indent();
1570
1571 pool = pj_pool_create(mem, NULL, 512, 512, NULL);
1572
1573 rc = create_stun_config(pool, &stun_cfg);
1574 if (rc != PJ_SUCCESS) {
1575 pj_pool_release(pool);
1576 pj_log_pop_indent();
1577 return -10;
1578 }
1579
1580 for (i = 0; i < PJ_ARRAY_SIZE(cfg) && !rc; ++i) {
1581 unsigned c1, c2;
1582 cfg[i].ua1.trickle = PJ_ICE_SESS_TRICKLE_FULL;
1583 cfg[i].ua2.trickle = PJ_ICE_SESS_TRICKLE_FULL;
1584 for (c1 = 1; c1 <= 2 && !rc; ++c1) {
1585 for (c2 = 1; c2 <= 2 && !rc; ++c2) {
1586 pj_bzero(&test_param, sizeof(test_param));
1587 cfg[i].ua1.comp_cnt = c1;
1588 cfg[i].ua2.comp_cnt = c2;
1589 rc = perform_trickle_test(cfg[i].title,
1590 &stun_cfg,
1591 cfg[i].server_flag,
1592 &cfg[i].ua1,
1593 &cfg[i].ua2,
1594 &test_param);
1595 }
1596 }
1597 }
1598
1599 destroy_stun_config(&stun_cfg);
1600 pj_pool_release(pool);
1601 pj_log_pop_indent();
1602
1603 return rc;
1604 }
1605