diff options
Diffstat (limited to 'channels/sip/reqresp_parser.c')
-rw-r--r-- | channels/sip/reqresp_parser.c | 231 |
1 files changed, 231 insertions, 0 deletions
diff --git a/channels/sip/reqresp_parser.c b/channels/sip/reqresp_parser.c index 446e9637f..bf35bc226 100644 --- a/channels/sip/reqresp_parser.c +++ b/channels/sip/reqresp_parser.c @@ -1575,6 +1575,235 @@ AST_TEST_DEFINE(parse_contact_header_test) return res; } +/*! + * \brief Parse supported header in incoming packet + * + * \details This function parses through the options parameters and + * builds a bit field representing all the SIP options in that field. When an + * item is found that is not supported, it is copied to the unsupported + * out buffer. + * + * \param option list + * \param unsupported out buffer (optional) + * \param unsupported out buffer length (optional) + */ +unsigned int parse_sip_options(const char *options, char *unsupported, size_t unsupported_len) +{ + char *next, *sep; + char *temp; + int i, found, supported; + unsigned int profile = 0; + + char *out = unsupported; + size_t outlen = unsupported_len; + char *cur_out = out; + + if (out && (outlen > 0)) { + memset(out, 0, outlen); + } + + if (ast_strlen_zero(options) ) + return 0; + + temp = ast_strdupa(options); + + ast_debug(3, "Begin: parsing SIP \"Supported: %s\"\n", options); + + for (next = temp; next; next = sep) { + found = FALSE; + supported = FALSE; + if ((sep = strchr(next, ',')) != NULL) { + *sep++ = '\0'; + } + + /* trim leading and trailing whitespace */ + next = ast_strip(next); + + if (ast_strlen_zero(next)) { + continue; /* if there is a blank argument in there just skip it */ + } + + ast_debug(3, "Found SIP option: -%s-\n", next); + for (i = 0; i < ARRAY_LEN(sip_options); i++) { + if (!strcasecmp(next, sip_options[i].text)) { + profile |= sip_options[i].id; + if (sip_options[i].supported == SUPPORTED) { + supported = TRUE; + } + found = TRUE; + ast_debug(3, "Matched SIP option: %s\n", next); + break; + } + } + + /* If option is not supported, add to unsupported out buffer */ + if (!supported && out && outlen) { + size_t copylen = strlen(next); + size_t cur_outlen = strlen(out); + /* Check to see if there is enough room to store this option. + * Copy length is string length plus 2 for the ',' and '\0' */ + if ((cur_outlen + copylen + 2) < outlen) { + /* if this isn't the first item, add the ',' */ + if (cur_outlen) { + *cur_out = ','; + cur_out++; + cur_outlen++; + } + ast_copy_string(cur_out, next, (outlen - cur_outlen)); + cur_out += copylen; + } + } + + if (!found) { + profile |= SIP_OPT_UNKNOWN; + if (!strncasecmp(next, "x-", 2)) + ast_debug(3, "Found private SIP option, not supported: %s\n", next); + else + ast_debug(3, "Found no match for SIP option: %s (Please file bug report!)\n", next); + } + } + + return profile; +} + +AST_TEST_DEFINE(sip_parse_options_test) +{ + int res = AST_TEST_PASS; + char unsupported[64]; + unsigned int option_profile = 0; + struct testdata { + char *name; + char *input_options; + char *expected_unsupported; + unsigned int expected_profile; + AST_LIST_ENTRY(testdata) list; + }; + + struct testdata *testdataptr; + static AST_LIST_HEAD_NOLOCK(testdataliststruct, testdata) testdatalist; + + struct testdata test1 = { + .name = "test_all_unsupported", + .input_options = "unsupported1,,, ,unsupported2,unsupported3,unsupported4", + .expected_unsupported = "unsupported1,unsupported2,unsupported3,unsupported4", + .expected_profile = SIP_OPT_UNKNOWN, + }; + struct testdata test2 = { + .name = "test_all_unsupported_one_supported", + .input_options = " unsupported1, replaces, unsupported3 , , , ,unsupported4", + .expected_unsupported = "unsupported1,unsupported3,unsupported4", + .expected_profile = SIP_OPT_UNKNOWN | SIP_OPT_REPLACES + }; + struct testdata test3 = { + .name = "test_two_supported_two_unsupported", + .input_options = ",, timer ,replaces ,unsupported3,unsupported4", + .expected_unsupported = "unsupported3,unsupported4", + .expected_profile = SIP_OPT_UNKNOWN | SIP_OPT_REPLACES | SIP_OPT_TIMER, + }; + + struct testdata test4 = { + .name = "test_all_supported", + .input_options = "timer,replaces", + .expected_unsupported = "", + .expected_profile = SIP_OPT_REPLACES | SIP_OPT_TIMER, + }; + + struct testdata test5 = { + .name = "test_all_supported_redundant", + .input_options = "timer,replaces,timer,replace,timer,replaces", + .expected_unsupported = "", + .expected_profile = SIP_OPT_REPLACES | SIP_OPT_TIMER, + }; + struct testdata test6 = { + .name = "test_buffer_overflow", + .input_options = "unsupported1,replaces,timer,unsupported4,unsupported_huge____" + "____________________________________,__________________________________________" + "________________________________________________", + .expected_unsupported = "unsupported1,unsupported4", + .expected_profile = SIP_OPT_UNKNOWN | SIP_OPT_REPLACES | SIP_OPT_TIMER, + }; + struct testdata test7 = { + .name = "test_null_input", + .input_options = NULL, + .expected_unsupported = "", + .expected_profile = 0, + }; + struct testdata test8 = { + .name = "test_whitespace_input", + .input_options = " ", + .expected_unsupported = "", + .expected_profile = 0, + }; + struct testdata test9 = { + .name = "test_whitespace_plus_option_input", + .input_options = " , , ,timer , , , , , ", + .expected_unsupported = "", + .expected_profile = SIP_OPT_TIMER, + }; + + switch (cmd) { + case TEST_INIT: + info->name = "sip_parse_options_test"; + info->category = "channels/chan_sip/"; + info->summary = "Tests parsing of sip options"; + info->description = + "Tests parsing of SIP options from supported and required " + "header fields. Verifies when unsupported options are encountered " + "that they are appended to the unsupported out buffer and that the " + "correct bit field representnig the option profile is returned."; + return AST_TEST_NOT_RUN; + case TEST_EXECUTE: + break; + } + + AST_LIST_HEAD_SET_NOLOCK(&testdatalist, &test1); + AST_LIST_INSERT_TAIL(&testdatalist, &test2, list); + AST_LIST_INSERT_TAIL(&testdatalist, &test3, list); + AST_LIST_INSERT_TAIL(&testdatalist, &test4, list); + AST_LIST_INSERT_TAIL(&testdatalist, &test5, list); + AST_LIST_INSERT_TAIL(&testdatalist, &test6, list); + AST_LIST_INSERT_TAIL(&testdatalist, &test7, list); + AST_LIST_INSERT_TAIL(&testdatalist, &test8, list); + AST_LIST_INSERT_TAIL(&testdatalist, &test9, list); + + /* Test with unsupported char buffer */ + AST_LIST_TRAVERSE(&testdatalist, testdataptr, list) { + option_profile = parse_sip_options(testdataptr->input_options, unsupported, ARRAY_LEN(unsupported)); + if (option_profile != testdataptr->expected_profile || + strcmp(unsupported, testdataptr->expected_unsupported)) { + ast_test_status_update(test, "Test with output buffer \"%s\", expected unsupported: %s actual unsupported:" + "%s expected bit profile: %x actual bit profile: %x\n", + testdataptr->name, + testdataptr->expected_unsupported, + unsupported, + testdataptr->expected_profile, + option_profile); + res = AST_TEST_FAIL; + } else { + ast_test_status_update(test, "\"%s\" passed got expected unsupported: %s and bit profile: %x\n", + testdataptr->name, + unsupported, + option_profile); + } + + option_profile = parse_sip_options(testdataptr->input_options, NULL, 0); + if (option_profile != testdataptr->expected_profile) { + ast_test_status_update(test, "NULL output test \"%s\", expected bit profile: %x actual bit profile: %x\n", + testdataptr->name, + testdataptr->expected_profile, + option_profile); + res = AST_TEST_FAIL; + } else { + ast_test_status_update(test, "\"%s\" with NULL output buf passed, bit profile: %x\n", + testdataptr->name, + option_profile); + } + } + + return res; +} + + void sip_request_parser_register_tests(void) { AST_TEST_REGISTER(get_calleridname_test); @@ -1584,6 +1813,7 @@ void sip_request_parser_register_tests(void) AST_TEST_REGISTER(sip_parse_uri_fully_test); AST_TEST_REGISTER(parse_name_andor_addr_test); AST_TEST_REGISTER(parse_contact_header_test); + AST_TEST_REGISTER(sip_parse_options_test); } void sip_request_parser_unregister_tests(void) { @@ -1594,4 +1824,5 @@ void sip_request_parser_unregister_tests(void) AST_TEST_UNREGISTER(sip_parse_uri_fully_test); AST_TEST_UNREGISTER(parse_name_andor_addr_test); AST_TEST_UNREGISTER(parse_contact_header_test); + AST_TEST_UNREGISTER(sip_parse_options_test); } |