aboutsummaryrefslogtreecommitdiffstats
path: root/channels/sip/reqresp_parser.c
diff options
context:
space:
mode:
Diffstat (limited to 'channels/sip/reqresp_parser.c')
-rw-r--r--channels/sip/reqresp_parser.c231
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);
}