Skip to content
Snippets Groups Projects
Commit a7d856cd authored by Joshua Colp's avatar Joshua Colp
Browse files

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
parent 6962a134
No related branches found
No related tags found
No related merge requests found
...@@ -110,6 +110,12 @@ res_pjsip ...@@ -110,6 +110,12 @@ res_pjsip
ID, AuthenticateQualify, OutboundProxy, Path, QualifyFrequency and ID, AuthenticateQualify, OutboundProxy, Path, QualifyFrequency and
QualifyTimeout. Existing fields have not been disturbed. 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 ------------ --- Functionality changes from Asterisk 14.1.0 to Asterisk 14.2.0 ------------
------------------------------------------------------------------------------ ------------------------------------------------------------------------------
......
"""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')
...@@ -51,6 +51,13 @@ ...@@ -51,6 +51,13 @@
mask with a slash ('/') mask with a slash ('/')
</para></description> </para></description>
</configOption> </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"> <configOption name="type">
<synopsis>Must be of type 'identify'.</synopsis> <synopsis>Must be of type 'identify'.</synopsis>
</configOption> </configOption>
...@@ -70,6 +77,8 @@ struct ip_identify_match { ...@@ -70,6 +77,8 @@ struct ip_identify_match {
); );
/*! \brief Networks or addresses that should match this */ /*! \brief Networks or addresses that should match this */
struct ast_ha *matches; struct ast_ha *matches;
/*! \brief Perform SRV resolution of hostnames */
unsigned int srv_lookups;
}; };
/*! \brief Destructor function for a matching object */ /*! \brief Destructor function for a matching object */
...@@ -153,6 +162,72 @@ static struct ast_sip_endpoint_identifier ip_identifier = { ...@@ -153,6 +162,72 @@ static struct ast_sip_endpoint_identifier ip_identifier = {
.identify_endpoint = ip_identify, .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 */ /*! \brief Custom handler for match field */
static int ip_identify_match_handler(const struct aco_option *opt, struct ast_variable *var, void *obj) 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 ...@@ -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, ",")))) { while ((current_string = ast_strip(strsep(&input_string, ",")))) {
struct ast_sockaddr *addrs;
int num_addrs = 0, error = 0, i;
char *mask = strrchr(current_string, '/'); char *mask = strrchr(current_string, '/');
struct ast_sockaddr address;
int error, results = 0;
if (ast_strlen_zero(current_string)) { if (ast_strlen_zero(current_string)) {
continue; continue;
...@@ -185,28 +260,28 @@ static int ip_identify_match_handler(const struct aco_option *opt, struct ast_va ...@@ -185,28 +260,28 @@ static int ip_identify_match_handler(const struct aco_option *opt, struct ast_va
continue; continue;
} }
num_addrs = ast_sockaddr_resolve(&addrs, current_string, PARSE_PORT_FORBID, AST_AF_UNSPEC); /* If the provided string is not an IP address perform SRV resolution on it */
if (!num_addrs) { if (identify->srv_lookups && !ast_sockaddr_parse(&address, current_string, 0)) {
ast_log(LOG_ERROR, "Address '%s' provided on ip endpoint identifier '%s' did not resolve to any address\n", results = ip_identify_match_srv_lookup(identify, "_sip._udp", current_string);
var->value, ast_sorcery_object_get_id(obj)); if (results != -1) {
return -1; results += ip_identify_match_srv_lookup(identify, "_sip._tcp", current_string);
} }
if (results != -1) {
for (i = 0; i < num_addrs; ++i) { results += ip_identify_match_srv_lookup(identify, "_sips._tcp", current_string);
/* 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;
} }
} }
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; return -1;
} }
} }
...@@ -469,6 +544,7 @@ static int load_module(void) ...@@ -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", "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(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", "", 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_sorcery_load_object(ast_sip_get_sorcery(), "identify");
ast_sip_register_endpoint_identifier_with_name(&ip_identifier, "ip"); ast_sip_register_endpoint_identifier_with_name(&ip_identifier, "ip");
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment