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

Joshua Colp asteriskteam at digium.com
Thu Jan 9 15:08:09 CST 2020


Joshua Colp has submitted this change. ( https://gerrit.asterisk.org/c/asterisk/+/13300 )

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:5060/32, 1.2.3.4:6000/32, asterisk.org:4444

If the IP matches but the source port does not, we reject and search for
alternatives. SRV lookups are still performed if enabled (srv_lookups = yes),
unless the configured FQDN includes a port number in which case just a host
lookup is performed.

ASTERISK-28639 #close
Reported by: Mitch Claborn

Change-Id: I256d5bd5d478b95f526e2f80ace31b690eebba92
---
M configs/samples/pjsip.conf.sample
A doc/CHANGES-staging/res_pjsip_endpoint_identifier_ip_match_port.txt
M include/asterisk/acl.h
M main/acl.c
M res/res_pjsip_endpoint_identifier_ip.c
5 files changed, 120 insertions(+), 24 deletions(-)

Approvals:
  Joshua Colp: Looks good to me, but someone else must approve; Approved for Submit
  Benjamin Keith Ford: Looks good to me, but someone else must approve
  George Joseph: Looks good to me, approved



diff --git a/configs/samples/pjsip.conf.sample b/configs/samples/pjsip.conf.sample
index c7553b7..92d9aaa 100644
--- a/configs/samples/pjsip.conf.sample
+++ b/configs/samples/pjsip.conf.sample
@@ -263,6 +263,7 @@
 ;endpoint=mytrunk
 ;match=198.51.100.1
 ;match=198.51.100.2
+;match=192.168.10.0:5061/24
 
 
 ;=============ENDPOINT CONFIGURED AS A TRUNK, INBOUND AUTH AND REGISTRATION===
diff --git a/doc/CHANGES-staging/res_pjsip_endpoint_identifier_ip_match_port.txt b/doc/CHANGES-staging/res_pjsip_endpoint_identifier_ip_match_port.txt
new file mode 100644
index 0000000..3881d64
--- /dev/null
+++ b/doc/CHANGES-staging/res_pjsip_endpoint_identifier_ip_match_port.txt
@@ -0,0 +1,8 @@
+Subject: res_pjsip_endpoint_identifier_ip
+
+In 'type = identify' sections, the addresses specified for the 'match'
+clause can now include a port number. For IP addresses, the port is
+provided by including a colon after the address, followed by the
+desired port number. If supplied, the netmask should follow the port
+number. To specify a port for IPv6 addresses, the address itself must
+be enclosed in brackets to be parsed correctly.
diff --git a/include/asterisk/acl.h b/include/asterisk/acl.h
index f7655fe..4abe9fb 100644
--- a/include/asterisk/acl.h
+++ b/include/asterisk/acl.h
@@ -135,6 +135,29 @@
 struct ast_ha *ast_append_ha(const char *sense, const char *stuff, struct ast_ha *path, int *error);
 
 /*!
+ * \brief Add a new rule with optional port to a list of HAs
+ * \since 13.31.0, 16.8.0, 17.2.0
+ *
+ * \details
+ * This adds the new host access rule to the end of the list
+ * whose head is specified by the path parameter. Rules are
+ * evaluated in a way such that if multiple rules apply to
+ * a single IP address/subnet mask, then the rule latest
+ * in the list will be used.
+ *
+ * \param sense Either "permit" or "deny" (Actually any 'p' word will result
+ * in permission, and any other word will result in denial)
+ * \param stuff The IP address and subnet mask, separated with a '/'. The subnet
+ * mask can either be in dotted-decimal format or in CIDR notation (i.e. 0-32). A
+ * port can be provided by placing it after the IP address, separated with a ':'.
+ * \param path The head of the HA list to which we wish to append our new rule. If
+ * NULL is passed, then the new rule will become the head of the list
+ * \param[out] error The integer error points to will be set non-zero if an error occurs
+ * \return The head of the HA list
+ */
+struct ast_ha *ast_append_ha_with_port(const char *sense, const char *stuff, struct ast_ha *path, int *error);
+
+/*!
  * \brief Convert HAs to a comma separated string value
  * \param ha the starting ha head
  * \param buf string buffer to convert data to
diff --git a/main/acl.c b/main/acl.c
index 27da4ee..bca1e50 100644
--- a/main/acl.c
+++ b/main/acl.c
@@ -572,7 +572,7 @@
 		ha->sense);
 }
 
-struct ast_ha *ast_append_ha(const char *sense, const char *stuff, struct ast_ha *path, int *error)
+static struct ast_ha *append_ha_core(const char *sense, const char *stuff, struct ast_ha *path, int *error, int port_flags)
 {
 	struct ast_ha *ha;
 	struct ast_ha *prev = NULL;
@@ -589,6 +589,8 @@
 	}
 
 	while ((tmp = strsep(&list, ","))) {
+		uint16_t save_port;
+
 		if (!(ha = ast_calloc(1, sizeof(*ha)))) {
 			if (error) {
 				*error = 1;
@@ -610,7 +612,7 @@
 			ha->sense = allowing;
 		}
 
-		if (!ast_sockaddr_parse(&ha->addr, address, PARSE_PORT_FORBID)) {
+		if (!ast_sockaddr_parse(&ha->addr, address, port_flags)) {
 			ast_log(LOG_WARNING, "Invalid IP address: %s\n", address);
 			ast_free_ha(ha);
 			if (error) {
@@ -619,6 +621,11 @@
 			return ret;
 		}
 
+		/* Be pedantic and zero out the port if we don't want it */
+		if ((port_flags & PARSE_PORT_MASK) == PARSE_PORT_FORBID) {
+			ast_sockaddr_set_port(&ha->addr, 0);
+		}
+
 		/* If someone specifies an IPv4-mapped IPv6 address,
 		 * we just convert this to an IPv4 ACL
 		 */
@@ -667,6 +674,10 @@
 			return ret;
 		}
 
+		/* ast_sockaddr_apply_netmask() does not preserve the port, so we need to save and
+		 * restore it */
+		save_port = ast_sockaddr_port(&ha->addr);
+
 		if (ast_sockaddr_apply_netmask(&ha->addr, &ha->netmask, &ha->addr)) {
 			/* This shouldn't happen because ast_sockaddr_parse would
 			 * have failed much earlier on an unsupported address scheme
@@ -681,6 +692,8 @@
 			return ret;
 		}
 
+		ast_sockaddr_set_port(&ha->addr, save_port);
+
 		if (prev) {
 			prev->next = ha;
 		} else {
@@ -696,12 +709,30 @@
 	return ret;
 }
 
+struct ast_ha *ast_append_ha(const char *sense, const char *stuff, struct ast_ha *path, int *error)
+{
+	return append_ha_core(sense, stuff, path, error, PARSE_PORT_FORBID);
+}
+
+struct ast_ha *ast_append_ha_with_port(const char *sense, const char *stuff, struct ast_ha *path, int *error)
+{
+	return append_ha_core(sense, stuff, path, error, 0);
+}
+
 void ast_ha_join(const struct ast_ha *ha, struct ast_str **buf)
 {
 	for (; ha; ha = ha->next) {
+		const char *addr;
+
+		if (ast_sockaddr_port(&ha->addr)) {
+			addr = ast_sockaddr_stringify(&ha->addr);
+		} else {
+			addr = ast_sockaddr_stringify_addr(&ha->addr);
+		}
+
 		ast_str_append(buf, 0, "%s%s/",
 			ha->sense == AST_SENSE_ALLOW ? "!" : "",
-			ast_sockaddr_stringify_addr(&ha->addr));
+			addr);
 		/* Separated to avoid duplicating stringified addresses. */
 		ast_str_append(buf, 0, "%s", ast_sockaddr_stringify_addr(&ha->netmask));
 		if (ha->next) {
@@ -783,6 +814,7 @@
 		struct ast_sockaddr result;
 		struct ast_sockaddr mapped_addr;
 		const struct ast_sockaddr *addr_to_use;
+		uint16_t save_port;
 #if 0	/* debugging code */
 		char iabuf[INET_ADDRSTRLEN];
 		char iabuf2[INET_ADDRSTRLEN];
@@ -818,13 +850,22 @@
 			}
 		}
 
+		/* ast_sockaddr_apply_netmask() does not preserve the port, so we need to save and
+		 * restore it */
+		save_port = ast_sockaddr_port(addr_to_use);
+
 		/* For each rule, if this address and the netmask = the net address
 		   apply the current rule */
 		if (ast_sockaddr_apply_netmask(addr_to_use, &current_ha->netmask, &result)) {
 			/* Unlikely to happen since we know the address to be IPv4 or IPv6 */
 			continue;
 		}
-		if (!ast_sockaddr_cmp_addr(&result, &current_ha->addr)) {
+
+		ast_sockaddr_set_port(&result, save_port);
+
+		if (!ast_sockaddr_cmp_addr(&result, &current_ha->addr)
+		   && (!ast_sockaddr_port(&current_ha->addr)
+			  || ast_sockaddr_port(&current_ha->addr) == ast_sockaddr_port(&result))) {
 			res = current_ha->sense;
 		}
 	}
diff --git a/res/res_pjsip_endpoint_identifier_ip.c b/res/res_pjsip_endpoint_identifier_ip.c
index a4bdab4..db4edbf 100644
--- a/res/res_pjsip_endpoint_identifier_ip.c
+++ b/res/res_pjsip_endpoint_identifier_ip.c
@@ -63,7 +63,9 @@
 						hostnames.  IP addresses may have a subnet mask appended.  The
 						subnet mask may be written in either CIDR or dotted-decimal
 						notation.  Separate the IP address and subnet mask with a slash
-						('/').
+						('/'). A source port can also be specified by adding a colon (':')
+						after the address but before the subnet mask, e.g.
+						3.2.1.0:5061/24.
 						</para>
 					</description>
 				</configOption>
@@ -310,7 +312,7 @@
 	int num_addrs = 0, error = 0, i;
 	int results = 0;
 
-	num_addrs = ast_sockaddr_resolve(&addrs, host, PARSE_PORT_FORBID, AST_AF_UNSPEC);
+	num_addrs = ast_sockaddr_resolve(&addrs, host, 0, AST_AF_UNSPEC);
 	if (!num_addrs) {
 		return -1;
 	}
@@ -322,7 +324,7 @@
 		}
 
 		/* We deny what we actually want to match because there is an implicit permit all rule for ACLs */
-		identify->matches = ast_append_ha("d", ast_sockaddr_stringify_addr(&addrs[i]), identify->matches, &error);
+		identify->matches = ast_append_ha_with_port("d", ast_sockaddr_stringify(&addrs[i]), identify->matches, &error);
 
 		if (!identify->matches || error) {
 			results = -1;
@@ -380,15 +382,20 @@
 	}
 
 	while ((current_string = ast_strip(strsep(&input_string, ",")))) {
-		char *mask = strrchr(current_string, '/');
+		char *mask;
+		struct ast_sockaddr address;
 		int error = 0;
 
 		if (ast_strlen_zero(current_string)) {
 			continue;
 		}
 
-		if (mask) {
-			identify->matches = ast_append_ha("d", current_string, identify->matches, &error);
+		mask = strrchr(current_string, '/');
+
+		/* If it looks like a netmask is present, or we can immediately parse as an IP,
+		 * hand things off to the ACL */
+		if (mask || ast_sockaddr_parse(&address, current_string, 0)) {
+			identify->matches = ast_append_ha_with_port("d", current_string, identify->matches, &error);
 
 			if (!identify->matches || error) {
 				ast_log(LOG_ERROR, "Failed to add address '%s' to ip endpoint identifier '%s'\n",
@@ -498,20 +505,23 @@
 	/* Resolve the match addresses now */
 	i = ao2_iterator_init(identify->hosts, 0);
 	while ((current_string = ao2_iterator_next(&i))) {
-		struct ast_sockaddr address;
 		int results = 0;
+		char *colon = strrchr(current_string, ':');
 
-		/* If the provided string is not an IP address perform SRV resolution on it */
-		if (identify->srv_lookups && !ast_sockaddr_parse(&address, current_string, 0)) {
-			results = ip_identify_match_srv_lookup(identify, "_sip._udp", current_string,
-				results);
-			if (results != -1) {
-				results = ip_identify_match_srv_lookup(identify, "_sip._tcp",
-					current_string, results);
-			}
-			if (results != -1) {
-				results = ip_identify_match_srv_lookup(identify, "_sips._tcp",
-					current_string, results);
+		/* We skip SRV lookup if a colon is present, assuming a port was specified */
+		if (!colon) {
+			/* No port, and we know this is not an IP address, so perform SRV resolution on it */
+			if (identify->srv_lookups) {
+				results = ip_identify_match_srv_lookup(identify, "_sip._udp", current_string,
+					results);
+				if (results != -1) {
+					results = ip_identify_match_srv_lookup(identify, "_sip._tcp",
+						current_string, results);
+				}
+				if (results != -1) {
+					results = ip_identify_match_srv_lookup(identify, "_sips._tcp",
+						current_string, results);
+				}
 			}
 		}
 
@@ -554,7 +564,14 @@
 static void match_to_var_list_append(struct ast_variable **head, struct ast_ha *ha)
 {
 	char str[MAX_OBJECT_FIELD];
-	const char *addr = ast_strdupa(ast_sockaddr_stringify_addr(&ha->addr));
+	const char *addr;
+
+	if (ast_sockaddr_port(&ha->addr)) {
+		addr = ast_strdupa(ast_sockaddr_stringify(&ha->addr));
+	} else {
+		addr = ast_strdupa(ast_sockaddr_stringify_addr(&ha->addr));
+	}
+
 	snprintf(str, MAX_OBJECT_FIELD, "%s%s/%s", ha->sense == AST_SENSE_ALLOW ? "!" : "",
 			 addr, ast_sockaddr_stringify_addr(&ha->netmask));
 
@@ -737,7 +754,13 @@
 		indent = CLI_INDENT_TO_SPACES(context->indent_level);
 
 		for (match = ident->matches; match; match = match->next) {
-			const char *addr = ast_sockaddr_stringify_addr(&match->addr);
+			const char *addr;
+
+			if (ast_sockaddr_port(&match->addr)) {
+				addr = ast_sockaddr_stringify(&match->addr);
+			} else {
+				addr = ast_sockaddr_stringify_addr(&match->addr);
+			}
 
 			ast_str_append(&context->output_buffer, 0, "%*s: %s%s/%d\n",
 				indent,

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

Gerrit-Project: asterisk
Gerrit-Branch: master
Gerrit-Change-Id: I256d5bd5d478b95f526e2f80ace31b690eebba92
Gerrit-Change-Number: 13300
Gerrit-PatchSet: 9
Gerrit-Owner: Sean Bright <sean.bright at gmail.com>
Gerrit-Reviewer: Benjamin Keith Ford <bford at digium.com>
Gerrit-Reviewer: Friendly Automation
Gerrit-Reviewer: George Joseph <gjoseph at digium.com>
Gerrit-Reviewer: Joshua Colp <jcolp at sangoma.com>
Gerrit-Reviewer: Sean Bright <sean.bright at gmail.com>
Gerrit-MessageType: merged
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.digium.com/pipermail/asterisk-code-review/attachments/20200109/6e45a16b/attachment-0001.html>


More information about the asterisk-code-review mailing list