]> git.ipfire.org Git - thirdparty/asterisk.git/commitdiff
res_pjsip_endpoint_identifier_ip: Endpoint identifier request URI
authorSperl Viktor <viktike32@gmail.com>
Thu, 28 Mar 2024 13:20:26 +0000 (14:20 +0100)
committerasterisk-org-access-app[bot] <120671045+asterisk-org-access-app[bot]@users.noreply.github.com>
Thu, 28 Mar 2024 15:05:05 +0000 (15:05 +0000)
Add ability to match against PJSIP request URI.

UserNote: this new feature let users match endpoints based on the
indound SIP requests' URI. To do so, add 'request_uri' to the
endpoint's 'identify_by' option. The 'match_request_uri' option of
the identify can be an exact match for the entire request uri, or a
regular expression (between slashes). It's quite similar to the
header identifer.

Fixes: #599
configs/samples/pjsip.conf.sample
contrib/ast-db-manage/config/versions/cf150a175fd3_add_match_request_uri_attribute_to_.py [new file with mode: 0644]
include/asterisk/res_pjsip.h
res/res_pjsip/pjsip_config.xml
res/res_pjsip/pjsip_configuration.c
res/res_pjsip_endpoint_identifier_ip.c

index 9d454089d3f7a738489355c3780d710ffafecb8d..6b8936b2d4c5c7e5691be7f74a07ef0bd8f9353c 100644 (file)
                         ; "auth_username": Identify by the Authorization username and realm
                         ; "ip": Identify by the source IP address
                         ; "header": Identify by a configured SIP header value.
+                        ; "request_uri": Identify by the configured SIP request URI.
                         ; In the username and auth_username cases, if an exact match
                         ; on both username and domain/realm fails, the match is
                         ; retried with just the username.
diff --git a/contrib/ast-db-manage/config/versions/cf150a175fd3_add_match_request_uri_attribute_to_.py b/contrib/ast-db-manage/config/versions/cf150a175fd3_add_match_request_uri_attribute_to_.py
new file mode 100644 (file)
index 0000000..9c9f38f
--- /dev/null
@@ -0,0 +1,20 @@
+"""add match_request_uri attribute to identify
+
+Revision ID: cf150a175fd3
+Revises: 8fce8496f03e
+Create Date: 2024-03-28 14:19:15.033869
+
+"""
+
+# revision identifiers, used by Alembic.
+revision = 'cf150a175fd3'
+down_revision = '8fce8496f03e'
+
+from alembic import op
+import sqlalchemy as sa
+
+def upgrade():
+    op.add_column('ps_endpoint_id_ips', sa.Column('match_request_uri', sa.String(255)))
+
+def downgrade():
+    op.drop_column('ps_endpoint_id_ips', 'match_request_uri')
index 214605209accb44a81142ed0b4e9f8a51728f539..d0e1e5922350cd555c8b828830b5fe78dba81792 100644 (file)
@@ -615,6 +615,8 @@ enum ast_sip_endpoint_identifier_type {
        AST_SIP_ENDPOINT_IDENTIFY_BY_IP = (1 << 2),
        /*! Identify based on arbitrary headers */
        AST_SIP_ENDPOINT_IDENTIFY_BY_HEADER = (1 << 3),
+       /*! Identify based on request uri */
+       AST_SIP_ENDPOINT_IDENTIFY_BY_REQUEST_URI = (1 << 4),
 };
 AST_VECTOR(ast_sip_identify_by_vector, enum ast_sip_endpoint_identifier_type);
 
index c88ec548134d355f9ff6af04c38f890ee9e83e82..753037b55590ebeccbe269cc0c50fcea6fac225a 100644 (file)
                                                                endpoint identification.
                                                                </para>
                                                        </enum>
+                                                       <enum name="request_uri">
+                                                               <para>Matches the endpoint based on the configured SIP
+                                                               request uri.
+                                                               </para>
+                                                               <para>This method of identification is not configured here
+                                                               but simply allowed by this configuration option.
+                                                               </para>
+                                                       </enum>
                                                </enumlist>
                                        </description>
                                </configOption>
index 7dbe7ad16e3688e2043dd4f6fbe2a6485460d095..58a50c484a2f4a8a087892f5a5863b63b367b9f6 100644 (file)
@@ -423,6 +423,9 @@ static const char *sip_endpoint_identifier_type2str(enum ast_sip_endpoint_identi
        case AST_SIP_ENDPOINT_IDENTIFY_BY_HEADER:
                str = "header";
                break;
+       case AST_SIP_ENDPOINT_IDENTIFY_BY_REQUEST_URI:
+               str = "request_uri";
+               break;
        }
        return str;
 }
@@ -448,6 +451,8 @@ static int sip_endpoint_identifier_str2type(const char *str)
                method = AST_SIP_ENDPOINT_IDENTIFY_BY_IP;
        } else if (!strcasecmp(str, "header")) {
                method = AST_SIP_ENDPOINT_IDENTIFY_BY_HEADER;
+       } else if (!strcasecmp(str, "request_uri")) {
+               method = AST_SIP_ENDPOINT_IDENTIFY_BY_REQUEST_URI;
        } else {
                method = -1;
        }
index d8de26112ce83f7a9b923126bec55129a8c660d9..da5c363f3aa78d99f5aeaeac1e940e10af4b3157 100644 (file)
                                                </para></note>
                                        </description>
                                </configOption>
+                               <configOption name="match_request_uri">
+                                       <synopsis>Request URI to match against.</synopsis>
+                                       <description>
+                                               <para>The SIP request URI is used to match against.
+                                               </para>
+                                               <para>The specified SIP request URI can be a regular
+                                               expression if the value is of the form
+                                               /<replaceable>regex</replaceable>/.
+                                               </para>
+                                               <note><para>Use of a regex is expensive so be sure you need
+                                               to use a regex to match your endpoint.
+                                               </para></note>
+                                       </description>
+                               </configOption>
                                <configOption name="type">
                                        <synopsis>Must be of type 'identify'.</synopsis>
                                </configOption>
@@ -129,6 +143,8 @@ struct ip_identify_match {
        AST_DECLARE_STRING_FIELDS(
                /*! The name of the endpoint */
                AST_STRING_FIELD(endpoint_name);
+               /*! If matching by request, the value to match against */
+               AST_STRING_FIELD(match_request_uri);
                /*! If matching by header, the header/value to match against */
                AST_STRING_FIELD(match_header);
                /*! SIP header name of the match_header string */
@@ -136,16 +152,20 @@ struct ip_identify_match {
                /*! SIP header value of the match_header string */
                AST_STRING_FIELD(match_header_value);
        );
-       /*! Compiled match_header regular expression when is_regex is non-zero */
-       regex_t regex_buf;
+       /*! Compiled match_header regular expression when is_header_regex is non-zero */
+       regex_t regex_header_buf;
+       /*! Compiled match_request_uri regular expression when is_request_uri_regex is non-zero */
+       regex_t regex_request_uri_buf;
        /*! \brief Networks or addresses that should match this */
        struct ast_ha *matches;
        /*! \brief Hosts to be resolved when applying configuration */
        struct ao2_container *hosts;
        /*! \brief Perform SRV resolution of hostnames */
        unsigned int srv_lookups;
-       /*! Non-zero if match_header has a regular expression (i.e., regex_buf is valid) */
-       unsigned int is_regex:1;
+       /*! Non-zero if match_header has a regular expression (i.e., regex_header_buf is valid) */
+       unsigned int is_header_regex:1;
+       /*! Non-zero if match_header or match_request has a regular expression (i.e., regex_request_uri_buf is valid) */
+       unsigned int is_request_uri_regex:1;
 };
 
 /*! \brief Destructor function for a matching object */
@@ -156,8 +176,11 @@ static void ip_identify_destroy(void *obj)
        ast_string_field_free_memory(identify);
        ast_free_ha(identify->matches);
        ao2_cleanup(identify->hosts);
-       if (identify->is_regex) {
-               regfree(&identify->regex_buf);
+       if (identify->is_header_regex) {
+               regfree(&identify->regex_header_buf);
+       }
+       if (identify->is_request_uri_regex) {
+               regfree(&identify->regex_request_uri_buf);
        }
 }
 
@@ -219,8 +242,8 @@ static int header_identify_match_check(void *obj, void *arg, int flags)
                pos = ast_strip(pos + 1);
 
                /* Does header value match what we are looking for? */
-               if (identify->is_regex) {
-                       if (!regexec(&identify->regex_buf, pos, 0, NULL, 0)) {
+               if (identify->is_header_regex) {
+                       if (!regexec(&identify->regex_header_buf, pos, 0, NULL, 0)) {
                                return CMP_MATCH;
                        }
                } else if (!strcmp(identify->match_header_value, pos)) {
@@ -241,6 +264,41 @@ static int header_identify_match_check(void *obj, void *arg, int flags)
        return 0;
 }
 
+/*! \brief Comparator function for matching an object by request URI */
+static int request_identify_match_check(void *obj, void *arg, int flags)
+{
+       struct ip_identify_match *identify = obj;
+       struct pjsip_rx_data *rdata = arg;
+       int len;
+       char buf[PJSIP_MAX_URL_SIZE];
+
+       if (ast_strlen_zero(identify->match_request_uri)) {
+               return 0;
+       }
+
+       /* Print request URI to req_buf */
+       len = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, rdata->msg_info.msg->line.req.uri, buf, sizeof(buf) - 1);
+       if (len < 0) {
+               /* Buffer not large enough or no pj uri vptr! */
+               ast_assert(0);
+       } else {
+               /* Terminate the pj_str */
+               buf[len] = '\0';
+               /* Does request URI match what we are looking for? */
+               if (identify->is_request_uri_regex) {
+                       if (!regexec(&identify->regex_request_uri_buf, buf, 0, NULL, 0)) {
+                               return CMP_MATCH;
+                       }
+               } else if (!strcmp(identify->match_request_uri, buf)) {
+                       return CMP_MATCH;
+               }
+               ast_debug(3, "Identify '%s': request URI not match '%s' (value='%s').\n",
+                       ast_sorcery_object_get_id(identify), identify->match_request_uri, buf);
+       }
+
+       return 0;
+}
+
 /*! \brief Comparator function for matching an object by IP address */
 static int ip_identify_match_check(void *obj, void *arg, int flags)
 {
@@ -314,10 +372,19 @@ static struct ast_sip_endpoint *header_identify(pjsip_rx_data *rdata)
        return common_identify(header_identify_match_check, rdata);
 }
 
+static struct ast_sip_endpoint *request_identify(pjsip_rx_data *rdata)
+{
+       return common_identify(request_identify_match_check, rdata);
+}
+
 static struct ast_sip_endpoint_identifier header_identifier = {
        .identify_endpoint = header_identify,
 };
 
+static struct ast_sip_endpoint_identifier request_identifier = {
+       .identify_endpoint = request_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)
 {
@@ -453,6 +520,7 @@ static int ip_identify_apply(const struct ast_sorcery *sorcery, void *obj)
                return -1;
        }
        if (ast_strlen_zero(identify->match_header) /* No header to match */
+               && ast_strlen_zero(identify->match_request_uri) /* and no request to match */
                /* and no static IP addresses with a mask */
                && !identify->matches
                /* and no addresses to resolve */
@@ -501,12 +569,38 @@ static int ip_identify_apply(const struct ast_sorcery *sorcery, void *obj)
                        c_value[len - 1] = '\0';
                        ++c_value;
 
-                       if (regcomp(&identify->regex_buf, c_value, REG_EXTENDED | REG_NOSUB)) {
-                               ast_log(LOG_ERROR, "Identify '%s' failed to compile match_header regex '%s'.\n",
+                       if (regcomp(&identify->regex_header_buf, c_value, REG_EXTENDED | REG_NOSUB)) {
+                               ast_log(LOG_ERROR, "Identify '%s' failed to compile match_request_uri regex '%s'.\n",
                                        ast_sorcery_object_get_id(identify), c_value);
                                return -1;
                        }
-                       identify->is_regex = 1;
+                       identify->is_header_regex = 1;
+               }
+       }
+
+       if (!ast_strlen_zero(identify->match_request_uri)) {
+               char *c_string;
+               int len;
+
+               len = strlen(identify->match_request_uri);
+               c_string = ast_strdupa(identify->match_request_uri);
+
+               if (!strcmp(c_string, "//")) {
+                       /* An empty regex is the same as an empty literal string. */
+                       c_string = "";
+               }
+
+               if (2 < len && c_string[0] == '/' && c_string[len - 1] == '/') {
+                       /* Make "/regex/" into "regex" */
+                       c_string[len - 1] = '\0';
+                       ++c_string;
+
+                       if (regcomp(&identify->regex_request_uri_buf, c_string, REG_EXTENDED | REG_NOSUB)) {
+                               ast_log(LOG_ERROR, "Identify '%s' failed to compile match_header regex '%s'.\n",
+                                       ast_sorcery_object_get_id(identify), c_string);
+                               return -1;
+                       }
+                       identify->is_request_uri_regex = 1;
                }
        }
 
@@ -788,10 +882,17 @@ static int cli_print_body(void *obj, void *arg, int flags)
                if (!ast_strlen_zero(ident->match_header)) {
                        ast_str_append(&context->output_buffer, 0, "%*s: %s\n",
                                indent,
-                               "Match",
+                               "Header",
                                ident->match_header);
                }
 
+               if (!ast_strlen_zero(ident->match_request_uri)) {
+                       ast_str_append(&context->output_buffer, 0, "%*s: %s\n",
+                               indent,
+                               "RequestURI",
+                               ident->match_request_uri);
+               }
+
                context->indent_level--;
 
                if (context->indent_level == 0) {
@@ -851,11 +952,13 @@ static int load_module(void)
        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", "match_header", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ip_identify_match, match_header));
+       ast_sorcery_object_field_register(ast_sip_get_sorcery(), "identify", "match_request_uri", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ip_identify_match, match_request_uri));
        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");
        ast_sip_register_endpoint_identifier_with_name(&header_identifier, "header");
+       ast_sip_register_endpoint_identifier_with_name(&request_identifier, "request_uri");
        ast_sip_register_endpoint_formatter(&endpoint_identify_formatter);
 
        cli_formatter = ao2_alloc(sizeof(struct ast_sip_cli_formatter_entry), NULL);
@@ -890,6 +993,7 @@ static int unload_module(void)
        ast_sip_unregister_cli_formatter(cli_formatter);
        ast_sip_unregister_endpoint_formatter(&endpoint_identify_formatter);
        ast_sip_unregister_endpoint_identifier(&header_identifier);
+       ast_sip_unregister_endpoint_identifier(&request_identifier);
        ast_sip_unregister_endpoint_identifier(&ip_identifier);
 
        return 0;