chan_sip: Support RFC-3966 TEL URIs in inbound INVITE requests
authorMatthew Jordan <mjordan@digium.com>
Sat, 12 Apr 2014 02:27:43 +0000 (02:27 +0000)
committerMatthew Jordan <mjordan@digium.com>
Sat, 12 Apr 2014 02:27:43 +0000 (02:27 +0000)
This patch adds support for handling TEL URIs in inbound INVITE requests.
This includes the Request URI and the From URI. The number specified in
the Request URI will be the destination of the inbound channel in the dialplan.
The phone-context specified in the Request URI will be stored in the
TELPHONECONTEXT channel variable.

Review: https://reviewboard.asterisk.org/r/3349

ASTERISK-17179 #close
Reported by: Geert Van Pamel
Tested by: Geert Van Pamel
patches:
  asterisk-12.0.0-chan_sip-RFC3966_patch.txt uploaded by Geert Van Pamel (License 6140)
  asterisk-12.0.0-reqresp_parser-RFC3966_patch.txt uploaded by Geert Van Pamel (License 6140)

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@412292 65c4cc65-6c06-0410-ace0-fbb531ad65f3

CHANGES
channels/chan_sip.c
channels/sip/reqresp_parser.c

diff --git a/CHANGES b/CHANGES
index f5f6524..8c3a253 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -86,6 +86,17 @@ MixMonitor
  * A new function, MIXMONITOR, has been added to allow access to individual
    instances of MixMonitor on a channel.
 
+
+Channel Drivers
+-------------------------
+
+chan_sip
+-------------------------
+ * TEL URI support for inbound INVITE requests has been added. chan_sip will
+   now handle TEL schemes in the Request and From URIs. The phone-context in
+   the Request URI will be stored in the TELPHONECONTEXT channel variable on
+   the inbound channel.
+
 Debugging
 -------------------------
  * Core Show Locks output now includes Thread/LWP ID if the platform
index 68d07a6..24a9370 100644 (file)
@@ -17691,7 +17691,7 @@ static enum sip_get_dest_result get_destination(struct sip_pvt *p, struct sip_re
 
        uri = ast_strdupa(get_in_brackets(tmp));
 
-       if (parse_uri_legacy_check(uri, "sip:,sips:", &uri, &unused_password, &domain, NULL)) {
+       if (parse_uri_legacy_check(uri, "sip:,sips:,tel:", &uri, &unused_password, &domain, NULL)) {
                ast_log(LOG_WARNING, "Not a SIP header (%s)?\n", uri);
                return SIP_GET_DEST_INVALID_URI;
        }
@@ -17719,7 +17719,7 @@ static enum sip_get_dest_result get_destination(struct sip_pvt *p, struct sip_re
        ast_copy_string(tmpf, sip_get_header(req, "From"), sizeof(tmpf));
        if (!ast_strlen_zero(tmpf)) {
                from = get_in_brackets(tmpf);
-               if (parse_uri_legacy_check(from, "sip:,sips:", &from, NULL, &domain, NULL)) {
+               if (parse_uri_legacy_check(from, "sip:,sips:,tel:", &from, NULL, &domain, NULL)) {
                        ast_log(LOG_WARNING, "Not a SIP header (%s)?\n", from);
                        return SIP_GET_DEST_INVALID_URI;
                }
@@ -18607,10 +18607,13 @@ static enum check_auth_result check_user_full(struct sip_pvt *p, struct sip_requ
 
        if (ast_strlen_zero(p->exten)) {
                char *t = uri2;
-               if (!strncasecmp(t, "sip:", 4))
-                       t+= 4;
-               else if (!strncasecmp(t, "sips:", 5))
+               if (!strncasecmp(t, "sip:", 4)) {
+                       t += 4;
+               } else if (!strncasecmp(t, "sips:", 5)) {
                        t += 5;
+               } else if (!strncasecmp(t, "tel:", 4)) {        /* TEL URI INVITE */
+                       t += 4;
+               }
                ast_string_field_set(p, exten, t);
                t = strchr(p->exten, '@');
                if (t)
@@ -18625,7 +18628,7 @@ static enum check_auth_result check_user_full(struct sip_pvt *p, struct sip_requ
        /* save the URI part of the From header */
        ast_string_field_set(p, from, of);
 
-       if (parse_uri_legacy_check(of, "sip:,sips:", &name, &unused_password, &domain, NULL)) {
+       if (parse_uri_legacy_check(of, "sip:,sips:,tel:", &name, &unused_password, &domain, NULL)) {
                ast_log(LOG_NOTICE, "From address missing 'sip:', using it anyway\n");
        }
 
index 7d44457..5c2f2eb 100644 (file)
@@ -45,6 +45,7 @@ int parse_uri_full(char *uri, const char *scheme, char **user, char **pass,
        char *endparams = NULL;
        char *c = NULL;
        int error = 0;
+       int teluri_scheme = 0;
 
        /*
         * Initialize requested strings - some functions don't care if parse_uri fails
@@ -79,6 +80,7 @@ int parse_uri_full(char *uri, const char *scheme, char **user, char **pass,
                for (; !ast_strlen_zero(cur); cur = strsep(&scheme2, ",")) {
                        l = strlen(cur);
                        if (!strncasecmp(uri, cur, l)) {
+                               teluri_scheme = !strncasecmp(uri, "tel:", 4);   /* TEL URI */
                                uri += l;
                                break;
                        }
@@ -93,6 +95,42 @@ int parse_uri_full(char *uri, const char *scheme, char **user, char **pass,
                /* if we don't want to split around hostport, keep everything as a
                 * userinfo - cos thats how old parse_uri operated*/
                userinfo = uri;
+       } else if (teluri_scheme) {
+               /*
+                * tel: TEL URI INVITE RFC 3966 patch
+                * See http://www.ietf.org/rfc/rfc3966.txt
+                *
+                * Once the full RFC 3966 parsing is implemented,
+                * the ext= or isub= parameters would be extracted from userinfo.
+                * When this kind of subaddressing would be implemented, the userinfo must be further parsed.
+                * Those parameters would be used for ISDN or PSTN local extensions.
+                *
+                * Current restrictions:
+                * We currently consider the ";isub=" or the ";ext=" as part of the userinfo (unparsed).
+                */
+
+               if ((c = strstr(uri, ";phone-context="))) {
+                       /*
+                        * Local number with context or domain.
+                        * ext= or isub= TEL URI parameters should be upfront.
+                        * All other parameters should come after the ";phone-context=" parameter.
+                        * If other parameters would occur before ";phone-context=" they will be ignored.
+                        */
+
+                        *c = '\0';
+                        userinfo = uri;
+                        uri = c + 15;
+                       *hostport = uri;
+                } else if ('+' == uri[0]) {
+                       /* Global number without context or domain; possibly followed by RFC 3966 and optional other parameters. */
+
+                        userinfo = uri;
+                       *hostport = uri;
+               } else {
+                       ast_debug(1, "No RFC 3966 global number or context found in '%s'; returning local number anyway\n", uri);
+                        userinfo = uri;                /* Return local number anyway */
+                       error = -1;
+               }
        } else {
                char *dom = "";
                if ((c = strchr(uri, '@'))) {
@@ -375,6 +413,51 @@ AST_TEST_DEFINE(sip_parse_uri_full_test)
                .params.user = ""
        };
 
+       /* RFC 3966 TEL URI INVITE */
+       struct testdata td11 = {
+               .desc = "tel local number",
+               .uri = "tel:0987654321;phone-context=+32987654321",
+               .user = "0987654321",
+               .pass = "",
+               .hostport = "+32987654321",
+               .headers = "",
+               .residue = "",
+               .params.transport = "",
+               .params.lr = 0,
+               .params.user = ""
+       };
+
+       struct testdata td12 = {
+               .desc = "tel global number",
+               .uri = "tel:+32987654321",
+               .user = "+32987654321",
+               .pass = "",
+               .hostport = "+32987654321",
+               .headers = "",
+               .residue = "",
+               .params.transport = "",
+               .params.lr = 0,
+               .params.user = ""
+       };
+
+       /*
+        * Once the full RFC 3966 parsing is implemented,
+        * only the ext= or isub= parameters would be extracted from .user
+        * Then the ;param=discard would be ignored,
+        * and the .user would only contain "0987654321"
+        */
+       struct testdata td13 = {
+               .desc = "tel local number",
+               .uri = "tel:0987654321;ext=1234;param=discard;phone-context=+32987654321;transport=udp;param2=discard2?header=blah&header2=blah2;param3=residue",
+               .user = "0987654321;ext=1234;param=discard",
+               .pass = "",
+               .hostport = "+32987654321",
+               .headers = "header=blah&header2=blah2",
+               .residue = "param3=residue",
+               .params.transport = "udp",
+               .params.lr = 0,
+               .params.user = ""
+       };
 
        AST_LIST_HEAD_SET_NOLOCK(&testdatalist, &td1);
        AST_LIST_INSERT_TAIL(&testdatalist, &td2, list);
@@ -386,7 +469,9 @@ AST_TEST_DEFINE(sip_parse_uri_full_test)
        AST_LIST_INSERT_TAIL(&testdatalist, &td8, list);
        AST_LIST_INSERT_TAIL(&testdatalist, &td9, list);
        AST_LIST_INSERT_TAIL(&testdatalist, &td10, list);
-
+       AST_LIST_INSERT_TAIL(&testdatalist, &td11, list);
+       AST_LIST_INSERT_TAIL(&testdatalist, &td12, list);
+       AST_LIST_INSERT_TAIL(&testdatalist, &td13, list);
 
        switch (cmd) {
        case TEST_INIT:
@@ -407,7 +492,7 @@ AST_TEST_DEFINE(sip_parse_uri_full_test)
                params.lr = 0;
 
                ast_copy_string(uri,testdataptr->uri,sizeof(uri));
-               if (parse_uri_full(uri, "sip:,sips:", &user,
+               if (parse_uri_full(uri, "sip:,sips:,tel:", &user,
                                   &pass, &hostport,
                                   &params,
                                   &headers,
@@ -460,6 +545,7 @@ AST_TEST_DEFINE(sip_parse_uri_test)
        char uri9[] = "sip:host:port;transport=tcp?headers=%40%40testblah&headers2=blah%20blah";
        char uri10[] = "host:port;transport=tcp?headers=%40%40testblah&headers2=blah%20blah";
        char uri11[] = "host";
+       char uri12[] = "tel:911";       /* TEL URI Local number without context or global number */
 
        switch (cmd) {
        case TEST_INIT:
@@ -588,6 +674,17 @@ AST_TEST_DEFINE(sip_parse_uri_test)
                res = AST_TEST_FAIL;
        }
 
+       /* Test 12, simple URI */
+       name = pass = hostport = transport = NULL;
+       if (!parse_uri(uri12, "sip:,sips:,tel:", &name, &pass, &hostport, &transport) ||
+                       strcmp(name, "911")      ||     /* We return local number anyway */
+                       !ast_strlen_zero(pass)      ||
+                       !ast_strlen_zero(hostport)      ||      /* No global number nor context */
+                       !ast_strlen_zero(transport)) {
+               ast_test_status_update(test, "Test 12: TEL URI INVITE failed.\n");
+               res = AST_TEST_FAIL;
+       }
+
        return res;
 }