From: Matthew Jordan Date: Sat, 12 Apr 2014 02:27:43 +0000 (+0000) Subject: chan_sip: Support RFC-3966 TEL URIs in inbound INVITE requests X-Git-Tag: 13.0.0-beta1~354 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=eed03fc01ac8c6ef424eff65c718f7802a4cb1ef;p=thirdparty%2Fasterisk.git chan_sip: Support RFC-3966 TEL URIs in inbound INVITE requests 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 --- diff --git a/CHANGES b/CHANGES index f5f6524485..8c3a253471 100644 --- 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 diff --git a/channels/chan_sip.c b/channels/chan_sip.c index 68d07a63cb..24a9370a71 100644 --- a/channels/chan_sip.c +++ b/channels/chan_sip.c @@ -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"); } diff --git a/channels/sip/reqresp_parser.c b/channels/sip/reqresp_parser.c index 7d4445784a..5c2f2eb1f3 100644 --- a/channels/sip/reqresp_parser.c +++ b/channels/sip/reqresp_parser.c @@ -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, ¶ms, &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; }