summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoshua Colp <jcolp@digium.com>2017-01-05 12:11:43 +0000
committerJoshua Colp <jcolp@digium.com>2017-01-06 14:56:41 +0000
commit37aaaa2da24ee7f3631a63b26c96fd3586cedbcb (patch)
treedfa410b63cf4032bf5c1ae145c3343316ad0e4bb
parentd7e5a747c312de18647213359103ce6022776864 (diff)
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
-rw-r--r--CHANGES6
-rw-r--r--contrib/ast-db-manage/config/versions/28ab27a7826d_add_srv_lookups_to_identify.py31
-rw-r--r--res/res_pjsip_endpoint_identifier_ip.c116
3 files changed, 133 insertions, 20 deletions
diff --git a/CHANGES b/CHANGES
index f54dccd98..e42cfcb8b 100644
--- a/CHANGES
+++ b/CHANGES
@@ -44,6 +44,12 @@ res_pjsip
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 13.12.0 to Asterisk 13.13.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 000000000..8831e2002
--- /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 b1ffd2cc3..cf950ea18 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 @@ struct ip_identify_match {
);
/*! \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 @@ static struct ast_sip_endpoint_identifier ip_identifier = {
.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 @@ static int ip_identify_match_handler(const struct aco_option *opt, struct ast_va
}
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 @@ static int ip_identify_match_handler(const struct aco_option *opt, struct ast_va
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 @@ static int load_module(void)
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");