Add support for ICE/STUN/TURN in res_rtp_asterisk and chan_sip.
[asterisk/asterisk.git] / res / pjproject / pjsip / src / test / regc_test.c
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
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
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 PJ_TRUE;
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     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 */
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
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 */
265 static struct client client_result;
266 static 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 */
394 static 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 */
470 static 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 */
528 static 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     }
584     if ((void*)h1->next == (void*)&tdata->msg->hdr)
585         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 */
594     if ((void*)h2->next != (void*)&tdata->msg->hdr &&
595         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         }
664         if ((void*)h1->next == (void*)&tdata->msg->hdr)
665             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 */
674         if ((void*)h2->next != (void*)&tdata->msg->hdr &&
675             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
696 on_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 */
704 static 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 /************************************************************************/
737 enum
738 {
739     OFF     = 1,
740     ON      = 2,
741     ON_OFF  = 3,
742 };
743
744 int 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, 502,    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
1149 on_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