1
/* $Id: stun_sock_test.c 4360 2013-02-21 11:26:35Z bennylp $ */
3
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
4
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
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.
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.
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
22
#define THIS_FILE "stun_sock_test.c"
37
pj_activesock_t *asock;
41
pj_ioqueue_op_key_t send_key;
43
pj_uint16_t port_to_send;
46
static pj_bool_t srv_on_data_recvfrom(pj_activesock_t *asock,
49
const pj_sockaddr_t *src_addr,
56
srv = (struct stun_srv*) pj_activesock_get_user_data(asock);
59
if (status != PJ_SUCCESS)
64
/* Ignore if we're not responding */
65
if (srv->flag & RESPOND_STUN) {
67
pj_stun_msg *req_msg, *res_msg;
69
pool = pj_pool_create(mem, "stunsrv", 512, 512, NULL);
72
status = pj_stun_msg_decode(pool, (pj_uint8_t*)data, size,
73
PJ_STUN_IS_DATAGRAM | PJ_STUN_CHECK_PACKET,
74
&req_msg, NULL, NULL);
75
if (status != PJ_SUCCESS) {
76
app_perror(" pj_stun_msg_decode()", status);
77
pj_pool_release(pool);
82
status = pj_stun_msg_create(pool, PJ_STUN_BINDING_RESPONSE, PJ_STUN_MAGIC,
83
req_msg->hdr.tsx_id, &res_msg);
84
if (status != PJ_SUCCESS) {
85
app_perror(" pj_stun_msg_create()", status);
86
pj_pool_release(pool);
90
/* Add MAPPED-ADDRESS or XOR-MAPPED-ADDRESS (or don't add) */
91
if (srv->flag & WITH_MAPPED) {
94
pj_sockaddr_in_init(&addr, &srv->ip_to_send, srv->port_to_send);
95
pj_stun_msg_add_sockaddr_attr(pool, res_msg, PJ_STUN_ATTR_MAPPED_ADDR,
96
PJ_FALSE, &addr, sizeof(addr));
97
} else if (srv->flag & WITH_XOR_MAPPED) {
100
pj_sockaddr_in_init(&addr, &srv->ip_to_send, srv->port_to_send);
101
pj_stun_msg_add_sockaddr_attr(pool, res_msg,
102
PJ_STUN_ATTR_XOR_MAPPED_ADDR,
103
PJ_TRUE, &addr, sizeof(addr));
107
status = pj_stun_msg_encode(res_msg, (pj_uint8_t*)data, 100, 0,
109
if (status != PJ_SUCCESS) {
110
app_perror(" pj_stun_msg_encode()", status);
111
pj_pool_release(pool);
117
pj_activesock_sendto(asock, &srv->send_key, data, &sent, 0,
120
pj_pool_release(pool);
122
} else if (srv->flag & ECHO) {
125
pj_activesock_sendto(asock, &srv->send_key, data, &sent, 0,
133
static pj_status_t create_server(pj_pool_t *pool,
134
pj_ioqueue_t *ioqueue,
136
struct stun_srv **p_srv)
138
struct stun_srv *srv;
139
pj_activesock_cb activesock_cb;
142
srv = PJ_POOL_ZALLOC_T(pool, struct stun_srv);
144
srv->ip_to_send = pj_str("1.1.1.1");
145
srv->port_to_send = 1000;
147
status = pj_sockaddr_in_init(&srv->addr.ipv4, NULL, 0);
148
if (status != PJ_SUCCESS)
151
pj_bzero(&activesock_cb, sizeof(activesock_cb));
152
activesock_cb.on_data_recvfrom = &srv_on_data_recvfrom;
153
status = pj_activesock_create_udp(pool, &srv->addr, NULL, ioqueue,
154
&activesock_cb, srv, &srv->asock,
156
if (status != PJ_SUCCESS)
159
pj_ioqueue_op_key_init(&srv->send_key, sizeof(srv->send_key));
161
status = pj_activesock_start_recvfrom(srv->asock, pool, 512, 0);
162
if (status != PJ_SUCCESS) {
163
pj_activesock_close(srv->asock);
171
static void destroy_server(struct stun_srv *srv)
173
pj_activesock_close(srv->asock);
182
pj_ioqueue_op_key_t send_key;
183
pj_bool_t destroy_on_err;
185
unsigned on_status_cnt;
186
pj_stun_sock_op last_op;
187
pj_status_t last_status;
189
unsigned on_rx_data_cnt;
192
static pj_bool_t stun_sock_on_status(pj_stun_sock *stun_sock,
196
struct stun_client *client;
198
client = (struct stun_client*) pj_stun_sock_get_user_data(stun_sock);
199
client->on_status_cnt++;
200
client->last_op = op;
201
client->last_status = status;
203
if (status != PJ_SUCCESS && client->destroy_on_err) {
204
pj_stun_sock_destroy(client->sock);
212
static pj_bool_t stun_sock_on_rx_data(pj_stun_sock *stun_sock,
215
const pj_sockaddr_t *src_addr,
218
struct stun_client *client;
221
PJ_UNUSED_ARG(pkt_len);
222
PJ_UNUSED_ARG(src_addr);
223
PJ_UNUSED_ARG(addr_len);
225
client = (struct stun_client*) pj_stun_sock_get_user_data(stun_sock);
226
client->on_rx_data_cnt++;
231
static pj_status_t create_client(pj_stun_config *cfg,
232
struct stun_client **p_client,
233
pj_bool_t destroy_on_err)
236
struct stun_client *client;
237
pj_stun_sock_cfg sock_cfg;
241
pool = pj_pool_create(mem, "test", 512, 512, NULL);
242
client = PJ_POOL_ZALLOC_T(pool, struct stun_client);
245
pj_stun_sock_cfg_default(&sock_cfg);
247
pj_bzero(&cb, sizeof(cb));
248
cb.on_status = &stun_sock_on_status;
249
cb.on_rx_data = &stun_sock_on_rx_data;
250
status = pj_stun_sock_create(cfg, NULL, pj_AF_INET(), &cb,
251
&sock_cfg, client, &client->sock);
252
if (status != PJ_SUCCESS) {
253
app_perror(" pj_stun_sock_create()", status);
254
pj_pool_release(pool);
258
pj_stun_sock_set_user_data(client->sock, client);
260
pj_ioqueue_op_key_init(&client->send_key, sizeof(client->send_key));
262
client->destroy_on_err = destroy_on_err;
270
static void destroy_client(struct stun_client *client)
273
pj_stun_sock_destroy(client->sock);
276
pj_pool_release(client->pool);
279
static void handle_events(pj_stun_config *cfg, unsigned msec_delay)
283
pj_timer_heap_poll(cfg->timer_heap, NULL);
286
delay.msec = msec_delay;
287
pj_time_val_normalize(&delay);
289
pj_ioqueue_poll(cfg->ioqueue, &delay);
293
* Timeout test: scenario when no response is received from server
295
static int timeout_test(pj_stun_config *cfg, pj_bool_t destroy_on_err)
297
struct stun_srv *srv;
298
struct stun_client *client;
300
pj_time_val timeout, t;
304
PJ_LOG(3,(THIS_FILE, " timeout test [%d]", destroy_on_err));
306
status = create_client(cfg, &client, destroy_on_err);
307
if (status != PJ_SUCCESS)
310
status = create_server(client->pool, cfg->ioqueue, 0, &srv);
311
if (status != PJ_SUCCESS) {
312
destroy_client(client);
316
srv_addr = pj_str("127.0.0.1");
317
status = pj_stun_sock_start(client->sock, &srv_addr,
318
pj_ntohs(srv->addr.ipv4.sin_port), NULL);
319
if (status != PJ_SUCCESS) {
321
destroy_client(client);
325
/* Wait until on_status() callback is called with the failure */
326
pj_gettimeofday(&timeout);
329
handle_events(cfg, 100);
331
} while (client->on_status_cnt==0 && PJ_TIME_VAL_LT(t, timeout));
333
/* Check that callback with correct operation is called */
334
if (client->last_op != PJ_STUN_SOCK_BINDING_OP) {
335
PJ_LOG(3,(THIS_FILE, " error: expecting Binding operation status"));
339
/* .. and with the correct status */
340
if (client->last_status != PJNATH_ESTUNTIMEDOUT) {
341
PJ_LOG(3,(THIS_FILE, " error: expecting PJNATH_ESTUNTIMEDOUT"));
345
/* Check that server received correct retransmissions */
346
if (srv->rx_cnt != PJ_STUN_MAX_TRANSMIT_COUNT) {
347
PJ_LOG(3,(THIS_FILE, " error: expecting %d retransmissions, got %d",
348
PJ_STUN_MAX_TRANSMIT_COUNT, srv->rx_cnt));
352
/* Check that client doesn't receive anything */
353
if (client->on_rx_data_cnt != 0) {
354
PJ_LOG(3,(THIS_FILE, " error: client shouldn't have received anything"));
361
destroy_client(client);
363
handle_events(cfg, 50);
369
* Invalid response scenario: when server returns no MAPPED-ADDRESS or
370
* XOR-MAPPED-ADDRESS attribute.
372
static int missing_attr_test(pj_stun_config *cfg, pj_bool_t destroy_on_err)
374
struct stun_srv *srv;
375
struct stun_client *client;
377
pj_time_val timeout, t;
381
PJ_LOG(3,(THIS_FILE, " missing attribute test [%d]", destroy_on_err));
383
status = create_client(cfg, &client, destroy_on_err);
384
if (status != PJ_SUCCESS)
387
status = create_server(client->pool, cfg->ioqueue, RESPOND_STUN, &srv);
388
if (status != PJ_SUCCESS) {
389
destroy_client(client);
393
srv_addr = pj_str("127.0.0.1");
394
status = pj_stun_sock_start(client->sock, &srv_addr,
395
pj_ntohs(srv->addr.ipv4.sin_port), NULL);
396
if (status != PJ_SUCCESS) {
398
destroy_client(client);
402
/* Wait until on_status() callback is called with the failure */
403
pj_gettimeofday(&timeout);
406
handle_events(cfg, 100);
408
} while (client->on_status_cnt==0 && PJ_TIME_VAL_LT(t, timeout));
410
/* Check that callback with correct operation is called */
411
if (client->last_op != PJ_STUN_SOCK_BINDING_OP) {
412
PJ_LOG(3,(THIS_FILE, " error: expecting Binding operation status"));
416
if (client->last_status != PJNATH_ESTUNNOMAPPEDADDR) {
417
PJ_LOG(3,(THIS_FILE, " error: expecting PJNATH_ESTUNNOMAPPEDADDR"));
421
/* Check that client doesn't receive anything */
422
if (client->on_rx_data_cnt != 0) {
423
PJ_LOG(3,(THIS_FILE, " error: client shouldn't have received anything"));
430
destroy_client(client);
432
handle_events(cfg, 50);
439
static int keep_alive_test(pj_stun_config *cfg)
441
struct stun_srv *srv;
442
struct stun_client *client;
443
pj_sockaddr_in mapped_addr;
444
pj_stun_sock_info info;
446
pj_time_val timeout, t;
450
PJ_LOG(3,(THIS_FILE, " normal operation"));
452
status = create_client(cfg, &client, PJ_TRUE);
453
if (status != PJ_SUCCESS)
456
status = create_server(client->pool, cfg->ioqueue, RESPOND_STUN|WITH_XOR_MAPPED, &srv);
457
if (status != PJ_SUCCESS) {
458
destroy_client(client);
463
* Part 1: initial Binding resolution.
465
PJ_LOG(3,(THIS_FILE, " initial Binding request"));
466
srv_addr = pj_str("127.0.0.1");
467
status = pj_stun_sock_start(client->sock, &srv_addr,
468
pj_ntohs(srv->addr.ipv4.sin_port), NULL);
469
if (status != PJ_SUCCESS) {
471
destroy_client(client);
475
/* Wait until on_status() callback is called with success status */
476
pj_gettimeofday(&timeout);
479
handle_events(cfg, 100);
481
} while (client->on_status_cnt==0 && PJ_TIME_VAL_LT(t, timeout));
483
/* Check that callback with correct operation is called */
484
if (client->last_op != PJ_STUN_SOCK_BINDING_OP) {
485
PJ_LOG(3,(THIS_FILE, " error: expecting Binding operation status"));
489
if (client->last_status != PJ_SUCCESS) {
490
PJ_LOG(3,(THIS_FILE, " error: expecting PJ_SUCCESS status"));
494
/* Check that client doesn't receive anything */
495
if (client->on_rx_data_cnt != 0) {
496
PJ_LOG(3,(THIS_FILE, " error: client shouldn't have received anything"));
502
pj_bzero(&info, sizeof(info));
503
pj_stun_sock_get_info(client->sock, &info);
505
/* Check that we have server address */
506
if (!pj_sockaddr_has_addr(&info.srv_addr)) {
507
PJ_LOG(3,(THIS_FILE, " error: missing server address"));
511
/* .. and bound address port must not be zero */
512
if (pj_sockaddr_get_port(&info.bound_addr)==0) {
513
PJ_LOG(3,(THIS_FILE, " error: bound address is zero"));
517
/* .. and mapped address */
518
if (!pj_sockaddr_has_addr(&info.mapped_addr)) {
519
PJ_LOG(3,(THIS_FILE, " error: missing mapped address"));
523
/* verify the mapped address */
524
pj_sockaddr_in_init(&mapped_addr, &srv->ip_to_send, srv->port_to_send);
525
if (pj_sockaddr_cmp(&info.mapped_addr, &mapped_addr) != 0) {
526
PJ_LOG(3,(THIS_FILE, " error: mapped address mismatched"));
531
/* .. and at least one alias */
532
if (info.alias_cnt == 0) {
533
PJ_LOG(3,(THIS_FILE, " error: must have at least one alias"));
537
if (!pj_sockaddr_has_addr(&info.aliases[0])) {
538
PJ_LOG(3,(THIS_FILE, " error: missing alias"));
545
* Part 2: sending and receiving data
547
PJ_LOG(3,(THIS_FILE, " sending/receiving data"));
549
/* Change server operation mode to echo back data */
555
/* Client sending data to echo server */
558
PJ_LOG(3,(THIS_FILE, " sending to %s", pj_sockaddr_print(&info.srv_addr, txt, sizeof(txt), 3)));
560
status = pj_stun_sock_sendto(client->sock, NULL, &ret, sizeof(ret),
562
pj_sockaddr_get_len(&info.srv_addr));
563
if (status != PJ_SUCCESS && status != PJ_EPENDING) {
564
app_perror(" error: server sending data", status);
569
/* Wait for a short period until client receives data. We can't wait for
570
* too long otherwise the keep-alive will kick in.
572
pj_gettimeofday(&timeout);
575
handle_events(cfg, 100);
577
} while (client->on_rx_data_cnt==0 && PJ_TIME_VAL_LT(t, timeout));
579
/* Check that data is received in server */
580
if (srv->rx_cnt == 0) {
581
PJ_LOG(3,(THIS_FILE, " error: server didn't receive data"));
586
/* Check that status is still OK */
587
if (client->last_status != PJ_SUCCESS) {
588
app_perror(" error: client has failed", client->last_status);
592
/* Check that data has been received */
593
if (client->on_rx_data_cnt == 0) {
594
PJ_LOG(3,(THIS_FILE, " error: client doesn't receive data"));
600
* Part 3: Successful keep-alive,
602
PJ_LOG(3,(THIS_FILE, " successful keep-alive scenario"));
604
/* Change server operation mode to normal mode */
605
srv->flag = RESPOND_STUN | WITH_XOR_MAPPED;
611
client->on_status_cnt = 0;
612
client->last_status = PJ_SUCCESS;
613
client->on_rx_data_cnt = 0;
615
/* Wait for keep-alive duration to see if client actually sends the
618
pj_gettimeofday(&timeout);
619
timeout.sec += (PJ_STUN_KEEP_ALIVE_SEC + 1);
621
handle_events(cfg, 100);
623
} while (PJ_TIME_VAL_LT(t, timeout));
625
/* Check that server receives some packets */
626
if (srv->rx_cnt == 0) {
627
PJ_LOG(3, (THIS_FILE, " error: no keep-alive was received"));
631
/* Check that client status is still okay and on_status() callback is NOT
634
/* No longer valid due to this ticket:
635
* http://trac.pjsip.org/repos/ticket/742
637
if (client->on_status_cnt != 0) {
638
PJ_LOG(3, (THIS_FILE, " error: on_status() must not be called on successful"
639
"keep-alive when mapped-address does not change"));
644
/* Check that client doesn't receive anything */
645
if (client->on_rx_data_cnt != 0) {
646
PJ_LOG(3,(THIS_FILE, " error: client shouldn't have received anything"));
653
* Part 4: Successful keep-alive with IP address change
655
PJ_LOG(3,(THIS_FILE, " mapped IP address change"));
657
/* Change server operation mode to normal mode */
658
srv->flag = RESPOND_STUN | WITH_XOR_MAPPED;
660
/* Change mapped address in the response */
661
srv->ip_to_send = pj_str("2.2.2.2");
668
client->on_status_cnt = 0;
669
client->last_status = PJ_SUCCESS;
670
client->on_rx_data_cnt = 0;
672
/* Wait for keep-alive duration to see if client actually sends the
675
pj_gettimeofday(&timeout);
676
timeout.sec += (PJ_STUN_KEEP_ALIVE_SEC + 1);
678
handle_events(cfg, 100);
680
} while (PJ_TIME_VAL_LT(t, timeout));
682
/* Check that server receives some packets */
683
if (srv->rx_cnt == 0) {
684
PJ_LOG(3, (THIS_FILE, " error: no keep-alive was received"));
688
/* Check that on_status() callback is called (because mapped address
691
if (client->on_status_cnt != 1) {
692
PJ_LOG(3, (THIS_FILE, " error: on_status() was not called"));
696
/* Check that callback was called with correct operation */
697
if (client->last_op != PJ_STUN_SOCK_MAPPED_ADDR_CHANGE) {
698
PJ_LOG(3,(THIS_FILE, " error: expecting keep-alive operation status"));
702
/* Check that last status is still success */
703
if (client->last_status != PJ_SUCCESS) {
704
PJ_LOG(3, (THIS_FILE, " error: expecting successful status"));
708
/* Check that client doesn't receive anything */
709
if (client->on_rx_data_cnt != 0) {
710
PJ_LOG(3,(THIS_FILE, " error: client shouldn't have received anything"));
716
pj_bzero(&info, sizeof(info));
717
pj_stun_sock_get_info(client->sock, &info);
719
/* Check that we have server address */
720
if (!pj_sockaddr_has_addr(&info.srv_addr)) {
721
PJ_LOG(3,(THIS_FILE, " error: missing server address"));
725
/* .. and mapped address */
726
if (!pj_sockaddr_has_addr(&info.mapped_addr)) {
727
PJ_LOG(3,(THIS_FILE, " error: missing mapped address"));
731
/* verify the mapped address */
732
pj_sockaddr_in_init(&mapped_addr, &srv->ip_to_send, srv->port_to_send);
733
if (pj_sockaddr_cmp(&info.mapped_addr, &mapped_addr) != 0) {
734
PJ_LOG(3,(THIS_FILE, " error: mapped address mismatched"));
739
/* .. and at least one alias */
740
if (info.alias_cnt == 0) {
741
PJ_LOG(3,(THIS_FILE, " error: must have at least one alias"));
745
if (!pj_sockaddr_has_addr(&info.aliases[0])) {
746
PJ_LOG(3,(THIS_FILE, " error: missing alias"));
753
* Part 5: Failed keep-alive
755
PJ_LOG(3,(THIS_FILE, " failed keep-alive scenario"));
757
/* Change server operation mode to respond without attribute */
758
srv->flag = RESPOND_STUN;
764
client->on_status_cnt = 0;
765
client->last_status = PJ_SUCCESS;
766
client->on_rx_data_cnt = 0;
768
/* Wait until on_status() is called with failure. */
769
pj_gettimeofday(&timeout);
770
timeout.sec += (PJ_STUN_KEEP_ALIVE_SEC + PJ_STUN_TIMEOUT_VALUE + 5);
772
handle_events(cfg, 100);
774
} while (client->on_status_cnt==0 && PJ_TIME_VAL_LT(t, timeout));
776
/* Check that callback with correct operation is called */
777
if (client->last_op != PJ_STUN_SOCK_KEEP_ALIVE_OP) {
778
PJ_LOG(3,(THIS_FILE, " error: expecting keep-alive operation status"));
782
if (client->last_status == PJ_SUCCESS) {
783
PJ_LOG(3,(THIS_FILE, " error: expecting failed keep-alive"));
787
/* Check that client doesn't receive anything */
788
if (client->on_rx_data_cnt != 0) {
789
PJ_LOG(3,(THIS_FILE, " error: client shouldn't have received anything"));
797
destroy_client(client);
799
handle_events(cfg, 50);
804
#define DO_TEST(expr) \
805
capture_pjlib_state(&stun_cfg, &pjlib_state); \
807
if (ret != 0) goto on_return; \
808
ret = check_pjlib_state(&stun_cfg, &pjlib_state); \
809
if (ret != 0) goto on_return;
812
int stun_sock_test(void)
814
struct pjlib_state pjlib_state;
815
pj_stun_config stun_cfg;
816
pj_ioqueue_t *ioqueue = NULL;
817
pj_timer_heap_t *timer_heap = NULL;
818
pj_pool_t *pool = NULL;
822
pool = pj_pool_create(mem, NULL, 512, 512, NULL);
824
status = pj_ioqueue_create(pool, 12, &ioqueue);
825
if (status != PJ_SUCCESS) {
826
app_perror(" pj_ioqueue_create()", status);
831
status = pj_timer_heap_create(pool, 100, &timer_heap);
832
if (status != PJ_SUCCESS) {
833
app_perror(" pj_timer_heap_create()", status);
838
pj_stun_config_init(&stun_cfg, mem, 0, ioqueue, timer_heap);
840
DO_TEST(timeout_test(&stun_cfg, PJ_FALSE));
841
DO_TEST(timeout_test(&stun_cfg, PJ_TRUE));
843
DO_TEST(missing_attr_test(&stun_cfg, PJ_FALSE));
844
DO_TEST(missing_attr_test(&stun_cfg, PJ_TRUE));
846
DO_TEST(keep_alive_test(&stun_cfg));
849
if (timer_heap) pj_timer_heap_destroy(timer_heap);
850
if (ioqueue) pj_ioqueue_destroy(ioqueue);
851
if (pool) pj_pool_release(pool);