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