[Asterisk-code-review] res pjsip endpoint identifier ip: Add support for SRV lookups. (asterisk[master])

Anonymous Coward asteriskteam at digium.com
Mon Jan 9 12:44:58 CST 2017


Anonymous Coward #1000019 has submitted this change and it was merged. ( https://gerrit.asterisk.org/4699 )

Change subject: res_pjsip_endpoint_identifier_ip: Add support for SRV lookups.
......................................................................


res_pjsip_endpoint_identifier_ip: Add support for SRV lookups.

This change implements SRV support for the IP based endpoint
identifier module. All possible addresses through SRV are looked
up and added as matches. If no SRV records are available a
fallback to normal host resolution is done. If an IP address
is provided then no SRV lookup occurs.

This is configured using the "srv_lookups" option on the
identify section and defaults to "yes".

ASTERISK-26693

Change-Id: I6b641e275bf96629320efa8b479737062aed82ac
---
M CHANGES
A contrib/ast-db-manage/config/versions/28ab27a7826d_add_srv_lookups_to_identify.py
M res/res_pjsip_endpoint_identifier_ip.c
3 files changed, 133 insertions(+), 20 deletions(-)

Approvals:
  George Joseph: Looks good to me, but someone else must approve
  Anonymous Coward #1000019: Verified
  Matthew Fredrickson: Looks good to me, approved



diff --git a/CHANGES b/CHANGES
index ad16a40..d843848 100644
--- a/CHANGES
+++ b/CHANGES
@@ -110,6 +110,12 @@
    ID, AuthenticateQualify, OutboundProxy, Path, QualifyFrequency and
    QualifyTimeout.  Existing fields have not been disturbed.
 
+res_pjsip_endpoint_identifier_ip
+------------------
+ * SRV lookups can now be done on provided hostnames to determine additional
+   source IP addresses for requests. This is configurable using the
+   "srv_lookups" option on the identify and defaults to "yes".
+
 ------------------------------------------------------------------------------
 --- Functionality changes from Asterisk 14.1.0 to Asterisk 14.2.0 ------------
 ------------------------------------------------------------------------------
diff --git a/contrib/ast-db-manage/config/versions/28ab27a7826d_add_srv_lookups_to_identify.py b/contrib/ast-db-manage/config/versions/28ab27a7826d_add_srv_lookups_to_identify.py
new file mode 100644
index 0000000..8831e20
--- /dev/null
+++ b/contrib/ast-db-manage/config/versions/28ab27a7826d_add_srv_lookups_to_identify.py
@@ -0,0 +1,31 @@
+"""add srv_lookups to identify
+
+Revision ID: 28ab27a7826d
+Revises: 4468b4a91372
+Create Date: 2017-01-06 14:53:38.829655
+
+"""
+
+# revision identifiers, used by Alembic.
+revision = '28ab27a7826d'
+down_revision = '4468b4a91372'
+
+from alembic import op
+import sqlalchemy as sa
+from sqlalchemy.dialects.postgresql import ENUM
+
+YESNO_NAME = 'yesno_values'
+YESNO_VALUES = ['yes', 'no']
+
+def upgrade():
+    ############################# Enums ##############################
+
+    # yesno_values have already been created, so use postgres enum object
+    # type to get around "already created" issue - works okay with mysql
+    yesno_values = ENUM(*YESNO_VALUES, name=YESNO_NAME, create_type=False)
+
+    op.add_column('ps_endpoint_id_ips', sa.Column('srv_lookups', yesno_values))
+
+
+def downgrade():
+    op.drop_column('ps_endpoint_id_ips', 'srv_lookups')
diff --git a/res/res_pjsip_endpoint_identifier_ip.c b/res/res_pjsip_endpoint_identifier_ip.c
index 7a7af0b..e095a96 100644
--- a/res/res_pjsip_endpoint_identifier_ip.c
+++ b/res/res_pjsip_endpoint_identifier_ip.c
@@ -51,6 +51,13 @@
 						mask with a slash ('/')
 					</para></description>
 				</configOption>
+				<configOption name="srv_lookups" default="yes">
+					<synopsis>Perform SRV lookups for provided hostnames.</synopsis>
+					<description><para>When enabled, <replaceable>srv_lookups</replaceable> will
+					perform SRV lookups for _sip._udp, _sip._tcp, and _sips._tcp of the given
+					hostnames to determine additional addresses that traffic may originate from.
+					</para></description>
+                                </configOption>
 				<configOption name="type">
 					<synopsis>Must be of type 'identify'.</synopsis>
 				</configOption>
@@ -70,6 +77,8 @@
 	);
 	/*! \brief Networks or addresses that should match this */
 	struct ast_ha *matches;
+	/*! \brief Perform SRV resolution of hostnames */
+	unsigned int srv_lookups;
 };
 
 /*! \brief Destructor function for a matching object */
@@ -153,6 +162,72 @@
 	.identify_endpoint = ip_identify,
 };
 
+/*! \brief Helper function which performs a host lookup and adds result to identify match */
+static int ip_identify_match_host_lookup(struct ip_identify_match *identify, const char *host)
+{
+	struct ast_sockaddr *addrs;
+	int num_addrs = 0, error = 0, i;
+	int results = 0;
+
+	num_addrs = ast_sockaddr_resolve(&addrs, host, PARSE_PORT_FORBID, AST_AF_UNSPEC);
+	if (!num_addrs) {
+		return -1;
+	}
+
+	for (i = 0; i < num_addrs; ++i) {
+		/* Check if the address is already in the list, if so don't bother adding it again */
+		if (identify->matches && (ast_apply_ha(identify->matches, &addrs[i]) != AST_SENSE_ALLOW)) {
+			continue;
+		}
+
+		/* 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);
+
+		if (!identify->matches || error) {
+			results = -1;
+			break;
+		}
+
+		results += 1;
+	}
+
+	ast_free(addrs);
+
+	return results;
+}
+
+/*! \brief Helper function which performs an SRV lookup and then resolves the hostname */
+static int ip_identify_match_srv_lookup(struct ip_identify_match *identify, const char *prefix, const char *host)
+{
+	char service[NI_MAXHOST];
+	struct srv_context *context = NULL;
+	int srv_ret;
+	const char *srvhost;
+	unsigned short srvport;
+	int results = 0;
+
+	snprintf(service, sizeof(service), "%s.%s", prefix, host);
+
+	while (!(srv_ret = ast_srv_lookup(&context, service, &srvhost, &srvport))) {
+		int hosts;
+
+		/* In the case of the SRV lookup we don't care if it fails, we will output a log message
+		 * when we fallback to a normal lookup.
+		 */
+		hosts = ip_identify_match_host_lookup(identify, srvhost);
+		if (hosts == -1) {
+			results = -1;
+			break;
+		} else {
+			results += hosts;
+		}
+	}
+
+	ast_srv_cleanup(&context);
+
+	return results;
+}
+
 /*! \brief Custom handler for match field */
 static int ip_identify_match_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
 {
@@ -165,9 +240,9 @@
 	}
 
 	while ((current_string = ast_strip(strsep(&input_string, ",")))) {
-		struct ast_sockaddr *addrs;
-		int num_addrs = 0, error = 0, i;
 		char *mask = strrchr(current_string, '/');
+		struct ast_sockaddr address;
+		int error, results = 0;
 
 		if (ast_strlen_zero(current_string)) {
 			continue;
@@ -185,28 +260,28 @@
 			continue;
 		}
 
-		num_addrs = ast_sockaddr_resolve(&addrs, current_string, PARSE_PORT_FORBID, AST_AF_UNSPEC);
-		if (!num_addrs) {
-			ast_log(LOG_ERROR, "Address '%s' provided on ip endpoint identifier '%s' did not resolve to any address\n",
-				var->value, ast_sorcery_object_get_id(obj));
-			return -1;
-		}
-
-		for (i = 0; i < num_addrs; ++i) {
-			/* 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);
-
-			if (!identify->matches || error) {
-				ast_log(LOG_ERROR, "Failed to add address '%s' to ip endpoint identifier '%s'\n",
-					ast_sockaddr_stringify_addr(&addrs[i]), ast_sorcery_object_get_id(obj));
-				error = -1;
-				break;
+		/* 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);
+			if (results != -1) {
+				results += ip_identify_match_srv_lookup(identify, "_sip._tcp", current_string);
+			}
+			if (results != -1) {
+				results += ip_identify_match_srv_lookup(identify, "_sips._tcp", current_string);
 			}
 		}
 
-		ast_free(addrs);
+		/* If SRV falls fall back to a normal lookup on the host itself */
+		if (!results) {
+			results = ip_identify_match_host_lookup(identify, current_string);
+		}
 
-		if (error) {
+		if (results == 0) {
+			ast_log(LOG_ERROR, "Address '%s' provided on ip endpoint identifier '%s' did not resolve to any address\n",
+				current_string, ast_sorcery_object_get_id(obj));
+		} else if (results == -1) {
+			ast_log(LOG_ERROR, "An error occurred when adding resolution results of '%s' on '%s'\n",
+				current_string, ast_sorcery_object_get_id(obj));
 			return -1;
 		}
 	}
@@ -469,6 +544,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(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");
 
 	ast_sip_register_endpoint_identifier_with_name(&ip_identifier, "ip");

-- 
To view, visit https://gerrit.asterisk.org/4699
To unsubscribe, visit https://gerrit.asterisk.org/settings

Gerrit-MessageType: merged
Gerrit-Change-Id: I6b641e275bf96629320efa8b479737062aed82ac
Gerrit-PatchSet: 3
Gerrit-Project: asterisk
Gerrit-Branch: master
Gerrit-Owner: Joshua Colp <jcolp at digium.com>
Gerrit-Reviewer: Anonymous Coward #1000019
Gerrit-Reviewer: George Joseph <gjoseph at digium.com>
Gerrit-Reviewer: Joshua Colp <jcolp at digium.com>
Gerrit-Reviewer: Matthew Fredrickson <creslin at digium.com>



More information about the asterisk-code-review mailing list