blob: 52d2242f9a41cedc9ae151497ac1793f385383d3 [file] [log] [blame]
Benny Prijonoff1df042008-06-06 14:47:10 +00001/* $Id$ */
2/*
3 * Copyright (C) 2003-2007 Benny Prijono <benny@prijono.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19#include "test.h"
20#include "server.h"
21
22#define SRV_DOMAIN "pjsip.lab.domain"
23#define KA_INTERVAL 50
24
25struct test_result
26{
27 unsigned state_called;
28 unsigned rx_data_cnt;
29};
30
31struct test_session
32{
33 pj_pool_t *pool;
34 pj_stun_config *stun_cfg;
35 pj_turn_sock *turn_sock;
36 pj_dns_resolver *resolver;
37 test_server *test_srv;
38
39 pj_bool_t destroy_called;
40 int destroy_on_state;
41 struct test_result result;
42};
43
44struct test_session_cfg
45{
46 struct {
47 pj_bool_t enable_dns_srv;
48 int destroy_on_state;
49 } client;
50
51 struct {
52 pj_uint32_t flags;
53 pj_bool_t respond_allocate;
54 pj_bool_t respond_refresh;
55 } srv;
56};
57
58static void turn_on_rx_data(pj_turn_sock *turn_sock,
59 void *pkt,
60 unsigned pkt_len,
61 const pj_sockaddr_t *peer_addr,
62 unsigned addr_len);
63static void turn_on_state(pj_turn_sock *turn_sock,
64 pj_turn_state_t old_state,
65 pj_turn_state_t new_state);
66
67static void destroy_session(struct test_session *sess)
68{
69 if (sess->resolver) {
70 pj_dns_resolver_destroy(sess->resolver, PJ_TRUE);
71 sess->resolver = NULL;
72 }
73
74 if (sess->turn_sock) {
75 if (!sess->destroy_called) {
76 sess->destroy_called = PJ_TRUE;
77 pj_turn_sock_destroy(sess->turn_sock);
78 }
79 sess->turn_sock = NULL;
80 }
81
82 if (sess->test_srv) {
83 destroy_test_server(sess->test_srv);
84 sess->test_srv = NULL;
85 }
86
87 if (sess->pool) {
88 pj_pool_release(sess->pool);
89 }
90}
91
92
93
94static int create_test_session(pj_stun_config *stun_cfg,
95 const struct test_session_cfg *cfg,
96 struct test_session **p_sess)
97{
98 struct test_session *sess;
99 pj_pool_t *pool;
100 pj_turn_sock_cb turn_sock_cb;
101 pj_turn_alloc_param alloc_param;
102 pj_stun_auth_cred cred;
103 pj_status_t status;
104
105 /* Create client */
106 pool = pj_pool_create(mem, "turnclient", 512, 512, NULL);
107 sess = PJ_POOL_ZALLOC_T(pool, struct test_session);
108 sess->pool = pool;
109 sess->stun_cfg = stun_cfg;
110 sess->destroy_on_state = cfg->client.destroy_on_state;
111
112 pj_bzero(&turn_sock_cb, sizeof(turn_sock_cb));
113 turn_sock_cb.on_rx_data = &turn_on_rx_data;
114 turn_sock_cb.on_state = &turn_on_state;
115 status = pj_turn_sock_create(sess->stun_cfg, pj_AF_INET(), PJ_TURN_TP_UDP,
116 &turn_sock_cb, 0, sess, &sess->turn_sock);
117 if (status != PJ_SUCCESS) {
118 destroy_session(sess);
119 return -20;
120 }
121
122 /* Create test server */
123 status = create_test_server(sess->stun_cfg, cfg->srv.flags,
124 SRV_DOMAIN, &sess->test_srv);
125 if (status != PJ_SUCCESS) {
126 destroy_session(sess);
127 return -30;
128 }
129
130 sess->test_srv->turn_respond_allocate = cfg->srv.respond_allocate;
131 sess->test_srv->turn_respond_refresh = cfg->srv.respond_refresh;
132
133 /* Create client resolver */
134 status = pj_dns_resolver_create(mem, "resolver", 0, sess->stun_cfg->timer_heap,
135 sess->stun_cfg->ioqueue, &sess->resolver);
136 if (status != PJ_SUCCESS) {
137 destroy_session(sess);
138 return -40;
139
140 } else {
141 pj_str_t dns_srv = pj_str("127.0.0.1");
142 pj_uint16_t dns_srv_port = (pj_uint16_t) DNS_SERVER_PORT;
143 status = pj_dns_resolver_set_ns(sess->resolver, 1, &dns_srv, &dns_srv_port);
144
145 if (status != PJ_SUCCESS) {
146 destroy_session(sess);
147 return -50;
148 }
149 }
150
151 /* Init TURN credential */
152 pj_bzero(&cred, sizeof(cred));
153 cred.type = PJ_STUN_AUTH_CRED_STATIC;
154 cred.data.static_cred.realm = pj_str(SRV_DOMAIN);
155 cred.data.static_cred.username = pj_str(TURN_USERNAME);
156 cred.data.static_cred.data_type = PJ_STUN_PASSWD_PLAIN;
157 cred.data.static_cred.data = pj_str(TURN_PASSWD);
158
159 /* Init TURN allocate parameter */
160 pj_turn_alloc_param_default(&alloc_param);
161 alloc_param.ka_interval = KA_INTERVAL;
162
163 /* Start the client */
164 if (cfg->client.enable_dns_srv) {
165 /* Use DNS SRV to resolve server, may fallback to DNS A */
166 pj_str_t domain = pj_str(SRV_DOMAIN);
167 status = pj_turn_sock_alloc(sess->turn_sock, &domain, TURN_SERVER_PORT,
168 sess->resolver, &cred, &alloc_param);
169
170 } else {
171 /* Explicitly specify server address */
172 pj_str_t host = pj_str("127.0.0.1");
173 status = pj_turn_sock_alloc(sess->turn_sock, &host, TURN_SERVER_PORT,
174 NULL, &cred, &alloc_param);
175
176 }
177
178 if (status != PJ_SUCCESS) {
179 if (cfg->client.destroy_on_state >= PJ_TURN_STATE_READY) {
180 destroy_session(sess);
181 return -70;
182 }
183 }
184
185 *p_sess = sess;
186 return 0;
187}
188
189
190static void turn_on_rx_data(pj_turn_sock *turn_sock,
191 void *pkt,
192 unsigned pkt_len,
193 const pj_sockaddr_t *peer_addr,
194 unsigned addr_len)
195{
196 struct test_session *sess;
197
198 PJ_UNUSED_ARG(pkt);
199 PJ_UNUSED_ARG(pkt_len);
200 PJ_UNUSED_ARG(peer_addr);
201 PJ_UNUSED_ARG(addr_len);
202
203 sess = (struct test_session*) pj_turn_sock_get_user_data(turn_sock);
204 if (sess == NULL)
205 return;
206
207 sess->result.rx_data_cnt++;
208}
209
210
211static void turn_on_state(pj_turn_sock *turn_sock,
212 pj_turn_state_t old_state,
213 pj_turn_state_t new_state)
214{
215 struct test_session *sess;
216 unsigned i, mask;
217
218 PJ_UNUSED_ARG(old_state);
219
220 sess = (struct test_session*) pj_turn_sock_get_user_data(turn_sock);
221 if (sess == NULL)
222 return;
223
224 /* This state must not be called before */
225 pj_assert((sess->result.state_called & (1<<new_state)) == 0);
226
227 /* new_state must be greater than old_state */
228 pj_assert(new_state > old_state);
229
230 /* must not call any greater state before */
231 mask = 0;
232 for (i=new_state+1; i<31; ++i) mask |= (1 << i);
233
234 pj_assert((sess->result.state_called & mask) == 0);
235
236 sess->result.state_called |= (1 << new_state);
237
238 if (new_state >= sess->destroy_on_state && !sess->destroy_called) {
239 sess->destroy_called = PJ_TRUE;
240 pj_turn_sock_destroy(turn_sock);
241 }
242
243 if (new_state >= PJ_TURN_STATE_DESTROYING) {
244 pj_turn_sock_set_user_data(sess->turn_sock, NULL);
245 sess->turn_sock = NULL;
246 }
247}
248
249
250/////////////////////////////////////////////////////////////////////
251
252static int state_progression_test(pj_stun_config *stun_cfg)
253{
254 struct test_session_cfg test_cfg =
255 {
256 { /* Client cfg */
257 /* DNS SRV */ /* Destroy on state */
258 PJ_TRUE, 0xFFFF
259 },
260 { /* Server cfg */
261 0xFFFFFFFF, /* flags */
262 PJ_TRUE, /* respond to allocate */
263 PJ_TRUE /* respond to refresh */
264 }
265 };
266 struct test_session *sess;
267 unsigned i;
268 int rc;
269
270 PJ_LOG(3,("", " state progression tests"));
271
272 for (i=0; i<=1; ++i) {
273 enum { TIMEOUT = 60 };
274 pjlib_state pjlib_state;
275 pj_turn_session_info info;
276 struct test_result result;
277 pj_time_val tstart;
278
279 PJ_LOG(3,("", " %s DNS SRV resolution",
280 (i==0? "without" : "with")));
281
282 capture_pjlib_state(stun_cfg, &pjlib_state);
283
284 test_cfg.client.enable_dns_srv = i;
285
286 rc = create_test_session(stun_cfg, &test_cfg, &sess);
287 if (rc != 0)
288 return rc;
289
290 pj_bzero(&info, sizeof(info));
291
292 /* Wait until state is READY */
293 pj_gettimeofday(&tstart);
294 while (sess->turn_sock) {
295 pj_time_val now;
296
297 poll_events(stun_cfg, 10, PJ_FALSE);
298 rc = pj_turn_sock_get_info(sess->turn_sock, &info);
299 if (rc!=PJ_SUCCESS)
300 break;
301
302 if (info.state >= PJ_TURN_STATE_READY)
303 break;
304
305 pj_gettimeofday(&now);
306 if (now.sec - tstart.sec > TIMEOUT) {
307 PJ_LOG(3,("", " timed-out"));
308 break;
309 }
310 }
311
312 if (info.state != PJ_TURN_STATE_READY) {
313 PJ_LOG(3,("", " error: state is not READY"));
314 destroy_session(sess);
315 return -130;
316 }
317
318 /* Deallocate */
319 pj_turn_sock_destroy(sess->turn_sock);
320
321 /* Wait for couple of seconds.
322 * We can't poll the session info since the session may have
323 * been destroyed
324 */
325 poll_events(stun_cfg, 2000, PJ_FALSE);
326 sess->turn_sock = NULL;
327 pj_memcpy(&result, &sess->result, sizeof(result));
328 destroy_session(sess);
329
330 /* Check the result */
331 if ((result.state_called & (1<<PJ_TURN_STATE_RESOLVING)) == 0) {
332 PJ_LOG(3,("", " error: PJ_TURN_STATE_RESOLVING is not called"));
333 return -140;
334 }
335
336 if ((result.state_called & (1<<PJ_TURN_STATE_RESOLVED)) == 0) {
337 PJ_LOG(3,("", " error: PJ_TURN_STATE_RESOLVED is not called"));
338 return -150;
339 }
340
341 if ((result.state_called & (1<<PJ_TURN_STATE_ALLOCATING)) == 0) {
342 PJ_LOG(3,("", " error: PJ_TURN_STATE_ALLOCATING is not called"));
343 return -155;
344 }
345
346 if ((result.state_called & (1<<PJ_TURN_STATE_READY)) == 0) {
347 PJ_LOG(3,("", " error: PJ_TURN_STATE_READY is not called"));
348 return -160;
349 }
350
351 if ((result.state_called & (1<<PJ_TURN_STATE_DEALLOCATING)) == 0) {
352 PJ_LOG(3,("", " error: PJ_TURN_STATE_DEALLOCATING is not called"));
353 return -170;
354 }
355
356 if ((result.state_called & (1<<PJ_TURN_STATE_DEALLOCATED)) == 0) {
357 PJ_LOG(3,("", " error: PJ_TURN_STATE_DEALLOCATED is not called"));
358 return -180;
359 }
360
361 if ((result.state_called & (1<<PJ_TURN_STATE_DESTROYING)) == 0) {
362 PJ_LOG(3,("", " error: PJ_TURN_STATE_DESTROYING is not called"));
363 return -190;
364 }
365
366 poll_events(stun_cfg, 500, PJ_FALSE);
367 rc = check_pjlib_state(stun_cfg, &pjlib_state);
368 if (rc != 0) {
369 PJ_LOG(3,("", " error: memory/timer-heap leak detected"));
370 return rc;
371 }
372 }
373
374 return 0;
375}
376
377
378/////////////////////////////////////////////////////////////////////
379
380static int destroy_test(pj_stun_config *stun_cfg,
381 pj_bool_t with_dns_srv,
382 pj_bool_t in_callback)
383{
384 struct test_session_cfg test_cfg =
385 {
386 { /* Client cfg */
387 /* DNS SRV */ /* Destroy on state */
388 PJ_TRUE, 0xFFFF
389 },
390 { /* Server cfg */
391 0xFFFFFFFF, /* flags */
392 PJ_TRUE, /* respond to allocate */
393 PJ_TRUE /* respond to refresh */
394 }
395 };
396 struct test_session *sess;
397 int target_state;
398 int rc;
399
400 PJ_LOG(3,("", " destroy test %s %s",
401 (in_callback? "in callback" : ""),
402 (with_dns_srv? "with DNS srv" : "")
403 ));
404
405 test_cfg.client.enable_dns_srv = with_dns_srv;
406
407 for (target_state=PJ_TURN_STATE_RESOLVING; target_state<=PJ_TURN_STATE_READY; ++target_state) {
408 enum { TIMEOUT = 60 };
409 pjlib_state pjlib_state;
410 pj_turn_session_info info;
411 pj_time_val tstart;
412
413 capture_pjlib_state(stun_cfg, &pjlib_state);
414
415 PJ_LOG(3,("", " %s", pj_turn_state_name((pj_turn_state_t)target_state)));
416
417 if (in_callback)
418 test_cfg.client.destroy_on_state = target_state;
419
420 rc = create_test_session(stun_cfg, &test_cfg, &sess);
421 if (rc != 0)
422 return rc;
423
424 if (in_callback) {
425 pj_gettimeofday(&tstart);
426 rc = 0;
427 while (sess->turn_sock) {
428 pj_time_val now;
429
430 poll_events(stun_cfg, 100, PJ_FALSE);
431
432 pj_gettimeofday(&now);
433 if (now.sec - tstart.sec > TIMEOUT) {
434 rc = -7;
435 break;
436 }
437 }
438
439 } else {
440 pj_gettimeofday(&tstart);
441 rc = 0;
442 while (sess->turn_sock) {
443 pj_time_val now;
444
445 poll_events(stun_cfg, 1, PJ_FALSE);
446
447 pj_turn_sock_get_info(sess->turn_sock, &info);
448
449 if (info.state >= target_state) {
450 pj_turn_sock_destroy(sess->turn_sock);
451 break;
452 }
453
454 pj_gettimeofday(&now);
455 if (now.sec - tstart.sec > TIMEOUT) {
456 rc = -8;
457 break;
458 }
459 }
460 }
461
462
463 if (rc != 0) {
464 PJ_LOG(3,("", " error: timeout"));
465 return rc;
466 }
467
468 poll_events(stun_cfg, 1000, PJ_FALSE);
469 destroy_session(sess);
470
471 rc = check_pjlib_state(stun_cfg, &pjlib_state);
472 if (rc != 0) {
473 PJ_LOG(3,("", " error: memory/timer-heap leak detected"));
474 return rc;
475 }
476 }
477
478 return 0;
479}
480
481
482/////////////////////////////////////////////////////////////////////
483
484int turn_sock_test(void)
485{
486 pj_pool_t *pool;
487 pj_stun_config stun_cfg;
488 int i, rc = 0;
489
490 pool = pj_pool_create(mem, "turntest", 512, 512, NULL);
491 rc = create_stun_config(pool, &stun_cfg);
492 if (rc != PJ_SUCCESS) {
493 pj_pool_release(pool);
494 return -2;
495 }
496
497 rc = state_progression_test(&stun_cfg);
498 if (rc != 0)
499 goto on_return;
500
501 for (i=0; i<=1; ++i) {
502 int j;
503 for (j=0; j<=1; ++j) {
504 rc = destroy_test(&stun_cfg, i, j);
505 if (rc != 0)
506 goto on_return;
507 }
508 }
509
510on_return:
511 destroy_stun_config(&stun_cfg);
512 pj_pool_release(pool);
513 return rc;
514}
515