[Asterisk-code-review] res pjsip endpoint identifier ip.c: Added regex support to m... (asterisk[15])

Kevin Harwell asteriskteam at digium.com
Fri Aug 3 13:26:12 CDT 2018


Kevin Harwell has submitted this change and it was merged. ( https://gerrit.asterisk.org/9719 )

Change subject: res_pjsip_endpoint_identifier_ip.c: Added regex support to match_header
......................................................................

res_pjsip_endpoint_identifier_ip.c: Added regex support to match_header

This patch adds regular expression support to make the identify section's
match_header option more useful when attempting to match complex headers
like the 'To' or 'From' headers.  The 'From' header has variable
components such as the tag parameter that you cannot predict.  To specify
a regular expression put slashes around the regular expression in place of
the header value.

[identify-alice]
type=identify
endpoint=alice
match_header=From: /<sip:alice at 127\\.0\\.0\\.1>/

* Added regex support to match_header so you could match a 'To' header
among other complex headers.

Fixed reported crashes when trying to match special headers like 'Contact'.
The identify section's match_header method used code that assumed you were
matching a generic header.  Any other type of header could cause a crash
if the header structure variant did not match the generic header enough.

* Made use code that will work for any header type instead of code
specific to generic headers.

Other fixes while in the area:

* Made check all headers of the requested name.
* Added some more sanity checks to the configured identify matching
options when applying the configuration.

ASTERISK-27548

Change-Id: I27dfd4ff5e2259b906640e3c330681b76b4ed1f1
---
M CHANGES
M res/res_pjsip_endpoint_identifier_ip.c
2 files changed, 125 insertions(+), 38 deletions(-)

Approvals:
  Joshua Colp: Looks good to me, but someone else must approve
  Kevin Harwell: Looks good to me, approved; Approved for Submit



diff --git a/CHANGES b/CHANGES
index 58f4204..c326de1 100644
--- a/CHANGES
+++ b/CHANGES
@@ -19,6 +19,13 @@
    when both 'SIP' and 'Q.850' Reason headers are received.  This option allows
    the 'Q.850' Reason header to be suppressed.  The default value is 'no'.
 
+res_pjsip_endpoint_identifier_ip
+------------------
+ * Added regex support to the identify section match_header option.  You
+   specify a regex instead of an explicit string by surrounding the header
+   value with slashes:
+   match_header = SIPHeader: /regex/
+
 ------------------------------------------------------------------------------
 --- Functionality changes from Asterisk 15.4.0 to Asterisk 15.5.0 ------------
 ------------------------------------------------------------------------------
diff --git a/res/res_pjsip_endpoint_identifier_ip.c b/res/res_pjsip_endpoint_identifier_ip.c
index 9676e1b..849c271 100644
--- a/res/res_pjsip_endpoint_identifier_ip.c
+++ b/res/res_pjsip_endpoint_identifier_ip.c
@@ -42,7 +42,7 @@
 				<description>
 					<para>This module provides alternatives to matching inbound requests to
 					a configured endpoint. At least one of the matching mechanisms
-					must be provided, or the object configuration will be invalid.</para>
+					must be provided, or the object configuration is invalid.</para>
 					<para>The matching mechanisms are provided by the following
 					configuration options:</para>
 					<enumlist>
@@ -86,6 +86,13 @@
 						specified with a <literal>:</literal>, as in
 						<literal>match_header = SIPHeader: value</literal>.
 						</para>
+						<para>The specified SIP header value can be a regular
+						expression if the value is of the form
+						/<replaceable>regex</replaceable>/.
+						</para>
+						<note><para>Use of a regex is expensive so be sure you need
+						to use a regex to match your endpoint.
+						</para></note>
 					</description>
 				</configOption>
 				<configOption name="type">
@@ -109,13 +116,21 @@
 		AST_STRING_FIELD(endpoint_name);
 		/*! If matching by header, the header/value to match against */
 		AST_STRING_FIELD(match_header);
+		/*! SIP header name of the match_header string */
+		AST_STRING_FIELD(match_header_name);
+		/*! SIP header value of the match_header string */
+		AST_STRING_FIELD(match_header_value);
 	);
+	/*! Compiled match_header regular expression when is_regex is non-zero */
+	regex_t regex_buf;
 	/*! \brief Networks or addresses that should match this */
 	struct ast_ha *matches;
-	/*! \brief Perform SRV resolution of hostnames */
-	unsigned int srv_lookups;
 	/*! \brief Hosts to be resolved when applying configuration */
 	struct ao2_container *hosts;
+	/*! \brief Perform SRV resolution of hostnames */
+	unsigned int srv_lookups;
+	/*! Non-zero if match_header has a regular expression (i.e., regex_buf is valid) */
+	unsigned int is_regex:1;
 };
 
 /*! \brief Destructor function for a matching object */
@@ -126,6 +141,9 @@
 	ast_string_field_free_memory(identify);
 	ast_free_ha(identify->matches);
 	ao2_cleanup(identify->hosts);
+	if (identify->is_regex) {
+		regfree(&identify->regex_buf);
+	}
 }
 
 /*! \brief Allocator function for a matching object */
@@ -146,47 +164,66 @@
 {
 	struct ip_identify_match *identify = obj;
 	struct pjsip_rx_data *rdata = arg;
-	pjsip_generic_string_hdr *header;
+	pjsip_hdr *header;
 	pj_str_t pj_header_name;
-	pj_str_t pj_header_value;
-	char *c_header;
-	char *c_value;
+	int header_present;
 
 	if (ast_strlen_zero(identify->match_header)) {
 		return 0;
 	}
 
-	c_header = ast_strdupa(identify->match_header);
-	c_value = strchr(c_header, ':');
-	if (!c_value) {
-		/* This should not be possible.  The object cannot be created if so. */
-		ast_assert(0);
-		return 0;
-	}
-	*c_value = '\0';
-	c_value++;
-	c_value = ast_strip(c_value);
+	pj_header_name = pj_str((void *) identify->match_header_name);
 
-	pj_header_name = pj_str(c_header);
-	header = pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &pj_header_name, NULL);
-	if (!header) {
-		ast_debug(3, "Identify '%s': SIP message does not have header '%s'\n",
+	/* Check all headers of the given name for a match. */
+	header_present = 0;
+	for (header = NULL;
+		(header = pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &pj_header_name, header));
+		header = header->next) {
+		char *pos;
+		int len;
+		char buf[PATH_MAX];
+
+		header_present = 1;
+
+		/* Print header line to buf */
+		len = pjsip_hdr_print_on(header, buf, sizeof(buf) - 1);
+		if (len < 0) {
+			/* Buffer not large enough or no header vptr! */
+			ast_assert(0);
+			continue;
+		}
+		buf[len] = '\0';
+
+		/* Remove header name from pj_buf and trim blanks. */
+		pos = strchr(buf, ':');
+		if (!pos) {
+			/* No header name?  Bug in PJPROJECT if so. */
+			ast_assert(0);
+			continue;
+		}
+		pos = ast_strip(pos + 1);
+
+		/* Does header value match what we are looking for? */
+		if (identify->is_regex) {
+			if (!regexec(&identify->regex_buf, pos, 0, NULL, 0)) {
+				return CMP_MATCH;
+			}
+		} else if (!strcmp(identify->match_header_value, pos)) {
+			return CMP_MATCH;
+		}
+
+		ast_debug(3, "Identify '%s': SIP message has '%s' header but value '%s' does not match '%s'.\n",
 			ast_sorcery_object_get_id(identify),
-			c_header);
-		return 0;
+			identify->match_header_name,
+			pos,
+			identify->match_header_value);
 	}
-
-	pj_header_value = pj_str(c_value);
-	if (pj_strcmp(&pj_header_value, &header->hvalue)) {
-		ast_debug(3, "Identify '%s': SIP message has header '%s' but value '%.*s' does not match '%s'\n",
+	if (!header_present) {
+		ast_debug(3, "Identify '%s': SIP message does not have '%s' header.\n",
 			ast_sorcery_object_get_id(identify),
-			c_header,
-			(int) pj_strlen(&header->hvalue), pj_strbuf(&header->hvalue),
-			c_value);
-		return 0;
+			identify->match_header_name);
 	}
-
-	return CMP_MATCH;
+	return 0;
 }
 
 /*! \brief Comparator function for matching an object by IP address */
@@ -404,14 +441,57 @@
 			ast_sorcery_object_get_id(identify));
 		return -1;
 	}
-	if (!ast_strlen_zero(identify->match_header)
-		&& !strchr(identify->match_header, ':')) {
-		ast_log(LOG_ERROR, "Identify '%s' missing ':' separator in match_header '%s'.\n",
-			ast_sorcery_object_get_id(identify), identify->match_header);
-		return -1;
+
+	if (!ast_strlen_zero(identify->match_header)) {
+		char *c_header;
+		char *c_value;
+		int len;
+
+		/* Split the header name and value */
+		c_header = ast_strdupa(identify->match_header);
+		c_value = strchr(c_header, ':');
+		if (!c_value) {
+			ast_log(LOG_ERROR, "Identify '%s' missing ':' separator in match_header '%s'.\n",
+				ast_sorcery_object_get_id(identify), identify->match_header);
+			return -1;
+		}
+		*c_value = '\0';
+		c_value = ast_strip(c_value + 1);
+		c_header = ast_strip(c_header);
+
+		if (ast_strlen_zero(c_header)) {
+			ast_log(LOG_ERROR, "Identify '%s' has no SIP header to match in match_header '%s'.\n",
+				ast_sorcery_object_get_id(identify), identify->match_header);
+			return -1;
+		}
+
+		if (!strcmp(c_value, "//")) {
+			/* An empty regex is the same as an empty literal string. */
+			c_value = "";
+		}
+
+		if (ast_string_field_set(identify, match_header_name, c_header)
+			|| ast_string_field_set(identify, match_header_value, c_value)) {
+			return -1;
+		}
+
+		len = strlen(c_value);
+		if (2 < len && c_value[0] == '/' && c_value[len - 1] == '/') {
+			/* Make "/regex/" into "regex" */
+			c_value[len - 1] = '\0';
+			++c_value;
+
+			if (regcomp(&identify->regex_buf, c_value, REG_EXTENDED | REG_NOSUB)) {
+				ast_log(LOG_ERROR, "Identify '%s' failed to compile match_header regex '%s'.\n",
+					ast_sorcery_object_get_id(identify), c_value);
+				return -1;
+			}
+			identify->is_regex = 1;
+		}
 	}
 
 	if (!identify->hosts) {
+		/* No match addresses to resolve */
 		return 0;
 	}
 

-- 
To view, visit https://gerrit.asterisk.org/9719
To unsubscribe, or for help writing mail filters, visit https://gerrit.asterisk.org/settings

Gerrit-Project: asterisk
Gerrit-Branch: 15
Gerrit-MessageType: merged
Gerrit-Change-Id: I27dfd4ff5e2259b906640e3c330681b76b4ed1f1
Gerrit-Change-Number: 9719
Gerrit-PatchSet: 3
Gerrit-Owner: Richard Mudgett <rmudgett at digium.com>
Gerrit-Reviewer: George Joseph <gjoseph at digium.com>
Gerrit-Reviewer: Jenkins2
Gerrit-Reviewer: Joshua Colp <jcolp at digium.com>
Gerrit-Reviewer: Kevin Harwell <kharwell at digium.com>
Gerrit-Reviewer: Richard Mudgett <rmudgett at digium.com>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.digium.com/pipermail/asterisk-code-review/attachments/20180803/bcd57347/attachment-0001.html>


More information about the asterisk-code-review mailing list