<p>Sean Bright has uploaded this change for <strong>review</strong>.</p><p><a href="https://gerrit.asterisk.org/c/asterisk/+/13312">View Change</a></p><pre style="font-family: monospace,monospace; white-space: pre-wrap;">res_pjsip_endpoint_identifier_ip.c: Add port matching support<br><br>Adds source port matching support when IP matching is used:<br><br>  [example]<br>  type = identify<br>  match = 1.2.3.4/32<br>  match_port = 5060, 6000<br><br>If the IP matches but the source port does not, we reject and search for<br>alternatives.<br><br>ASTERISK-28639 #close<br>Reported by: MLC in #asterisk<br><br>Change-Id: I256d5bd5d478b95f526e2f80ace31b690eebba92<br>---<br>A contrib/ast-db-manage/config/versions/e6420d7ca697_add_match_port_to_ip_identify.py<br>M res/res_pjsip_endpoint_identifier_ip.c<br>2 files changed, 163 insertions(+), 6 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">git pull ssh://gerrit.asterisk.org:29418/asterisk refs/changes/12/13312/1</pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>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</span><br><span>new file mode 100644</span><br><span>index 0000000..d2c0f73</span><br><span>--- /dev/null</span><br><span>+++ b/contrib/ast-db-manage/config/versions/e6420d7ca697_add_match_port_to_ip_identify.py</span><br><span>@@ -0,0 +1,21 @@</span><br><span style="color: hsl(120, 100%, 40%);">+"""Add match_port to IP identify</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+Revision ID: e6420d7ca697</span><br><span style="color: hsl(120, 100%, 40%);">+Revises: 339e1dfa644d</span><br><span style="color: hsl(120, 100%, 40%);">+Create Date: 2019-11-30 12:59:55.548684</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+"""</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+# revision identifiers, used by Alembic.</span><br><span style="color: hsl(120, 100%, 40%);">+revision = 'e6420d7ca697'</span><br><span style="color: hsl(120, 100%, 40%);">+down_revision = '339e1dfa644d'</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+from alembic import op</span><br><span style="color: hsl(120, 100%, 40%);">+import sqlalchemy as sa</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+def upgrade():</span><br><span style="color: hsl(120, 100%, 40%);">+    op.add_column('ps_endpoint_id_ips', sa.Column('match_port', sa.String(255)))</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+def downgrade():</span><br><span style="color: hsl(120, 100%, 40%);">+    op.drop_column('ps_endpoint_id_ips', 'match_port')</span><br><span>diff --git a/res/res_pjsip_endpoint_identifier_ip.c b/res/res_pjsip_endpoint_identifier_ip.c</span><br><span>index ac4057b..9e0f2e9 100644</span><br><span>--- a/res/res_pjsip_endpoint_identifier_ip.c</span><br><span>+++ b/res/res_pjsip_endpoint_identifier_ip.c</span><br><span>@@ -67,6 +67,14 @@</span><br><span>                                              </para></span><br><span>                                        </description></span><br><span>                                 </configOption></span><br><span style="color: hsl(120, 100%, 40%);">+                         <configOption name="match_port"></span><br><span style="color: hsl(120, 100%, 40%);">+                                      <synopsis>IP ports to match against.</synopsis></span><br><span style="color: hsl(120, 100%, 40%);">+                                   <description></span><br><span style="color: hsl(120, 100%, 40%);">+                                           <para>When used with <literal>match</literal>, only matches</span><br><span style="color: hsl(120, 100%, 40%);">+                                         inbound requests whose source port is one of the ports provided.</span><br><span style="color: hsl(120, 100%, 40%);">+                                              </para></span><br><span style="color: hsl(120, 100%, 40%);">+                                 </description></span><br><span style="color: hsl(120, 100%, 40%);">+                          </configOption></span><br><span>                                <configOption name="srv_lookups" default="yes"></span><br><span>                                    <synopsis>Perform SRV lookups for provided hostnames.</synopsis></span><br><span>                                         <description></span><br><span>@@ -125,6 +133,8 @@</span><br><span>    regex_t regex_buf;</span><br><span>   /*! \brief Networks or addresses that should match this */</span><br><span>   struct ast_ha *matches;</span><br><span style="color: hsl(120, 100%, 40%);">+       /*! \brief Ports that should match this */</span><br><span style="color: hsl(120, 100%, 40%);">+    AST_VECTOR(, uint16_t) match_ports;</span><br><span>  /*! \brief Hosts to be resolved when applying configuration */</span><br><span>       struct ao2_container *hosts;</span><br><span>         /*! \brief Perform SRV resolution of hostnames */</span><br><span>@@ -140,6 +150,7 @@</span><br><span> </span><br><span>  ast_string_field_free_memory(identify);</span><br><span>      ast_free_ha(identify->matches);</span><br><span style="color: hsl(120, 100%, 40%);">+    AST_VECTOR_FREE(&identify->match_ports);</span><br><span>      ao2_cleanup(identify->hosts);</span><br><span>     if (identify->is_regex) {</span><br><span>                 regfree(&identify->regex_buf);</span><br><span>@@ -151,7 +162,7 @@</span><br><span> {</span><br><span>     struct ip_identify_match *identify = ast_sorcery_generic_alloc(sizeof(*identify), ip_identify_destroy);</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-     if (!identify || ast_string_field_init(identify, 256)) {</span><br><span style="color: hsl(120, 100%, 40%);">+      if (!identify || ast_string_field_init(identify, 256) || AST_VECTOR_INIT(&identify->match_ports, 8)) {</span><br><span>                ao2_cleanup(identify);</span><br><span>               return NULL;</span><br><span>         }</span><br><span>@@ -226,6 +237,25 @@</span><br><span>     return 0;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+static int port_cmp(uint16_t a, uint16_t b)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    return a == b;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int port_sort(uint16_t a, uint16_t b)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  return a - b;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int check_port_match(struct ip_identify_match *identify, uint16_t source_port)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  if (AST_VECTOR_SIZE(&identify->match_ports) == 0</span><br><span style="color: hsl(120, 100%, 40%);">+          || AST_VECTOR_GET_CMP(&identify->match_ports, source_port, port_cmp)) {</span><br><span style="color: hsl(120, 100%, 40%);">+             return 1;</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+     return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> /*! \brief Comparator function for matching an object by IP address */</span><br><span> static int ip_identify_match_check(void *obj, void *arg, int flags)</span><br><span> {</span><br><span>@@ -234,7 +264,8 @@</span><br><span>   int sense;</span><br><span> </span><br><span>       sense = ast_apply_ha(identify->matches, addr);</span><br><span style="color: hsl(0, 100%, 40%);">-       if (sense != AST_SENSE_ALLOW) {</span><br><span style="color: hsl(120, 100%, 40%);">+       if (sense != AST_SENSE_ALLOW</span><br><span style="color: hsl(120, 100%, 40%);">+     && check_port_match(identify, ast_sockaddr_port(addr))) {</span><br><span>                 ast_debug(3, "Source address %s matches identify '%s'\n",</span><br><span>                          ast_sockaddr_stringify(addr),</span><br><span>                                ast_sorcery_object_get_id(identify));</span><br><span>@@ -369,6 +400,47 @@</span><br><span> }</span><br><span> </span><br><span> /*! \brief Custom handler for match field */</span><br><span style="color: hsl(120, 100%, 40%);">+static int ip_identify_match_port_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        struct ip_identify_match *identify = obj;</span><br><span style="color: hsl(120, 100%, 40%);">+     char *input_string = ast_strdupa(var->value);</span><br><span style="color: hsl(120, 100%, 40%);">+      char *current_string;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       if (ast_strlen_zero(var->value)) {</span><br><span style="color: hsl(120, 100%, 40%);">+         return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   while ((current_string = ast_strip(strsep(&input_string, ",")))) {</span><br><span style="color: hsl(120, 100%, 40%);">+              unsigned long val;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+          if (ast_strlen_zero(current_string)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                        continue;</span><br><span style="color: hsl(120, 100%, 40%);">+             }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+           errno = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+            val = strtoul(current_string, NULL, 10);</span><br><span style="color: hsl(120, 100%, 40%);">+              if (errno) {</span><br><span style="color: hsl(120, 100%, 40%);">+                  ast_log(LOG_ERROR, "Failed to parse '%s' as a port number for ip endpoint identifier '%s'\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                                       current_string, ast_sorcery_object_get_id(obj));</span><br><span style="color: hsl(120, 100%, 40%);">+                      return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+            }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+           if (val > UINT_MAX) {</span><br><span style="color: hsl(120, 100%, 40%);">+                      ast_log(LOG_ERROR, "Invalid match_port specified. Must be no greater than %u\n", UINT_MAX);</span><br><span style="color: hsl(120, 100%, 40%);">+                 return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+            }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+           if (AST_VECTOR_ADD_SORTED(&identify->match_ports, val, port_sort)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                   ast_log(LOG_ERROR, "Failed to add port '%lu' to ip endpoint indentifier '%s'\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                                    val, ast_sorcery_object_get_id(obj));</span><br><span style="color: hsl(120, 100%, 40%);">+                 return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+            }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! \brief Custom handler for match field */</span><br><span> static int ip_identify_match_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)</span><br><span> {</span><br><span>         struct ip_identify_match *identify = obj;</span><br><span>@@ -425,6 +497,7 @@</span><br><span>      struct ip_identify_match *identify = obj;</span><br><span>    char *current_string;</span><br><span>        struct ao2_iterator i;</span><br><span style="color: hsl(120, 100%, 40%);">+        int matching_by_addr = 0;</span><br><span> </span><br><span>        /* Validate the identify object configuration */</span><br><span>     if (ast_strlen_zero(identify->endpoint_name)) {</span><br><span>@@ -432,16 +505,23 @@</span><br><span>                   ast_sorcery_object_get_id(identify));</span><br><span>                return -1;</span><br><span>   }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   matching_by_addr = identify->matches</span><br><span style="color: hsl(120, 100%, 40%);">+               || (identify->hosts && ao2_container_count(identify->hosts));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>        if (ast_strlen_zero(identify->match_header) /* No header to match */</span><br><span style="color: hsl(0, 100%, 40%);">-         /* and no static IP addresses with a mask */</span><br><span style="color: hsl(0, 100%, 40%);">-            && !identify->matches</span><br><span style="color: hsl(0, 100%, 40%);">-                /* and no addresses to resolve */</span><br><span style="color: hsl(0, 100%, 40%);">-               && (!identify->hosts || !ao2_container_count(identify->hosts))) {</span><br><span style="color: hsl(120, 100%, 40%);">+               && !matching_by_addr) {</span><br><span>              ast_log(LOG_ERROR, "Identify '%s' is not configured to match anything.\n",</span><br><span>                         ast_sorcery_object_get_id(identify));</span><br><span>                return -1;</span><br><span>   }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ if (AST_VECTOR_SIZE(&identify->match_ports) && !matching_by_addr) {</span><br><span style="color: hsl(120, 100%, 40%);">+            ast_log(LOG_ERROR, "Identify '%s' cannot use 'match_port' without 'match'.\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                      ast_sorcery_object_get_id(identify));</span><br><span style="color: hsl(120, 100%, 40%);">+         return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>  if (!ast_strlen_zero(identify->match_header)) {</span><br><span>           char *c_header;</span><br><span>              char *c_value;</span><br><span>@@ -541,6 +621,50 @@</span><br><span>        return 0;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+static int ports_to_str(const void *obj, const intptr_t *args, char **buf)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     const struct ip_identify_match *identify = obj;</span><br><span style="color: hsl(120, 100%, 40%);">+       char *list;</span><br><span style="color: hsl(120, 100%, 40%);">+   int i;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      /* Port range is [0, 65536), so we need at most 5 characters for the port</span><br><span style="color: hsl(120, 100%, 40%);">+      * and 1 for the comma. The extra 1 is only strictly necessary if the</span><br><span style="color: hsl(120, 100%, 40%);">+  * list size is 0. */</span><br><span style="color: hsl(120, 100%, 40%);">+ *buf = list = ast_calloc(1, AST_VECTOR_SIZE(&identify->match_ports) * 6 + 1);</span><br><span style="color: hsl(120, 100%, 40%);">+  if (!list) {</span><br><span style="color: hsl(120, 100%, 40%);">+          return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+    }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   for (i = 0; i < AST_VECTOR_SIZE(&identify->match_ports); i++) {</span><br><span style="color: hsl(120, 100%, 40%);">+             if (i > 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+                       list += sprintf(list, ",");</span><br><span style="color: hsl(120, 100%, 40%);">+         }</span><br><span style="color: hsl(120, 100%, 40%);">+             list += sprintf(list, "%u", AST_VECTOR_GET(&identify->match_ports, i));</span><br><span style="color: hsl(120, 100%, 40%);">+      }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int ports_to_var_list(const void *obj, struct ast_variable **fields)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        const struct ip_identify_match *identify = obj;</span><br><span style="color: hsl(120, 100%, 40%);">+       struct ast_variable *head = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+     int i;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      for (i = 0; i < AST_VECTOR_SIZE(&identify->match_ports); i++) {</span><br><span style="color: hsl(120, 100%, 40%);">+             char str[6];</span><br><span style="color: hsl(120, 100%, 40%);">+          if (snprintf(str, sizeof(str), "%u", AST_VECTOR_GET(&identify->match_ports, i)) < sizeof(str)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                  ast_variable_list_append(&head, ast_variable_new("match_port", str, ""));</span><br><span style="color: hsl(120, 100%, 40%);">+             }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (head) {</span><br><span style="color: hsl(120, 100%, 40%);">+           *fields = head;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> static int match_to_str(const void *obj, const intptr_t *args, char **buf)</span><br><span> {</span><br><span>    RAII_VAR(struct ast_str *, str, ast_str_create(MAX_OBJECT_FIELD), ast_free);</span><br><span>@@ -746,6 +870,17 @@</span><br><span>                          addr, ast_sockaddr_cidr_bits(&match->netmask));</span><br><span>               }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+         if (AST_VECTOR_SIZE(&ident->match_ports)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                    char *ports = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+                   if (!ports_to_str(ident, NULL, &ports)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                         ast_str_append(&context->output_buffer, 0, "%*s: Src Port(s) %s\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                             indent,</span><br><span style="color: hsl(120, 100%, 40%);">+                               "Match",</span><br><span style="color: hsl(120, 100%, 40%);">+                            ports);</span><br><span style="color: hsl(120, 100%, 40%);">+                            ast_free(ports);</span><br><span style="color: hsl(120, 100%, 40%);">+                      }</span><br><span style="color: hsl(120, 100%, 40%);">+             }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>          if (!ast_strlen_zero(ident->match_header)) {</span><br><span>                      ast_str_append(&context->output_buffer, 0, "%*s: %s\n",</span><br><span>                             indent,</span><br><span>@@ -813,6 +948,7 @@</span><br><span>        ast_sorcery_object_field_register(ast_sip_get_sorcery(), "identify", "type", "", OPT_NOOP_T, 0, 0);</span><br><span>    ast_sorcery_object_field_register(ast_sip_get_sorcery(), "identify", "endpoint", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ip_identify_match, endpoint_name));</span><br><span>        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);</span><br><span style="color: hsl(120, 100%, 40%);">+     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);</span><br><span>  ast_sorcery_object_field_register(ast_sip_get_sorcery(), "identify", "match_header", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ip_identify_match, match_header));</span><br><span>     ast_sorcery_object_field_register(ast_sip_get_sorcery(), "identify", "srv_lookups", "yes", OPT_BOOL_T, 1, FLDSET(struct ip_identify_match, srv_lookups));</span><br><span>      ast_sorcery_load_object(ast_sip_get_sorcery(), "identify");</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.asterisk.org/c/asterisk/+/13312">change 13312</a>. To unsubscribe, or for help writing mail filters, visit <a href="https://gerrit.asterisk.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://gerrit.asterisk.org/c/asterisk/+/13312"/><meta itemprop="name" content="View Change"/></div></div>

<div style="display:none"> Gerrit-Project: asterisk </div>
<div style="display:none"> Gerrit-Branch: 13 </div>
<div style="display:none"> Gerrit-Change-Id: I256d5bd5d478b95f526e2f80ace31b690eebba92 </div>
<div style="display:none"> Gerrit-Change-Number: 13312 </div>
<div style="display:none"> Gerrit-PatchSet: 1 </div>
<div style="display:none"> Gerrit-Owner: Sean Bright <sean.bright@gmail.com> </div>
<div style="display:none"> Gerrit-MessageType: newchange </div>