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