diff options
author | mmichelson <mmichelson@f38db490-d61c-443f-a65b-d21fe96a405b> | 2010-07-23 16:33:52 +0000 |
---|---|---|
committer | mmichelson <mmichelson@f38db490-d61c-443f-a65b-d21fe96a405b> | 2010-07-23 16:33:52 +0000 |
commit | f8b73836c1f6565bc4038ec7a2972a201e07f687 (patch) | |
tree | 1e0879f4f888be3421d0133c9f2076c53bf41297 /channels | |
parent | 6bb04df2e6f7377453ef49c4263bf1ab27a542bb (diff) |
SIP URI comparison fixes.
This initially was created to work around the issue of
using a string comparison instead of a binary comparison
for IP addresses. It evolved a bit when test cases were
created and it was discovered that comparison of URI
parameters was not working exactly as it should.
sip_uri_cmp() and its helpers have been moved to reqresp_parser.c
and a new test has been added.
(closes issue #17662)
Reported by: oej
Review: https://reviewboard.asterisk.org/r/792
git-svn-id: http://svn.digium.com/svn/asterisk/trunk@278980 f38db490-d61c-443f-a65b-d21fe96a405b
Diffstat (limited to 'channels')
-rw-r--r-- | channels/chan_sip.c | 294 | ||||
-rw-r--r-- | channels/sip/include/reqresp_parser.h | 23 | ||||
-rw-r--r-- | channels/sip/reqresp_parser.c | 484 |
3 files changed, 514 insertions, 287 deletions
diff --git a/channels/chan_sip.c b/channels/chan_sip.c index 884dd7c23..7a7a38b14 100644 --- a/channels/chan_sip.c +++ b/channels/chan_sip.c @@ -20343,292 +20343,6 @@ static int handle_invite_replaces(struct sip_pvt *p, struct sip_request *req, in return 0; } -/*! \brief helper routine for sip_uri_cmp - * - * This takes the parameters from two SIP URIs and determines - * if the URIs match. The rules for parameters *suck*. Here's a breakdown - * 1. If a parameter appears in both URIs, then they must have the same value - * in order for the URIs to match - * 2. If one URI has a user, maddr, ttl, or method parameter, then the other - * URI must also have that parameter and must have the same value - * in order for the URIs to match - * 3. All other headers appearing in only one URI are not considered when - * determining if URIs match - * - * \param input1 Parameters from URI 1 - * \param input2 Parameters from URI 2 - * \return Return 0 if the URIs' parameters match, 1 if they do not - */ -static int sip_uri_params_cmp(const char *input1, const char *input2) -{ - char *params1 = NULL; - char *params2 = NULL; - char *pos1; - char *pos2; - int zerolength1 = 0; - int zerolength2 = 0; - int maddrmatch = 0; - int ttlmatch = 0; - int usermatch = 0; - int methodmatch = 0; - - if (ast_strlen_zero(input1)) { - zerolength1 = 1; - } else { - params1 = ast_strdupa(input1); - } - if (ast_strlen_zero(input2)) { - zerolength2 = 1; - } else { - params2 = ast_strdupa(input2); - } - - /*Quick optimization. If both params are zero-length, then - * they match - */ - if (zerolength1 && zerolength2) { - return 0; - } - - pos1 = params1; - while (!ast_strlen_zero(pos1)) { - char *name1 = pos1; - char *value1 = strchr(pos1, '='); - char *semicolon1 = strchr(pos1, ';'); - int matched = 0; - if (semicolon1) { - *semicolon1++ = '\0'; - } - if (!value1) { - goto fail; - } - *value1++ = '\0'; - /* Checkpoint reached. We have the name and value parsed for param1 - * We have to duplicate params2 each time through the second loop - * or else we can't search and replace the semicolons with \0 each - * time - */ - pos2 = ast_strdupa(params2); - while (!ast_strlen_zero(pos2)) { - char *name2 = pos2; - char *value2 = strchr(pos2, '='); - char *semicolon2 = strchr(pos2, ';'); - if (semicolon2) { - *semicolon2++ = '\0'; - } - if (!value2) { - goto fail; - } - *value2++ = '\0'; - if (!strcasecmp(name1, name2)) { - if (strcasecmp(value1, value2)) { - goto fail; - } else { - matched = 1; - break; - } - } - pos2 = semicolon2; - } - /* Need to see if the parameter we're looking at is one of the 'must-match' parameters */ - if (!strcasecmp(name1, "maddr")) { - if (matched) { - maddrmatch = 1; - } else { - goto fail; - } - } else if (!strcasecmp(name1, "ttl")) { - if (matched) { - ttlmatch = 1; - } else { - goto fail; - } - } else if (!strcasecmp(name1, "user")) { - if (matched) { - usermatch = 1; - } else { - goto fail; - } - } else if (!strcasecmp(name1, "method")) { - if (matched) { - methodmatch = 1; - } else { - goto fail; - } - } - pos1 = semicolon1; - } - - /* We've made it out of that horrible O(m*n) construct and there are no - * failures yet. We're not done yet, though, because params2 could have - * an maddr, ttl, user, or method header and params1 did not. - */ - pos2 = params2; - while (!ast_strlen_zero(pos2)) { - char *name2 = pos2; - char *value2 = strchr(pos2, '='); - char *semicolon2 = strchr(pos2, ';'); - if (semicolon2) { - *semicolon2++ = '\0'; - } - if (!value2) { - goto fail; - } - *value2++ = '\0'; - if ((!strcasecmp(name2, "maddr") && !maddrmatch) || - (!strcasecmp(name2, "ttl") && !ttlmatch) || - (!strcasecmp(name2, "user") && !usermatch) || - (!strcasecmp(name2, "method") && !methodmatch)) { - goto fail; - } - } - return 0; - -fail: - return 1; -} - -/*! \brief helper routine for sip_uri_cmp - * - * This takes the "headers" from two SIP URIs and determines - * if the URIs match. The rules for headers is simple. If a header - * appears in one URI, then it must also appear in the other URI. The - * order in which the headers appear does not matter. - * - * \param input1 Headers from URI 1 - * \param input2 Headers from URI 2 - * \return Return 0 if the URIs' headers match, 1 if they do not - */ -static int sip_uri_headers_cmp(const char *input1, const char *input2) -{ - char *headers1 = NULL; - char *headers2 = NULL; - int zerolength1 = 0; - int zerolength2 = 0; - int different = 0; - char *header1; - - if (ast_strlen_zero(input1)) { - zerolength1 = 1; - } else { - headers1 = ast_strdupa(input1); - } - - if (ast_strlen_zero(input2)) { - zerolength2 = 1; - } else { - headers2 = ast_strdupa(input2); - } - - if ((zerolength1 && !zerolength2) || - (zerolength2 && !zerolength1)) - return 1; - - if (zerolength1 && zerolength2) - return 0; - - /* At this point, we can definitively state that both inputs are - * not zero-length. First, one more optimization. If the length - * of the headers is not equal, then we definitely have no match - */ - if (strlen(headers1) != strlen(headers2)) { - return 1; - } - - for (header1 = strsep(&headers1, "&"); header1; header1 = strsep(&headers1, "&")) { - if (!strcasestr(headers2, header1)) { - different = 1; - break; - } - } - - return different; -} - -static int sip_uri_cmp(const char *input1, const char *input2) -{ - char *uri1 = ast_strdupa(input1); - char *uri2 = ast_strdupa(input2); - char *host1; - char *host2; - char *params1; - char *params2; - char *headers1; - char *headers2; - - /* Strip off "sip:" from the URI. We know this is present - * because it was checked back in parse_request() - */ - strsep(&uri1, ":"); - strsep(&uri2, ":"); - - if ((host1 = strchr(uri1, '@'))) { - *host1++ = '\0'; - } - if ((host2 = strchr(uri2, '@'))) { - *host2++ = '\0'; - } - - /* Check for mismatched username and passwords. This is the - * only case-sensitive comparison of a SIP URI - */ - if ((host1 && !host2) || - (host2 && !host1) || - (host1 && host2 && strcmp(uri1, uri2))) { - return 1; - } - - if (!host1) { - host1 = uri1; - } - if (!host2) { - host2 = uri2; - } - - /* Strip off the parameters and headers so we can compare - * host and port - */ - - if ((params1 = strchr(host1, ';'))) { - *params1++ = '\0'; - } - if ((params2 = strchr(host2, ';'))) { - *params2++ = '\0'; - } - - /* Headers come after parameters, but there may be headers without - * parameters, thus the S_OR - */ - if ((headers1 = strchr(S_OR(params1, host1), '?'))) { - *headers1++ = '\0'; - } - if ((headers2 = strchr(S_OR(params2, host2), '?'))) { - *headers2++ = '\0'; - } - - /* Now the host/port are properly isolated. We can get by with a string comparison - * because the SIP URI checking rules have some interesting exceptions that make - * this possible. I will note 2 in particular - * 1. hostnames which resolve to the same IP address as well as a hostname and its - * IP address are not considered a match with SIP URI's. - * 2. If one URI specifies a port and the other does not, then the URIs do not match. - * This includes if one URI explicitly contains port 5060 and the other implies it - * by not having a port specified. - */ - - if (strcasecmp(host1, host2)) { - return 1; - } - - /* Headers have easier rules to follow, so do those first */ - if (sip_uri_headers_cmp(headers1, headers2)) { - return 1; - } - - /* And now the parameters. Ugh */ - return sip_uri_params_cmp(params1, params2); -} - /*! \note No channel or pvt locks should be held while calling this function. */ static int do_magic_pickup(struct ast_channel *channel, const char *extension, const char *context) { @@ -28322,9 +28036,16 @@ static int load_module(void) sip_send_all_registers(); sip_send_all_mwi_subscriptions(); initialize_escs(); + if (sip_epa_register(&cc_epa_static_data)) { return AST_MODULE_LOAD_DECLINE; } + + if (sip_reqresp_parser_init() == -1) { + ast_log(LOG_ERROR, "Unable to initialize the SIP request and response parser\n"); + return AST_MODULE_LOAD_DECLINE; + } + if (can_parse_xml) { /* SIP CC agents require the ability to parse XML PIDF bodies * in incoming PUBLISH requests @@ -28498,6 +28219,7 @@ static int unload_module(void) ast_cc_monitor_unregister(&sip_cc_monitor_callbacks); ast_cc_agent_unregister(&sip_cc_agent_callbacks); + sip_reqresp_parser_exit(); sip_unregister_tests(); return 0; diff --git a/channels/sip/include/reqresp_parser.h b/channels/sip/include/reqresp_parser.h index d8631c982..2957151aa 100644 --- a/channels/sip/include/reqresp_parser.h +++ b/channels/sip/include/reqresp_parser.h @@ -142,4 +142,27 @@ void sip_request_parser_unregister_tests(void); */ unsigned int parse_sip_options(const char *options, char *unsupported, size_t unsupported_len); +/*! + * \brief Compare two URIs as described in RFC 3261 Section 19.1.4 + * + * \param input1 First URI + * \param input2 Second URI + * \retval 0 URIs match + * \retval nonzero URIs do not match or one or both is malformed + */ +int sip_uri_cmp(const char *input1, const char *input2); + +/*! + * \brief initialize request and response parser data + * + * \retval 0 Success + * \retval -1 Failure + */ +int sip_reqresp_parser_init(void); + +/*! + * \brief Free resources used by request and response parser + */ +void sip_reqresp_parser_exit(void); + #endif diff --git a/channels/sip/reqresp_parser.c b/channels/sip/reqresp_parser.c index 4e9e19175..460fca16d 100644 --- a/channels/sip/reqresp_parser.c +++ b/channels/sip/reqresp_parser.c @@ -27,6 +27,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "include/sip_utils.h" #include "include/reqresp_parser.h" +locale_t c_locale; + /*! \brief * parses a URI in its components.*/ int parse_uri_full(char *uri, const char *scheme, char **user, char **pass, char **domain, struct uriparams *params, char **headers, @@ -635,7 +637,7 @@ AST_TEST_DEFINE(sip_parse_uri_test) const char *get_calleridname(const char *input, char *output, size_t outputsize) { /* From RFC3261: - * + * * From = ( "From" / "f" ) HCOLON from-spec * from-spec = ( name-addr / addr-spec ) *( SEMI from-param ) * name-addr = [ display-name ] LAQUOT addr-spec RAQUOT @@ -1773,6 +1775,467 @@ AST_TEST_DEFINE(sip_parse_options_test) return res; } +/*! \brief helper routine for sip_uri_cmp to compare URI parameters + * + * This takes the parameters from two SIP URIs and determines + * if the URIs match. The rules for parameters *suck*. Here's a breakdown + * 1. If a parameter appears in both URIs, then they must have the same value + * in order for the URIs to match + * 2. If one URI has a user, maddr, ttl, or method parameter, then the other + * URI must also have that parameter and must have the same value + * in order for the URIs to match + * 3. All other headers appearing in only one URI are not considered when + * determining if URIs match + * + * \param input1 Parameters from URI 1 + * \param input2 Parameters from URI 2 + * \retval 0 URIs' parameters match + * \retval nonzero URIs' parameters do not match + */ +static int sip_uri_params_cmp(const char *input1, const char *input2) +{ + char *params1 = NULL; + char *params2 = NULL; + char *pos1; + char *pos2; + int zerolength1 = 0; + int zerolength2 = 0; + int maddrmatch = 0; + int ttlmatch = 0; + int usermatch = 0; + int methodmatch = 0; + + if (ast_strlen_zero(input1)) { + zerolength1 = 1; + } else { + params1 = ast_strdupa(input1); + } + if (ast_strlen_zero(input2)) { + zerolength2 = 1; + } else { + params2 = ast_strdupa(input2); + } + + /* Quick optimization. If both params are zero-length, then + * they match + */ + if (zerolength1 && zerolength2) { + return 0; + } + + for (pos1 = strsep(¶ms1, ";"); pos1; pos1 = strsep(¶ms1, ";")) { + char *value1 = pos1; + char *name1 = strsep(&value1, "="); + char *params2dup = NULL; + int matched = 0; + if (!value1) { + value1 = ""; + } + /* Checkpoint reached. We have the name and value parsed for param1 + * We have to duplicate params2 each time through this loop + * or else the inner loop below will not work properly. + */ + if (!zerolength2) { + params2dup = ast_strdupa(params2); + } + for (pos2 = strsep(¶ms2dup, ";"); pos2; pos2 = strsep(¶ms2dup, ";")) { + char *name2 = pos2; + char *value2 = strchr(pos2, '='); + if (!value2) { + value2 = ""; + } else { + *value2++ = '\0'; + } + if (!strcasecmp(name1, name2)) { + if (strcasecmp(value1, value2)) { + goto fail; + } else { + matched = 1; + break; + } + } + } + /* Check to see if the parameter is one of the 'must-match' parameters */ + if (!strcasecmp(name1, "maddr")) { + if (matched) { + maddrmatch = 1; + } else { + goto fail; + } + } else if (!strcasecmp(name1, "ttl")) { + if (matched) { + ttlmatch = 1; + } else { + goto fail; + } + } else if (!strcasecmp(name1, "user")) { + if (matched) { + usermatch = 1; + } else { + goto fail; + } + } else if (!strcasecmp(name1, "method")) { + if (matched) { + methodmatch = 1; + } else { + goto fail; + } + } + } + + /* We've made it out of that horrible O(m*n) construct and there are no + * failures yet. We're not done yet, though, because params2 could have + * an maddr, ttl, user, or method header and params1 did not. + */ + for (pos2 = strsep(¶ms2, ";"); pos2; pos2 = strsep(¶ms2, ";")) { + char *value2 = pos2; + char *name2 = strsep(&value2, "="); + if (!value2) { + value2 = ""; + } + if ((!strcasecmp(name2, "maddr") && !maddrmatch) || + (!strcasecmp(name2, "ttl") && !ttlmatch) || + (!strcasecmp(name2, "user") && !usermatch) || + (!strcasecmp(name2, "method") && !methodmatch)) { + goto fail; + } + } + return 0; + +fail: + return 1; +} + +/*! \brief helper routine for sip_uri_cmp to compare URI headers + * + * This takes the headers from two SIP URIs and determines + * if the URIs match. The rules for headers is simple. If a header + * appears in one URI, then it must also appear in the other URI. The + * order in which the headers appear does not matter. + * + * \param input1 Headers from URI 1 + * \param input2 Headers from URI 2 + * \retval 0 URI headers match + * \retval nonzero URI headers do not match + */ +static int sip_uri_headers_cmp(const char *input1, const char *input2) +{ + char *headers1 = NULL; + char *headers2 = NULL; + int zerolength1 = 0; + int zerolength2 = 0; + int different = 0; + char *header1; + + if (ast_strlen_zero(input1)) { + zerolength1 = 1; + } else { + headers1 = ast_strdupa(input1); + } + + if (ast_strlen_zero(input2)) { + zerolength2 = 1; + } else { + headers2 = ast_strdupa(input2); + } + + /* If one URI contains no headers and the other + * does, then they cannot possibly match + */ + if (zerolength1 != zerolength2) { + return 1; + } + + if (zerolength1 && zerolength2) + return 0; + + /* At this point, we can definitively state that both inputs are + * not zero-length. First, one more optimization. If the length + * of the headers is not equal, then we definitely have no match + */ + if (strlen(headers1) != strlen(headers2)) { + return 1; + } + + for (header1 = strsep(&headers1, "&"); header1; header1 = strsep(&headers1, "&")) { + if (!strcasestr(headers2, header1)) { + different = 1; + break; + } + } + + return different; +} + +/*! + * \brief Compare domain sections of SIP URIs + * + * For hostnames, a case insensitive string comparison is + * used. For IP addresses, a binary comparison is used. This + * is mainly because IPv6 addresses have many ways of writing + * the same address. + * + * For specifics about IP address comparison, see the following + * document: http://tools.ietf.org/html/draft-ietf-sip-ipv6-abnf-fix-05 + * + * \param host1 The domain from the first URI + * \param host2 THe domain from the second URI + * \retval 0 The domains match + * \retval nonzero The domains do not match + */ +static int sip_uri_domain_cmp(const char *host1, const char *host2) +{ + struct ast_sockaddr addr1; + struct ast_sockaddr addr2; + int addr1_parsed; + int addr2_parsed; + + addr1_parsed = ast_sockaddr_parse(&addr1, host1, 0); + addr2_parsed = ast_sockaddr_parse(&addr2, host2, 0); + + if (addr1_parsed != addr2_parsed) { + /* One domain was an IP address and the other had + * a host name. FAIL! + */ + return 1; + } + + /* Both are host names. A string comparison will work + * perfectly here. Specifying the "C" locale ensures that + * The LC_CTYPE conventions use those defined in ANSI C, + * i.e. ASCII. + */ + if (!addr1_parsed) { + return strcasecmp_l(host1, host2, c_locale); + } + + /* Both contain IP addresses */ + return ast_sockaddr_cmp(&addr1, &addr2); +} + +int sip_uri_cmp(const char *input1, const char *input2) +{ + char *uri1; + char *uri2; + char *uri_scheme1; + char *uri_scheme2; + char *host1; + char *host2; + char *params1; + char *params2; + char *headers1; + char *headers2; + + /* XXX It would be really nice if we could just use parse_uri_full() here + * to separate the components of the URI, but unfortunately it is written + * in a way that can cause URI parameters to be discarded. + */ + + if (!input1 || !input2) { + return 1; + } + + uri1 = ast_strdupa(input1); + uri2 = ast_strdupa(input2); + + ast_uri_decode(uri1); + ast_uri_decode(uri2); + + uri_scheme1 = strsep(&uri1, ":"); + uri_scheme2 = strsep(&uri2, ":"); + + if (strcmp(uri_scheme1, uri_scheme2)) { + return 1; + } + + /* This function is tailored for SIP and SIPS URIs. There's no + * need to check uri_scheme2 since we have determined uri_scheme1 + * and uri_scheme2 are equivalent already. + */ + if (strcmp(uri_scheme1, "sip") && strcmp(uri_scheme1, "sips")) { + return 1; + } + + if (ast_strlen_zero(uri1) || ast_strlen_zero(uri2)) { + return 1; + } + + if ((host1 = strchr(uri1, '@'))) { + *host1++ = '\0'; + } + if ((host2 = strchr(uri2, '@'))) { + *host2++ = '\0'; + } + + /* Check for mismatched username and passwords. This is the + * only case-sensitive comparison of a SIP URI + */ + if ((host1 && !host2) || + (host2 && !host1) || + (host1 && host2 && strcmp(uri1, uri2))) { + return 1; + } + + if (!host1) { + host1 = uri1; + } + if (!host2) { + host2 = uri2; + } + + /* Strip off the parameters and headers so we can compare + * host and port + */ + + if ((params1 = strchr(host1, ';'))) { + *params1++ = '\0'; + } + if ((params2 = strchr(host2, ';'))) { + *params2++ = '\0'; + } + + /* Headers come after parameters, but there may be headers without + * parameters, thus the S_OR + */ + if ((headers1 = strchr(S_OR(params1, host1), '?'))) { + *headers1++ = '\0'; + } + if ((headers2 = strchr(S_OR(params2, host2), '?'))) { + *headers2++ = '\0'; + } + + if (sip_uri_domain_cmp(host1, host2)) { + return 1; + } + + /* Headers have easier rules to follow, so do those first */ + if (sip_uri_headers_cmp(headers1, headers2)) { + return 1; + } + + /* And now the parameters. Ugh */ + return sip_uri_params_cmp(params1, params2); +} + +#define URI_CMP_MATCH 0 +#define URI_CMP_NOMATCH 1 + +AST_TEST_DEFINE(sip_uri_cmp_test) +{ + static const struct { + const char *uri1; + const char *uri2; + int expected_result; + } uri_cmp_tests [] = { + /* These are identical, so they match */ + { "sip:bob@example.com", "sip:bob@example.com", URI_CMP_MATCH }, + /* Different usernames. No match */ + { "sip:alice@example.com", "sip:bob@example.com", URI_CMP_NOMATCH }, + /* Different hosts. No match */ + { "sip:bob@example.com", "sip:bob@examplez.com", URI_CMP_NOMATCH }, + /* Now start using IP addresses. Identical, so they match */ + { "sip:bob@1.2.3.4", "sip:bob@1.2.3.4", URI_CMP_MATCH }, + /* Two identical IPv4 addresses represented differently. Match */ + { "sip:bob@1.2.3.4", "sip:bob@001.002.003.004", URI_CMP_MATCH }, + /* Logically equivalent IPv4 Address and hostname. No Match */ + { "sip:bob@127.0.0.1", "sip:bob@localhost", URI_CMP_NOMATCH }, + /* Logically equivalent IPv6 address and hostname. No Match */ + { "sip:bob@[::1]", "sip:bob@localhost", URI_CMP_NOMATCH }, + /* Try an IPv6 one as well */ + { "sip:bob@[2001:db8::1234]", "sip:bob@[2001:db8::1234]", URI_CMP_MATCH }, + /* Two identical IPv6 addresses represented differently. Match */ + { "sip:bob@[2001:db8::1234]", "sip:bob@[2001:0db8::1234]", URI_CMP_MATCH }, + /* Different ports. No match */ + { "sip:bob@1.2.3.4:5060", "sip:bob@1.2.3.4:5061", URI_CMP_NOMATCH }, + /* Same port logically, but only one address specifies it. No match */ + { "sip:bob@1.2.3.4:5060", "sip:bob@1.2.3.4", URI_CMP_NOMATCH }, + /* And for safety, try with IPv6 */ + { "sip:bob@[2001:db8:1234]:5060", "sip:bob@[2001:db8:1234]", URI_CMP_NOMATCH }, + /* User comparison is case sensitive. No match */ + { "sip:bob@example.com", "sip:BOB@example.com", URI_CMP_NOMATCH }, + /* Host comparison is case insensitive. Match */ + { "sip:bob@example.com", "sip:bob@EXAMPLE.COM", URI_CMP_MATCH }, + /* Add headers to the URI. Identical, so they match */ + { "sip:bob@example.com?header1=value1&header2=value2", "sip:bob@example.com?header1=value1&header2=value2", URI_CMP_MATCH }, + /* Headers in URI 1 are not in URI 2. No Match */ + { "sip:bob@example.com?header1=value1&header2=value2", "sip:bob@example.com", URI_CMP_NOMATCH }, + /* Header present in both URIs does not have matching values. No match */ + { "sip:bob@example.com?header1=value1&header2=value2", "sip:bob@example.com?header1=value1&header2=value3", URI_CMP_NOMATCH }, + /* Add parameters to the URI. Identical so they match */ + { "sip:bob@example.com;param1=value1;param2=value2", "sip:bob@example.com;param1=value1;param2=value2", URI_CMP_MATCH }, + /* Same parameters in both URIs but appear in different order. Match */ + { "sip:bob@example.com;param2=value2;param1=value1", "sip:bob@example.com;param1=value1;param2=value2", URI_CMP_MATCH }, + /* params in URI 1 are not in URI 2. Match */ + { "sip:bob@example.com;param1=value1;param2=value2", "sip:bob@example.com", URI_CMP_MATCH }, + /* param present in both URIs does not have matching values. No match */ + { "sip:bob@example.com;param1=value1;param2=value2", "sip:bob@example.com;param1=value1;param2=value3", URI_CMP_NOMATCH }, + /* URI 1 has a maddr param but URI 2 does not. No match */ + { "sip:bob@example.com;param1=value1;maddr=192.168.0.1", "sip:bob@example.com;param1=value1", URI_CMP_NOMATCH }, + /* URI 1 and URI 2 both have identical maddr params. Match */ + { "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 }, + /* URI 1 is a SIPS URI and URI 2 is a SIP URI. No Match */ + { "sips:bob@example.com", "sip:bob@example.com", URI_CMP_NOMATCH }, + /* No URI schemes. No match */ + { "bob@example.com", "bob@example.com", URI_CMP_NOMATCH }, + /* Crashiness tests. Just an address scheme. No match */ + { "sip", "sips", URI_CMP_NOMATCH }, + /* Still just an address scheme. Even though they're the same, No match */ + { "sip", "sip", URI_CMP_NOMATCH }, + /* Empty strings. No match */ + { "", "", URI_CMP_NOMATCH }, + /* An empty string and a NULL. No match */ + { "", NULL, URI_CMP_NOMATCH }, + }; + int i; + int test_res = AST_TEST_PASS; + switch (cmd) { + case TEST_INIT: + info->name = "sip_uri_cmp_test"; + info->category = "/channels/chan_sip/"; + info->summary = "Tests comparison of SIP URIs"; + info->description = "Several would-be tricky URI comparisons are performed"; + return AST_TEST_NOT_RUN; + case TEST_EXECUTE: + break; + } + + for (i = 0; i < ARRAY_LEN(uri_cmp_tests); ++i) { + int cmp_res1; + int cmp_res2; + if ((cmp_res1 = sip_uri_cmp(uri_cmp_tests[i].uri1, uri_cmp_tests[i].uri2))) { + /* URI comparison may return -1 or +1 depending on the failure. Standardize + * the return value to be URI_CMP_NOMATCH on any failure + */ + cmp_res1 = URI_CMP_NOMATCH; + } + if (cmp_res1 != uri_cmp_tests[i].expected_result) { + ast_test_status_update(test, "Unexpected comparison result for URIs %s and %s. " + "Expected %s but got %s\n", uri_cmp_tests[i].uri1, uri_cmp_tests[i].uri2, + uri_cmp_tests[i].expected_result == URI_CMP_MATCH ? "Match" : "No Match", + cmp_res1 == URI_CMP_MATCH ? "Match" : "No Match"); + test_res = AST_TEST_FAIL; + } + + /* All URI comparisons are commutative, so for the sake of being thorough, we'll + * rerun the comparison with the parameters reversed + */ + if ((cmp_res2 = sip_uri_cmp(uri_cmp_tests[i].uri2, uri_cmp_tests[i].uri1))) { + /* URI comparison may return -1 or +1 depending on the failure. Standardize + * the return value to be URI_CMP_NOMATCH on any failure + */ + cmp_res2 = URI_CMP_NOMATCH; + } + if (cmp_res2 != uri_cmp_tests[i].expected_result) { + ast_test_status_update(test, "Unexpected comparison result for URIs %s and %s. " + "Expected %s but got %s\n", uri_cmp_tests[i].uri2, uri_cmp_tests[i].uri1, + uri_cmp_tests[i].expected_result == URI_CMP_MATCH ? "Match" : "No Match", + cmp_res2 == URI_CMP_MATCH ? "Match" : "No Match"); + test_res = AST_TEST_FAIL; + } + } + + return test_res; +} void sip_request_parser_register_tests(void) { @@ -1784,6 +2247,7 @@ void sip_request_parser_register_tests(void) AST_TEST_REGISTER(parse_name_andor_addr_test); AST_TEST_REGISTER(parse_contact_header_test); AST_TEST_REGISTER(sip_parse_options_test); + AST_TEST_REGISTER(sip_uri_cmp_test); } void sip_request_parser_unregister_tests(void) { @@ -1795,4 +2259,22 @@ void sip_request_parser_unregister_tests(void) AST_TEST_UNREGISTER(parse_name_andor_addr_test); AST_TEST_UNREGISTER(parse_contact_header_test); AST_TEST_UNREGISTER(sip_parse_options_test); + AST_TEST_UNREGISTER(sip_uri_cmp_test); +} + +int sip_reqresp_parser_init(void) +{ + c_locale = newlocale(LC_CTYPE_MASK, "C", NULL); + if (!c_locale) { + return -1; + } + return 0; +} + +void sip_reqresp_parser_exit(void) +{ + if (c_locale) { + freelocale(c_locale); + c_locale = NULL; + } } |