<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>