2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 2010, Digium, Inc.
6 * See http://www.asterisk.org for more information about
7 * the Asterisk project. Please do not directly contact
8 * any of the maintainers of this project for assistance;
9 * the project provides a web site, mailing lists and IRC
10 * channels for your use.
12 * This program is free software, distributed under the terms of
13 * the GNU General Public License Version 2. See the LICENSE file
14 * at the top of the source tree.
19 * \brief sip request parsing functions and unit tests
24 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
26 #include "include/sip.h"
27 #include "include/sip_utils.h"
28 #include "include/reqresp_parser.h"
34 /*! \brief * parses a URI in its components.*/
35 int parse_uri_full(char *uri, const char *scheme, char **user, char **pass,
36 char **domain, struct uriparams *params, char **headers,
39 char *userinfo = NULL;
40 char *parameters = NULL;
41 char *endparams = NULL;
46 * Initialize requested strings - some functions don't care if parse_uri fails
47 * and will attempt to use string pointers passed into parse_uri even after a
66 /* check for valid input */
67 if (ast_strlen_zero(uri)) {
73 char *scheme2 = ast_strdupa(scheme);
74 char *cur = strsep(&scheme2, ",");
75 for (; !ast_strlen_zero(cur); cur = strsep(&scheme2, ",")) {
77 if (!strncasecmp(uri, cur, l)) {
82 if (ast_strlen_zero(cur)) {
83 ast_debug(1, "No supported scheme found in '%s' using the scheme[s] %s\n", uri, scheme);
89 /* if we don't want to split around domain, keep everything as a
90 * userinfo - cos thats how old parse_uri operated*/
94 if ((c = strchr(uri, '@'))) {
98 uri = c; /* userinfo can contain ? and ; chars so step forward before looking for params and headers */
100 /* domain-only URI, according to the SIP RFC. */
108 if (pass && (c = strchr(userinfo, ':'))) { /* user:password */
120 /* strip [?headers] from end of uri - even if no header pointer exists*/
121 if ((c = strrchr(uri, '?'))) {
127 if ((c = strrchr(uri, ';'))) {
130 c = strrchr(uri, '\0');
132 uri = c; /* residue */
135 } else if (headers) {
139 /* parse parameters */
140 endparams = strchr(parameters,'\0');
141 if ((c = strchr(parameters, ';'))) {
145 parameters = endparams;
149 char *rem = parameters; /* unparsed or unrecognised remainder */
154 params->transport = "";
163 while ((value = strchr(parameters, '=')) || (lr = !strncmp(parameters, "lr", 2))) {
164 /* The while condition will not continue evaluation to set lr if it matches "lr=" */
171 if ((c = strchr(value, ';'))) {
175 parameters = endparams;
178 if (!strcmp(label, "transport")) {
179 if (params) {params->transport=value;}
181 } else if (!strcmp(label, "user")) {
182 if (params) {params->user=value;}
184 } else if (!strcmp(label, "method")) {
185 if (params) {params->method=value;}
187 } else if (!strcmp(label, "ttl")) {
188 if (params) {params->ttl=value;}
190 } else if (!strcmp(label, "maddr")) {
191 if (params) {params->maddr=value;}
193 /* Treat "lr", "lr=yes", "lr=on", "lr=1", "lr=almostanything" as lr enabled and "", "lr=no", "lr=off", "lr=0", "lr=" and "lranything" as lr disabled */
194 } else if ((!strcmp(label, "lr") && strcmp(value, "no") && strcmp(value, "off") && strcmp(value, "0") && strcmp(value, "")) || ((lr) && strcmp(value, "lr"))) {
195 if (params) {params->lr=1;}
206 if (rem > uri) { /* no headers */
220 AST_TEST_DEFINE(sip_parse_uri_fully_test)
222 int res = AST_TEST_PASS;
224 char *user, *pass, *domain, *headers, *residue;
225 struct uriparams params;
235 struct uriparams *paramsptr;
241 struct uriparams params;
242 AST_LIST_ENTRY(testdata) list;
246 struct testdata *testdataptr;
248 static AST_LIST_HEAD_NOLOCK(testdataliststruct, testdata) testdatalist;
250 struct testdata td1 = {
251 .desc = "no headers",
252 .uri = "sip:user:secret@host:5060;param=discard;transport=tcp;param2=residue",
255 .domainptr = &domain,
256 .headersptr = &headers,
257 .residueptr = &residue,
258 .paramsptr = ¶ms,
261 .domain = "host:5060",
263 .residue = "param2=residue",
264 .params.transport = "tcp",
269 struct testdata td2 = {
270 .desc = "with headers",
271 .uri = "sip:user:secret@host:5060;param=discard;transport=tcp;param2=discard2?header=blah&header2=blah2;param3=residue",
274 .domainptr = &domain,
275 .headersptr = &headers,
276 .residueptr = &residue,
277 .paramsptr = ¶ms,
280 .domain = "host:5060",
281 .headers = "header=blah&header2=blah2",
282 .residue = "param3=residue",
283 .params.transport = "tcp",
288 struct testdata td3 = {
289 .desc = "difficult user",
290 .uri = "sip:-_.!~*'()&=+$,;?/:secret@host:5060;transport=tcp",
293 .domainptr = &domain,
294 .headersptr = &headers,
295 .residueptr = &residue,
296 .paramsptr = ¶ms,
297 .user = "-_.!~*'()&=+$,;?/",
299 .domain = "host:5060",
302 .params.transport = "tcp",
307 struct testdata td4 = {
308 .desc = "difficult pass",
309 .uri = "sip:user:-_.!~*'()&=+$,@host:5060;transport=tcp",
312 .domainptr = &domain,
313 .headersptr = &headers,
314 .residueptr = &residue,
315 .paramsptr = ¶ms,
317 .pass = "-_.!~*'()&=+$,",
318 .domain = "host:5060",
321 .params.transport = "tcp",
326 struct testdata td5 = {
327 .desc = "difficult host",
328 .uri = "sip:user:secret@1-1.a-1.:5060;transport=tcp",
331 .domainptr = &domain,
332 .headersptr = &headers,
333 .residueptr = &residue,
334 .paramsptr = ¶ms,
337 .domain = "1-1.a-1.:5060",
340 .params.transport = "tcp",
345 struct testdata td6 = {
346 .desc = "difficult params near transport",
347 .uri = "sip:user:secret@host:5060;-_.!~*'()[]/:&+$=-_.!~*'()[]/:&+$;transport=tcp",
350 .domainptr = &domain,
351 .headersptr = &headers,
352 .residueptr = &residue,
353 .paramsptr = ¶ms,
356 .domain = "host:5060",
359 .params.transport = "tcp",
364 struct testdata td7 = {
365 .desc = "difficult params near headers",
366 .uri = "sip:user:secret@host:5060;-_.!~*'()[]/:&+$=-_.!~*'()[]/:&+$?header=blah&header2=blah2;-_.!~*'()[]/:&+$=residue",
369 .domainptr = &domain,
370 .headersptr = &headers,
371 .residueptr = &residue,
372 .paramsptr = ¶ms,
375 .domain = "host:5060",
376 .headers = "header=blah&header2=blah2",
377 .residue = "-_.!~*'()[]/:&+$=residue",
378 .params.transport = "",
383 struct testdata td8 = {
384 .desc = "lr parameter",
385 .uri = "sip:user:secret@host:5060;param=discard;lr?header=blah",
388 .domainptr = &domain,
389 .headersptr = &headers,
390 .residueptr = &residue,
391 .paramsptr = ¶ms,
394 .domain = "host:5060",
395 .headers = "header=blah",
397 .params.transport = "",
402 struct testdata td9 = {
403 .desc = "alternative lr parameter",
404 .uri = "sip:user:secret@host:5060;param=discard;lr=yes?header=blah",
407 .domainptr = &domain,
408 .headersptr = &headers,
409 .residueptr = &residue,
410 .paramsptr = ¶ms,
413 .domain = "host:5060",
414 .headers = "header=blah",
416 .params.transport = "",
421 struct testdata td10 = {
422 .desc = "no lr parameter",
423 .uri = "sip:user:secret@host:5060;paramlr=lr;lr=no;lr=off;lr=0;lr=;=lr;lrextra;lrparam2=lr?header=blah",
426 .domainptr = &domain,
427 .headersptr = &headers,
428 .residueptr = &residue,
429 .paramsptr = ¶ms,
432 .domain = "host:5060",
433 .headers = "header=blah",
435 .params.transport = "",
441 AST_LIST_HEAD_SET_NOLOCK(&testdatalist, &td1);
442 AST_LIST_INSERT_TAIL(&testdatalist, &td2, list);
443 AST_LIST_INSERT_TAIL(&testdatalist, &td3, list);
444 AST_LIST_INSERT_TAIL(&testdatalist, &td4, list);
445 AST_LIST_INSERT_TAIL(&testdatalist, &td5, list);
446 AST_LIST_INSERT_TAIL(&testdatalist, &td6, list);
447 AST_LIST_INSERT_TAIL(&testdatalist, &td7, list);
448 AST_LIST_INSERT_TAIL(&testdatalist, &td8, list);
449 AST_LIST_INSERT_TAIL(&testdatalist, &td9, list);
450 AST_LIST_INSERT_TAIL(&testdatalist, &td10, list);
455 info->name = "sip_uri_full_parse_test";
456 info->category = "/channels/chan_sip/";
457 info->summary = "tests sip full uri parsing";
459 "Tests full parsing of various URIs "
460 "Verifies output matches expected behavior.";
461 return AST_TEST_NOT_RUN;
466 AST_LIST_TRAVERSE(&testdatalist, testdataptr, list) {
467 user = pass = domain = headers = residue = NULL;
468 params.transport = params.user = params.method = params.ttl = params.maddr = NULL;
471 ast_copy_string(uri,testdataptr->uri,sizeof(uri));
472 if (parse_uri_full(uri, "sip:,sips:", testdataptr->userptr,
473 testdataptr->passptr, testdataptr->domainptr,
474 testdataptr->paramsptr,
475 testdataptr->headersptr,
476 testdataptr->residueptr) ||
477 ((testdataptr->userptr) && strcmp(testdataptr->user, user)) ||
478 ((testdataptr->passptr) && strcmp(testdataptr->pass, pass)) ||
479 ((testdataptr->domainptr) && strcmp(testdataptr->domain, domain)) ||
480 ((testdataptr->headersptr) && strcmp(testdataptr->headers, headers)) ||
481 ((testdataptr->residueptr) && strcmp(testdataptr->residue, residue)) ||
482 ((testdataptr->paramsptr) && strcmp(testdataptr->params.transport,params.transport)) ||
483 ((testdataptr->paramsptr) && (testdataptr->params.lr != params.lr)) ||
484 ((testdataptr->paramsptr) && strcmp(testdataptr->params.user,params.user))
486 ast_test_status_update(test, "Sub-Test: %s, failed.\n", testdataptr->desc);
496 int parse_uri(char *uri, const char *scheme, char **user, char **pass,
497 char **domain, char **transport) {
500 struct uriparams params;
503 ret = parse_uri_full(uri, scheme, user, pass, domain, ¶ms, &headers, NULL);
505 *transport=params.transport;
510 AST_TEST_DEFINE(sip_parse_uri_test)
512 int res = AST_TEST_PASS;
513 char *name, *pass, *domain, *transport;
514 char uri1[] = "sip:name@host";
515 char uri2[] = "sip:name@host;transport=tcp";
516 char uri3[] = "sip:name:secret@host;transport=tcp";
517 char uri4[] = "sip:name:secret@host:port;transport=tcp?headers=%40%40testblah&headers2=blah%20blah";
518 /* test 5 is for NULL input */
519 char uri6[] = "sip:name:secret@host:port;transport=tcp?headers=%40%40testblah&headers2=blah%20blah";
520 char uri7[] = "sip:name:secret@host:port;transport=tcp?headers=%40%40testblah&headers2=blah%20blah";
521 char uri8[] = "sip:host";
522 char uri9[] = "sip:host:port;transport=tcp?headers=%40%40testblah&headers2=blah%20blah";
523 char uri10[] = "host:port;transport=tcp?headers=%40%40testblah&headers2=blah%20blah";
524 char uri11[] = "host";
528 info->name = "sip_uri_parse_test";
529 info->category = "/channels/chan_sip/";
530 info->summary = "tests sip uri parsing";
532 "Tests parsing of various URIs "
533 "Verifies output matches expected behavior.";
534 return AST_TEST_NOT_RUN;
539 /* Test 1, simple URI */
540 name = pass = domain = transport = NULL;
541 if (parse_uri(uri1, "sip:,sips:", &name, &pass, &domain, &transport) ||
542 strcmp(name, "name") ||
543 !ast_strlen_zero(pass) ||
544 strcmp(domain, "host") ||
545 !ast_strlen_zero(transport)) {
546 ast_test_status_update(test, "Test 1: simple uri failed. \n");
550 /* Test 2, add tcp transport */
551 name = pass = domain = transport = NULL;
552 if (parse_uri(uri2, "sip:,sips:", &name, &pass, &domain, &transport) ||
553 strcmp(name, "name") ||
554 !ast_strlen_zero(pass) ||
555 strcmp(domain, "host") ||
556 strcmp(transport, "tcp")) {
557 ast_test_status_update(test, "Test 2: uri with addtion of tcp transport failed. \n");
561 /* Test 3, add secret */
562 name = pass = domain = transport = NULL;
563 if (parse_uri(uri3, "sip:,sips:", &name, &pass, &domain, &transport) ||
564 strcmp(name, "name") ||
565 strcmp(pass, "secret") ||
566 strcmp(domain, "host") ||
567 strcmp(transport, "tcp")) {
568 ast_test_status_update(test, "Test 3: uri with addition of secret failed.\n");
572 /* Test 4, add port and unparsed header field*/
573 name = pass = domain = transport = NULL;
574 if (parse_uri(uri4, "sip:,sips:", &name, &pass, &domain, &transport) ||
575 strcmp(name, "name") ||
576 strcmp(pass, "secret") ||
577 strcmp(domain, "host:port") ||
578 strcmp(transport, "tcp")) {
579 ast_test_status_update(test, "Test 4: add port and unparsed header field failed.\n");
583 /* Test 5, verify parse_uri does not crash when given a NULL uri */
584 name = pass = domain = transport = NULL;
585 if (!parse_uri(NULL, "sip:,sips:", &name, &pass, &domain, &transport)) {
586 ast_test_status_update(test, "Test 5: passing a NULL uri failed.\n");
590 /* Test 6, verify parse_uri does not crash when given a NULL output parameters */
591 name = pass = domain = transport = NULL;
592 if (parse_uri(uri6, "sip:,sips:", NULL, NULL, NULL, NULL)) {
593 ast_test_status_update(test, "Test 6: passing NULL output parameters failed.\n");
597 /* Test 7, verify parse_uri returns user:secret and domain when no port or secret output parameters are supplied. */
598 name = pass = domain = transport = NULL;
599 if (parse_uri(uri7, "sip:,sips:", &name, NULL, &domain, NULL) ||
600 strcmp(name, "name:secret") ||
601 strcmp(domain, "host:port")) {
603 ast_test_status_update(test, "Test 7: providing no port and secret output parameters failed.\n");
607 /* Test 8, verify parse_uri can handle a domain only uri */
608 name = pass = domain = transport = NULL;
609 if (parse_uri(uri8, "sip:,sips:", &name, &pass, &domain, &transport) ||
610 strcmp(domain, "host") ||
611 !ast_strlen_zero(name)) {
612 ast_test_status_update(test, "Test 8: add port and unparsed header field failed.\n");
616 /* Test 9, add port and unparsed header field with domain only uri*/
617 name = pass = domain = transport = NULL;
618 if (parse_uri(uri9, "sip:,sips:", &name, &pass, &domain, &transport) ||
619 !ast_strlen_zero(name) ||
620 !ast_strlen_zero(pass) ||
621 strcmp(domain, "host:port") ||
622 strcmp(transport, "tcp")) {
623 ast_test_status_update(test, "Test 9: domain only uri failed \n");
627 /* Test 10, handle invalid/missing "sip:,sips:" scheme
628 * we expect parse_uri to return an error, but still parse
629 * the results correctly here */
630 name = pass = domain = transport = NULL;
631 if (!parse_uri(uri10, "sip:,sips:", &name, &pass, &domain, &transport) ||
632 !ast_strlen_zero(name) ||
633 !ast_strlen_zero(pass) ||
634 strcmp(domain, "host:port") ||
635 strcmp(transport, "tcp")) {
636 ast_test_status_update(test, "Test 10: missing \"sip:sips:\" scheme failed\n");
640 /* Test 11, simple domain only URI with missing scheme
641 * we expect parse_uri to return an error, but still parse
642 * the results correctly here */
643 name = pass = domain = transport = NULL;
644 if (!parse_uri(uri11, "sip:,sips:", &name, &pass, &domain, &transport) ||
645 !ast_strlen_zero(name) ||
646 !ast_strlen_zero(pass) ||
647 strcmp(domain, "host") ||
648 !ast_strlen_zero(transport)) {
649 ast_test_status_update(test, "Test 11: simple uri with missing scheme failed. \n");
656 /*! \brief Get caller id name from SIP headers, copy into output buffer
658 * \retval input string pointer placed after display-name field if possible
660 const char *get_calleridname(const char *input, char *output, size_t outputsize)
664 * From = ( "From" / "f" ) HCOLON from-spec
665 * from-spec = ( name-addr / addr-spec ) *( SEMI from-param )
666 * name-addr = [ display-name ] LAQUOT addr-spec RAQUOT
667 * display-name = *(token LWS)/ quoted-string
668 * token = 1*(alphanum / "-" / "." / "!" / "%" / "*"
669 * / "_" / "+" / "`" / "'" / "~" )
670 * quoted-string = SWS DQUOTE *(qdtext / quoted-pair ) DQUOTE
671 * qdtext = LWS / %x21 / %x23-5B / %x5D-7E
673 * quoted-pair = "\" (%x00-09 / %x0B-0C / %x0E-7F)
675 * HCOLON = *WSP ":" SWS
677 * LWS = *[*WSP CRLF] 1*WSP
680 * Deviations from it:
681 * - following CRLF's in LWS is not done (here at least)
682 * - ascii NUL is never legal as it terminates the C-string
683 * - utf8-nonascii is not checked for validity
685 char *orig_output = output;
686 const char *orig_input = input;
688 /* clear any empty characters in the beginning */
689 input = ast_skip_blanks(input);
691 /* no data at all or no storage room? */
692 if (!input || *input == '<' || !outputsize || !output) {
696 /* make sure the output buffer is initilized */
699 /* make room for '\0' at the end of the output buffer */
702 /* quoted-string rules */
703 if (input[0] == '"') {
704 input++; /* skip the first " */
706 for (;((outputsize > 0) && *input); input++) {
707 if (*input == '"') { /* end of quoted-string */
709 } else if (*input == 0x5c) { /* quoted-pair = "\" (%x00-09 / %x0B-0C / %x0E-7F) */
711 if (!*input || (unsigned char)*input > 0x7f || *input == 0xa || *input == 0xd) {
712 continue; /* not a valid quoted-pair, so skip it */
714 } else if (((*input != 0x9) && ((unsigned char) *input < 0x20)) ||
716 continue; /* skip this invalid character. */
723 /* if this is successful, input should be at the ending quote */
724 if (!input || *input != '"') {
725 ast_log(LOG_WARNING, "No ending quote for display-name was found\n");
730 /* make sure input is past the last quote */
733 /* terminate outbuf */
735 } else { /* either an addr-spec or tokenLWS-combo */
736 for (;((outputsize > 0) && *input); input++) {
737 /* token or WSP (without LWS) */
738 if ((*input >= '0' && *input <= '9') || (*input >= 'A' && *input <= 'Z')
739 || (*input >= 'a' && *input <= 'z') || *input == '-' || *input == '.'
740 || *input == '!' || *input == '%' || *input == '*' || *input == '_'
741 || *input == '+' || *input == '`' || *input == '\'' || *input == '~'
742 || *input == 0x9 || *input == ' ') {
745 } else if (*input == '<') { /* end of tokenLWS-combo */
746 /* we could assert that the previous char is LWS, but we don't care */
748 } else if (*input == ':') {
749 /* This invalid character which indicates this is addr-spec rather than display-name. */
752 } else { /* else, invalid character we can skip. */
753 continue; /* skip this character */
757 if (*input != '<') { /* if we never found the start of addr-spec then this is invalid */
762 /* set NULL while trimming trailing whitespace */
765 } while (*output == 0x9 || *output == ' '); /* we won't go past orig_output as first was a non-space */
771 AST_TEST_DEFINE(get_calleridname_test)
773 int res = AST_TEST_PASS;
774 const char *in1 = "\" quoted-text internal \\\" quote \"<stuff>";
775 const char *in2 = " token text with no quotes <stuff>";
776 const char *overflow1 = " \"quoted-text overflow 1234567890123456789012345678901234567890\" <stuff>";
777 const char *noendquote = " \"quoted-text no end <stuff>";
778 const char *addrspec = " \"sip:blah@blah <stuff>";
779 const char *no_quotes_no_brackets = "blah@blah";
780 const char *after_dname;
785 info->name = "sip_get_calleridname_test";
786 info->category = "/channels/chan_sip/";
787 info->summary = "decodes callerid name from sip header";
788 info->description = "Decodes display-name field of sip header. Checks for valid output and expected failure cases.";
789 return AST_TEST_NOT_RUN;
794 /* quoted-text with backslash escaped quote */
795 after_dname = get_calleridname(in1, dname, sizeof(dname));
796 ast_test_status_update(test, "display-name1: %s\nafter: %s\n", dname, after_dname);
797 if (strcmp(dname, " quoted-text internal \" quote ")) {
798 ast_test_status_update(test, "display-name1 test failed\n");
803 after_dname = get_calleridname(in2, dname, sizeof(dname));
804 ast_test_status_update(test, "display-name2: %s\nafter: %s\n", dname, after_dname);
805 if (strcmp(dname, "token text with no quotes")) {
806 ast_test_status_update(test, "display-name2 test failed\n");
810 /* quoted-text buffer overflow */
811 after_dname = get_calleridname(overflow1, dname, sizeof(dname));
812 ast_test_status_update(test, "overflow display-name1: %s\nafter: %s\n", dname, after_dname);
813 if (*dname != '\0' && after_dname != overflow1) {
814 ast_test_status_update(test, "overflow display-name1 test failed\n");
818 /* quoted-text buffer with no terminating end quote */
819 after_dname = get_calleridname(noendquote, dname, sizeof(dname));
820 ast_test_status_update(test, "noendquote display-name1: %s\nafter: %s\n", dname, after_dname);
821 if (*dname != '\0' && after_dname != noendquote) {
822 ast_test_status_update(test, "no end quote for quoted-text display-name failed\n");
826 /* addr-spec rather than display-name. */
827 after_dname = get_calleridname(addrspec, dname, sizeof(dname));
828 ast_test_status_update(test, "noendquote display-name1: %s\nafter: %s\n", dname, after_dname);
829 if (*dname != '\0' && after_dname != addrspec) {
830 ast_test_status_update(test, "detection of addr-spec failed\n");
834 /* no quotes, no brackets */
835 after_dname = get_calleridname(no_quotes_no_brackets, dname, sizeof(dname));
836 ast_test_status_update(test, "no_quotes_no_brackets display-name1: %s\nafter: %s\n", dname, after_dname);
837 if (*dname != '\0' && after_dname != no_quotes_no_brackets) {
838 ast_test_status_update(test, "detection of addr-spec failed\n");
846 int get_name_and_number(const char *hdr, char **name, char **number)
849 char tmp_name[50] = { 0, };
850 char *tmp_number = NULL;
854 if (!name || !number || ast_strlen_zero(hdr)) {
860 ast_copy_string(header, hdr, sizeof(header));
862 /* strip the display-name portion off the beginning of the header. */
863 get_calleridname(header, tmp_name, sizeof(tmp_name));
865 /* get uri within < > brackets */
866 tmp_number = get_in_brackets(header);
868 /* parse out the number here */
869 if (parse_uri(tmp_number, "sip:,sips:", &tmp_number, &dummy, &domain, NULL) || ast_strlen_zero(tmp_number)) {
870 ast_log(LOG_ERROR, "can not parse name and number from sip header.\n");
874 /* number is not option, and must be present at this point */
875 *number = ast_strdup(tmp_number);
876 ast_uri_decode(*number, ast_uri_sip_user);
878 /* name is optional and may not be present at this point */
879 if (!ast_strlen_zero(tmp_name)) {
880 *name = ast_strdup(tmp_name);
886 AST_TEST_DEFINE(get_name_and_number_test)
888 int res = AST_TEST_PASS;
891 const char *in1 = "NAME <sip:NUMBER@place>";
892 const char *in2 = "\"NA><ME\" <sip:NUMBER@place>";
893 const char *in3 = "NAME";
894 const char *in4 = "<sip:NUMBER@place>";
895 const char *in5 = "This is a screwed up string <sip:LOLCLOWNS<sip:>@place>";
899 info->name = "sip_get_name_and_number_test";
900 info->category = "/channels/chan_sip/";
901 info->summary = "Tests getting name and number from sip header";
903 "Runs through various test situations in which a name and "
904 "and number can be retrieved from a sip header.";
905 return AST_TEST_NOT_RUN;
910 /* Test 1. get name and number */
911 number = name = NULL;
912 if ((get_name_and_number(in1, &name, &number)) ||
913 strcmp(name, "NAME") ||
914 strcmp(number, "NUMBER")) {
916 ast_test_status_update(test, "Test 1, simple get name and number failed.\n");
922 /* Test 2. get quoted name and number */
923 number = name = NULL;
924 if ((get_name_and_number(in2, &name, &number)) ||
925 strcmp(name, "NA><ME") ||
926 strcmp(number, "NUMBER")) {
928 ast_test_status_update(test, "Test 2, get quoted name and number failed.\n");
934 /* Test 3. name only */
935 number = name = NULL;
936 if (!(get_name_and_number(in3, &name, &number))) {
938 ast_test_status_update(test, "Test 3, get name only was expected to fail but did not.\n");
944 /* Test 4. number only */
945 number = name = NULL;
946 if ((get_name_and_number(in4, &name, &number)) ||
947 !ast_strlen_zero(name) ||
948 strcmp(number, "NUMBER")) {
950 ast_test_status_update(test, "Test 4, get number with no name present failed.\n");
956 /* Test 5. malformed string, since number can not be parsed, this should return an error. */
957 number = name = NULL;
958 if (!(get_name_and_number(in5, &name, &number)) ||
959 !ast_strlen_zero(name) ||
960 !ast_strlen_zero(number)) {
962 ast_test_status_update(test, "Test 5, processing malformed string failed.\n");
968 /* Test 6. NULL output parameters */
969 number = name = NULL;
970 if (!(get_name_and_number(in5, NULL, NULL))) {
972 ast_test_status_update(test, "Test 6, NULL output parameters failed.\n");
976 /* Test 7. NULL input parameter */
977 number = name = NULL;
978 if (!(get_name_and_number(NULL, &name, &number)) ||
979 !ast_strlen_zero(name) ||
980 !ast_strlen_zero(number)) {
982 ast_test_status_update(test, "Test 7, NULL input parameter failed.\n");
991 int get_in_brackets_full(char *tmp,char **out,char **residue)
993 const char *parse = tmp;
995 char *second_bracket;
1004 if (ast_strlen_zero(tmp)) {
1009 * Skip any quoted text until we find the part in brackets.
1010 * On any error give up and return -1
1012 while ( (first_bracket = strchr(parse, '<')) ) {
1013 char *first_quote = strchr(parse, '"');
1015 if (!first_quote || first_quote >= first_bracket) {
1016 break; /* no need to look at quoted part */
1018 /* the bracket is within quotes, so ignore it */
1019 parse = find_closing_quote(first_quote + 1, NULL);
1021 ast_log(LOG_WARNING, "No closing quote found in '%s'\n", tmp);
1027 /* If no first bracket then still look for a second bracket as some other parsing functions
1028 may overwrite first bracket with NULL when terminating a token based display-name. As this
1029 only affects token based display-names there is no danger of brackets being in quotes */
1030 if (first_bracket) {
1031 parse = first_bracket;
1036 if ((second_bracket = strchr(parse, '>'))) {
1037 *second_bracket++ = '\0';
1039 *out = first_bracket;
1042 *residue = second_bracket;
1047 if ((first_bracket)) {
1048 ast_log(LOG_WARNING, "No closing bracket found in '%s'\n", tmp);
1059 char *get_in_brackets(char *tmp)
1063 if ((get_in_brackets_full(tmp, &out, NULL))) {
1069 AST_TEST_DEFINE(get_in_brackets_test)
1071 int res = AST_TEST_PASS;
1072 char *in_brackets = "<sip:name:secret@host:port;transport=tcp?headers=testblah&headers2=blahblah>";
1073 char no_name[] = "<sip:name:secret@host:port;transport=tcp?headers=testblah&headers2=blahblah>";
1074 char quoted_string[] = "\"I'm a quote stri><ng\" <sip:name:secret@host:port;transport=tcp?headers=testblah&headers2=blahblah>";
1075 char missing_end_quote[] = "\"I'm a quote string <sip:name:secret@host:port;transport=tcp?headers=testblah&headers2=blahblah>";
1076 char name_no_quotes[] = "name not in quotes <sip:name:secret@host:port;transport=tcp?headers=testblah&headers2=blahblah>";
1077 char no_end_bracket[] = "name not in quotes <sip:name:secret@host:port;transport=tcp?headers=testblah&headers2=blahblah";
1078 char no_name_no_brackets[] = "sip:name@host";
1083 info->name = "sip_get_in_brackets_test";
1084 info->category = "/channels/chan_sip/";
1085 info->summary = "Tests getting a sip uri in <> brackets within a sip header.";
1087 "Runs through various test situations in which a sip uri "
1088 "in angle brackets needs to be retrieved";
1089 return AST_TEST_NOT_RUN;
1094 /* Test 1, simple get in brackets */
1095 if (!(uri = get_in_brackets(no_name)) || !(strcmp(uri, in_brackets))) {
1097 ast_test_status_update(test, "Test 1, simple get in brackets failed.\n");
1098 res = AST_TEST_FAIL;
1101 /* Test 2, starts with quoted string */
1102 if (!(uri = get_in_brackets(quoted_string)) || !(strcmp(uri, in_brackets))) {
1104 ast_test_status_update(test, "Test 2, get in brackets with quoted string in front failed.\n");
1105 res = AST_TEST_FAIL;
1108 /* Test 3, missing end quote */
1109 if (!(uri = get_in_brackets(missing_end_quote)) || !(strcmp(uri, in_brackets))) {
1111 ast_test_status_update(test, "Test 3, missing end quote failed.\n");
1112 res = AST_TEST_FAIL;
1115 /* Test 4, starts with a name not in quotes */
1116 if (!(uri = get_in_brackets(name_no_quotes)) || !(strcmp(uri, in_brackets))) {
1118 ast_test_status_update(test, "Test 4, passing name not in quotes failed.\n");
1119 res = AST_TEST_FAIL;
1122 /* Test 5, no end bracket, should just return everything after the first '<' */
1123 if (!(uri = get_in_brackets(no_end_bracket)) || !(strcmp(uri, in_brackets))) {
1125 ast_test_status_update(test, "Test 5, no end bracket failed.\n");
1126 res = AST_TEST_FAIL;
1129 /* Test 6, NULL input */
1130 if ((uri = get_in_brackets(NULL))) {
1132 ast_test_status_update(test, "Test 6, NULL input failed.\n");
1133 res = AST_TEST_FAIL;
1136 /* Test 7, no name, and no brackets. */
1137 if (!(uri = get_in_brackets(no_name_no_brackets)) || (strcmp(uri, "sip:name@host"))) {
1139 ast_test_status_update(test, "Test 7 failed. %s\n", uri);
1140 res = AST_TEST_FAIL;
1147 int parse_name_andor_addr(char *uri, const char *scheme, char **name,
1148 char **user, char **pass, char **domain,
1149 struct uriparams *params, char **headers,
1153 char **residue2=residue;
1156 get_calleridname(uri,buf,sizeof(buf));
1159 ret = get_in_brackets_full(uri,&uri,residue);
1160 if (ret == 0) { /* uri is in brackets so do not treat unknown trailing uri parameters as potential messageheader parameters */
1161 *residue = *residue + 1; /* step over the first semicolon so as per parse uri residue */
1165 return parse_uri_full(uri, scheme, user, pass, domain, params, headers,
1169 AST_TEST_DEFINE(parse_name_andor_addr_test)
1171 int res = AST_TEST_PASS;
1173 char *name, *user, *pass, *domain, *headers, *residue;
1174 struct uriparams params;
1185 struct uriparams *paramsptr;
1192 struct uriparams params;
1193 AST_LIST_ENTRY(testdata) list;
1196 struct testdata *testdataptr;
1198 static AST_LIST_HEAD_NOLOCK(testdataliststruct, testdata) testdatalist;
1200 struct testdata td1 = {
1201 .desc = "quotes and brackets",
1202 .uri = "\"name :@ \" <sip:user:secret@host:5060;param=discard;transport=tcp>;tag=tag",
1206 .domainptr = &domain,
1207 .headersptr = &headers,
1208 .residueptr = &residue,
1209 .paramsptr = ¶ms,
1213 .domain = "host:5060",
1215 .residue = "tag=tag",
1216 .params.transport = "tcp",
1221 struct testdata td2 = {
1222 .desc = "no quotes",
1223 .uri = "givenname familyname <sip:user:secret@host:5060;param=discard;transport=tcp>;expires=3600",
1227 .domainptr = &domain,
1228 .headersptr = &headers,
1229 .residueptr = &residue,
1230 .paramsptr = ¶ms,
1231 .name = "givenname familyname",
1234 .domain = "host:5060",
1236 .residue = "expires=3600",
1237 .params.transport = "tcp",
1242 struct testdata td3 = {
1243 .desc = "no brackets",
1244 .uri = "sip:user:secret@host:5060;param=discard;transport=tcp;q=1",
1248 .domainptr = &domain,
1249 .headersptr = &headers,
1250 .residueptr = &residue,
1251 .paramsptr = ¶ms,
1255 .domain = "host:5060",
1258 .params.transport = "tcp",
1263 struct testdata td4 = {
1264 .desc = "just host",
1269 .domainptr = &domain,
1270 .headersptr = &headers,
1271 .residueptr = &residue,
1272 .paramsptr = ¶ms,
1279 .params.transport = "",
1285 AST_LIST_HEAD_SET_NOLOCK(&testdatalist, &td1);
1286 AST_LIST_INSERT_TAIL(&testdatalist, &td2, list);
1287 AST_LIST_INSERT_TAIL(&testdatalist, &td3, list);
1288 AST_LIST_INSERT_TAIL(&testdatalist, &td4, list);
1293 info->name = "parse_name_andor_addr_test";
1294 info->category = "/channels/chan_sip/";
1295 info->summary = "tests parsing of name_andor_addr abnf structure";
1297 "Tests parsing of abnf name-andor-addr = name-addr / addr-spec "
1298 "Verifies output matches expected behavior.";
1299 return AST_TEST_NOT_RUN;
1304 AST_LIST_TRAVERSE(&testdatalist, testdataptr, list) {
1305 name = user = pass = domain = headers = residue = NULL;
1306 params.transport = params.user = params.method = params.ttl = params.maddr = NULL;
1308 ast_copy_string(uri,testdataptr->uri,sizeof(uri));
1309 if (parse_name_andor_addr(uri, "sip:,sips:",
1310 testdataptr->nameptr,
1311 testdataptr->userptr,
1312 testdataptr->passptr,
1313 testdataptr->domainptr,
1314 testdataptr->paramsptr,
1315 testdataptr->headersptr,
1316 testdataptr->residueptr) ||
1317 ((testdataptr->nameptr) && strcmp(testdataptr->name, name)) ||
1318 ((testdataptr->userptr) && strcmp(testdataptr->user, user)) ||
1319 ((testdataptr->passptr) && strcmp(testdataptr->pass, pass)) ||
1320 ((testdataptr->domainptr) && strcmp(testdataptr->domain, domain)) ||
1321 ((testdataptr->headersptr) && strcmp(testdataptr->headers, headers)) ||
1322 ((testdataptr->residueptr) && strcmp(testdataptr->residue, residue)) ||
1323 ((testdataptr->paramsptr) && strcmp(testdataptr->params.transport,params.transport)) ||
1324 ((testdataptr->paramsptr) && strcmp(testdataptr->params.user,params.user))
1326 ast_test_status_update(test, "Sub-Test: %s,failed.\n", testdataptr->desc);
1327 res = AST_TEST_FAIL;
1335 int get_comma(char *in, char **out) {
1342 /* Skip any quoted text */
1344 if ((c = strchr(parse, '"'))) {
1345 in = (char *)find_closing_quote((const char *)c + 1, NULL);
1347 ast_log(LOG_WARNING, "No closing quote found in '%s'\n", c);
1359 /* Skip any userinfo components of a uri as they may contain commas */
1360 if ((c = strchr(parse,'@'))) {
1363 if ((out) && (c = strchr(parse,','))) {
1371 int parse_contact_header(char *contactheader, struct contactliststruct *contactlist) {
1378 struct contact *contact=NULL;
1380 if (*contactheader == '*') {
1384 contact = malloc(sizeof(*contact));
1386 AST_LIST_HEAD_SET_NOLOCK(contactlist, contact);
1387 while ((last = get_comma(contactheader,&comma)) != -1) {
1389 res = parse_name_andor_addr(contactheader, "sip:,sips:",
1390 &contact->name, &contact->user,
1391 &contact->pass, &contact->domain,
1392 &contact->params, &contact->headers,
1398 /* parse contact params */
1399 contact->expires = contact->q = "";
1401 while ((value = strchr(residue,'='))) {
1405 if ((residue = strchr(value,';'))) {
1411 if (!strcmp(param,"expires")) {
1412 contact->expires = value;
1413 } else if (!strcmp(param,"q")) {
1421 contactheader = comma;
1423 contact = malloc(sizeof(*contact));
1424 AST_LIST_INSERT_TAIL(contactlist, contact, list);
1430 AST_TEST_DEFINE(parse_contact_header_test)
1432 int res = AST_TEST_PASS;
1433 char contactheader[1024];
1435 struct contactliststruct contactlist;
1436 struct contactliststruct *contactlistptr=&contactlist;
1440 char *contactheader;
1442 struct contactliststruct *contactlist;
1444 AST_LIST_ENTRY(testdata) list;
1447 struct testdata *testdataptr;
1448 struct contact *tdcontactptr;
1449 struct contact *contactptr;
1451 static AST_LIST_HEAD_NOLOCK(testdataliststruct, testdata) testdatalist;
1452 struct contactliststruct contactlist1, contactlist2;
1454 struct testdata td1 = {
1455 .desc = "single contact",
1456 .contactheader = "\"name :@;?&,\" <sip:user:secret@host:5082;param=discard;transport=tcp>;expires=3600",
1457 .contactlist = &contactlist1,
1460 struct contact contact11 = {
1461 .name = "name :@;?&,",
1464 .domain = "host:5082",
1465 .params.transport = "tcp",
1473 struct testdata td2 = {
1474 .desc = "multiple contacts",
1475 .contactheader = "sip:,user1,:,secret1,@host1;ttl=7;q=1;expires=3600,sips:host2",
1476 .contactlist = &contactlist2,
1479 struct contact contact21 = {
1482 .pass = ",secret1,",
1484 .params.transport = "",
1491 struct contact contact22 = {
1496 .params.transport = "",
1504 struct testdata td3 = {
1505 .desc = "star - all contacts",
1506 .contactheader = "*",
1511 AST_LIST_HEAD_SET_NOLOCK(&testdatalist, &td1);
1512 AST_LIST_INSERT_TAIL(&testdatalist, &td2, list);
1513 AST_LIST_INSERT_TAIL(&testdatalist, &td3, list);
1515 AST_LIST_HEAD_SET_NOLOCK(&contactlist1, &contact11);
1517 AST_LIST_HEAD_SET_NOLOCK(&contactlist2, &contact21);
1518 AST_LIST_INSERT_TAIL(&contactlist2, &contact22, list);
1523 info->name = "parse_contact_header_test";
1524 info->category = "/channels/chan_sip/";
1525 info->summary = "tests parsing of sip contact header";
1527 "Tests parsing of a contact header including those with multiple contacts "
1528 "Verifies output matches expected behavior.";
1529 return AST_TEST_NOT_RUN;
1534 AST_LIST_TRAVERSE(&testdatalist, testdataptr, list) {
1535 ast_copy_string(contactheader,testdataptr->contactheader,sizeof(contactheader));
1536 star = parse_contact_header(contactheader,contactlistptr);
1537 if (testdataptr->star) {
1538 /* expecting star rather than list of contacts */
1540 ast_test_status_update(test, "Sub-Test: %s,failed.\n", testdataptr->desc);
1541 res = AST_TEST_FAIL;
1545 contactptr = AST_LIST_FIRST(contactlistptr);
1546 AST_LIST_TRAVERSE(testdataptr->contactlist, tdcontactptr, list) {
1548 strcmp(tdcontactptr->name, contactptr->name) ||
1549 strcmp(tdcontactptr->user, contactptr->user) ||
1550 strcmp(tdcontactptr->pass, contactptr->pass) ||
1551 strcmp(tdcontactptr->domain, contactptr->domain) ||
1552 strcmp(tdcontactptr->headers, contactptr->headers) ||
1553 strcmp(tdcontactptr->expires, contactptr->expires) ||
1554 strcmp(tdcontactptr->q, contactptr->q) ||
1555 strcmp(tdcontactptr->params.transport, contactptr->params.transport) ||
1556 strcmp(tdcontactptr->params.ttl, contactptr->params.ttl) ||
1557 (tdcontactptr->params.lr != contactptr->params.lr)
1559 ast_test_status_update(test, "Sub-Test: %s,failed.\n", testdataptr->desc);
1560 res = AST_TEST_FAIL;
1564 contactptr = AST_LIST_NEXT(contactptr,list);
1574 * \brief Parse supported header in incoming packet
1576 * \details This function parses through the options parameters and
1577 * builds a bit field representing all the SIP options in that field. When an
1578 * item is found that is not supported, it is copied to the unsupported
1581 * \param option list
1582 * \param unsupported out buffer (optional)
1583 * \param unsupported out buffer length (optional)
1585 unsigned int parse_sip_options(const char *options, char *unsupported, size_t unsupported_len)
1589 int i, found, supported;
1590 unsigned int profile = 0;
1592 char *out = unsupported;
1593 size_t outlen = unsupported_len;
1594 char *cur_out = out;
1596 if (out && (outlen > 0)) {
1597 memset(out, 0, outlen);
1600 if (ast_strlen_zero(options) )
1603 temp = ast_strdupa(options);
1605 ast_debug(3, "Begin: parsing SIP \"Supported: %s\"\n", options);
1607 for (next = temp; next; next = sep) {
1610 if ((sep = strchr(next, ',')) != NULL) {
1614 /* trim leading and trailing whitespace */
1615 next = ast_strip(next);
1617 if (ast_strlen_zero(next)) {
1618 continue; /* if there is a blank argument in there just skip it */
1621 ast_debug(3, "Found SIP option: -%s-\n", next);
1622 for (i = 0; i < ARRAY_LEN(sip_options); i++) {
1623 if (!strcasecmp(next, sip_options[i].text)) {
1624 profile |= sip_options[i].id;
1625 if (sip_options[i].supported == SUPPORTED) {
1629 ast_debug(3, "Matched SIP option: %s\n", next);
1634 /* If option is not supported, add to unsupported out buffer */
1635 if (!supported && out && outlen) {
1636 size_t copylen = strlen(next);
1637 size_t cur_outlen = strlen(out);
1638 /* Check to see if there is enough room to store this option.
1639 * Copy length is string length plus 2 for the ',' and '\0' */
1640 if ((cur_outlen + copylen + 2) < outlen) {
1641 /* if this isn't the first item, add the ',' */
1647 ast_copy_string(cur_out, next, (outlen - cur_outlen));
1653 profile |= SIP_OPT_UNKNOWN;
1654 if (!strncasecmp(next, "x-", 2))
1655 ast_debug(3, "Found private SIP option, not supported: %s\n", next);
1657 ast_debug(3, "Found no match for SIP option: %s (Please file bug report!)\n", next);
1664 AST_TEST_DEFINE(sip_parse_options_test)
1666 int res = AST_TEST_PASS;
1667 char unsupported[64];
1668 unsigned int option_profile = 0;
1671 char *input_options;
1672 char *expected_unsupported;
1673 unsigned int expected_profile;
1674 AST_LIST_ENTRY(testdata) list;
1677 struct testdata *testdataptr;
1678 static AST_LIST_HEAD_NOLOCK(testdataliststruct, testdata) testdatalist;
1680 struct testdata test1 = {
1681 .name = "test_all_unsupported",
1682 .input_options = "unsupported1,,, ,unsupported2,unsupported3,unsupported4",
1683 .expected_unsupported = "unsupported1,unsupported2,unsupported3,unsupported4",
1684 .expected_profile = SIP_OPT_UNKNOWN,
1686 struct testdata test2 = {
1687 .name = "test_all_unsupported_one_supported",
1688 .input_options = " unsupported1, replaces, unsupported3 , , , ,unsupported4",
1689 .expected_unsupported = "unsupported1,unsupported3,unsupported4",
1690 .expected_profile = SIP_OPT_UNKNOWN | SIP_OPT_REPLACES
1692 struct testdata test3 = {
1693 .name = "test_two_supported_two_unsupported",
1694 .input_options = ",, timer ,replaces ,unsupported3,unsupported4",
1695 .expected_unsupported = "unsupported3,unsupported4",
1696 .expected_profile = SIP_OPT_UNKNOWN | SIP_OPT_REPLACES | SIP_OPT_TIMER,
1699 struct testdata test4 = {
1700 .name = "test_all_supported",
1701 .input_options = "timer,replaces",
1702 .expected_unsupported = "",
1703 .expected_profile = SIP_OPT_REPLACES | SIP_OPT_TIMER,
1706 struct testdata test5 = {
1707 .name = "test_all_supported_redundant",
1708 .input_options = "timer,replaces,timer,replace,timer,replaces",
1709 .expected_unsupported = "",
1710 .expected_profile = SIP_OPT_REPLACES | SIP_OPT_TIMER,
1712 struct testdata test6 = {
1713 .name = "test_buffer_overflow",
1714 .input_options = "unsupported1,replaces,timer,unsupported4,unsupported_huge____"
1715 "____________________________________,__________________________________________"
1716 "________________________________________________",
1717 .expected_unsupported = "unsupported1,unsupported4",
1718 .expected_profile = SIP_OPT_UNKNOWN | SIP_OPT_REPLACES | SIP_OPT_TIMER,
1720 struct testdata test7 = {
1721 .name = "test_null_input",
1722 .input_options = NULL,
1723 .expected_unsupported = "",
1724 .expected_profile = 0,
1726 struct testdata test8 = {
1727 .name = "test_whitespace_input",
1728 .input_options = " ",
1729 .expected_unsupported = "",
1730 .expected_profile = 0,
1732 struct testdata test9 = {
1733 .name = "test_whitespace_plus_option_input",
1734 .input_options = " , , ,timer , , , , , ",
1735 .expected_unsupported = "",
1736 .expected_profile = SIP_OPT_TIMER,
1741 info->name = "sip_parse_options_test";
1742 info->category = "/channels/chan_sip/";
1743 info->summary = "Tests parsing of sip options";
1745 "Tests parsing of SIP options from supported and required "
1746 "header fields. Verifies when unsupported options are encountered "
1747 "that they are appended to the unsupported out buffer and that the "
1748 "correct bit field representnig the option profile is returned.";
1749 return AST_TEST_NOT_RUN;
1754 AST_LIST_HEAD_SET_NOLOCK(&testdatalist, &test1);
1755 AST_LIST_INSERT_TAIL(&testdatalist, &test2, list);
1756 AST_LIST_INSERT_TAIL(&testdatalist, &test3, list);
1757 AST_LIST_INSERT_TAIL(&testdatalist, &test4, list);
1758 AST_LIST_INSERT_TAIL(&testdatalist, &test5, list);
1759 AST_LIST_INSERT_TAIL(&testdatalist, &test6, list);
1760 AST_LIST_INSERT_TAIL(&testdatalist, &test7, list);
1761 AST_LIST_INSERT_TAIL(&testdatalist, &test8, list);
1762 AST_LIST_INSERT_TAIL(&testdatalist, &test9, list);
1764 /* Test with unsupported char buffer */
1765 AST_LIST_TRAVERSE(&testdatalist, testdataptr, list) {
1766 option_profile = parse_sip_options(testdataptr->input_options, unsupported, ARRAY_LEN(unsupported));
1767 if (option_profile != testdataptr->expected_profile ||
1768 strcmp(unsupported, testdataptr->expected_unsupported)) {
1769 ast_test_status_update(test, "Test with output buffer \"%s\", expected unsupported: %s actual unsupported:"
1770 "%s expected bit profile: %x actual bit profile: %x\n",
1772 testdataptr->expected_unsupported,
1774 testdataptr->expected_profile,
1776 res = AST_TEST_FAIL;
1778 ast_test_status_update(test, "\"%s\" passed got expected unsupported: %s and bit profile: %x\n",
1784 option_profile = parse_sip_options(testdataptr->input_options, NULL, 0);
1785 if (option_profile != testdataptr->expected_profile) {
1786 ast_test_status_update(test, "NULL output test \"%s\", expected bit profile: %x actual bit profile: %x\n",
1788 testdataptr->expected_profile,
1790 res = AST_TEST_FAIL;
1792 ast_test_status_update(test, "\"%s\" with NULL output buf passed, bit profile: %x\n",
1801 /*! \brief helper routine for sip_uri_cmp to compare URI parameters
1803 * This takes the parameters from two SIP URIs and determines
1804 * if the URIs match. The rules for parameters *suck*. Here's a breakdown
1805 * 1. If a parameter appears in both URIs, then they must have the same value
1806 * in order for the URIs to match
1807 * 2. If one URI has a user, maddr, ttl, or method parameter, then the other
1808 * URI must also have that parameter and must have the same value
1809 * in order for the URIs to match
1810 * 3. All other headers appearing in only one URI are not considered when
1811 * determining if URIs match
1813 * \param input1 Parameters from URI 1
1814 * \param input2 Parameters from URI 2
1815 * \retval 0 URIs' parameters match
1816 * \retval nonzero URIs' parameters do not match
1818 static int sip_uri_params_cmp(const char *input1, const char *input2)
1820 char *params1 = NULL;
1821 char *params2 = NULL;
1824 int zerolength1 = 0;
1825 int zerolength2 = 0;
1829 int methodmatch = 0;
1831 if (ast_strlen_zero(input1)) {
1834 params1 = ast_strdupa(input1);
1836 if (ast_strlen_zero(input2)) {
1839 params2 = ast_strdupa(input2);
1842 /* Quick optimization. If both params are zero-length, then
1845 if (zerolength1 && zerolength2) {
1849 for (pos1 = strsep(¶ms1, ";"); pos1; pos1 = strsep(¶ms1, ";")) {
1850 char *value1 = pos1;
1851 char *name1 = strsep(&value1, "=");
1852 char *params2dup = NULL;
1857 /* Checkpoint reached. We have the name and value parsed for param1
1858 * We have to duplicate params2 each time through this loop
1859 * or else the inner loop below will not work properly.
1862 params2dup = ast_strdupa(params2);
1864 for (pos2 = strsep(¶ms2dup, ";"); pos2; pos2 = strsep(¶ms2dup, ";")) {
1866 char *value2 = strchr(pos2, '=');
1872 if (!strcasecmp(name1, name2)) {
1873 if (strcasecmp(value1, value2)) {
1881 /* Check to see if the parameter is one of the 'must-match' parameters */
1882 if (!strcasecmp(name1, "maddr")) {
1888 } else if (!strcasecmp(name1, "ttl")) {
1894 } else if (!strcasecmp(name1, "user")) {
1900 } else if (!strcasecmp(name1, "method")) {
1909 /* We've made it out of that horrible O(m*n) construct and there are no
1910 * failures yet. We're not done yet, though, because params2 could have
1911 * an maddr, ttl, user, or method header and params1 did not.
1913 for (pos2 = strsep(¶ms2, ";"); pos2; pos2 = strsep(¶ms2, ";")) {
1914 char *value2 = pos2;
1915 char *name2 = strsep(&value2, "=");
1919 if ((!strcasecmp(name2, "maddr") && !maddrmatch) ||
1920 (!strcasecmp(name2, "ttl") && !ttlmatch) ||
1921 (!strcasecmp(name2, "user") && !usermatch) ||
1922 (!strcasecmp(name2, "method") && !methodmatch)) {
1932 /*! \brief helper routine for sip_uri_cmp to compare URI headers
1934 * This takes the headers from two SIP URIs and determines
1935 * if the URIs match. The rules for headers is simple. If a header
1936 * appears in one URI, then it must also appear in the other URI. The
1937 * order in which the headers appear does not matter.
1939 * \param input1 Headers from URI 1
1940 * \param input2 Headers from URI 2
1941 * \retval 0 URI headers match
1942 * \retval nonzero URI headers do not match
1944 static int sip_uri_headers_cmp(const char *input1, const char *input2)
1946 char *headers1 = NULL;
1947 char *headers2 = NULL;
1948 int zerolength1 = 0;
1949 int zerolength2 = 0;
1953 if (ast_strlen_zero(input1)) {
1956 headers1 = ast_strdupa(input1);
1959 if (ast_strlen_zero(input2)) {
1962 headers2 = ast_strdupa(input2);
1965 /* If one URI contains no headers and the other
1966 * does, then they cannot possibly match
1968 if (zerolength1 != zerolength2) {
1972 if (zerolength1 && zerolength2)
1975 /* At this point, we can definitively state that both inputs are
1976 * not zero-length. First, one more optimization. If the length
1977 * of the headers is not equal, then we definitely have no match
1979 if (strlen(headers1) != strlen(headers2)) {
1983 for (header1 = strsep(&headers1, "&"); header1; header1 = strsep(&headers1, "&")) {
1984 if (!strcasestr(headers2, header1)) {
1994 * \brief Compare domain sections of SIP URIs
1996 * For hostnames, a case insensitive string comparison is
1997 * used. For IP addresses, a binary comparison is used. This
1998 * is mainly because IPv6 addresses have many ways of writing
2001 * For specifics about IP address comparison, see the following
2002 * document: http://tools.ietf.org/html/draft-ietf-sip-ipv6-abnf-fix-05
2004 * \param host1 The domain from the first URI
2005 * \param host2 THe domain from the second URI
2006 * \retval 0 The domains match
2007 * \retval nonzero The domains do not match
2009 static int sip_uri_domain_cmp(const char *host1, const char *host2)
2011 struct ast_sockaddr addr1;
2012 struct ast_sockaddr addr2;
2016 addr1_parsed = ast_sockaddr_parse(&addr1, host1, 0);
2017 addr2_parsed = ast_sockaddr_parse(&addr2, host2, 0);
2019 if (addr1_parsed != addr2_parsed) {
2020 /* One domain was an IP address and the other had
2021 * a host name. FAIL!
2026 /* Both are host names. A string comparison will work
2027 * perfectly here. Specifying the "C" locale ensures that
2028 * The LC_CTYPE conventions use those defined in ANSI C,
2031 if (!addr1_parsed) {
2032 #ifdef HAVE_XLOCALE_H
2034 return strcasecmp(host1, host2);
2036 return strcasecmp_l(host1, host2, c_locale);
2039 return strcasecmp(host1, host2);
2043 /* Both contain IP addresses */
2044 return ast_sockaddr_cmp(&addr1, &addr2);
2047 int sip_uri_cmp(const char *input1, const char *input2)
2060 /* XXX It would be really nice if we could just use parse_uri_full() here
2061 * to separate the components of the URI, but unfortunately it is written
2062 * in a way that can cause URI parameters to be discarded.
2065 if (!input1 || !input2) {
2069 uri1 = ast_strdupa(input1);
2070 uri2 = ast_strdupa(input2);
2072 ast_uri_decode(uri1, ast_uri_sip_user);
2073 ast_uri_decode(uri2, ast_uri_sip_user);
2075 uri_scheme1 = strsep(&uri1, ":");
2076 uri_scheme2 = strsep(&uri2, ":");
2078 if (strcmp(uri_scheme1, uri_scheme2)) {
2082 /* This function is tailored for SIP and SIPS URIs. There's no
2083 * need to check uri_scheme2 since we have determined uri_scheme1
2084 * and uri_scheme2 are equivalent already.
2086 if (strcmp(uri_scheme1, "sip") && strcmp(uri_scheme1, "sips")) {
2090 if (ast_strlen_zero(uri1) || ast_strlen_zero(uri2)) {
2094 if ((host1 = strchr(uri1, '@'))) {
2097 if ((host2 = strchr(uri2, '@'))) {
2101 /* Check for mismatched username and passwords. This is the
2102 * only case-sensitive comparison of a SIP URI
2104 if ((host1 && !host2) ||
2105 (host2 && !host1) ||
2106 (host1 && host2 && strcmp(uri1, uri2))) {
2117 /* Strip off the parameters and headers so we can compare
2121 if ((params1 = strchr(host1, ';'))) {
2124 if ((params2 = strchr(host2, ';'))) {
2128 /* Headers come after parameters, but there may be headers without
2129 * parameters, thus the S_OR
2131 if ((headers1 = strchr(S_OR(params1, host1), '?'))) {
2134 if ((headers2 = strchr(S_OR(params2, host2), '?'))) {
2138 if (sip_uri_domain_cmp(host1, host2)) {
2142 /* Headers have easier rules to follow, so do those first */
2143 if (sip_uri_headers_cmp(headers1, headers2)) {
2147 /* And now the parameters. Ugh */
2148 return sip_uri_params_cmp(params1, params2);
2151 #define URI_CMP_MATCH 0
2152 #define URI_CMP_NOMATCH 1
2154 AST_TEST_DEFINE(sip_uri_cmp_test)
2156 static const struct {
2159 int expected_result;
2160 } uri_cmp_tests [] = {
2161 /* These are identical, so they match */
2162 { "sip:bob@example.com", "sip:bob@example.com", URI_CMP_MATCH },
2163 /* Different usernames. No match */
2164 { "sip:alice@example.com", "sip:bob@example.com", URI_CMP_NOMATCH },
2165 /* Different hosts. No match */
2166 { "sip:bob@example.com", "sip:bob@examplez.com", URI_CMP_NOMATCH },
2167 /* Now start using IP addresses. Identical, so they match */
2168 { "sip:bob@1.2.3.4", "sip:bob@1.2.3.4", URI_CMP_MATCH },
2169 /* Two identical IPv4 addresses represented differently. Match */
2170 { "sip:bob@1.2.3.4", "sip:bob@001.002.003.004", URI_CMP_MATCH },
2171 /* Logically equivalent IPv4 Address and hostname. No Match */
2172 { "sip:bob@127.0.0.1", "sip:bob@localhost", URI_CMP_NOMATCH },
2173 /* Logically equivalent IPv6 address and hostname. No Match */
2174 { "sip:bob@[::1]", "sip:bob@localhost", URI_CMP_NOMATCH },
2175 /* Try an IPv6 one as well */
2176 { "sip:bob@[2001:db8::1234]", "sip:bob@[2001:db8::1234]", URI_CMP_MATCH },
2177 /* Two identical IPv6 addresses represented differently. Match */
2178 { "sip:bob@[2001:db8::1234]", "sip:bob@[2001:0db8::1234]", URI_CMP_MATCH },
2179 /* Different ports. No match */
2180 { "sip:bob@1.2.3.4:5060", "sip:bob@1.2.3.4:5061", URI_CMP_NOMATCH },
2181 /* Same port logically, but only one address specifies it. No match */
2182 { "sip:bob@1.2.3.4:5060", "sip:bob@1.2.3.4", URI_CMP_NOMATCH },
2183 /* And for safety, try with IPv6 */
2184 { "sip:bob@[2001:db8:1234]:5060", "sip:bob@[2001:db8:1234]", URI_CMP_NOMATCH },
2185 /* User comparison is case sensitive. No match */
2186 { "sip:bob@example.com", "sip:BOB@example.com", URI_CMP_NOMATCH },
2187 /* Host comparison is case insensitive. Match */
2188 { "sip:bob@example.com", "sip:bob@EXAMPLE.COM", URI_CMP_MATCH },
2189 /* Add headers to the URI. Identical, so they match */
2190 { "sip:bob@example.com?header1=value1&header2=value2", "sip:bob@example.com?header1=value1&header2=value2", URI_CMP_MATCH },
2191 /* Headers in URI 1 are not in URI 2. No Match */
2192 { "sip:bob@example.com?header1=value1&header2=value2", "sip:bob@example.com", URI_CMP_NOMATCH },
2193 /* Header present in both URIs does not have matching values. No match */
2194 { "sip:bob@example.com?header1=value1&header2=value2", "sip:bob@example.com?header1=value1&header2=value3", URI_CMP_NOMATCH },
2195 /* Add parameters to the URI. Identical so they match */
2196 { "sip:bob@example.com;param1=value1;param2=value2", "sip:bob@example.com;param1=value1;param2=value2", URI_CMP_MATCH },
2197 /* Same parameters in both URIs but appear in different order. Match */
2198 { "sip:bob@example.com;param2=value2;param1=value1", "sip:bob@example.com;param1=value1;param2=value2", URI_CMP_MATCH },
2199 /* params in URI 1 are not in URI 2. Match */
2200 { "sip:bob@example.com;param1=value1;param2=value2", "sip:bob@example.com", URI_CMP_MATCH },
2201 /* param present in both URIs does not have matching values. No match */
2202 { "sip:bob@example.com;param1=value1;param2=value2", "sip:bob@example.com;param1=value1;param2=value3", URI_CMP_NOMATCH },
2203 /* URI 1 has a maddr param but URI 2 does not. No match */
2204 { "sip:bob@example.com;param1=value1;maddr=192.168.0.1", "sip:bob@example.com;param1=value1", URI_CMP_NOMATCH },
2205 /* URI 1 and URI 2 both have identical maddr params. Match */
2206 { "sip:bob@example.com;param1=value1;maddr=192.168.0.1", "sip:bob@example.com;param1=value1;maddr=192.168.0.1", URI_CMP_MATCH },
2207 /* URI 1 is a SIPS URI and URI 2 is a SIP URI. No Match */
2208 { "sips:bob@example.com", "sip:bob@example.com", URI_CMP_NOMATCH },
2209 /* No URI schemes. No match */
2210 { "bob@example.com", "bob@example.com", URI_CMP_NOMATCH },
2211 /* Crashiness tests. Just an address scheme. No match */
2212 { "sip", "sips", URI_CMP_NOMATCH },
2213 /* Still just an address scheme. Even though they're the same, No match */
2214 { "sip", "sip", URI_CMP_NOMATCH },
2215 /* Empty strings. No match */
2216 { "", "", URI_CMP_NOMATCH },
2217 /* An empty string and a NULL. No match */
2218 { "", NULL, URI_CMP_NOMATCH },
2221 int test_res = AST_TEST_PASS;
2224 info->name = "sip_uri_cmp_test";
2225 info->category = "/channels/chan_sip/";
2226 info->summary = "Tests comparison of SIP URIs";
2227 info->description = "Several would-be tricky URI comparisons are performed";
2228 return AST_TEST_NOT_RUN;
2233 for (i = 0; i < ARRAY_LEN(uri_cmp_tests); ++i) {
2236 if ((cmp_res1 = sip_uri_cmp(uri_cmp_tests[i].uri1, uri_cmp_tests[i].uri2))) {
2237 /* URI comparison may return -1 or +1 depending on the failure. Standardize
2238 * the return value to be URI_CMP_NOMATCH on any failure
2240 cmp_res1 = URI_CMP_NOMATCH;
2242 if (cmp_res1 != uri_cmp_tests[i].expected_result) {
2243 ast_test_status_update(test, "Unexpected comparison result for URIs %s and %s. "
2244 "Expected %s but got %s\n", uri_cmp_tests[i].uri1, uri_cmp_tests[i].uri2,
2245 uri_cmp_tests[i].expected_result == URI_CMP_MATCH ? "Match" : "No Match",
2246 cmp_res1 == URI_CMP_MATCH ? "Match" : "No Match");
2247 test_res = AST_TEST_FAIL;
2250 /* All URI comparisons are commutative, so for the sake of being thorough, we'll
2251 * rerun the comparison with the parameters reversed
2253 if ((cmp_res2 = sip_uri_cmp(uri_cmp_tests[i].uri2, uri_cmp_tests[i].uri1))) {
2254 /* URI comparison may return -1 or +1 depending on the failure. Standardize
2255 * the return value to be URI_CMP_NOMATCH on any failure
2257 cmp_res2 = URI_CMP_NOMATCH;
2259 if (cmp_res2 != uri_cmp_tests[i].expected_result) {
2260 ast_test_status_update(test, "Unexpected comparison result for URIs %s and %s. "
2261 "Expected %s but got %s\n", uri_cmp_tests[i].uri2, uri_cmp_tests[i].uri1,
2262 uri_cmp_tests[i].expected_result == URI_CMP_MATCH ? "Match" : "No Match",
2263 cmp_res2 == URI_CMP_MATCH ? "Match" : "No Match");
2264 test_res = AST_TEST_FAIL;
2271 void free_via(struct sip_via *v)
2281 struct sip_via *parse_via(const char *header)
2283 struct sip_via *v = ast_calloc(1, sizeof(*v));
2290 v->via = ast_strdup(header);
2295 if (ast_strlen_zero(via)) {
2296 ast_log(LOG_ERROR, "received request without a Via header\n");
2301 /* seperate the first via-parm */
2302 via = strsep(&via, ",");
2304 /* chop off sent-protocol */
2305 v->protocol = strsep(&via, " \t\r\n");
2306 if (ast_strlen_zero(v->protocol)) {
2307 ast_log(LOG_ERROR, "missing sent-protocol in Via header\n");
2311 v->protocol = ast_skip_blanks(v->protocol);
2314 via = ast_skip_blanks(via);
2317 /* chop off sent-by */
2318 v->sent_by = strsep(&via, "; \t\r\n");
2319 if (ast_strlen_zero(v->sent_by)) {
2320 ast_log(LOG_ERROR, "missing sent-by in Via header\n");
2324 v->sent_by = ast_skip_blanks(v->sent_by);
2326 /* store the port, we have to handle ipv6 addresses containing ':'
2327 * characters gracefully */
2328 if (((parm = strchr(v->sent_by, ']')) && *(++parm) == ':') || (parm = strchr(v->sent_by, ':'))) {
2331 v->port = strtol(++parm, &endptr, 10);
2334 /* evaluate any via-parms */
2335 while ((parm = strsep(&via, "; \t\r\n"))) {
2337 if ((c = strstr(parm, "maddr="))) {
2338 v->maddr = ast_skip_blanks(c + sizeof("maddr=") - 1);
2339 } else if ((c = strstr(parm, "branch="))) {
2340 v->branch = ast_skip_blanks(c + sizeof("branch=") - 1);
2341 } else if ((c = strstr(parm, "ttl="))) {
2343 c = ast_skip_blanks(c + sizeof("ttl=") - 1);
2344 v->ttl = strtol(c, &endptr, 10);
2346 /* make sure we got a valid ttl value */
2356 AST_TEST_DEFINE(parse_via_test)
2358 int res = AST_TEST_PASS;
2360 struct sip_via *via;
2363 char *expected_protocol;
2364 char *expected_branch;
2365 char *expected_sent_by;
2366 char *expected_maddr;
2367 unsigned int expected_port;
2368 unsigned char expected_ttl;
2370 AST_LIST_ENTRY(testdata) list;
2372 struct testdata *testdataptr;
2373 static AST_LIST_HEAD_NOLOCK(testdataliststruct, testdata) testdatalist;
2374 struct testdata t1 = {
2375 .in = "SIP/2.0/UDP host:port;branch=thebranch",
2376 .expected_protocol = "SIP/2.0/UDP",
2377 .expected_sent_by = "host:port",
2378 .expected_branch = "thebranch",
2380 struct testdata t2 = {
2381 .in = "SIP/2.0/UDP host:port",
2382 .expected_protocol = "SIP/2.0/UDP",
2383 .expected_sent_by = "host:port",
2384 .expected_branch = "",
2386 struct testdata t3 = {
2387 .in = "SIP/2.0/UDP",
2390 struct testdata t4 = {
2391 .in = "BLAH/BLAH/BLAH host:port;branch=",
2392 .expected_protocol = "BLAH/BLAH/BLAH",
2393 .expected_sent_by = "host:port",
2394 .expected_branch = "",
2396 struct testdata t5 = {
2397 .in = "SIP/2.0/UDP host:5060;branch=thebranch;maddr=224.0.0.1;ttl=1",
2398 .expected_protocol = "SIP/2.0/UDP",
2399 .expected_sent_by = "host:5060",
2400 .expected_port = 5060,
2401 .expected_branch = "thebranch",
2402 .expected_maddr = "224.0.0.1",
2405 struct testdata t6 = {
2406 .in = "SIP/2.0/UDP host:5060;\n branch=thebranch;\r\n maddr=224.0.0.1; ttl=1",
2407 .expected_protocol = "SIP/2.0/UDP",
2408 .expected_sent_by = "host:5060",
2409 .expected_port = 5060,
2410 .expected_branch = "thebranch",
2411 .expected_maddr = "224.0.0.1",
2414 struct testdata t7 = {
2415 .in = "SIP/2.0/UDP [::1]:5060",
2416 .expected_protocol = "SIP/2.0/UDP",
2417 .expected_sent_by = "[::1]:5060",
2418 .expected_port = 5060,
2419 .expected_branch = "",
2423 info->name = "parse_via_test";
2424 info->category = "/channels/chan_sip/";
2425 info->summary = "Tests parsing the Via header";
2427 "Runs through various test situations in which various "
2428 " parameters parameter must be extracted from a VIA header";
2429 return AST_TEST_NOT_RUN;
2434 AST_LIST_HEAD_SET_NOLOCK(&testdatalist, &t1);
2435 AST_LIST_INSERT_TAIL(&testdatalist, &t2, list);
2436 AST_LIST_INSERT_TAIL(&testdatalist, &t3, list);
2437 AST_LIST_INSERT_TAIL(&testdatalist, &t4, list);
2438 AST_LIST_INSERT_TAIL(&testdatalist, &t5, list);
2439 AST_LIST_INSERT_TAIL(&testdatalist, &t6, list);
2440 AST_LIST_INSERT_TAIL(&testdatalist, &t7, list);
2443 AST_LIST_TRAVERSE(&testdatalist, testdataptr, list) {
2444 via = parse_via(testdataptr->in);
2446 if (!testdataptr->expected_null) {
2447 ast_test_status_update(test, "TEST#%d FAILED: VIA = \"%s\"\n"
2448 "failed to parse header\n",
2449 i, testdataptr->in);
2450 res = AST_TEST_FAIL;
2456 if (testdataptr->expected_null) {
2457 ast_test_status_update(test, "TEST#%d FAILED: VIA = \"%s\"\n"
2458 "successfully parased invalid via header\n",
2459 i, testdataptr->in);
2460 res = AST_TEST_FAIL;
2466 if ((ast_strlen_zero(via->protocol) && !ast_strlen_zero(testdataptr->expected_protocol))
2467 || (!ast_strlen_zero(via->protocol) && strcmp(via->protocol, testdataptr->expected_protocol))) {
2469 ast_test_status_update(test, "TEST#%d FAILED: VIA = \"%s\"\n"
2470 "parsed protocol = \"%s\"\n"
2471 "expected = \"%s\"\n"
2472 "failed to parse protocol\n",
2473 i, testdataptr->in, via->protocol, testdataptr->expected_protocol);
2474 res = AST_TEST_FAIL;
2477 if ((ast_strlen_zero(via->sent_by) && !ast_strlen_zero(testdataptr->expected_sent_by))
2478 || (!ast_strlen_zero(via->sent_by) && strcmp(via->sent_by, testdataptr->expected_sent_by))) {
2480 ast_test_status_update(test, "TEST#%d FAILED: VIA = \"%s\"\n"
2481 "parsed sent_by = \"%s\"\n"
2482 "expected = \"%s\"\n"
2483 "failed to parse sent-by\n",
2484 i, testdataptr->in, via->sent_by, testdataptr->expected_sent_by);
2485 res = AST_TEST_FAIL;
2488 if (testdataptr->expected_port && testdataptr->expected_port != via->port) {
2489 ast_test_status_update(test, "TEST#%d FAILED: VIA = \"%s\"\n"
2490 "parsed port = \"%d\"\n"
2491 "expected = \"%d\"\n"
2492 "failed to parse port\n",
2493 i, testdataptr->in, via->port, testdataptr->expected_port);
2494 res = AST_TEST_FAIL;
2497 if ((ast_strlen_zero(via->branch) && !ast_strlen_zero(testdataptr->expected_branch))
2498 || (!ast_strlen_zero(via->branch) && strcmp(via->branch, testdataptr->expected_branch))) {
2500 ast_test_status_update(test, "TEST#%d FAILED: VIA = \"%s\"\n"
2501 "parsed branch = \"%s\"\n"
2502 "expected = \"%s\"\n"
2503 "failed to parse branch\n",
2504 i, testdataptr->in, via->branch, testdataptr->expected_branch);
2505 res = AST_TEST_FAIL;
2508 if ((ast_strlen_zero(via->maddr) && !ast_strlen_zero(testdataptr->expected_maddr))
2509 || (!ast_strlen_zero(via->maddr) && strcmp(via->maddr, testdataptr->expected_maddr))) {
2511 ast_test_status_update(test, "TEST#%d FAILED: VIA = \"%s\"\n"
2512 "parsed maddr = \"%s\"\n"
2513 "expected = \"%s\"\n"
2514 "failed to parse maddr\n",
2515 i, testdataptr->in, via->maddr, testdataptr->expected_maddr);
2516 res = AST_TEST_FAIL;
2519 if (testdataptr->expected_ttl && testdataptr->expected_ttl != via->ttl) {
2520 ast_test_status_update(test, "TEST#%d FAILED: VIA = \"%s\"\n"
2521 "parsed ttl = \"%d\"\n"
2522 "expected = \"%d\"\n"
2523 "failed to parse ttl\n",
2524 i, testdataptr->in, via->ttl, testdataptr->expected_ttl);
2525 res = AST_TEST_FAIL;
2534 void sip_request_parser_register_tests(void)
2536 AST_TEST_REGISTER(get_calleridname_test);
2537 AST_TEST_REGISTER(sip_parse_uri_test);
2538 AST_TEST_REGISTER(get_in_brackets_test);
2539 AST_TEST_REGISTER(get_name_and_number_test);
2540 AST_TEST_REGISTER(sip_parse_uri_fully_test);
2541 AST_TEST_REGISTER(parse_name_andor_addr_test);
2542 AST_TEST_REGISTER(parse_contact_header_test);
2543 AST_TEST_REGISTER(sip_parse_options_test);
2544 AST_TEST_REGISTER(sip_uri_cmp_test);
2545 AST_TEST_REGISTER(parse_via_test);
2547 void sip_request_parser_unregister_tests(void)
2549 AST_TEST_UNREGISTER(sip_parse_uri_test);
2550 AST_TEST_UNREGISTER(get_calleridname_test);
2551 AST_TEST_UNREGISTER(get_in_brackets_test);
2552 AST_TEST_UNREGISTER(get_name_and_number_test);
2553 AST_TEST_UNREGISTER(sip_parse_uri_fully_test);
2554 AST_TEST_UNREGISTER(parse_name_andor_addr_test);
2555 AST_TEST_UNREGISTER(parse_contact_header_test);
2556 AST_TEST_UNREGISTER(sip_parse_options_test);
2557 AST_TEST_UNREGISTER(sip_uri_cmp_test);
2558 AST_TEST_UNREGISTER(parse_via_test);
2561 int sip_reqresp_parser_init(void)
2563 #ifdef HAVE_XLOCALE_H
2564 c_locale = newlocale(LC_CTYPE_MASK, "C", NULL);
2572 void sip_reqresp_parser_exit(void)
2574 #ifdef HAVE_XLOCALE_H
2576 freelocale(c_locale);