[Asterisk-code-review] res_pjsip_endpoint_identifier_ip.c: Add port matching support (asterisk[13])

Sean Bright asteriskteam at digium.com
Sat Nov 30 15:44:49 CST 2019


Sean Bright has uploaded this change for review. ( https://gerrit.asterisk.org/c/asterisk/+/13312 )


Change subject: res_pjsip_endpoint_identifier_ip.c: Add port matching support
......................................................................

res_pjsip_endpoint_identifier_ip.c: Add port matching support

Adds source port matching support when IP matching is used:

  [example]
  type = identify
  match = 1.2.3.4/32
  match_port = 5060, 6000

If the IP matches but the source port does not, we reject and search for
alternatives.

ASTERISK-28639 #close
Reported by: MLC in #asterisk

Change-Id: I256d5bd5d478b95f526e2f80ace31b690eebba92
---
A contrib/ast-db-manage/config/versions/e6420d7ca697_add_match_port_to_ip_identify.py
M res/res_pjsip_endpoint_identifier_ip.c
2 files changed, 163 insertions(+), 6 deletions(-)



  git pull ssh://gerrit.asterisk.org:29418/asterisk refs/changes/12/13312/1

diff --git a/contrib/ast-db-manage/config/versions/e6420d7ca697_add_match_port_to_ip_identify.py b/contrib/ast-db-manage/config/versions/e6420d7ca697_add_match_port_to_ip_identify.py
new file mode 100644
index 0000000..d2c0f73
--- /dev/null
+++ b/contrib/ast-db-manage/config/versions/e6420d7ca697_add_match_port_to_ip_identify.py
@@ -0,0 +1,21 @@
+"""Add match_port to IP identify
+
+Revision ID: e6420d7ca697
+Revises: 339e1dfa644d
+Create Date: 2019-11-30 12:59:55.548684
+
+"""
+
+# revision identifiers, used by Alembic.
+revision = 'e6420d7ca697'
+down_revision = '339e1dfa644d'
+
+from alembic import op
+import sqlalchemy as sa
+
+
+def upgrade():
+    op.add_column('ps_endpoint_id_ips', sa.Column('match_port', sa.String(255)))
+
+def downgrade():
+    op.drop_column('ps_endpoint_id_ips', 'match_port')
diff --git a/res/res_pjsip_endpoint_identifier_ip.c b/res/res_pjsip_endpoint_identifier_ip.c
index ac4057b..9e0f2e9 100644
--- a/res/res_pjsip_endpoint_identifier_ip.c
+++ b/res/res_pjsip_endpoint_identifier_ip.c
@@ -67,6 +67,14 @@
 						</para>
 					</description>
 				</configOption>
+				<configOption name="match_port">
+					<synopsis>IP ports to match against.</synopsis>
+					<description>
+						<para>When used with <literal>match</literal>, only matches
+						inbound requests whose source port is one of the ports provided.
+						</para>
+					</description>
+				</configOption>
 				<configOption name="srv_lookups" default="yes">
 					<synopsis>Perform SRV lookups for provided hostnames.</synopsis>
 					<description>
@@ -125,6 +133,8 @@
 	regex_t regex_buf;
 	/*! \brief Networks or addresses that should match this */
 	struct ast_ha *matches;
+	/*! \brief Ports that should match this */
+	AST_VECTOR(, uint16_t) match_ports;
 	/*! \brief Hosts to be resolved when applying configuration */
 	struct ao2_container *hosts;
 	/*! \brief Perform SRV resolution of hostnames */
@@ -140,6 +150,7 @@
 
 	ast_string_field_free_memory(identify);
 	ast_free_ha(identify->matches);
+	AST_VECTOR_FREE(&identify->match_ports);
 	ao2_cleanup(identify->hosts);
 	if (identify->is_regex) {
 		regfree(&identify->regex_buf);
@@ -151,7 +162,7 @@
 {
 	struct ip_identify_match *identify = ast_sorcery_generic_alloc(sizeof(*identify), ip_identify_destroy);
 
-	if (!identify || ast_string_field_init(identify, 256)) {
+	if (!identify || ast_string_field_init(identify, 256) || AST_VECTOR_INIT(&identify->match_ports, 8)) {
 		ao2_cleanup(identify);
 		return NULL;
 	}
@@ -226,6 +237,25 @@
 	return 0;
 }
 
+static int port_cmp(uint16_t a, uint16_t b)
+{
+	return a == b;
+}
+
+static int port_sort(uint16_t a, uint16_t b)
+{
+	return a - b;
+}
+
+static int check_port_match(struct ip_identify_match *identify, uint16_t source_port)
+{
+	if (AST_VECTOR_SIZE(&identify->match_ports) == 0
+	   || AST_VECTOR_GET_CMP(&identify->match_ports, source_port, port_cmp)) {
+		return 1;
+	}
+	return 0;
+}
+
 /*! \brief Comparator function for matching an object by IP address */
 static int ip_identify_match_check(void *obj, void *arg, int flags)
 {
@@ -234,7 +264,8 @@
 	int sense;
 
 	sense = ast_apply_ha(identify->matches, addr);
-	if (sense != AST_SENSE_ALLOW) {
+	if (sense != AST_SENSE_ALLOW
+	   && check_port_match(identify, ast_sockaddr_port(addr))) {
 		ast_debug(3, "Source address %s matches identify '%s'\n",
 				ast_sockaddr_stringify(addr),
 				ast_sorcery_object_get_id(identify));
@@ -369,6 +400,47 @@
 }
 
 /*! \brief Custom handler for match field */
+static int ip_identify_match_port_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
+{
+	struct ip_identify_match *identify = obj;
+	char *input_string = ast_strdupa(var->value);
+	char *current_string;
+
+	if (ast_strlen_zero(var->value)) {
+		return 0;
+	}
+
+	while ((current_string = ast_strip(strsep(&input_string, ",")))) {
+		unsigned long val;
+
+		if (ast_strlen_zero(current_string)) {
+			continue;
+		}
+
+		errno = 0;
+		val = strtoul(current_string, NULL, 10);
+		if (errno) {
+			ast_log(LOG_ERROR, "Failed to parse '%s' as a port number for ip endpoint identifier '%s'\n",
+					current_string, ast_sorcery_object_get_id(obj));
+			return -1;
+		}
+
+		if (val > UINT_MAX) {
+			ast_log(LOG_ERROR, "Invalid match_port specified. Must be no greater than %u\n", UINT_MAX);
+			return -1;
+		}
+
+		if (AST_VECTOR_ADD_SORTED(&identify->match_ports, val, port_sort)) {
+			ast_log(LOG_ERROR, "Failed to add port '%lu' to ip endpoint indentifier '%s'\n",
+					val, ast_sorcery_object_get_id(obj));
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+/*! \brief Custom handler for match field */
 static int ip_identify_match_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
 {
 	struct ip_identify_match *identify = obj;
@@ -425,6 +497,7 @@
 	struct ip_identify_match *identify = obj;
 	char *current_string;
 	struct ao2_iterator i;
+	int matching_by_addr = 0;
 
 	/* Validate the identify object configuration */
 	if (ast_strlen_zero(identify->endpoint_name)) {
@@ -432,16 +505,23 @@
 			ast_sorcery_object_get_id(identify));
 		return -1;
 	}
+
+	matching_by_addr = identify->matches
+		|| (identify->hosts && ao2_container_count(identify->hosts));
+
 	if (ast_strlen_zero(identify->match_header) /* No header to match */
-		/* and no static IP addresses with a mask */
-		&& !identify->matches
-		/* and no addresses to resolve */
-		&& (!identify->hosts || !ao2_container_count(identify->hosts))) {
+		&& !matching_by_addr) {
 		ast_log(LOG_ERROR, "Identify '%s' is not configured to match anything.\n",
 			ast_sorcery_object_get_id(identify));
 		return -1;
 	}
 
+	if (AST_VECTOR_SIZE(&identify->match_ports) && !matching_by_addr) {
+		ast_log(LOG_ERROR, "Identify '%s' cannot use 'match_port' without 'match'.\n",
+			ast_sorcery_object_get_id(identify));
+		return -1;
+	}
+
 	if (!ast_strlen_zero(identify->match_header)) {
 		char *c_header;
 		char *c_value;
@@ -541,6 +621,50 @@
 	return 0;
 }
 
+static int ports_to_str(const void *obj, const intptr_t *args, char **buf)
+{
+	const struct ip_identify_match *identify = obj;
+	char *list;
+	int i;
+
+	/* Port range is [0, 65536), so we need at most 5 characters for the port
+	 * and 1 for the comma. The extra 1 is only strictly necessary if the
+	 * list size is 0. */
+	*buf = list = ast_calloc(1, AST_VECTOR_SIZE(&identify->match_ports) * 6 + 1);
+	if (!list) {
+		return -1;
+	}
+
+	for (i = 0; i < AST_VECTOR_SIZE(&identify->match_ports); i++) {
+		if (i > 0) {
+			list += sprintf(list, ",");
+		}
+		list += sprintf(list, "%u", AST_VECTOR_GET(&identify->match_ports, i));
+	}
+
+	return 0;
+}
+
+static int ports_to_var_list(const void *obj, struct ast_variable **fields)
+{
+	const struct ip_identify_match *identify = obj;
+	struct ast_variable *head = NULL;
+	int i;
+
+	for (i = 0; i < AST_VECTOR_SIZE(&identify->match_ports); i++) {
+		char str[6];
+		if (snprintf(str, sizeof(str), "%u", AST_VECTOR_GET(&identify->match_ports, i)) < sizeof(str)) {
+			ast_variable_list_append(&head, ast_variable_new("match_port", str, ""));
+		}
+	}
+
+	if (head) {
+		*fields = head;
+	}
+
+	return 0;
+}
+
 static int match_to_str(const void *obj, const intptr_t *args, char **buf)
 {
 	RAII_VAR(struct ast_str *, str, ast_str_create(MAX_OBJECT_FIELD), ast_free);
@@ -746,6 +870,17 @@
 				addr, ast_sockaddr_cidr_bits(&match->netmask));
 		}
 
+		if (AST_VECTOR_SIZE(&ident->match_ports)) {
+			char *ports = NULL;
+			if (!ports_to_str(ident, NULL, &ports)) {
+				ast_str_append(&context->output_buffer, 0, "%*s: Src Port(s) %s\n",
+				   indent,
+				   "Match",
+				   ports);
+				ast_free(ports);
+			}
+		}
+
 		if (!ast_strlen_zero(ident->match_header)) {
 			ast_str_append(&context->output_buffer, 0, "%*s: %s\n",
 				indent,
@@ -813,6 +948,7 @@
 	ast_sorcery_object_field_register(ast_sip_get_sorcery(), "identify", "type", "", OPT_NOOP_T, 0, 0);
 	ast_sorcery_object_field_register(ast_sip_get_sorcery(), "identify", "endpoint", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ip_identify_match, endpoint_name));
 	ast_sorcery_object_field_register_custom(ast_sip_get_sorcery(), "identify", "match", "", ip_identify_match_handler, match_to_str, match_to_var_list, 0, 0);
+	ast_sorcery_object_field_register_custom(ast_sip_get_sorcery(), "identify", "match_port", "", ip_identify_match_port_handler, ports_to_str, ports_to_var_list, 0, 0);
 	ast_sorcery_object_field_register(ast_sip_get_sorcery(), "identify", "match_header", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ip_identify_match, match_header));
 	ast_sorcery_object_field_register(ast_sip_get_sorcery(), "identify", "srv_lookups", "yes", OPT_BOOL_T, 1, FLDSET(struct ip_identify_match, srv_lookups));
 	ast_sorcery_load_object(ast_sip_get_sorcery(), "identify");

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

Gerrit-Project: asterisk
Gerrit-Branch: 13
Gerrit-Change-Id: I256d5bd5d478b95f526e2f80ace31b690eebba92
Gerrit-Change-Number: 13312
Gerrit-PatchSet: 1
Gerrit-Owner: Sean Bright <sean.bright at gmail.com>
Gerrit-MessageType: newchange
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.digium.com/pipermail/asterisk-code-review/attachments/20191130/8ccd4914/attachment-0001.html>


More information about the asterisk-code-review mailing list