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 	&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 
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 						       &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 (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(&registrar.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, &regc);
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, &regc);
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, &regc);
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, &regc);
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, &regc);
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, &registrar.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 				 &reg_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(&registrar_uri);
1125     if (rc != 0)
1126 	goto on_return;
1127 
1128     /* Send error on refresh without destroy on callback */
1129     rc = refresh_error(&registrar_uri, PJ_FALSE);
1130     if (rc != 0)
1131 	goto on_return;
1132 
1133     /* Send error on refresh, destroy on callback */
1134     rc = refresh_error(&registrar_uri, PJ_TRUE);
1135     if (rc != 0)
1136 	goto on_return;
1137 
1138     /* Updating contact */
1139     rc = update_test(&registrar_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(&registrar_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(&registrar_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, &registrar.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