[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