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
23 <support_level>core</support_level>
28 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
30 #include "include/sip.h"
31 #include "include/sip_utils.h"
32 #include "include/reqresp_parser.h"
38 /*! \brief * parses a URI in its components.*/
39 int parse_uri_full(char *uri, const char *scheme, char **user, char **pass,
40 char **hostport, struct uriparams *params, char **headers,
43 char *userinfo = NULL;
44 char *parameters = NULL;
45 char *endparams = NULL;
50 * Initialize requested strings - some functions don't care if parse_uri fails
51 * and will attempt to use string pointers passed into parse_uri even after a
70 /* check for valid input */
71 if (ast_strlen_zero(uri)) {
77 char *scheme2 = ast_strdupa(scheme);
78 char *cur = strsep(&scheme2, ",");
79 for (; !ast_strlen_zero(cur); cur = strsep(&scheme2, ",")) {
81 if (!strncasecmp(uri, cur, l)) {
86 if (ast_strlen_zero(cur)) {
87 ast_debug(1, "No supported scheme found in '%s' using the scheme[s] %s\n", uri, scheme);
93 /* if we don't want to split around hostport, keep everything as a
94 * userinfo - cos thats how old parse_uri operated*/
98 if ((c = strchr(uri, '@'))) {
102 uri = c; /* userinfo can contain ? and ; chars so step forward before looking for params and headers */
104 /* domain-only URI, according to the SIP RFC. */
112 if (pass && (c = strchr(userinfo, ':'))) { /* user:password */
124 /* strip [?headers] from end of uri - even if no header pointer exists*/
125 if ((c = strrchr(uri, '?'))) {
131 if ((c = strrchr(uri, ';'))) {
134 c = strrchr(uri, '\0');
136 uri = c; /* residue */
139 } else if (headers) {
143 /* parse parameters */
144 endparams = strchr(parameters,'\0');
145 if ((c = strchr(parameters, ';'))) {
149 parameters = endparams;
153 char *rem = parameters; /* unparsed or unrecognised remainder */
158 params->transport = "";
167 while ((value = strchr(parameters, '=')) || (lr = !strncmp(parameters, "lr", 2))) {
168 /* The while condition will not continue evaluation to set lr if it matches "lr=" */
175 if ((c = strchr(value, ';'))) {
179 parameters = endparams;
182 if (!strcmp(label, "transport")) {
183 params->transport = value;
185 } else if (!strcmp(label, "user")) {
186 params->user = value;
188 } else if (!strcmp(label, "method")) {
189 params->method = value;
191 } else if (!strcmp(label, "ttl")) {
194 } else if (!strcmp(label, "maddr")) {
195 params->maddr = value;
197 /* 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 */
198 } else if ((!strcmp(label, "lr") && strcmp(value, "no") && strcmp(value, "off") && strcmp(value, "0") && strcmp(value, "")) || ((lr) && strcmp(value, "lr"))) {
210 if (rem > uri) { /* no headers */
224 AST_TEST_DEFINE(sip_parse_uri_full_test)
226 int res = AST_TEST_PASS;
228 char *user, *pass, *hostport, *headers, *residue;
229 struct uriparams params;
239 struct uriparams params;
240 AST_LIST_ENTRY(testdata) list;
244 struct testdata *testdataptr;
246 static AST_LIST_HEAD_NOLOCK(testdataliststruct, testdata) testdatalist;
248 struct testdata td1 = {
249 .desc = "no headers",
250 .uri = "sip:user:secret@host:5060;param=discard;transport=tcp;param2=residue",
253 .hostport = "host:5060",
255 .residue = "param2=residue",
256 .params.transport = "tcp",
261 struct testdata td2 = {
262 .desc = "with headers",
263 .uri = "sip:user:secret@host:5060;param=discard;transport=tcp;param2=discard2?header=blah&header2=blah2;param3=residue",
266 .hostport = "host:5060",
267 .headers = "header=blah&header2=blah2",
268 .residue = "param3=residue",
269 .params.transport = "tcp",
274 struct testdata td3 = {
275 .desc = "difficult user",
276 .uri = "sip:-_.!~*'()&=+$,;?/:secret@host:5060;transport=tcp",
277 .user = "-_.!~*'()&=+$,;?/",
279 .hostport = "host:5060",
282 .params.transport = "tcp",
287 struct testdata td4 = {
288 .desc = "difficult pass",
289 .uri = "sip:user:-_.!~*'()&=+$,@host:5060;transport=tcp",
291 .pass = "-_.!~*'()&=+$,",
292 .hostport = "host:5060",
295 .params.transport = "tcp",
300 struct testdata td5 = {
301 .desc = "difficult host",
302 .uri = "sip:user:secret@1-1.a-1.:5060;transport=tcp",
305 .hostport = "1-1.a-1.:5060",
308 .params.transport = "tcp",
313 struct testdata td6 = {
314 .desc = "difficult params near transport",
315 .uri = "sip:user:secret@host:5060;-_.!~*'()[]/:&+$=-_.!~*'()[]/:&+$;transport=tcp",
318 .hostport = "host:5060",
321 .params.transport = "tcp",
326 struct testdata td7 = {
327 .desc = "difficult params near headers",
328 .uri = "sip:user:secret@host:5060;-_.!~*'()[]/:&+$=-_.!~*'()[]/:&+$?header=blah&header2=blah2;-_.!~*'()[]/:&+$=residue",
331 .hostport = "host:5060",
332 .headers = "header=blah&header2=blah2",
333 .residue = "-_.!~*'()[]/:&+$=residue",
334 .params.transport = "",
339 struct testdata td8 = {
340 .desc = "lr parameter",
341 .uri = "sip:user:secret@host:5060;param=discard;lr?header=blah",
344 .hostport = "host:5060",
345 .headers = "header=blah",
347 .params.transport = "",
352 struct testdata td9 = {
353 .desc = "alternative lr parameter",
354 .uri = "sip:user:secret@host:5060;param=discard;lr=yes?header=blah",
357 .hostport = "host:5060",
358 .headers = "header=blah",
360 .params.transport = "",
365 struct testdata td10 = {
366 .desc = "no lr parameter",
367 .uri = "sip:user:secret@host:5060;paramlr=lr;lr=no;lr=off;lr=0;lr=;=lr;lrextra;lrparam2=lr?header=blah",
370 .hostport = "host:5060",
371 .headers = "header=blah",
373 .params.transport = "",
379 AST_LIST_HEAD_SET_NOLOCK(&testdatalist, &td1);
380 AST_LIST_INSERT_TAIL(&testdatalist, &td2, list);
381 AST_LIST_INSERT_TAIL(&testdatalist, &td3, list);
382 AST_LIST_INSERT_TAIL(&testdatalist, &td4, list);
383 AST_LIST_INSERT_TAIL(&testdatalist, &td5, list);
384 AST_LIST_INSERT_TAIL(&testdatalist, &td6, list);
385 AST_LIST_INSERT_TAIL(&testdatalist, &td7, list);
386 AST_LIST_INSERT_TAIL(&testdatalist, &td8, list);
387 AST_LIST_INSERT_TAIL(&testdatalist, &td9, list);
388 AST_LIST_INSERT_TAIL(&testdatalist, &td10, list);
393 info->name = "sip_uri_full_parse_test";
394 info->category = "/channels/chan_sip/";
395 info->summary = "tests sip full uri parsing";
397 "Tests full parsing of various URIs "
398 "Verifies output matches expected behavior.";
399 return AST_TEST_NOT_RUN;
404 AST_LIST_TRAVERSE(&testdatalist, testdataptr, list) {
405 user = pass = hostport = headers = residue = NULL;
406 params.transport = params.user = params.method = params.ttl = params.maddr = NULL;
409 ast_copy_string(uri,testdataptr->uri,sizeof(uri));
410 if (parse_uri_full(uri, "sip:,sips:", &user,
415 (user && strcmp(testdataptr->user, user)) ||
416 (pass && strcmp(testdataptr->pass, pass)) ||
417 (hostport && strcmp(testdataptr->hostport, hostport)) ||
418 (headers && strcmp(testdataptr->headers, headers)) ||
419 (residue && strcmp(testdataptr->residue, residue)) ||
420 (strcmp(testdataptr->params.transport,params.transport)) ||
421 (testdataptr->params.lr != params.lr) ||
422 (strcmp(testdataptr->params.user,params.user))
424 ast_test_status_update(test, "Sub-Test: %s, failed.\n", testdataptr->desc);
434 int parse_uri(char *uri, const char *scheme, char **user, char **pass,
435 char **hostport, char **transport) {
438 struct uriparams params;
441 ret = parse_uri_full(uri, scheme, user, pass, hostport, ¶ms, &headers, NULL);
443 *transport=params.transport;
448 AST_TEST_DEFINE(sip_parse_uri_test)
450 int res = AST_TEST_PASS;
451 char *name, *pass, *hostport, *transport;
452 char uri1[] = "sip:name@host";
453 char uri2[] = "sip:name@host;transport=tcp";
454 char uri3[] = "sip:name:secret@host;transport=tcp";
455 char uri4[] = "sip:name:secret@host:port;transport=tcp?headers=%40%40testblah&headers2=blah%20blah";
456 /* test 5 is for NULL input */
457 char uri6[] = "sip:name:secret@host:port;transport=tcp?headers=%40%40testblah&headers2=blah%20blah";
458 char uri7[] = "sip:name:secret@host:port;transport=tcp?headers=%40%40testblah&headers2=blah%20blah";
459 char uri8[] = "sip:host";
460 char uri9[] = "sip:host:port;transport=tcp?headers=%40%40testblah&headers2=blah%20blah";
461 char uri10[] = "host:port;transport=tcp?headers=%40%40testblah&headers2=blah%20blah";
462 char uri11[] = "host";
466 info->name = "sip_uri_parse_test";
467 info->category = "/channels/chan_sip/";
468 info->summary = "tests sip uri parsing";
470 "Tests parsing of various URIs "
471 "Verifies output matches expected behavior.";
472 return AST_TEST_NOT_RUN;
477 /* Test 1, simple URI */
478 name = pass = hostport = transport = NULL;
479 if (parse_uri(uri1, "sip:,sips:", &name, &pass, &hostport, &transport) ||
480 strcmp(name, "name") ||
481 !ast_strlen_zero(pass) ||
482 strcmp(hostport, "host") ||
483 !ast_strlen_zero(transport)) {
484 ast_test_status_update(test, "Test 1: simple uri failed. \n");
488 /* Test 2, add tcp transport */
489 name = pass = hostport = transport = NULL;
490 if (parse_uri(uri2, "sip:,sips:", &name, &pass, &hostport, &transport) ||
491 strcmp(name, "name") ||
492 !ast_strlen_zero(pass) ||
493 strcmp(hostport, "host") ||
494 strcmp(transport, "tcp")) {
495 ast_test_status_update(test, "Test 2: uri with addtion of tcp transport failed. \n");
499 /* Test 3, add secret */
500 name = pass = hostport = transport = NULL;
501 if (parse_uri(uri3, "sip:,sips:", &name, &pass, &hostport, &transport) ||
502 strcmp(name, "name") ||
503 strcmp(pass, "secret") ||
504 strcmp(hostport, "host") ||
505 strcmp(transport, "tcp")) {
506 ast_test_status_update(test, "Test 3: uri with addition of secret failed.\n");
510 /* Test 4, add port and unparsed header field*/
511 name = pass = hostport = transport = NULL;
512 if (parse_uri(uri4, "sip:,sips:", &name, &pass, &hostport, &transport) ||
513 strcmp(name, "name") ||
514 strcmp(pass, "secret") ||
515 strcmp(hostport, "host:port") ||
516 strcmp(transport, "tcp")) {
517 ast_test_status_update(test, "Test 4: add port and unparsed header field failed.\n");
521 /* Test 5, verify parse_uri does not crash when given a NULL uri */
522 name = pass = hostport = transport = NULL;
523 if (!parse_uri(NULL, "sip:,sips:", &name, &pass, &hostport, &transport)) {
524 ast_test_status_update(test, "Test 5: passing a NULL uri failed.\n");
528 /* Test 6, verify parse_uri does not crash when given a NULL output parameters */
529 name = pass = hostport = transport = NULL;
530 if (parse_uri(uri6, "sip:,sips:", NULL, NULL, NULL, NULL)) {
531 ast_test_status_update(test, "Test 6: passing NULL output parameters failed.\n");
535 /* Test 7, verify parse_uri returns user:secret and hostport when no port or secret output parameters are supplied. */
536 name = pass = hostport = transport = NULL;
537 if (parse_uri(uri7, "sip:,sips:", &name, NULL, &hostport, NULL) ||
538 strcmp(name, "name:secret") ||
539 strcmp(hostport, "host:port")) {
541 ast_test_status_update(test, "Test 7: providing no port and secret output parameters failed.\n");
545 /* Test 8, verify parse_uri can handle a hostport only uri */
546 name = pass = hostport = transport = NULL;
547 if (parse_uri(uri8, "sip:,sips:", &name, &pass, &hostport, &transport) ||
548 strcmp(hostport, "host") ||
549 !ast_strlen_zero(name)) {
550 ast_test_status_update(test, "Test 8: add port and unparsed header field failed.\n");
554 /* Test 9, add port and unparsed header field with hostport only uri*/
555 name = pass = hostport = transport = NULL;
556 if (parse_uri(uri9, "sip:,sips:", &name, &pass, &hostport, &transport) ||
557 !ast_strlen_zero(name) ||
558 !ast_strlen_zero(pass) ||
559 strcmp(hostport, "host:port") ||
560 strcmp(transport, "tcp")) {
561 ast_test_status_update(test, "Test 9: hostport only uri failed \n");
565 /* Test 10, handle invalid/missing "sip:,sips:" scheme
566 * we expect parse_uri to return an error, but still parse
567 * the results correctly here */
568 name = pass = hostport = transport = NULL;
569 if (!parse_uri(uri10, "sip:,sips:", &name, &pass, &hostport, &transport) ||
570 !ast_strlen_zero(name) ||
571 !ast_strlen_zero(pass) ||
572 strcmp(hostport, "host:port") ||
573 strcmp(transport, "tcp")) {
574 ast_test_status_update(test, "Test 10: missing \"sip:sips:\" scheme failed\n");
578 /* Test 11, simple hostport only URI with missing scheme
579 * we expect parse_uri to return an error, but still parse
580 * the results correctly here */
581 name = pass = hostport = transport = NULL;
582 if (!parse_uri(uri11, "sip:,sips:", &name, &pass, &hostport, &transport) ||
583 !ast_strlen_zero(name) ||
584 !ast_strlen_zero(pass) ||
585 strcmp(hostport, "host") ||
586 !ast_strlen_zero(transport)) {
587 ast_test_status_update(test, "Test 11: simple uri with missing scheme failed. \n");
594 /*! \brief Get caller id name from SIP headers, copy into output buffer
596 * \retval input string pointer placed after display-name field if possible
598 const char *get_calleridname(const char *input, char *output, size_t outputsize)
602 * From = ( "From" / "f" ) HCOLON from-spec
603 * from-spec = ( name-addr / addr-spec ) *( SEMI from-param )
604 * name-addr = [ display-name ] LAQUOT addr-spec RAQUOT
605 * display-name = *(token LWS)/ quoted-string
606 * token = 1*(alphanum / "-" / "." / "!" / "%" / "*"
607 * / "_" / "+" / "`" / "'" / "~" )
608 * quoted-string = SWS DQUOTE *(qdtext / quoted-pair ) DQUOTE
609 * qdtext = LWS / %x21 / %x23-5B / %x5D-7E
611 * quoted-pair = "\" (%x00-09 / %x0B-0C / %x0E-7F)
613 * HCOLON = *WSP ":" SWS
615 * LWS = *[*WSP CRLF] 1*WSP
618 * Deviations from it:
619 * - following CRLF's in LWS is not done (here at least)
620 * - ascii NUL is never legal as it terminates the C-string
621 * - utf8-nonascii is not checked for validity
623 char *orig_output = output;
624 const char *orig_input = input;
626 if (!output || !outputsize) {
627 /* Bad output parameters. Should never happen. */
631 /* clear any empty characters in the beginning */
632 input = ast_skip_blanks(input);
634 /* make sure the output buffer is initilized */
637 /* make room for '\0' at the end of the output buffer */
640 /* no data at all or no display name? */
641 if (!input || *input == '<') {
645 /* quoted-string rules */
646 if (input[0] == '"') {
647 input++; /* skip the first " */
649 for (; *input; ++input) {
650 if (*input == '"') { /* end of quoted-string */
652 } else if (*input == 0x5c) { /* quoted-pair = "\" (%x00-09 / %x0B-0C / %x0E-7F) */
657 if ((unsigned char) *input > 0x7f || *input == 0xa || *input == 0xd) {
658 continue; /* not a valid quoted-pair, so skip it */
660 } else if ((*input != 0x9 && (unsigned char) *input < 0x20)
662 continue; /* skip this invalid character. */
665 if (0 < outputsize) {
666 /* We still have room for the output display-name. */
672 /* if this is successful, input should be at the ending quote */
674 ast_log(LOG_WARNING, "No ending quote for display-name was found\n");
679 /* make sure input is past the last quote */
682 /* terminate output */
684 } else { /* either an addr-spec or tokenLWS-combo */
685 for (; *input; ++input) {
686 /* token or WSP (without LWS) */
687 if ((*input >= '0' && *input <= '9') || (*input >= 'A' && *input <= 'Z')
688 || (*input >= 'a' && *input <= 'z') || *input == '-' || *input == '.'
689 || *input == '!' || *input == '%' || *input == '*' || *input == '_'
690 || *input == '+' || *input == '`' || *input == '\'' || *input == '~'
691 || *input == 0x9 || *input == ' ') {
692 if (0 < outputsize) {
693 /* We still have room for the output display-name. */
697 } else if (*input == '<') { /* end of tokenLWS-combo */
698 /* we could assert that the previous char is LWS, but we don't care */
700 } else if (*input == ':') {
701 /* This invalid character which indicates this is addr-spec rather than display-name. */
704 } else { /* else, invalid character we can skip. */
705 continue; /* skip this character */
709 if (*input != '<') { /* if we never found the start of addr-spec then this is invalid */
714 /* terminate output while trimming any trailing whitespace */
717 } while (orig_output <= output && (*output == 0x9 || *output == ' '));
723 AST_TEST_DEFINE(get_calleridname_test)
725 int res = AST_TEST_PASS;
726 const char *in1 = " \" quoted-text internal \\\" quote \"<stuff>";
727 const char *in2 = " token text with no quotes <stuff>";
728 const char *overflow1 = " \"quoted-text overflow 1234567890123456789012345678901234567890\" <stuff>";
729 const char *overflow2 = " non-quoted text overflow 1234567890123456789012345678901234567890 <stuff>";
730 const char *noendquote = " \"quoted-text no end <stuff>";
731 const char *addrspec = " sip:blah@blah";
732 const char *no_quotes_no_brackets = "blah@blah";
733 const char *after_dname;
738 info->name = "sip_get_calleridname_test";
739 info->category = "/channels/chan_sip/";
740 info->summary = "decodes callerid name from sip header";
741 info->description = "Decodes display-name field of sip header. Checks for valid output and expected failure cases.";
742 return AST_TEST_NOT_RUN;
747 /* quoted-text with backslash escaped quote */
748 after_dname = get_calleridname(in1, dname, sizeof(dname));
749 ast_test_status_update(test, "display-name1: %s\nafter: %s\n", dname, after_dname);
750 if (strcmp(dname, " quoted-text internal \" quote ")) {
751 ast_test_status_update(test, "display-name1 test failed\n");
756 after_dname = get_calleridname(in2, dname, sizeof(dname));
757 ast_test_status_update(test, "display-name2: %s\nafter: %s\n", dname, after_dname);
758 if (strcmp(dname, "token text with no quotes")) {
759 ast_test_status_update(test, "display-name2 test failed\n");
763 /* quoted-text buffer overflow */
764 after_dname = get_calleridname(overflow1, dname, sizeof(dname));
765 ast_test_status_update(test, "overflow display-name1: %s\nafter: %s\n", dname, after_dname);
766 if (strcmp(dname, "quoted-text overflow 123456789012345678")) {
767 ast_test_status_update(test, "overflow display-name1 test failed\n");
771 /* non-quoted-text buffer overflow */
772 after_dname = get_calleridname(overflow2, dname, sizeof(dname));
773 ast_test_status_update(test, "overflow display-name2: %s\nafter: %s\n", dname, after_dname);
774 if (strcmp(dname, "non-quoted text overflow 12345678901234")) {
775 ast_test_status_update(test, "overflow display-name2 test failed\n");
779 /* quoted-text buffer with no terminating end quote */
780 after_dname = get_calleridname(noendquote, dname, sizeof(dname));
781 ast_test_status_update(test, "noendquote display-name1: %s\nafter: %s\n", dname, after_dname);
782 if (*dname != '\0' && after_dname != noendquote) {
783 ast_test_status_update(test, "no end quote for quoted-text display-name failed\n");
787 /* addr-spec rather than display-name. */
788 after_dname = get_calleridname(addrspec, dname, sizeof(dname));
789 ast_test_status_update(test, "addr-spec display-name1: %s\nafter: %s\n", dname, after_dname);
790 if (*dname != '\0' && after_dname != addrspec) {
791 ast_test_status_update(test, "detection of addr-spec failed\n");
795 /* no quotes, no brackets */
796 after_dname = get_calleridname(no_quotes_no_brackets, dname, sizeof(dname));
797 ast_test_status_update(test, "no_quotes_no_brackets display-name1: %s\nafter: %s\n", dname, after_dname);
798 if (*dname != '\0' && after_dname != no_quotes_no_brackets) {
799 ast_test_status_update(test, "detection of addr-spec failed\n");
806 int get_name_and_number(const char *hdr, char **name, char **number)
810 char *tmp_number = NULL;
811 char *hostport = NULL;
814 if (!name || !number || ast_strlen_zero(hdr)) {
820 ast_copy_string(header, hdr, sizeof(header));
822 /* strip the display-name portion off the beginning of the header. */
823 get_calleridname(header, tmp_name, sizeof(tmp_name));
825 /* get uri within < > brackets */
826 tmp_number = get_in_brackets(header);
828 /* parse out the number here */
829 if (parse_uri(tmp_number, "sip:,sips:", &tmp_number, &dummy, &hostport, NULL) || ast_strlen_zero(tmp_number)) {
830 ast_log(LOG_ERROR, "can not parse name and number from sip header.\n");
834 /* number is not option, and must be present at this point */
835 *number = ast_strdup(tmp_number);
836 ast_uri_decode(*number, ast_uri_sip_user);
838 /* name is optional and may not be present at this point */
839 if (!ast_strlen_zero(tmp_name)) {
840 *name = ast_strdup(tmp_name);
846 AST_TEST_DEFINE(get_name_and_number_test)
848 int res = AST_TEST_PASS;
851 const char *in1 = "NAME <sip:NUMBER@place>";
852 const char *in2 = "\"NA><ME\" <sip:NUMBER@place>";
853 const char *in3 = "NAME";
854 const char *in4 = "<sip:NUMBER@place>";
855 const char *in5 = "This is a screwed up string <sip:LOLCLOWNS<sip:>@place>";
859 info->name = "sip_get_name_and_number_test";
860 info->category = "/channels/chan_sip/";
861 info->summary = "Tests getting name and number from sip header";
863 "Runs through various test situations in which a name and "
864 "and number can be retrieved from a sip header.";
865 return AST_TEST_NOT_RUN;
870 /* Test 1. get name and number */
871 number = name = NULL;
872 if ((get_name_and_number(in1, &name, &number)) ||
873 strcmp(name, "NAME") ||
874 strcmp(number, "NUMBER")) {
876 ast_test_status_update(test, "Test 1, simple get name and number failed.\n");
882 /* Test 2. get quoted name and number */
883 number = name = NULL;
884 if ((get_name_and_number(in2, &name, &number)) ||
885 strcmp(name, "NA><ME") ||
886 strcmp(number, "NUMBER")) {
888 ast_test_status_update(test, "Test 2, get quoted name and number failed.\n");
894 /* Test 3. name only */
895 number = name = NULL;
896 if (!(get_name_and_number(in3, &name, &number))) {
898 ast_test_status_update(test, "Test 3, get name only was expected to fail but did not.\n");
904 /* Test 4. number only */
905 number = name = NULL;
906 if ((get_name_and_number(in4, &name, &number)) ||
907 !ast_strlen_zero(name) ||
908 strcmp(number, "NUMBER")) {
910 ast_test_status_update(test, "Test 4, get number with no name present failed.\n");
916 /* Test 5. malformed string, since number can not be parsed, this should return an error. */
917 number = name = NULL;
918 if (!(get_name_and_number(in5, &name, &number)) ||
919 !ast_strlen_zero(name) ||
920 !ast_strlen_zero(number)) {
922 ast_test_status_update(test, "Test 5, processing malformed string failed.\n");
928 /* Test 6. NULL output parameters */
929 number = name = NULL;
930 if (!(get_name_and_number(in5, NULL, NULL))) {
932 ast_test_status_update(test, "Test 6, NULL output parameters failed.\n");
936 /* Test 7. NULL input parameter */
937 number = name = NULL;
938 if (!(get_name_and_number(NULL, &name, &number)) ||
939 !ast_strlen_zero(name) ||
940 !ast_strlen_zero(number)) {
942 ast_test_status_update(test, "Test 7, NULL input parameter failed.\n");
951 int get_in_brackets_const(const char *src,const char **start,int *length)
953 const char *parse = src;
954 const char *first_bracket;
955 const char *second_bracket;
960 if (length == NULL) {
965 if (ast_strlen_zero(src)) {
970 * Skip any quoted text until we find the part in brackets.
971 * On any error give up and return -1
973 while ( (first_bracket = strchr(parse, '<')) ) {
974 const char *first_quote = strchr(parse, '"');
976 if (!first_quote || first_quote >= first_bracket) {
977 break; /* no need to look at quoted part */
979 /* the bracket is within quotes, so ignore it */
980 parse = find_closing_quote(first_quote + 1, NULL);
982 ast_log(LOG_WARNING, "No closing quote found in '%s'\n", src);
988 /* Require a first bracket. Unlike get_in_brackets_full, this procedure is passed a const,
989 * so it can expect a pointer to an original value */
990 if (!first_bracket) {
991 ast_log(LOG_WARNING, "No opening bracket found in '%s'\n", src);
995 if ((second_bracket = strchr(first_bracket, '>'))) {
996 *start = first_bracket;
997 *length = second_bracket - first_bracket;
1000 ast_log(LOG_WARNING, "No closing bracket found in '%s'\n", src);
1004 int get_in_brackets_full(char *tmp,char **out,char **residue)
1006 const char *parse = tmp;
1007 char *first_bracket;
1008 char *second_bracket;
1017 if (ast_strlen_zero(tmp)) {
1022 * Skip any quoted text until we find the part in brackets.
1023 * On any error give up and return -1
1025 while ( (first_bracket = strchr(parse, '<')) ) {
1026 char *first_quote = strchr(parse, '"');
1028 if (!first_quote || first_quote >= first_bracket) {
1029 break; /* no need to look at quoted part */
1031 /* the bracket is within quotes, so ignore it */
1032 parse = find_closing_quote(first_quote + 1, NULL);
1034 ast_log(LOG_WARNING, "No closing quote found in '%s'\n", tmp);
1040 /* If no first bracket then still look for a second bracket as some other parsing functions
1041 may overwrite first bracket with NULL when terminating a token based display-name. As this
1042 only affects token based display-names there is no danger of brackets being in quotes */
1043 if (first_bracket) {
1044 parse = first_bracket;
1049 if ((second_bracket = strchr(parse, '>'))) {
1050 *second_bracket++ = '\0';
1052 *out = (char *) parse;
1055 *residue = second_bracket;
1060 if ((first_bracket)) {
1061 ast_log(LOG_WARNING, "No closing bracket found in '%s'\n", tmp);
1072 char *get_in_brackets(char *tmp)
1076 if ((get_in_brackets_full(tmp, &out, NULL))) {
1082 AST_TEST_DEFINE(get_in_brackets_test)
1084 int res = AST_TEST_PASS;
1085 char in_brackets[] = "sip:name:secret@host:port;transport=tcp?headers=testblah&headers2=blahblah";
1086 char no_name[] = "<sip:name:secret@host:port;transport=tcp?headers=testblah&headers2=blahblah>";
1087 char quoted_string[] = "\"I'm a quote stri><ng\" <sip:name:secret@host:port;transport=tcp?headers=testblah&headers2=blahblah>";
1088 char missing_end_quote[] = "\"I'm a quote string <sip:name:secret@host:port;transport=tcp?headers=testblah&headers2=blahblah>";
1089 char name_no_quotes[] = "name not in quotes <sip:name:secret@host:port;transport=tcp?headers=testblah&headers2=blahblah>";
1090 char no_end_bracket[] = "name not in quotes <sip:name:secret@host:port;transport=tcp?headers=testblah&headers2=blahblah";
1091 char no_name_no_brackets[] = "sip:name@host";
1092 char missing_start_bracket[] = "sip:name:secret@host:port;transport=tcp?headers=testblah&headers2=blahblah>";
1097 info->name = "sip_get_in_brackets_test";
1098 info->category = "/channels/chan_sip/";
1099 info->summary = "Tests getting a sip uri in <> brackets within a sip header.";
1101 "Runs through various test situations in which a sip uri "
1102 "in angle brackets needs to be retrieved";
1103 return AST_TEST_NOT_RUN;
1108 /* Test 1, simple get in brackets */
1109 if (!(uri = get_in_brackets(no_name)) || strcmp(uri, in_brackets)) {
1110 ast_test_status_update(test, "Test 1, simple get in brackets failed. %s\n", uri);
1111 res = AST_TEST_FAIL;
1114 /* Test 2, starts with quoted string */
1115 if (!(uri = get_in_brackets(quoted_string)) || strcmp(uri, in_brackets)) {
1116 ast_test_status_update(test, "Test 2, get in brackets with quoted string in front failed. %s\n", uri);
1117 res = AST_TEST_FAIL;
1120 /* Test 3, missing end quote */
1121 if (!(uri = get_in_brackets(missing_end_quote)) || !strcmp(uri, in_brackets)) {
1122 ast_test_status_update(test, "Test 3, missing end quote failed. %s\n", uri);
1123 res = AST_TEST_FAIL;
1126 /* Test 4, starts with a name not in quotes */
1127 if (!(uri = get_in_brackets(name_no_quotes)) || strcmp(uri, in_brackets)) {
1128 ast_test_status_update(test, "Test 4, passing name not in quotes failed. %s\n", uri);
1129 res = AST_TEST_FAIL;
1132 /* Test 5, no end bracket, should just return everything after the first '<' */
1133 if (!(uri = get_in_brackets(no_end_bracket)) || !strcmp(uri, in_brackets)) {
1134 ast_test_status_update(test, "Test 5, no end bracket failed. %s\n", uri);
1135 res = AST_TEST_FAIL;
1138 /* Test 6, NULL input */
1139 if (get_in_brackets(NULL)) {
1140 ast_test_status_update(test, "Test 6, NULL input failed.\n");
1141 res = AST_TEST_FAIL;
1144 /* Test 7, no name, and no brackets. */
1145 if (!(uri = get_in_brackets(no_name_no_brackets)) || strcmp(uri, "sip:name@host")) {
1146 ast_test_status_update(test, "Test 7 failed. %s\n", uri);
1147 res = AST_TEST_FAIL;
1150 /* Test 8, no start bracket, but with ending bracket. */
1151 if (!(uri = get_in_brackets(missing_start_bracket)) || strcmp(uri, in_brackets)) {
1152 ast_test_status_update(test, "Test 8 failed. %s\n", uri);
1153 res = AST_TEST_FAIL;
1160 int parse_name_andor_addr(char *uri, const char *scheme, char **name,
1161 char **user, char **pass, char **hostport,
1162 struct uriparams *params, char **headers,
1166 char **residue2 = residue;
1167 char *orig_uri = uri;
1172 uri = (char *) get_calleridname(uri, buf, sizeof(buf));
1174 ret = get_in_brackets_full(uri, &uri, residue);
1177 * The uri is in brackets so do not treat unknown trailing uri
1178 * parameters as potential message header parameters.
1180 if (residue && **residue) {
1181 /* step over the first semicolon as per parse_uri_full residue */
1182 *residue = *residue + 1;
1190 * There is always room at orig_uri for the display-name because
1191 * at least one character has always been removed. A '"' or '<'
1194 strcpy(orig_uri, buf);
1201 return parse_uri_full(uri, scheme, user, pass, hostport, params, headers, residue2);
1204 AST_TEST_DEFINE(parse_name_andor_addr_test)
1206 int res = AST_TEST_PASS;
1208 char *name, *user, *pass, *hostport, *headers, *residue;
1209 struct uriparams params;
1220 struct uriparams params;
1221 AST_LIST_ENTRY(testdata) list;
1224 struct testdata *testdataptr;
1226 static AST_LIST_HEAD_NOLOCK(testdataliststruct, testdata) testdatalist;
1228 struct testdata td1 = {
1229 .desc = "quotes and brackets",
1230 .uri = "\"name :@ \" <sip:user:secret@host:5060;param=discard;transport=tcp>;tag=tag",
1234 .hostport = "host:5060",
1236 .residue = "tag=tag",
1237 .params.transport = "tcp",
1242 struct testdata td2 = {
1243 .desc = "no quotes",
1244 .uri = "givenname familyname <sip:user:secret@host:5060;param=discard;transport=tcp>;expires=3600",
1245 .name = "givenname familyname",
1248 .hostport = "host:5060",
1250 .residue = "expires=3600",
1251 .params.transport = "tcp",
1256 struct testdata td3 = {
1257 .desc = "no brackets",
1258 .uri = "sip:user:secret@host:5060;param=discard;transport=tcp;q=1",
1262 .hostport = "host:5060",
1265 .params.transport = "tcp",
1270 struct testdata td4 = {
1271 .desc = "just host",
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 = hostport = 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:",
1317 (name && strcmp(testdataptr->name, name)) ||
1318 (user && strcmp(testdataptr->user, user)) ||
1319 (pass && strcmp(testdataptr->pass, pass)) ||
1320 (hostport && strcmp(testdataptr->hostport, hostport)) ||
1321 (headers && strcmp(testdataptr->headers, headers)) ||
1322 (residue && strcmp(testdataptr->residue, residue)) ||
1323 (strcmp(testdataptr->params.transport,params.transport)) ||
1324 (strcmp(testdataptr->params.user,params.user))
1326 ast_test_status_update(test, "Sub-Test: %s,failed.\n", testdataptr->desc);
1327 res = AST_TEST_FAIL;
1334 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)
1379 struct contact *split_contact = NULL;
1381 if (*contactheader == '*') {
1385 split_contact = ast_calloc(1, sizeof(*split_contact));
1387 AST_LIST_HEAD_SET_NOLOCK(contactlist, split_contact);
1388 while ((last = get_comma(contactheader, &comma)) != -1) {
1389 res = parse_name_andor_addr(contactheader, "sip:,sips:",
1390 &split_contact->name, &split_contact->user,
1391 &split_contact->pass, &split_contact->hostport,
1392 &split_contact->params, &split_contact->headers,
1398 /* parse contact params */
1399 split_contact->expires = split_contact->q = "";
1401 while ((value = strchr(residue,'='))) {
1405 if ((residue = strchr(value,';'))) {
1411 if (!strcmp(param,"expires")) {
1412 split_contact->expires = value;
1413 } else if (!strcmp(param,"q")) {
1414 split_contact->q = value;
1421 contactheader = comma;
1423 split_contact = ast_calloc(1, sizeof(*split_contact));
1424 AST_LIST_INSERT_TAIL(contactlist, split_contact, list);
1429 AST_TEST_DEFINE(parse_contact_header_test)
1431 int res = AST_TEST_PASS;
1432 char contactheader[1024];
1434 struct contactliststruct contactlist;
1435 struct contactliststruct *contactlistptr=&contactlist;
1439 char *contactheader;
1441 struct contactliststruct *contactlist;
1443 AST_LIST_ENTRY(testdata) list;
1446 struct testdata *testdataptr;
1447 struct contact *tdcontactptr;
1448 struct contact *contactptr;
1450 static AST_LIST_HEAD_NOLOCK(testdataliststruct, testdata) testdatalist;
1451 struct contactliststruct contactlist1, contactlist2;
1453 struct testdata td1 = {
1454 .desc = "single contact",
1455 .contactheader = "\"name :@;?&,\" <sip:user:secret@host:5082;param=discard;transport=tcp>;expires=3600",
1456 .contactlist = &contactlist1,
1459 struct contact contact11 = {
1460 .name = "name :@;?&,",
1463 .hostport = "host:5082",
1464 .params.transport = "tcp",
1472 struct testdata td2 = {
1473 .desc = "multiple contacts",
1474 .contactheader = "sip:,user1,:,secret1,@host1;ttl=7;q=1;expires=3600,sips:host2",
1475 .contactlist = &contactlist2,
1478 struct contact contact21 = {
1481 .pass = ",secret1,",
1482 .hostport = "host1",
1483 .params.transport = "",
1490 struct contact contact22 = {
1494 .hostport = "host2",
1495 .params.transport = "",
1503 struct testdata td3 = {
1504 .desc = "star - all contacts",
1505 .contactheader = "*",
1510 AST_LIST_HEAD_SET_NOLOCK(&testdatalist, &td1);
1511 AST_LIST_INSERT_TAIL(&testdatalist, &td2, list);
1512 AST_LIST_INSERT_TAIL(&testdatalist, &td3, list);
1514 AST_LIST_HEAD_SET_NOLOCK(&contactlist1, &contact11);
1516 AST_LIST_HEAD_SET_NOLOCK(&contactlist2, &contact21);
1517 AST_LIST_INSERT_TAIL(&contactlist2, &contact22, list);
1522 info->name = "parse_contact_header_test";
1523 info->category = "/channels/chan_sip/";
1524 info->summary = "tests parsing of sip contact header";
1526 "Tests parsing of a contact header including those with multiple contacts "
1527 "Verifies output matches expected behavior.";
1528 return AST_TEST_NOT_RUN;
1533 AST_LIST_TRAVERSE(&testdatalist, testdataptr, list) {
1534 ast_copy_string(contactheader,testdataptr->contactheader,sizeof(contactheader));
1535 star = parse_contact_header(contactheader,contactlistptr);
1536 if (testdataptr->star) {
1537 /* expecting star rather than list of contacts */
1539 ast_test_status_update(test, "Sub-Test: %s,failed.\n", testdataptr->desc);
1540 res = AST_TEST_FAIL;
1544 contactptr = AST_LIST_FIRST(contactlistptr);
1545 AST_LIST_TRAVERSE(testdataptr->contactlist, tdcontactptr, list) {
1547 strcmp(tdcontactptr->name, contactptr->name) ||
1548 strcmp(tdcontactptr->user, contactptr->user) ||
1549 strcmp(tdcontactptr->pass, contactptr->pass) ||
1550 strcmp(tdcontactptr->hostport, contactptr->hostport) ||
1551 strcmp(tdcontactptr->headers, contactptr->headers) ||
1552 strcmp(tdcontactptr->expires, contactptr->expires) ||
1553 strcmp(tdcontactptr->q, contactptr->q) ||
1554 strcmp(tdcontactptr->params.transport, contactptr->params.transport) ||
1555 strcmp(tdcontactptr->params.ttl, contactptr->params.ttl) ||
1556 (tdcontactptr->params.lr != contactptr->params.lr)
1558 ast_test_status_update(test, "Sub-Test: %s,failed.\n", testdataptr->desc);
1559 res = AST_TEST_FAIL;
1563 contactptr = AST_LIST_NEXT(contactptr,list);
1572 * \brief Parse supported header in incoming packet
1574 * \details This function parses through the options parameters and
1575 * builds a bit field representing all the SIP options in that field. When an
1576 * item is found that is not supported, it is copied to the unsupported
1579 * \param options list
1580 * \param unsupported out buffer (optional)
1581 * \param unsupported_len out buffer length (optional)
1583 unsigned int parse_sip_options(const char *options, char *unsupported, size_t unsupported_len)
1587 int i, found, supported;
1588 unsigned int profile = 0;
1590 char *out = unsupported;
1591 size_t outlen = unsupported_len;
1592 char *cur_out = out;
1594 if (out && (outlen > 0)) {
1595 memset(out, 0, outlen);
1598 if (ast_strlen_zero(options) )
1601 temp = ast_strdupa(options);
1603 ast_debug(3, "Begin: parsing SIP \"Supported: %s\"\n", options);
1605 for (next = temp; next; next = sep) {
1608 if ((sep = strchr(next, ',')) != NULL) {
1612 /* trim leading and trailing whitespace */
1613 next = ast_strip(next);
1615 if (ast_strlen_zero(next)) {
1616 continue; /* if there is a blank argument in there just skip it */
1619 ast_debug(3, "Found SIP option: -%s-\n", next);
1620 for (i = 0; i < ARRAY_LEN(sip_options); i++) {
1621 if (!strcasecmp(next, sip_options[i].text)) {
1622 profile |= sip_options[i].id;
1623 if (sip_options[i].supported == SUPPORTED) {
1627 ast_debug(3, "Matched SIP option: %s\n", next);
1632 /* If option is not supported, add to unsupported out buffer */
1633 if (!supported && out && outlen) {
1634 size_t copylen = strlen(next);
1635 size_t cur_outlen = strlen(out);
1636 /* Check to see if there is enough room to store this option.
1637 * Copy length is string length plus 2 for the ',' and '\0' */
1638 if ((cur_outlen + copylen + 2) < outlen) {
1639 /* if this isn't the first item, add the ',' */
1645 ast_copy_string(cur_out, next, (outlen - cur_outlen));
1651 profile |= SIP_OPT_UNKNOWN;
1652 if (!strncasecmp(next, "x-", 2))
1653 ast_debug(3, "Found private SIP option, not supported: %s\n", next);
1655 ast_debug(3, "Found no match for SIP option: %s (Please file bug report!)\n", next);
1662 AST_TEST_DEFINE(sip_parse_options_test)
1664 int res = AST_TEST_PASS;
1665 char unsupported[64];
1666 unsigned int option_profile = 0;
1669 char *input_options;
1670 char *expected_unsupported;
1671 unsigned int expected_profile;
1672 AST_LIST_ENTRY(testdata) list;
1675 struct testdata *testdataptr;
1676 static AST_LIST_HEAD_NOLOCK(testdataliststruct, testdata) testdatalist;
1678 struct testdata test1 = {
1679 .name = "test_all_unsupported",
1680 .input_options = "unsupported1,,, ,unsupported2,unsupported3,unsupported4",
1681 .expected_unsupported = "unsupported1,unsupported2,unsupported3,unsupported4",
1682 .expected_profile = SIP_OPT_UNKNOWN,
1684 struct testdata test2 = {
1685 .name = "test_all_unsupported_one_supported",
1686 .input_options = " unsupported1, replaces, unsupported3 , , , ,unsupported4",
1687 .expected_unsupported = "unsupported1,unsupported3,unsupported4",
1688 .expected_profile = SIP_OPT_UNKNOWN | SIP_OPT_REPLACES
1690 struct testdata test3 = {
1691 .name = "test_two_supported_two_unsupported",
1692 .input_options = ",, timer ,replaces ,unsupported3,unsupported4",
1693 .expected_unsupported = "unsupported3,unsupported4",
1694 .expected_profile = SIP_OPT_UNKNOWN | SIP_OPT_REPLACES | SIP_OPT_TIMER,
1697 struct testdata test4 = {
1698 .name = "test_all_supported",
1699 .input_options = "timer,replaces",
1700 .expected_unsupported = "",
1701 .expected_profile = SIP_OPT_REPLACES | SIP_OPT_TIMER,
1704 struct testdata test5 = {
1705 .name = "test_all_supported_redundant",
1706 .input_options = "timer,replaces,timer,replace,timer,replaces",
1707 .expected_unsupported = "",
1708 .expected_profile = SIP_OPT_REPLACES | SIP_OPT_TIMER,
1710 struct testdata test6 = {
1711 .name = "test_buffer_overflow",
1712 .input_options = "unsupported1,replaces,timer,unsupported4,unsupported_huge____"
1713 "____________________________________,__________________________________________"
1714 "________________________________________________",
1715 .expected_unsupported = "unsupported1,unsupported4",
1716 .expected_profile = SIP_OPT_UNKNOWN | SIP_OPT_REPLACES | SIP_OPT_TIMER,
1718 struct testdata test7 = {
1719 .name = "test_null_input",
1720 .input_options = NULL,
1721 .expected_unsupported = "",
1722 .expected_profile = 0,
1724 struct testdata test8 = {
1725 .name = "test_whitespace_input",
1726 .input_options = " ",
1727 .expected_unsupported = "",
1728 .expected_profile = 0,
1730 struct testdata test9 = {
1731 .name = "test_whitespace_plus_option_input",
1732 .input_options = " , , ,timer , , , , , ",
1733 .expected_unsupported = "",
1734 .expected_profile = SIP_OPT_TIMER,
1739 info->name = "sip_parse_options_test";
1740 info->category = "/channels/chan_sip/";
1741 info->summary = "Tests parsing of sip options";
1743 "Tests parsing of SIP options from supported and required "
1744 "header fields. Verifies when unsupported options are encountered "
1745 "that they are appended to the unsupported out buffer and that the "
1746 "correct bit field representnig the option profile is returned.";
1747 return AST_TEST_NOT_RUN;
1752 AST_LIST_HEAD_SET_NOLOCK(&testdatalist, &test1);
1753 AST_LIST_INSERT_TAIL(&testdatalist, &test2, list);
1754 AST_LIST_INSERT_TAIL(&testdatalist, &test3, list);
1755 AST_LIST_INSERT_TAIL(&testdatalist, &test4, list);
1756 AST_LIST_INSERT_TAIL(&testdatalist, &test5, list);
1757 AST_LIST_INSERT_TAIL(&testdatalist, &test6, list);
1758 AST_LIST_INSERT_TAIL(&testdatalist, &test7, list);
1759 AST_LIST_INSERT_TAIL(&testdatalist, &test8, list);
1760 AST_LIST_INSERT_TAIL(&testdatalist, &test9, list);
1762 /* Test with unsupported char buffer */
1763 AST_LIST_TRAVERSE(&testdatalist, testdataptr, list) {
1764 option_profile = parse_sip_options(testdataptr->input_options, unsupported, ARRAY_LEN(unsupported));
1765 if (option_profile != testdataptr->expected_profile ||
1766 strcmp(unsupported, testdataptr->expected_unsupported)) {
1767 ast_test_status_update(test, "Test with output buffer \"%s\", expected unsupported: %s actual unsupported:"
1768 "%s expected bit profile: %x actual bit profile: %x\n",
1770 testdataptr->expected_unsupported,
1772 testdataptr->expected_profile,
1774 res = AST_TEST_FAIL;
1776 ast_test_status_update(test, "\"%s\" passed got expected unsupported: %s and bit profile: %x\n",
1782 option_profile = parse_sip_options(testdataptr->input_options, NULL, 0);
1783 if (option_profile != testdataptr->expected_profile) {
1784 ast_test_status_update(test, "NULL output test \"%s\", expected bit profile: %x actual bit profile: %x\n",
1786 testdataptr->expected_profile,
1788 res = AST_TEST_FAIL;
1790 ast_test_status_update(test, "\"%s\" with NULL output buf passed, bit profile: %x\n",
1799 /*! \brief helper routine for sip_uri_cmp to compare URI parameters
1801 * This takes the parameters from two SIP URIs and determines
1802 * if the URIs match. The rules for parameters *suck*. Here's a breakdown
1803 * 1. If a parameter appears in both URIs, then they must have the same value
1804 * in order for the URIs to match
1805 * 2. If one URI has a user, maddr, ttl, or method parameter, then the other
1806 * URI must also have that parameter and must have the same value
1807 * in order for the URIs to match
1808 * 3. All other headers appearing in only one URI are not considered when
1809 * determining if URIs match
1811 * \param input1 Parameters from URI 1
1812 * \param input2 Parameters from URI 2
1813 * \retval 0 URIs' parameters match
1814 * \retval nonzero URIs' parameters do not match
1816 static int sip_uri_params_cmp(const char *input1, const char *input2)
1818 char *params1 = NULL;
1819 char *params2 = NULL;
1822 int zerolength1 = 0;
1823 int zerolength2 = 0;
1827 int methodmatch = 0;
1829 if (ast_strlen_zero(input1)) {
1832 params1 = ast_strdupa(input1);
1834 if (ast_strlen_zero(input2)) {
1837 params2 = ast_strdupa(input2);
1840 /* Quick optimization. If both params are zero-length, then
1843 if (zerolength1 && zerolength2) {
1847 for (pos1 = strsep(¶ms1, ";"); pos1; pos1 = strsep(¶ms1, ";")) {
1848 char *value1 = pos1;
1849 char *name1 = strsep(&value1, "=");
1850 char *params2dup = NULL;
1855 /* Checkpoint reached. We have the name and value parsed for param1
1856 * We have to duplicate params2 each time through this loop
1857 * or else the inner loop below will not work properly.
1860 params2dup = ast_strdupa(params2);
1862 for (pos2 = strsep(¶ms2dup, ";"); pos2; pos2 = strsep(¶ms2dup, ";")) {
1864 char *value2 = strchr(pos2, '=');
1870 if (!strcasecmp(name1, name2)) {
1871 if (strcasecmp(value1, value2)) {
1879 /* Check to see if the parameter is one of the 'must-match' parameters */
1880 if (!strcasecmp(name1, "maddr")) {
1886 } else if (!strcasecmp(name1, "ttl")) {
1892 } else if (!strcasecmp(name1, "user")) {
1898 } else if (!strcasecmp(name1, "method")) {
1907 /* We've made it out of that horrible O(m*n) construct and there are no
1908 * failures yet. We're not done yet, though, because params2 could have
1909 * an maddr, ttl, user, or method header and params1 did not.
1911 for (pos2 = strsep(¶ms2, ";"); pos2; pos2 = strsep(¶ms2, ";")) {
1912 char *value2 = pos2;
1913 char *name2 = strsep(&value2, "=");
1917 if ((!strcasecmp(name2, "maddr") && !maddrmatch) ||
1918 (!strcasecmp(name2, "ttl") && !ttlmatch) ||
1919 (!strcasecmp(name2, "user") && !usermatch) ||
1920 (!strcasecmp(name2, "method") && !methodmatch)) {
1930 /*! \brief helper routine for sip_uri_cmp to compare URI headers
1932 * This takes the headers from two SIP URIs and determines
1933 * if the URIs match. The rules for headers is simple. If a header
1934 * appears in one URI, then it must also appear in the other URI. The
1935 * order in which the headers appear does not matter.
1937 * \param input1 Headers from URI 1
1938 * \param input2 Headers from URI 2
1939 * \retval 0 URI headers match
1940 * \retval nonzero URI headers do not match
1942 static int sip_uri_headers_cmp(const char *input1, const char *input2)
1944 char *headers1 = NULL;
1945 char *headers2 = NULL;
1946 int zerolength1 = 0;
1947 int zerolength2 = 0;
1951 if (ast_strlen_zero(input1)) {
1954 headers1 = ast_strdupa(input1);
1957 if (ast_strlen_zero(input2)) {
1960 headers2 = ast_strdupa(input2);
1963 /* If one URI contains no headers and the other
1964 * does, then they cannot possibly match
1966 if (zerolength1 != zerolength2) {
1970 if (zerolength1 && zerolength2)
1973 /* At this point, we can definitively state that both inputs are
1974 * not zero-length. First, one more optimization. If the length
1975 * of the headers is not equal, then we definitely have no match
1977 if (strlen(headers1) != strlen(headers2)) {
1981 for (header1 = strsep(&headers1, "&"); header1; header1 = strsep(&headers1, "&")) {
1982 if (!strcasestr(headers2, header1)) {
1992 * \brief Compare domain sections of SIP URIs
1994 * For hostnames, a case insensitive string comparison is
1995 * used. For IP addresses, a binary comparison is used. This
1996 * is mainly because IPv6 addresses have many ways of writing
1999 * For specifics about IP address comparison, see the following
2000 * document: http://tools.ietf.org/html/draft-ietf-sip-ipv6-abnf-fix-05
2002 * \param host1 The domain from the first URI
2003 * \param host2 THe domain from the second URI
2004 * \retval 0 The domains match
2005 * \retval nonzero The domains do not match
2007 static int sip_uri_domain_cmp(const char *host1, const char *host2)
2009 struct ast_sockaddr addr1;
2010 struct ast_sockaddr addr2;
2014 addr1_parsed = ast_sockaddr_parse(&addr1, host1, 0);
2015 addr2_parsed = ast_sockaddr_parse(&addr2, host2, 0);
2017 if (addr1_parsed != addr2_parsed) {
2018 /* One domain was an IP address and the other had
2019 * a host name. FAIL!
2024 /* Both are host names. A string comparison will work
2025 * perfectly here. Specifying the "C" locale ensures that
2026 * The LC_CTYPE conventions use those defined in ANSI C,
2029 if (!addr1_parsed) {
2030 #ifdef HAVE_XLOCALE_H
2032 return strcasecmp(host1, host2);
2034 return strcasecmp_l(host1, host2, c_locale);
2037 return strcasecmp(host1, host2);
2041 /* Both contain IP addresses */
2042 return ast_sockaddr_cmp(&addr1, &addr2);
2045 int sip_uri_cmp(const char *input1, const char *input2)
2058 /* XXX It would be really nice if we could just use parse_uri_full() here
2059 * to separate the components of the URI, but unfortunately it is written
2060 * in a way that can cause URI parameters to be discarded.
2063 if (!input1 || !input2) {
2067 uri1 = ast_strdupa(input1);
2068 uri2 = ast_strdupa(input2);
2070 ast_uri_decode(uri1, ast_uri_sip_user);
2071 ast_uri_decode(uri2, ast_uri_sip_user);
2073 uri_scheme1 = strsep(&uri1, ":");
2074 uri_scheme2 = strsep(&uri2, ":");
2076 if (strcmp(uri_scheme1, uri_scheme2)) {
2080 /* This function is tailored for SIP and SIPS URIs. There's no
2081 * need to check uri_scheme2 since we have determined uri_scheme1
2082 * and uri_scheme2 are equivalent already.
2084 if (strcmp(uri_scheme1, "sip") && strcmp(uri_scheme1, "sips")) {
2088 if (ast_strlen_zero(uri1) || ast_strlen_zero(uri2)) {
2092 if ((host1 = strchr(uri1, '@'))) {
2095 if ((host2 = strchr(uri2, '@'))) {
2099 /* Check for mismatched username and passwords. This is the
2100 * only case-sensitive comparison of a SIP URI
2102 if ((host1 && !host2) ||
2103 (host2 && !host1) ||
2104 (host1 && host2 && strcmp(uri1, uri2))) {
2115 /* Strip off the parameters and headers so we can compare
2119 if ((params1 = strchr(host1, ';'))) {
2122 if ((params2 = strchr(host2, ';'))) {
2126 /* Headers come after parameters, but there may be headers without
2127 * parameters, thus the S_OR
2129 if ((headers1 = strchr(S_OR(params1, host1), '?'))) {
2132 if ((headers2 = strchr(S_OR(params2, host2), '?'))) {
2136 if (sip_uri_domain_cmp(host1, host2)) {
2140 /* Headers have easier rules to follow, so do those first */
2141 if (sip_uri_headers_cmp(headers1, headers2)) {
2145 /* And now the parameters. Ugh */
2146 return sip_uri_params_cmp(params1, params2);
2149 #define URI_CMP_MATCH 0
2150 #define URI_CMP_NOMATCH 1
2152 AST_TEST_DEFINE(sip_uri_cmp_test)
2154 static const struct {
2157 int expected_result;
2158 } uri_cmp_tests [] = {
2159 /* These are identical, so they match */
2160 { "sip:bob@example.com", "sip:bob@example.com", URI_CMP_MATCH },
2161 /* Different usernames. No match */
2162 { "sip:alice@example.com", "sip:bob@example.com", URI_CMP_NOMATCH },
2163 /* Different hosts. No match */
2164 { "sip:bob@example.com", "sip:bob@examplez.com", URI_CMP_NOMATCH },
2165 /* Now start using IP addresses. Identical, so they match */
2166 { "sip:bob@1.2.3.4", "sip:bob@1.2.3.4", URI_CMP_MATCH },
2167 /* Two identical IPv4 addresses represented differently. Match */
2168 { "sip:bob@1.2.3.4", "sip:bob@001.002.003.004", URI_CMP_MATCH },
2169 /* Logically equivalent IPv4 Address and hostname. No Match */
2170 { "sip:bob@127.0.0.1", "sip:bob@localhost", URI_CMP_NOMATCH },
2171 /* Logically equivalent IPv6 address and hostname. No Match */
2172 { "sip:bob@[::1]", "sip:bob@localhost", URI_CMP_NOMATCH },
2173 /* Try an IPv6 one as well */
2174 { "sip:bob@[2001:db8::1234]", "sip:bob@[2001:db8::1234]", URI_CMP_MATCH },
2175 /* Two identical IPv6 addresses represented differently. Match */
2176 { "sip:bob@[2001:db8::1234]", "sip:bob@[2001:0db8::1234]", URI_CMP_MATCH },
2177 /* Different ports. No match */
2178 { "sip:bob@1.2.3.4:5060", "sip:bob@1.2.3.4:5061", URI_CMP_NOMATCH },
2179 /* Same port logically, but only one address specifies it. No match */
2180 { "sip:bob@1.2.3.4:5060", "sip:bob@1.2.3.4", URI_CMP_NOMATCH },
2181 /* And for safety, try with IPv6 */
2182 { "sip:bob@[2001:db8:1234]:5060", "sip:bob@[2001:db8:1234]", URI_CMP_NOMATCH },
2183 /* User comparison is case sensitive. No match */
2184 { "sip:bob@example.com", "sip:BOB@example.com", URI_CMP_NOMATCH },
2185 /* Host comparison is case insensitive. Match */
2186 { "sip:bob@example.com", "sip:bob@EXAMPLE.COM", URI_CMP_MATCH },
2187 /* Add headers to the URI. Identical, so they match */
2188 { "sip:bob@example.com?header1=value1&header2=value2", "sip:bob@example.com?header1=value1&header2=value2", URI_CMP_MATCH },
2189 /* Headers in URI 1 are not in URI 2. No Match */
2190 { "sip:bob@example.com?header1=value1&header2=value2", "sip:bob@example.com", URI_CMP_NOMATCH },
2191 /* Header present in both URIs does not have matching values. No match */
2192 { "sip:bob@example.com?header1=value1&header2=value2", "sip:bob@example.com?header1=value1&header2=value3", URI_CMP_NOMATCH },
2193 /* Add parameters to the URI. Identical so they match */
2194 { "sip:bob@example.com;param1=value1;param2=value2", "sip:bob@example.com;param1=value1;param2=value2", URI_CMP_MATCH },
2195 /* Same parameters in both URIs but appear in different order. Match */
2196 { "sip:bob@example.com;param2=value2;param1=value1", "sip:bob@example.com;param1=value1;param2=value2", URI_CMP_MATCH },
2197 /* params in URI 1 are not in URI 2. Match */
2198 { "sip:bob@example.com;param1=value1;param2=value2", "sip:bob@example.com", URI_CMP_MATCH },
2199 /* param present in both URIs does not have matching values. No match */
2200 { "sip:bob@example.com;param1=value1;param2=value2", "sip:bob@example.com;param1=value1;param2=value3", URI_CMP_NOMATCH },
2201 /* URI 1 has a maddr param but URI 2 does not. No match */
2202 { "sip:bob@example.com;param1=value1;maddr=192.168.0.1", "sip:bob@example.com;param1=value1", URI_CMP_NOMATCH },
2203 /* URI 1 and URI 2 both have identical maddr params. Match */
2204 { "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 },
2205 /* URI 1 is a SIPS URI and URI 2 is a SIP URI. No Match */
2206 { "sips:bob@example.com", "sip:bob@example.com", URI_CMP_NOMATCH },
2207 /* No URI schemes. No match */
2208 { "bob@example.com", "bob@example.com", URI_CMP_NOMATCH },
2209 /* Crashiness tests. Just an address scheme. No match */
2210 { "sip", "sips", URI_CMP_NOMATCH },
2211 /* Still just an address scheme. Even though they're the same, No match */
2212 { "sip", "sip", URI_CMP_NOMATCH },
2213 /* Empty strings. No match */
2214 { "", "", URI_CMP_NOMATCH },
2215 /* An empty string and a NULL. No match */
2216 { "", NULL, URI_CMP_NOMATCH },
2219 int test_res = AST_TEST_PASS;
2222 info->name = "sip_uri_cmp_test";
2223 info->category = "/channels/chan_sip/";
2224 info->summary = "Tests comparison of SIP URIs";
2225 info->description = "Several would-be tricky URI comparisons are performed";
2226 return AST_TEST_NOT_RUN;
2231 for (i = 0; i < ARRAY_LEN(uri_cmp_tests); ++i) {
2234 if ((cmp_res1 = sip_uri_cmp(uri_cmp_tests[i].uri1, uri_cmp_tests[i].uri2))) {
2235 /* URI comparison may return -1 or +1 depending on the failure. Standardize
2236 * the return value to be URI_CMP_NOMATCH on any failure
2238 cmp_res1 = URI_CMP_NOMATCH;
2240 if (cmp_res1 != uri_cmp_tests[i].expected_result) {
2241 ast_test_status_update(test, "Unexpected comparison result for URIs %s and %s. "
2242 "Expected %s but got %s\n", uri_cmp_tests[i].uri1, uri_cmp_tests[i].uri2,
2243 uri_cmp_tests[i].expected_result == URI_CMP_MATCH ? "Match" : "No Match",
2244 cmp_res1 == URI_CMP_MATCH ? "Match" : "No Match");
2245 test_res = AST_TEST_FAIL;
2248 /* All URI comparisons are commutative, so for the sake of being thorough, we'll
2249 * rerun the comparison with the parameters reversed
2251 if ((cmp_res2 = sip_uri_cmp(uri_cmp_tests[i].uri2, uri_cmp_tests[i].uri1))) {
2252 /* URI comparison may return -1 or +1 depending on the failure. Standardize
2253 * the return value to be URI_CMP_NOMATCH on any failure
2255 cmp_res2 = URI_CMP_NOMATCH;
2257 if (cmp_res2 != uri_cmp_tests[i].expected_result) {
2258 ast_test_status_update(test, "Unexpected comparison result for URIs %s and %s. "
2259 "Expected %s but got %s\n", uri_cmp_tests[i].uri2, uri_cmp_tests[i].uri1,
2260 uri_cmp_tests[i].expected_result == URI_CMP_MATCH ? "Match" : "No Match",
2261 cmp_res2 == URI_CMP_MATCH ? "Match" : "No Match");
2262 test_res = AST_TEST_FAIL;
2269 void free_via(struct sip_via *v)
2279 struct sip_via *parse_via(const char *header)
2281 struct sip_via *v = ast_calloc(1, sizeof(*v));
2288 v->via = ast_strdup(header);
2293 if (ast_strlen_zero(via)) {
2294 ast_log(LOG_ERROR, "received request without a Via header\n");
2299 /* seperate the first via-parm */
2300 via = strsep(&via, ",");
2302 /* chop off sent-protocol */
2303 v->protocol = strsep(&via, " \t\r\n");
2304 if (ast_strlen_zero(v->protocol)) {
2305 ast_log(LOG_ERROR, "missing sent-protocol in Via header\n");
2309 v->protocol = ast_skip_blanks(v->protocol);
2312 via = ast_skip_blanks(via);
2315 /* chop off sent-by */
2316 v->sent_by = strsep(&via, "; \t\r\n");
2317 if (ast_strlen_zero(v->sent_by)) {
2318 ast_log(LOG_ERROR, "missing sent-by in Via header\n");
2322 v->sent_by = ast_skip_blanks(v->sent_by);
2324 /* store the port, we have to handle ipv6 addresses containing ':'
2325 * characters gracefully */
2326 if (((parm = strchr(v->sent_by, ']')) && *(++parm) == ':') || (parm = strchr(v->sent_by, ':'))) {
2329 v->port = strtol(++parm, &endptr, 10);
2332 /* evaluate any via-parms */
2333 while ((parm = strsep(&via, "; \t\r\n"))) {
2335 if ((c = strstr(parm, "maddr="))) {
2336 v->maddr = ast_skip_blanks(c + sizeof("maddr=") - 1);
2337 } else if ((c = strstr(parm, "branch="))) {
2338 v->branch = ast_skip_blanks(c + sizeof("branch=") - 1);
2339 } else if ((c = strstr(parm, "ttl="))) {
2341 c = ast_skip_blanks(c + sizeof("ttl=") - 1);
2342 v->ttl = strtol(c, &endptr, 10);
2344 /* make sure we got a valid ttl value */
2354 AST_TEST_DEFINE(parse_via_test)
2356 int res = AST_TEST_PASS;
2358 struct sip_via *via;
2361 char *expected_protocol;
2362 char *expected_branch;
2363 char *expected_sent_by;
2364 char *expected_maddr;
2365 unsigned int expected_port;
2366 unsigned char expected_ttl;
2368 AST_LIST_ENTRY(testdata) list;
2370 struct testdata *testdataptr;
2371 static AST_LIST_HEAD_NOLOCK(testdataliststruct, testdata) testdatalist;
2372 struct testdata t1 = {
2373 .in = "SIP/2.0/UDP host:port;branch=thebranch",
2374 .expected_protocol = "SIP/2.0/UDP",
2375 .expected_sent_by = "host:port",
2376 .expected_branch = "thebranch",
2378 struct testdata t2 = {
2379 .in = "SIP/2.0/UDP host:port",
2380 .expected_protocol = "SIP/2.0/UDP",
2381 .expected_sent_by = "host:port",
2382 .expected_branch = "",
2384 struct testdata t3 = {
2385 .in = "SIP/2.0/UDP",
2388 struct testdata t4 = {
2389 .in = "BLAH/BLAH/BLAH host:port;branch=",
2390 .expected_protocol = "BLAH/BLAH/BLAH",
2391 .expected_sent_by = "host:port",
2392 .expected_branch = "",
2394 struct testdata t5 = {
2395 .in = "SIP/2.0/UDP host:5060;branch=thebranch;maddr=224.0.0.1;ttl=1",
2396 .expected_protocol = "SIP/2.0/UDP",
2397 .expected_sent_by = "host:5060",
2398 .expected_port = 5060,
2399 .expected_branch = "thebranch",
2400 .expected_maddr = "224.0.0.1",
2403 struct testdata t6 = {
2404 .in = "SIP/2.0/UDP host:5060;\n branch=thebranch;\r\n maddr=224.0.0.1; ttl=1",
2405 .expected_protocol = "SIP/2.0/UDP",
2406 .expected_sent_by = "host:5060",
2407 .expected_port = 5060,
2408 .expected_branch = "thebranch",
2409 .expected_maddr = "224.0.0.1",
2412 struct testdata t7 = {
2413 .in = "SIP/2.0/UDP [::1]:5060",
2414 .expected_protocol = "SIP/2.0/UDP",
2415 .expected_sent_by = "[::1]:5060",
2416 .expected_port = 5060,
2417 .expected_branch = "",
2421 info->name = "parse_via_test";
2422 info->category = "/channels/chan_sip/";
2423 info->summary = "Tests parsing the Via header";
2425 "Runs through various test situations in which various "
2426 " parameters parameter must be extracted from a VIA header";
2427 return AST_TEST_NOT_RUN;
2432 AST_LIST_HEAD_SET_NOLOCK(&testdatalist, &t1);
2433 AST_LIST_INSERT_TAIL(&testdatalist, &t2, list);
2434 AST_LIST_INSERT_TAIL(&testdatalist, &t3, list);
2435 AST_LIST_INSERT_TAIL(&testdatalist, &t4, list);
2436 AST_LIST_INSERT_TAIL(&testdatalist, &t5, list);
2437 AST_LIST_INSERT_TAIL(&testdatalist, &t6, list);
2438 AST_LIST_INSERT_TAIL(&testdatalist, &t7, list);
2441 AST_LIST_TRAVERSE(&testdatalist, testdataptr, list) {
2442 via = parse_via(testdataptr->in);
2444 if (!testdataptr->expected_null) {
2445 ast_test_status_update(test, "TEST#%d FAILED: VIA = \"%s\"\n"
2446 "failed to parse header\n",
2447 i, testdataptr->in);
2448 res = AST_TEST_FAIL;
2454 if (testdataptr->expected_null) {
2455 ast_test_status_update(test, "TEST#%d FAILED: VIA = \"%s\"\n"
2456 "successfully parased invalid via header\n",
2457 i, testdataptr->in);
2458 res = AST_TEST_FAIL;
2464 if ((ast_strlen_zero(via->protocol) && !ast_strlen_zero(testdataptr->expected_protocol))
2465 || (!ast_strlen_zero(via->protocol) && strcmp(via->protocol, testdataptr->expected_protocol))) {
2467 ast_test_status_update(test, "TEST#%d FAILED: VIA = \"%s\"\n"
2468 "parsed protocol = \"%s\"\n"
2469 "expected = \"%s\"\n"
2470 "failed to parse protocol\n",
2471 i, testdataptr->in, via->protocol, testdataptr->expected_protocol);
2472 res = AST_TEST_FAIL;
2475 if ((ast_strlen_zero(via->sent_by) && !ast_strlen_zero(testdataptr->expected_sent_by))
2476 || (!ast_strlen_zero(via->sent_by) && strcmp(via->sent_by, testdataptr->expected_sent_by))) {
2478 ast_test_status_update(test, "TEST#%d FAILED: VIA = \"%s\"\n"
2479 "parsed sent_by = \"%s\"\n"
2480 "expected = \"%s\"\n"
2481 "failed to parse sent-by\n",
2482 i, testdataptr->in, via->sent_by, testdataptr->expected_sent_by);
2483 res = AST_TEST_FAIL;
2486 if (testdataptr->expected_port && testdataptr->expected_port != via->port) {
2487 ast_test_status_update(test, "TEST#%d FAILED: VIA = \"%s\"\n"
2488 "parsed port = \"%d\"\n"
2489 "expected = \"%d\"\n"
2490 "failed to parse port\n",
2491 i, testdataptr->in, via->port, testdataptr->expected_port);
2492 res = AST_TEST_FAIL;
2495 if ((ast_strlen_zero(via->branch) && !ast_strlen_zero(testdataptr->expected_branch))
2496 || (!ast_strlen_zero(via->branch) && strcmp(via->branch, testdataptr->expected_branch))) {
2498 ast_test_status_update(test, "TEST#%d FAILED: VIA = \"%s\"\n"
2499 "parsed branch = \"%s\"\n"
2500 "expected = \"%s\"\n"
2501 "failed to parse branch\n",
2502 i, testdataptr->in, via->branch, testdataptr->expected_branch);
2503 res = AST_TEST_FAIL;
2506 if ((ast_strlen_zero(via->maddr) && !ast_strlen_zero(testdataptr->expected_maddr))
2507 || (!ast_strlen_zero(via->maddr) && strcmp(via->maddr, testdataptr->expected_maddr))) {
2509 ast_test_status_update(test, "TEST#%d FAILED: VIA = \"%s\"\n"
2510 "parsed maddr = \"%s\"\n"
2511 "expected = \"%s\"\n"
2512 "failed to parse maddr\n",
2513 i, testdataptr->in, via->maddr, testdataptr->expected_maddr);
2514 res = AST_TEST_FAIL;
2517 if (testdataptr->expected_ttl && testdataptr->expected_ttl != via->ttl) {
2518 ast_test_status_update(test, "TEST#%d FAILED: VIA = \"%s\"\n"
2519 "parsed ttl = \"%d\"\n"
2520 "expected = \"%d\"\n"
2521 "failed to parse ttl\n",
2522 i, testdataptr->in, via->ttl, testdataptr->expected_ttl);
2523 res = AST_TEST_FAIL;
2532 void sip_request_parser_register_tests(void)
2534 AST_TEST_REGISTER(get_calleridname_test);
2535 AST_TEST_REGISTER(sip_parse_uri_test);
2536 AST_TEST_REGISTER(get_in_brackets_test);
2537 AST_TEST_REGISTER(get_name_and_number_test);
2538 AST_TEST_REGISTER(sip_parse_uri_full_test);
2539 AST_TEST_REGISTER(parse_name_andor_addr_test);
2540 AST_TEST_REGISTER(parse_contact_header_test);
2541 AST_TEST_REGISTER(sip_parse_options_test);
2542 AST_TEST_REGISTER(sip_uri_cmp_test);
2543 AST_TEST_REGISTER(parse_via_test);
2545 void sip_request_parser_unregister_tests(void)
2547 AST_TEST_UNREGISTER(sip_parse_uri_test);
2548 AST_TEST_UNREGISTER(get_calleridname_test);
2549 AST_TEST_UNREGISTER(get_in_brackets_test);
2550 AST_TEST_UNREGISTER(get_name_and_number_test);
2551 AST_TEST_UNREGISTER(sip_parse_uri_full_test);
2552 AST_TEST_UNREGISTER(parse_name_andor_addr_test);
2553 AST_TEST_UNREGISTER(parse_contact_header_test);
2554 AST_TEST_UNREGISTER(sip_parse_options_test);
2555 AST_TEST_UNREGISTER(sip_uri_cmp_test);
2556 AST_TEST_UNREGISTER(parse_via_test);
2559 int sip_reqresp_parser_init(void)
2561 #ifdef HAVE_XLOCALE_H
2562 c_locale = newlocale(LC_CTYPE_MASK, "C", NULL);
2570 void sip_reqresp_parser_exit(void)
2572 #ifdef HAVE_XLOCALE_H
2574 freelocale(c_locale);