blob: 95cc5f953cf027722b28aee27d86533ee87610ee [file] [log] [blame]
Benny Prijonodd742da2008-05-17 12:45:00 +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 <pjsip_ua.h>
21#include <pjsip.h>
22#include <pjlib.h>
23
24#define THIS_FILE "regc_test.c"
25
26
27/************************************************************************/
28/* A module to inject error into outgoing sending operation */
29static pj_status_t mod_send_on_tx_request(pjsip_tx_data *tdata);
30
31static struct
32{
33 pjsip_module mod;
34 unsigned count;
35 unsigned count_before_reject;
36} send_mod =
37{
38 {
39 NULL, NULL, /* prev, next. */
40 { "mod-send", 8 }, /* Name. */
41 -1, /* Id */
42 PJSIP_MOD_PRIORITY_TRANSPORT_LAYER, /* Priority */
43 NULL, /* load() */
44 NULL, /* start() */
45 NULL, /* stop() */
46 NULL, /* unload() */
47 NULL, /* on_rx_request() */
48 NULL, /* on_rx_response() */
49 &mod_send_on_tx_request, /* on_tx_request. */
50 NULL, /* on_tx_response() */
51 NULL, /* on_tsx_state() */
52 },
53 0,
54 0xFFFF
55};
56
57
58static pj_status_t mod_send_on_tx_request(pjsip_tx_data *tdata)
59{
60 PJ_UNUSED_ARG(tdata);
61
62 if (++send_mod.count > send_mod.count_before_reject)
63 return PJ_ECANCELLED;
64 else
65 return PJ_SUCCESS;
66};
67
68
69/************************************************************************/
70/* Registrar for testing */
71static pj_bool_t regs_rx_request(pjsip_rx_data *rdata);
72
73enum contact_op
74{
75 NONE, /* don't put Contact header */
76 EXACT, /* return exact contact */
77 MODIFIED, /* return modified Contact header */
78};
79
80struct registrar_cfg
81{
82 pj_bool_t respond; /* should it respond at all */
83 unsigned status_code; /* final response status code */
84 pj_bool_t authenticate; /* should we authenticate? */
85 enum contact_op contact_op; /* What should we do with Contact */
86 unsigned expires_param; /* non-zero to put in expires param */
87 unsigned expires; /* non-zero to put in Expires header*/
88
89 pj_str_t more_contacts; /* Additional Contact headers to put*/
90};
91
92static struct registrar
93{
94 pjsip_module mod;
95 struct registrar_cfg cfg;
96 unsigned response_cnt;
97} registrar =
98{
99 {
100 NULL, NULL, /* prev, next. */
101 { "registrar", 9 }, /* Name. */
102 -1, /* Id */
103 PJSIP_MOD_PRIORITY_APPLICATION, /* Priority */
104 NULL, /* load() */
105 NULL, /* start() */
106 NULL, /* stop() */
107 NULL, /* unload() */
108 &regs_rx_request, /* on_rx_request() */
109 NULL, /* on_rx_response() */
110 NULL, /* on_tx_request. */
111 NULL, /* on_tx_response() */
112 NULL, /* on_tsx_state() */
113 }
114};
115
116static pj_bool_t regs_rx_request(pjsip_rx_data *rdata)
117{
118 pjsip_msg *msg = rdata->msg_info.msg;
119 pjsip_hdr hdr_list;
120 int code;
121 pj_status_t status;
122
123 if (msg->line.req.method.id != PJSIP_REGISTER_METHOD)
124 return PJ_FALSE;
125
126 if (!registrar.cfg.respond)
127 return PJ_TRUE;
128
129 pj_list_init(&hdr_list);
130
131 if (registrar.cfg.authenticate &&
132 pjsip_msg_find_hdr(msg, PJSIP_H_AUTHORIZATION, NULL)==NULL)
133 {
134 pjsip_generic_string_hdr *hwww;
135 const pj_str_t hname = pj_str("WWW-Authenticate");
136 const pj_str_t hvalue = pj_str("Digest realm=\"test\"");
137
138 hwww = pjsip_generic_string_hdr_create(rdata->tp_info.pool, &hname,
139 &hvalue);
140 pj_list_push_back(&hdr_list, hwww);
141
142 code = 401;
143
144 } else {
145 if (registrar.cfg.contact_op == EXACT ||
146 registrar.cfg.contact_op == MODIFIED)
147 {
148 pjsip_hdr *hsrc;
149
150 for (hsrc=msg->hdr.next; hsrc!=&msg->hdr; hsrc=hsrc->next) {
151 pjsip_contact_hdr *hdst;
152
153 if (hsrc->type != PJSIP_H_CONTACT)
154 continue;
155
156 hdst = (pjsip_contact_hdr*)
157 pjsip_hdr_clone(rdata->tp_info.pool, hsrc);
158
159 if (hdst->expires==0)
160 continue;
161
162 if (registrar.cfg.contact_op == MODIFIED) {
163 if (PJSIP_URI_SCHEME_IS_SIP(hdst->uri) ||
164 PJSIP_URI_SCHEME_IS_SIPS(hdst->uri))
165 {
166 pjsip_sip_uri *sip_uri = (pjsip_sip_uri*)
167 pjsip_uri_get_uri(hdst->uri);
168 sip_uri->host = pj_str("x-modified-host");
169 sip_uri->port = 1;
170 }
171 }
172
173 if (registrar.cfg.expires_param)
174 hdst->expires = registrar.cfg.expires_param;
175
176 pj_list_push_back(&hdr_list, hdst);
177 }
178 }
179
180 if (registrar.cfg.more_contacts.slen) {
181 pjsip_generic_string_hdr *hcontact;
182 const pj_str_t hname = pj_str("Contact");
183
184 hcontact = pjsip_generic_string_hdr_create(rdata->tp_info.pool, &hname,
185 &registrar.cfg.more_contacts);
186 pj_list_push_back(&hdr_list, hcontact);
187 }
188
189 if (registrar.cfg.expires) {
190 pjsip_expires_hdr *hexp;
191
192 hexp = pjsip_expires_hdr_create(rdata->tp_info.pool,
193 registrar.cfg.expires);
194 pj_list_push_back(&hdr_list, hexp);
195 }
196
197 registrar.response_cnt++;
198
199 code = registrar.cfg.status_code;
200 }
201
202 status = pjsip_endpt_respond(endpt, NULL, rdata, code, NULL,
203 &hdr_list, NULL, NULL);
204 pj_assert(status == PJ_SUCCESS);
205
206 return PJ_TRUE;
207}
208
209
210/************************************************************************/
211/* Client registration test session */
212struct client
213{
214 /* Result/expected result */
215 int error;
216 int code;
217 pj_bool_t have_reg;
218 int expiration;
219 unsigned contact_cnt;
220 pj_bool_t auth;
221
222 /* Commands */
223 pj_bool_t destroy_on_cb;
224
225 /* Status */
226 pj_bool_t done;
227
228 /* Additional results */
229 int interval;
230 int next_reg;
231};
232
233/* regc callback */
234static void client_cb(struct pjsip_regc_cbparam *param)
235{
236 struct client *client = (struct client*) param->token;
237 pjsip_regc_info info;
238 pj_status_t status;
239
240 client->done = PJ_TRUE;
241
242 status = pjsip_regc_get_info(param->regc, &info);
243 pj_assert(status == PJ_SUCCESS);
244
245 client->error = (param->status != PJ_SUCCESS);
246 client->code = param->code;
247
248 if (client->error)
249 return;
250
251 client->have_reg = info.auto_reg && info.interval>0 &&
252 param->expiration>0;
253 client->expiration = param->expiration;
254 client->contact_cnt = param->contact_cnt;
255 client->interval = info.interval;
256 client->next_reg = info.next_reg;
257
258 if (client->destroy_on_cb)
259 pjsip_regc_destroy(param->regc);
260}
261
262
263/* Generic client test session */
264static struct client client_result;
265static int do_test(const char *title,
266 const struct registrar_cfg *srv_cfg,
267 const struct client *client_cfg,
268 const pj_str_t *registrar_uri,
269 unsigned contact_cnt,
270 const pj_str_t contacts[],
271 unsigned expires,
272 pj_bool_t leave_session,
273 pjsip_regc **p_regc)
274{
275 pjsip_regc *regc;
276 unsigned i;
277 const pj_str_t aor = pj_str("<sip:regc-test@pjsip.org>");
278 pjsip_tx_data *tdata;
279 pj_status_t status;
280
281 PJ_LOG(3,(THIS_FILE, " %s", title));
282
283 /* Modify registrar settings */
284 pj_memcpy(&registrar.cfg, srv_cfg, sizeof(*srv_cfg));
285
286 pj_bzero(&client_result, sizeof(client_result));
287 client_result.destroy_on_cb = client_cfg->destroy_on_cb;
288
289 status = pjsip_regc_create(endpt, &client_result, &client_cb, &regc);
290 if (status != PJ_SUCCESS)
291 return -100;
292
293 status = pjsip_regc_init(regc, registrar_uri, &aor, &aor, contact_cnt,
294 contacts, expires ? expires : 60);
295 if (status != PJ_SUCCESS) {
296 pjsip_regc_destroy(regc);
297 return -110;
298 }
299
300 if (client_cfg->auth) {
301 pjsip_cred_info cred;
302
303 pj_bzero(&cred, sizeof(cred));
304 cred.realm = pj_str("*");
305 cred.scheme = pj_str("digest");
306 cred.username = pj_str("user");
307 cred.data_type = PJSIP_CRED_DATA_PLAIN_PASSWD;
308 cred.data = pj_str("password");
309
310 status = pjsip_regc_set_credentials(regc, 1, &cred);
311 if (status != PJ_SUCCESS) {
312 pjsip_regc_destroy(regc);
313 return -115;
314 }
315 }
316
317 /* Register */
318 status = pjsip_regc_register(regc, PJ_TRUE, &tdata);
319 if (status != PJ_SUCCESS) {
320 pjsip_regc_destroy(regc);
321 return -120;
322 }
323 status = pjsip_regc_send(regc, tdata);
324
325 /* That's it, wait until the callback is sent */
326 for (i=0; i<600 && !client_result.done; ++i) {
327 flush_events(100);
328 }
329
330 if (!client_result.done) {
331 PJ_LOG(3,(THIS_FILE, " error: test has timed out"));
332 pjsip_regc_destroy(regc);
333 return -200;
334 }
335
336 /* Destroy the regc, we're done with the test, unless we're
337 * instructed to leave the session open.
338 */
339 if (!leave_session && !client_cfg->destroy_on_cb)
340 pjsip_regc_destroy(regc);
341
342 /* Compare results with expected results */
343
344 if (client_result.error != client_cfg->error) {
345 PJ_LOG(3,(THIS_FILE, " error: expecting err=%d, got err=%d",
346 client_cfg->error, client_result.error));
347 return -210;
348 }
349 if (client_result.code != client_cfg->code) {
350 PJ_LOG(3,(THIS_FILE, " error: expecting code=%d, got code=%d",
351 client_cfg->code, client_result.code));
352 return -220;
353 }
354 if (client_result.expiration != client_cfg->expiration) {
355 PJ_LOG(3,(THIS_FILE, " error: expecting expiration=%d, got expiration=%d",
356 client_cfg->expiration, client_result.expiration));
357 return -240;
358 }
359 if (client_result.contact_cnt != client_cfg->contact_cnt) {
360 PJ_LOG(3,(THIS_FILE, " error: expecting contact_cnt=%d, got contact_cnt=%d",
361 client_cfg->contact_cnt, client_result.contact_cnt));
362 return -250;
363 }
364 if (client_result.have_reg != client_cfg->have_reg) {
365 PJ_LOG(3,(THIS_FILE, " error: expecting have_reg=%d, got have_reg=%d",
366 client_cfg->have_reg, client_result.have_reg));
367 return -260;
368 }
369 if (client_result.interval != client_result.expiration) {
370 PJ_LOG(3,(THIS_FILE, " error: interval (%d) is different than expiration (%d)",
371 client_result.interval, client_result.expiration));
372 return -270;
373 }
374 if (client_result.expiration > 0 && client_result.next_reg < 1) {
375 PJ_LOG(3,(THIS_FILE, " error: next_reg=%d, expecting positive number because expiration is %d",
376 client_result.next_reg, client_result.expiration));
377 return -280;
378 }
379
380 /* Looks like everything is okay. */
381 if (leave_session) {
382 *p_regc = regc;
383 }
384
385 return 0;
386}
387
388
389/************************************************************************/
390/* Customized tests */
391
392/* Check that client is sending register refresh */
393static int keep_alive_test(const pj_str_t *registrar_uri)
394{
395 enum { TIMEOUT = 40 };
396 struct registrar_cfg server_cfg =
397 /* respond code auth contact exp_prm expires more_contacts */
398 { PJ_TRUE, 200, PJ_FALSE, EXACT, TIMEOUT, 0, {NULL, 0}};
399 struct client client_cfg =
400 /* error code have_reg expiration contact_cnt auth? destroy*/
401 { PJ_FALSE, 200, PJ_TRUE, TIMEOUT, 1, PJ_FALSE,PJ_FALSE};
402 pj_str_t contact = pj_str("<sip:c@C>");
403
404
405 pjsip_regc *regc;
406 unsigned i;
407 int ret;
408
409 ret = do_test("register refresh (takes ~40 secs)", &server_cfg, &client_cfg, registrar_uri,
410 1, &contact, TIMEOUT, PJ_TRUE, &regc);
411 if (ret != 0)
412 return ret;
413
414 /* Reset server response_cnt */
415 registrar.response_cnt = 0;
416
417 /* Wait until keep-alive/refresh is done */
418 for (i=0; i<(TIMEOUT-1)*10 && registrar.response_cnt==0; ++i) {
419 flush_events(100);
420 }
421
422 if (registrar.response_cnt==0) {
423 PJ_LOG(3,(THIS_FILE, " error: no refresh is received"));
424 return -400;
425 }
426
427 if (client_result.error) {
428 PJ_LOG(3,(THIS_FILE, " error: got error"));
429 return -410;
430 }
431 if (client_result.code != 200) {
432 PJ_LOG(3,(THIS_FILE, " error: expecting code=%d, got code=%d",
433 200, client_result.code));
434 return -420;
435 }
436 if (client_result.expiration != TIMEOUT) {
437 PJ_LOG(3,(THIS_FILE, " error: expecting expiration=%d, got expiration=%d",
438 TIMEOUT, client_result.expiration));
439 return -440;
440 }
441 if (client_result.contact_cnt != 1) {
442 PJ_LOG(3,(THIS_FILE, " error: expecting contact_cnt=%d, got contact_cnt=%d",
443 TIMEOUT, client_result.contact_cnt));
444 return -450;
445 }
446 if (client_result.have_reg == 0) {
447 PJ_LOG(3,(THIS_FILE, " error: expecting have_reg=%d, got have_reg=%d",
448 1, client_result.have_reg));
449 return -460;
450 }
451 if (client_result.interval != TIMEOUT) {
452 PJ_LOG(3,(THIS_FILE, " error: interval (%d) is different than expiration (%d)",
453 client_result.interval, TIMEOUT));
454 return -470;
455 }
456 if (client_result.expiration > 0 && client_result.next_reg < 1) {
457 PJ_LOG(3,(THIS_FILE, " error: next_reg=%d, expecting positive number because expiration is %d",
458 client_result.next_reg, client_result.expiration));
459 return -480;
460 }
461
462 /* Success */
463 pjsip_regc_destroy(regc);
464 return 0;
465}
466
467
468/* Send error on refresh */
469static int refresh_error(const pj_str_t *registrar_uri,
470 pj_bool_t destroy_on_cb)
471{
472 enum { TIMEOUT = 40 };
473 struct registrar_cfg server_cfg =
474 /* respond code auth contact exp_prm expires more_contacts */
475 { PJ_TRUE, 200, PJ_FALSE, EXACT, TIMEOUT, 0, {NULL, 0}};
476 struct client client_cfg =
477 /* error code have_reg expiration contact_cnt auth? destroy*/
478 { PJ_FALSE, 200, PJ_TRUE, TIMEOUT, 1, PJ_FALSE,PJ_FALSE};
479 pj_str_t contact = pj_str("<sip:c@C>");
480
481 pjsip_regc *regc;
482 unsigned i;
483 int ret;
484
485 ret = do_test("refresh error (takes ~40 secs)", &server_cfg, &client_cfg, registrar_uri,
486 1, &contact, TIMEOUT, PJ_TRUE, &regc);
487 if (ret != 0)
488 return ret;
489
490 /* Reset server response_cnt */
491 registrar.response_cnt = 0;
492
493 /* inject error for transmission */
494 send_mod.count = 0;
495 send_mod.count_before_reject = 0;
496
497 /* reconfigure client */
498 client_result.done = PJ_FALSE;
499 client_result.destroy_on_cb = destroy_on_cb;
500
501 /* Wait until keep-alive/refresh is done */
502 for (i=0; i<TIMEOUT*10 && !client_result.done; ++i) {
503 flush_events(100);
504 }
505
506 send_mod.count_before_reject = 0xFFFF;
507
508 if (!destroy_on_cb)
509 pjsip_regc_destroy(regc);
510
511 if (!client_result.done) {
512 PJ_LOG(3,(THIS_FILE, " error: test has timed out"));
513 return -500;
514 }
515
516 /* Expecting error */
517 if (client_result.error==PJ_FALSE && client_result.code/100==2) {
518 PJ_LOG(3,(THIS_FILE, " error: expecting error got successfull result"));
519 return -510;
520 }
521
522 return PJ_SUCCESS;
523};
524
525
526/* Send error on refresh */
527static int update_test(const pj_str_t *registrar_uri)
528{
529 enum { TIMEOUT = 40 };
530 struct registrar_cfg server_cfg =
531 /* respond code auth contact exp_prm expires more_contacts */
532 { PJ_TRUE, 200, PJ_FALSE, EXACT, TIMEOUT, 0, {NULL, 0}};
533 struct client client_cfg =
534 /* error code have_reg expiration contact_cnt auth? destroy*/
535 { PJ_FALSE, 200, PJ_TRUE, TIMEOUT, 1, PJ_FALSE,PJ_FALSE};
536 pj_str_t contacts[] = {
537 { "<sip:a>", 7 },
538 { "<sip:b>", 7 },
539 { "<sip:c>", 7 }
540 };
541
542 pjsip_regc *regc;
543 pjsip_contact_hdr *h1, *h2;
544 pjsip_sip_uri *u1, *u2;
545 unsigned i;
546 pj_status_t status;
547 pjsip_tx_data *tdata = NULL;
548 int ret = 0;
549
550 /* initially only has 1 contact */
551 ret = do_test("update test", &server_cfg, &client_cfg, registrar_uri,
552 1, &contacts[0], TIMEOUT, PJ_TRUE, &regc);
553 if (ret != 0) {
554 return -600;
555 }
556
557 /*****
558 * replace the contact with new one
559 */
560 PJ_LOG(3,(THIS_FILE, " replacing contact"));
561 status = pjsip_regc_update_contact(regc, 1, &contacts[1]);
562 if (status != PJ_SUCCESS) {
563 ret = -610;
564 goto on_return;
565 }
566
567 status = pjsip_regc_register(regc, PJ_TRUE, &tdata);
568 if (status != PJ_SUCCESS) {
569 ret = -620;
570 goto on_return;
571 }
572
573 /* Check that the REGISTER contains two Contacts:
574 * - <sip:a>;expires=0,
575 * - <sip:b>
576 */
577 h1 = (pjsip_contact_hdr*)
578 pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CONTACT, NULL);
579 if (!h1) {
580 ret = -630;
581 goto on_return;
582 }
Benny Prijono43047252008-06-04 14:44:29 +0000583 if ((void*)h1->next == (void*)&tdata->msg->hdr)
Benny Prijonodd742da2008-05-17 12:45:00 +0000584 h2 = NULL;
585 else
586 h2 = (pjsip_contact_hdr*)
587 pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CONTACT, h1->next);
588 if (!h2) {
589 ret = -640;
590 goto on_return;
591 }
592 /* must not have other Contact header */
Benny Prijono43047252008-06-04 14:44:29 +0000593 if ((void*)h2->next != (void*)&tdata->msg->hdr &&
Benny Prijonodd742da2008-05-17 12:45:00 +0000594 pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CONTACT, h2->next) != NULL)
595 {
596 ret = -645;
597 goto on_return;
598 }
599
600 u1 = (pjsip_sip_uri*) pjsip_uri_get_uri(h1->uri);
601 u2 = (pjsip_sip_uri*) pjsip_uri_get_uri(h2->uri);
602
603 if (*u1->host.ptr == 'a') {
604 if (h1->expires != 0) {
605 ret = -650;
606 goto on_return;
607 }
608 if (h2->expires == 0) {
609 ret = -660;
610 goto on_return;
611 }
612
613 } else {
614 pj_assert(*u1->host.ptr == 'b');
615 if (h1->expires == 0) {
616 ret = -670;
617 goto on_return;
618 }
619 if (h2->expires != 0) {
620 ret = -680;
621 goto on_return;
622 }
623 }
624
625 /* Destroy tdata */
626 pjsip_tx_data_dec_ref(tdata);
627 tdata = NULL;
628
629
630
631 /**
632 * First loop, it will update with more contacts. Second loop
633 * should do nothing.
634 */
635 for (i=0; i<2; ++i) {
636 if (i==0)
637 PJ_LOG(3,(THIS_FILE, " replacing with more contacts"));
638 else
639 PJ_LOG(3,(THIS_FILE, " updating contacts with same contacts"));
640
641 status = pjsip_regc_update_contact(regc, 2, &contacts[1]);
642 if (status != PJ_SUCCESS) {
643 ret = -710;
644 goto on_return;
645 }
646
647 status = pjsip_regc_register(regc, PJ_TRUE, &tdata);
648 if (status != PJ_SUCCESS) {
649 ret = -720;
650 goto on_return;
651 }
652
653 /* Check that the REGISTER contains two Contacts:
654 * - <sip:b>
655 * - <sip:c>
656 */
657 h1 = (pjsip_contact_hdr*)
658 pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CONTACT, NULL);
659 if (!h1) {
660 ret = -730;
661 goto on_return;
662 }
Benny Prijono43047252008-06-04 14:44:29 +0000663 if ((void*)h1->next == (void*)&tdata->msg->hdr)
Benny Prijonodd742da2008-05-17 12:45:00 +0000664 h2 = NULL;
665 else
666 h2 = (pjsip_contact_hdr*)
667 pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CONTACT, h1->next);
668 if (!h2) {
669 ret = -740;
670 goto on_return;
671 }
672 /* must not have other Contact header */
Benny Prijono43047252008-06-04 14:44:29 +0000673 if ((void*)h2->next != (void*)&tdata->msg->hdr &&
Benny Prijonodd742da2008-05-17 12:45:00 +0000674 pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CONTACT, h2->next) != NULL)
675 {
676 ret = -745;
677 goto on_return;
678 }
679
680 /* both contacts must not have expires=0 parameter */
681 if (h1->expires == 0) {
682 ret = -750;
683 goto on_return;
684 }
685 if (h2->expires == 0) {
686 ret = -760;
687 goto on_return;
688 }
689
690 /* Destroy tdata */
691 pjsip_tx_data_dec_ref(tdata);
692 tdata = NULL;
693 }
694
695on_return:
696 if (tdata) pjsip_tx_data_dec_ref(tdata);
697 pjsip_regc_destroy(regc);
698 return ret;
699};
700
701
702/* send error on authentication */
703static int auth_send_error(const pj_str_t *registrar_uri,
704 pj_bool_t destroy_on_cb)
705{
706 enum { TIMEOUT = 40 };
707 struct registrar_cfg server_cfg =
708 /* respond code auth contact exp_prm expires more_contacts */
709 { PJ_TRUE, 200, PJ_TRUE, EXACT, 75, 0, {NULL, 0}};
710 struct client client_cfg =
711 /* error code have_reg expiration contact_cnt auth? destroy*/
712 { PJ_TRUE, 401, PJ_FALSE, -1, 0, PJ_TRUE, PJ_TRUE};
713 pj_str_t contact = pj_str("<sip:c@C>");
714
715 pjsip_regc *regc;
716 int ret;
717
718 client_cfg.destroy_on_cb = destroy_on_cb;
719
720 /* inject error for second request retry */
721 send_mod.count = 0;
722 send_mod.count_before_reject = 1;
723
724 ret = do_test("auth send error", &server_cfg, &client_cfg, registrar_uri,
725 1, &contact, TIMEOUT, PJ_TRUE, &regc);
726
727 send_mod.count_before_reject = 0xFFFF;
728
729 return ret;
730};
731
732
733
734
735/************************************************************************/
736enum
737{
738 OFF = 1,
739 ON = 2,
740 ON_OFF = 3,
741};
742
743int regc_test(void)
744{
745 struct test_rec {
746 unsigned check_contact;
747 unsigned add_xuid_param;
748
749 const char *title;
750 char *alt_registrar;
751 unsigned contact_cnt;
752 char *contacts[4];
753 unsigned expires;
754 struct registrar_cfg server_cfg;
755 struct client client_cfg;
756 } test_rec[] =
757 {
758 /* immediate error */
759 {
760 OFF, /* check_contact */
761 OFF, /* add_xuid_param */
762 "immediate error", /* title */
763 "sip:unresolved-host-xyy", /* alt_registrar */
764 1, /* contact cnt */
765 { "sip:user@127.0.0.1:5060" }, /* contacts[] */
766 600, /* expires */
767
768 /* registrar config: */
769 /* respond code auth contact exp_prm expires more_contacts */
770 { PJ_FALSE, 200, PJ_FALSE, NONE, 0, 0, {NULL, 0}},
771
772 /* client expected results: */
773 /* error code have_reg expiration contact_cnt auth?*/
774 { PJ_FALSE, 503, PJ_FALSE, -1, 0, PJ_FALSE}
775 },
776
777 /* timeout test */
778 {
779 OFF, /* check_contact */
780 OFF, /* add_xuid_param */
781 "timeout test (takes ~32 secs)",/* title */
782 NULL, /* alt_registrar */
783 1, /* contact cnt */
784 { "sip:user@127.0.0.1:5060" }, /* contacts[] */
785 600, /* expires */
786
787 /* registrar config: */
788 /* respond code auth contact exp_prm expires more_contacts */
789 { PJ_FALSE, 200, PJ_FALSE, NONE, 0, 0, {NULL, 0}},
790
791 /* client expected results: */
792 /* error code have_reg expiration contact_cnt auth? */
793 { PJ_FALSE, 408, PJ_FALSE, -1, 0, PJ_FALSE}
794 },
795
796 /* Basic successful registration scenario:
797 * a good registrar returns the Contact header as is and
798 * add expires parameter. In this test no additional bindings
799 * are returned.
800 */
801 {
802 ON_OFF, /* check_contact */
803 ON_OFF, /* add_xuid_param */
804 "basic", /* title */
805 NULL, /* alt_registrar */
806 1, /* contact cnt */
807 { "<sip:user@127.0.0.1:5060;transport=udp;x-param=1234>" }, /* contacts[] */
808 600, /* expires */
809
810 /* registrar config: */
811 /* respond code auth contact exp_prm expires more_contacts */
812 { PJ_TRUE, 200, PJ_FALSE, EXACT, 75, 65, {NULL, 0}},
813
814 /* client expected results: */
815 /* error code have_reg expiration contact_cnt auth?*/
816 { PJ_FALSE, 200, PJ_TRUE, 75, 1, PJ_FALSE}
817 },
818
819 /* Basic successful registration scenario with authentication
820 */
821 {
822 ON_OFF, /* check_contact */
823 ON_OFF, /* add_xuid_param */
824 "authentication", /* title */
825 NULL, /* alt_registrar */
826 1, /* contact cnt */
827 { "<sip:user@127.0.0.1:5060;transport=udp;x-param=1234>" }, /* contacts[] */
828 600, /* expires */
829
830 /* registrar config: */
831 /* respond code auth contact exp_prm expires more_contacts */
832 { PJ_TRUE, 200, PJ_TRUE, EXACT, 75, 65, {NULL, 0}},
833
834 /* client expected results: */
835 /* error code have_reg expiration contact_cnt auth?*/
836 { PJ_FALSE, 200, PJ_TRUE, 75, 1, PJ_TRUE}
837 },
838
839 /* a good registrar returns the Contact header as is and
840 * add expires parameter. Also it adds bindings from other
841 * clients in this test.
842 */
843 {
844 ON_OFF, /* check_contact */
845 ON, /* add_xuid_param */
846 "more bindings in response", /* title */
847 NULL, /* alt_registrar */
848 1, /* contact cnt */
849 { "<sip:user@127.0.0.1:5060;transport=udp>" }, /* contacts[] */
850 600, /* expires */
851
852 /* registrar config: */
853 /* respond code auth contact exp_prm expires more_contacts */
854 { PJ_TRUE, 200, PJ_FALSE, EXACT, 75, 65, {"<sip:a@a>;expires=70", 0}},
855
856 /* client expected results: */
857 /* error code have_reg expiration contact_cnt auth?*/
858 { PJ_FALSE, 200, PJ_TRUE, 75, 2, PJ_FALSE}
859 },
860
861
862 /* a bad registrar returns modified Contact header, but it
863 * still returns all parameters intact. In this case
864 * the expiration is taken from the expires param because
865 * of matching xuid param or because the number of
866 * Contact header matches.
867 */
868 {
869 ON_OFF, /* check_contact */
870 ON_OFF, /* add_xuid_param */
871 "registrar modifies Contact header", /* title */
872 NULL, /* alt_registrar */
873 1, /* contact cnt */
874 { "<sip:user@127.0.0.1:5060>" }, /* contacts[] */
875 600, /* expires */
876
877 /* registrar config: */
878 /* respond code auth contact exp_prm expires more_contacts */
879 { PJ_TRUE, 200, PJ_FALSE, MODIFIED, 75, 65, {NULL, 0}},
880
881 /* client expected results: */
882 /* error code have_reg expiration contact_cnt auth?*/
883 { PJ_FALSE, 200, PJ_TRUE, 75, 1, PJ_FALSE}
884 },
885
886
887 /* a bad registrar returns modified Contact header, but it
888 * still returns all parameters intact. In addition it returns
889 * bindings from other clients.
890 *
891 * In this case the expiration is taken from the expires param
892 * because add_xuid_param is enabled.
893 */
894 {
895 ON_OFF, /* check_contact */
896 ON, /* add_xuid_param */
897 "registrar modifies Contact header and add bindings", /* title */
898 NULL, /* alt_registrar */
899 1, /* contact cnt */
900 { "<sip:user@127.0.0.1:5060>" }, /* contacts[] */
901 600, /* expires */
902
903 /* registrar config: */
904 /* respond code auth contact exp_prm expires more_contacts */
905 { PJ_TRUE, 200, PJ_FALSE, MODIFIED, 75, 65, {"<sip:a@a>;expires=70", 0}},
906
907 /* client expected results: */
908 /* error code have_reg expiration contact_cnt auth?*/
909 { PJ_FALSE, 200, PJ_TRUE, 75, 2, PJ_FALSE}
910 },
911
912
913 /* a bad registrar returns completely different Contact and
914 * all parameters are gone. In this case the expiration is
915 * also taken from the expires param since the number of
916 * header matches.
917 */
918 {
919 ON_OFF, /* check_contact */
920 ON_OFF, /* add_xuid_param */
921 "registrar replaces Contact header", /* title */
922 NULL, /* alt_registrar */
923 1, /* contact cnt */
924 { "<sip:user@127.0.0.1:5060>" }, /* contacts[] */
925 600, /* expires */
926
927 /* registrar config: */
928 /* respond code auth contact exp_prm expires more_contacts */
929 { PJ_TRUE, 202, PJ_FALSE, NONE, 0, 65, {"<sip:a@A>;expires=75", 0}},
930
931 /* client expected results: */
932 /* error code have_reg expiration contact_cnt auth?*/
933 { PJ_FALSE, 202, PJ_TRUE, 75, 1, PJ_FALSE}
934 },
935
936
937 /* a bad registrar returns completely different Contact (and
938 * all parameters are gone) and it also includes bindings from
939 * other clients.
940 * In this case the expiration is taken from the Expires header.
941 */
942 {
943 ON_OFF, /* check_contact */
944 ON_OFF, /* add_xuid_param */
945 " as above with additional bindings", /* title */
946 NULL, /* alt_registrar */
947 1, /* contact cnt */
948 { "<sip:user@127.0.0.1:5060>" }, /* contacts[] */
949 600, /* expires */
950
951 /* registrar config: */
952 /* respond code auth contact exp_prm expires more_contacts */
953 { PJ_TRUE, 200, PJ_FALSE, NONE, 0, 65, {"<sip:a@A>;expires=75, <sip:b@B;expires=70>", 0}},
954
955 /* client expected results: */
956 /* error code have_reg expiration contact_cnt auth?*/
957 { PJ_FALSE, 200, PJ_TRUE, 65, 2, PJ_FALSE}
958 },
959
960 /* the registrar doesn't return any bindings, but for some
961 * reason it includes an Expires header.
962 * In this case the expiration is taken from the Expires header.
963 */
964 {
965 ON_OFF, /* check_contact */
966 ON_OFF, /* add_xuid_param */
967 "no Contact but with Expires", /* title */
968 NULL, /* alt_registrar */
969 1, /* contact cnt */
970 { "<sip:user@127.0.0.1:5060>" }, /* contacts[] */
971 600, /* expires */
972
973 /* registrar config: */
974 /* respond code auth contact exp_prm expires more_contacts */
975 { PJ_TRUE, 200, PJ_FALSE, NONE, 0, 65, {NULL, 0}},
976
977 /* client expected results: */
978 /* error code have_reg expiration contact_cnt auth?*/
979 { PJ_FALSE, 200, PJ_TRUE, 65, 0, PJ_FALSE}
980 },
981
982 /* Neither Contact header nor Expires header are present.
983 * In this case the expiration is taken from the request.
984 */
985 {
986 ON_OFF, /* check_contact */
987 ON_OFF, /* add_xuid_param */
988 "no Contact and no Expires", /* title */
989 NULL, /* alt_registrar */
990 1, /* contact cnt */
991 { "<sip:user@127.0.0.1:5060>" },/* contacts[] */
992 600, /* expires */
993
994 /* registrar config: */
995 /* respond code auth contact exp_prm expires more_contacts */
996 { PJ_TRUE, 200, PJ_FALSE, NONE, 0, 0, {NULL, 0}},
997
998 /* client expected results: */
999 /* error code have_reg expiration contact_cnt auth?*/
1000 { PJ_FALSE, 200, PJ_TRUE, 600, 0, PJ_FALSE}
1001 },
1002 };
1003
1004 unsigned i;
1005 pj_sockaddr_in addr;
1006 pjsip_transport *udp = NULL;
1007 pj_uint16_t port;
1008 char registrar_uri_buf[80];
1009 pj_str_t registrar_uri;
1010 int rc = 0;
1011
1012 pj_sockaddr_in_init(&addr, 0, 0);
1013
1014 /* Acquire existing transport, if any */
1015 rc = pjsip_endpt_acquire_transport(endpt, PJSIP_TRANSPORT_UDP, &addr, sizeof(addr), NULL, &udp);
1016 if (rc == PJ_SUCCESS) {
1017 port = pj_sockaddr_get_port(&udp->local_addr);
1018 pjsip_transport_dec_ref(udp);
1019 udp = NULL;
1020 } else {
1021 rc = pjsip_udp_transport_start(endpt, NULL, NULL, 1, &udp);
1022 if (rc != PJ_SUCCESS) {
1023 app_perror(" error creating UDP transport", rc);
1024 rc = -2;
1025 goto on_return;
1026 }
1027
1028 port = pj_sockaddr_get_port(&udp->local_addr);
1029 }
1030
1031 /* Register registrar module */
1032 rc = pjsip_endpt_register_module(endpt, &registrar.mod);
1033 if (rc != PJ_SUCCESS) {
1034 app_perror(" error registering module", rc);
1035 rc = -3;
1036 goto on_return;
1037 }
1038
1039 /* Register send module */
1040 rc = pjsip_endpt_register_module(endpt, &send_mod.mod);
1041 if (rc != PJ_SUCCESS) {
1042 app_perror(" error registering module", rc);
1043 rc = -3;
1044 goto on_return;
1045 }
1046
1047 pj_ansi_snprintf(registrar_uri_buf, sizeof(registrar_uri_buf),
1048 "sip:127.0.0.1:%d", (int)port);
1049 registrar_uri = pj_str(registrar_uri_buf);
1050
1051 for (i=0; i<PJ_ARRAY_SIZE(test_rec); ++i) {
1052 struct test_rec *t = &test_rec[i];
1053 unsigned j, x;
1054 pj_str_t reg_uri;
1055 pj_str_t contacts[8];
1056
1057 /* Fill in the registrar address if it's not specified */
1058 if (t->alt_registrar == NULL) {
1059 reg_uri = registrar_uri;
1060 } else {
1061 reg_uri = pj_str(t->alt_registrar);
1062 }
1063
1064 /* Build contact pj_str_t's */
1065 for (j=0; j<t->contact_cnt; ++j) {
1066 contacts[j] = pj_str(t->contacts[j]);
1067 }
1068
1069 /* Normalize more_contacts field */
1070 if (t->server_cfg.more_contacts.ptr)
1071 t->server_cfg.more_contacts.slen = strlen(t->server_cfg.more_contacts.ptr);
1072
1073 /* Do tests with three combinations:
1074 * - check_contact on/off
1075 * - add_xuid_param on/off
1076 * - destroy_on_callback on/off
1077 */
1078 for (x=1; x<=2; ++x) {
1079 unsigned y;
1080
1081 if ((t->check_contact & x) == 0)
1082 continue;
1083
1084 pjsip_cfg()->regc.check_contact = (x-1);
1085
1086 for (y=1; y<=2; ++y) {
1087 unsigned z;
1088
1089 if ((t->add_xuid_param & y) == 0)
1090 continue;
1091
1092 pjsip_cfg()->regc.add_xuid_param = (y-1);
1093
1094 for (z=0; z<=1; ++z) {
1095 char new_title[200];
1096
1097 t->client_cfg.destroy_on_cb = z;
1098
1099 sprintf(new_title, "%s [check=%d, xuid=%d, destroy=%d]",
1100 t->title, pjsip_cfg()->regc.check_contact,
1101 pjsip_cfg()->regc.add_xuid_param, z);
1102 rc = do_test(new_title, &t->server_cfg, &t->client_cfg,
1103 &reg_uri, t->contact_cnt, contacts,
1104 t->expires, PJ_FALSE, NULL);
1105 if (rc != 0)
1106 goto on_return;
1107 }
1108
1109 }
1110 }
1111
1112 /* Sleep between test groups to avoid using up too many
1113 * active transactions.
1114 */
1115 pj_thread_sleep(1000);
1116 }
1117
1118 /* keep-alive test */
1119 rc = keep_alive_test(&registrar_uri);
1120 if (rc != 0)
1121 goto on_return;
1122
1123 /* Send error on refresh without destroy on callback */
1124 rc = refresh_error(&registrar_uri, PJ_FALSE);
1125 if (rc != 0)
1126 goto on_return;
1127
1128 /* Send error on refresh, destroy on callback */
1129 rc = refresh_error(&registrar_uri, PJ_TRUE);
1130 if (rc != 0)
1131 goto on_return;
1132
1133 /* Updating contact */
1134 rc = update_test(&registrar_uri);
1135 if (rc != 0)
1136 goto on_return;
1137
1138 /* Send error during auth, don't destroy on callback */
1139 rc = auth_send_error(&registrar_uri, PJ_FALSE);
1140 if (rc != 0)
1141 goto on_return;
1142
1143 /* Send error during auth, destroy on callback */
1144 rc = auth_send_error(&registrar_uri, PJ_FALSE);
1145 if (rc != 0)
1146 goto on_return;
1147
1148on_return:
1149 if (registrar.mod.id != -1) {
1150 pjsip_endpt_unregister_module(endpt, &registrar.mod);
1151 }
1152 if (send_mod.mod.id != -1) {
1153 pjsip_endpt_unregister_module(endpt, &send_mod.mod);
1154 }
1155 if (udp) {
1156 pjsip_transport_dec_ref(udp);
1157 }
1158 return rc;
1159}
1160
1161