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