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