[Asterisk-code-review] chan_sip: Add custom SIP tag capabilities (asterisk[master])

N A asteriskteam at digium.com
Thu Oct 7 11:20:15 CDT 2021


N A has uploaded this change for review. ( https://gerrit.asterisk.org/c/asterisk/+/16569 )


Change subject: chan_sip: Add custom SIP tag capabilities
......................................................................

chan_sip: Add custom SIP tag capabilities

Some SIP environments require the addition of custom
SIP tags into certain headers (e.g. From) and also
require placement either before or after the "tag"
tag. This is especially common with SS7 trunking
over SIP.

This adds the ability to add custom tags to headers,
read tags from headers, and define the placement
of custom-injected tags within the From header, since
many carriers require that they be inside the closing
angle bracket.

Also adds built-in support for the outgoing OLI tag,
disabled by default.

ASTERISK-29681 #close

Change-Id: I1e99a220295c03275827050147622a6c1f5a4be9
---
M channels/chan_sip.c
M channels/sip/include/sip.h
M configs/samples/sip.conf.sample
3 files changed, 286 insertions(+), 2 deletions(-)



  git pull ssh://gerrit.asterisk.org:29418/asterisk refs/changes/69/16569/1

diff --git a/channels/chan_sip.c b/channels/chan_sip.c
index 23b8fa6..97f367a 100644
--- a/channels/chan_sip.c
+++ b/channels/chan_sip.c
@@ -311,6 +311,21 @@
 			<para>Changes the dtmfmode for a SIP call.</para>
 		</description>
 	</application>
+	<application name="SIPAddTag" language="en_US">
+		<synopsis>
+			Add a SIP tag to the From header in the outbound call.
+		</synopsis>
+		<syntax argsep=":">
+			<parameter name="Tag" required="true" />
+			<parameter name="Content" required="true" />
+		</syntax>
+		<description>
+			<para>Adds a tag to a SIP call placed with DIAL.</para>
+			<para>Use this with care. Adding the wrong tags may
+			jeopardize the SIP dialog.</para>
+			<para>Always returns <literal>0</literal>.</para>
+		</description>
+	</application>
 	<application name="SIPAddHeader" language="en_US">
 		<synopsis>
 			Add a SIP header to the outbound call.
@@ -367,6 +382,42 @@
 			application is only available if TEST_FRAMEWORK is defined.</para>
 		</description>
 	</application>
+	<function name="SIP_TAG" language="en_US">
+		<synopsis>
+			Gets the specified SIP tag from the specified SIP header from an incoming INVITE message.
+		</synopsis>
+		<syntax>
+			<parameter name="tag" required="true" />
+			<parameter name="name" required="true" />
+			<parameter name="number">
+				<para>If not specified, defaults to <literal>1</literal>.</para>
+			</parameter>
+		</syntax>
+		<description>
+			<para>This function returns the value of a SIP tag in a specified header.</para>
+			<para>Since there are several headers (such as Via) which can occur multiple
+			times, SIP_HEADER takes an optional second argument to specify which header with
+			that name to retrieve. Headers start at offset <literal>1</literal>.</para>
+			<para>This function does not access headers from the REFER message if the call
+			was transferred. To obtain the REFER headers, set the dialplan variable
+			<variable>GET_TRANSFERRER_DATA</variable> to the prefix of the headers of the
+			REFER message that you need to access; for example, <literal>X-</literal> to
+			get all headers starting with <literal>X-</literal>. The variable must be set
+			before a call to the application that starts the channel that may eventually
+			transfer back into the dialplan, and must be inherited by that channel, so prefix
+			it with the <literal>_</literal> or <literal>__</literal> when setting (or
+			set it in the pre-dial handler executed on the new channel). To get all headers
+			of the REFER message, set the value to <literal>*</literal>. Headers
+			are returned in the form of a dialplan hash TRANSFER_DATA, and can be accessed
+			with the functions <variable>HASHKEYS(TRANSFER_DATA)</variable> and, e. g.,
+			<variable>HASH(TRANSFER_DATA,X-That-Special-Header)</variable>.</para>
+			<para>Please also note that contents of the SDP (an attachment to the
+			SIP request) can't be accessed with this function.</para>
+		</description>
+		<see-also>
+			<ref type="function">SIP_HEADERS</ref>
+		</see-also>
+	</function>
 	<function name="SIP_HEADER" language="en_US">
 		<synopsis>
 			Gets the specified SIP header from an incoming INVITE message.
@@ -1384,6 +1435,7 @@
 static char *sip_cli_notify(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
 static char *sip_set_history(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
 static int sip_dtmfmode(struct ast_channel *chan, const char *data);
+static int sip_addtag(struct ast_channel *chan, const char *data);
 static int sip_addheader(struct ast_channel *chan, const char *data);
 static int sip_do_reload(enum channelreloadreason reason);
 static char *sip_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
@@ -6484,6 +6536,9 @@
 		} else if (!p->options->addsipheaders && !strncmp(ast_var_name(current), "SIPADDHEADER", strlen("SIPADDHEADER"))) {
 			/* Check whether there is a variable with a name starting with SIPADDHEADER */
 			p->options->addsipheaders = 1;
+		} else if (!p->options->addsiptags && !strncmp(ast_var_name(current), "SIPADDTAG", strlen("SIPADDTAG"))) {
+			/* Check whether there is a variable with a name starting with SIPADDTAG */
+			p->options->addsiptags = 1;
 		} else if (!strcmp(ast_var_name(current), "SIPFROMDOMAIN")) {
 			ast_string_field_set(p, fromdomain, ast_var_value(current));
 		} else if (!strcmp(ast_var_name(current), "SIPTRANSFER")) {
@@ -14518,9 +14573,85 @@
 	ourport = (p->fromdomainport && (p->fromdomainport != STANDARD_SIP_PORT)) ? p->fromdomainport : ast_sockaddr_port(&p->ourip);
 
 	if (!sip_standard_port(p->socket.type, ourport)) {
-		ret = ast_str_set(&from, 0, "<sip:%s@%s:%d>;tag=%s", tmp_l, d, ourport, p->tag);
+		ret = ast_str_set(&from, 0, "<sip:%s@%s:%d", tmp_l, d, ourport);
 	} else {
-		ret = ast_str_set(&from, 0, "<sip:%s@%s>;tag=%s", tmp_l, d, p->tag);
+		ret = ast_str_set(&from, 0, "<sip:%s@%s", tmp_l, d);
+	}
+
+	if (ret != AST_DYNSTR_BUILD_FAILED) {
+		/* The normal way is <sip:NUMBER at IP>;tag=SOMETHING;isup-oli=0;noa=1 (draft-haluska-dispatch-isup-oli-01)
+		 * Some carriers/Sonus/Genband/SS7 use <sip:NUMBER at IP;isup-oli=OLI;noa=1>;tag=SOMETHING instead (SIP-I/T)
+		 */
+		if (!sip_cfg.from_tags_inside) {
+			/* typically, tag should come before custom tags, but if we're doing it carrier-style, it still comes last */
+			ret = ast_str_append(&from, 0, ">;tag=%s", p->tag);
+		}
+
+		/* Add custom tags now */
+		if (ret != AST_DYNSTR_BUILD_FAILED && p->owner && sip_cfg.send_oli) {
+			ast_debug(3, "Detected ANI2/OLI %d on channel %s\n", ast_channel_caller(p->owner)->ani2, ast_channel_name(p->owner));
+			ret = ast_str_append(&from, 0, ";isup-oli=%d", ast_channel_caller(p->owner)->ani2);
+		}
+		if (ret != AST_DYNSTR_BUILD_FAILED && p->owner && p->options && p->options->addsiptags) {
+			struct ast_channel *chan = p->owner; /* The owner channel */
+			struct varshead *headp;
+
+			ast_channel_lock(chan);
+
+			headp = ast_channel_varshead(chan);
+
+			if (!headp) {
+				ast_log(LOG_WARNING, "No Headp for the channel...ooops!\n");
+			} else {
+				const struct ast_var_t *current;
+				AST_LIST_TRAVERSE(headp, current, entries) {
+					/* SIPADDTAG: Add SIP tag to From header in outgoing call */
+					if (!strncmp(ast_var_name(current), "SIPADDTAG", strlen("SIPADDTAG"))) {
+						char *content, *end;
+						const char *tag = ast_var_value(current);
+						char *headdup = ast_malloc(sizeof(tag));
+						if (!headdup) {
+							ast_log(LOG_ERROR, "Couldn't allocate tag\n");
+							break;
+						}
+						ast_copy_string(headdup, tag, strlen(tag) + 1);
+
+						/* Strip off the starting " (if it's there) */
+						if (*headdup == '"') {
+							headdup++;
+						}
+						if ((content = strchr(headdup, ':'))) {
+							*content = '\0';
+							content++;
+							content = ast_skip_blanks(content); /* Skip white space */
+							/* Strip the ending " (if it's there) */
+							end = content + strlen(content) -1;
+							if (*end == '"') {
+								*end = '\0';
+							}
+							if (sipdebug) {
+								ast_debug(3, "Adding SIP tag \"%s\" to %s header with content: %s\n", headdup, "From", content);
+							}
+							ret = ast_str_append(&from, 0, ";%s=%s", headdup, content);
+							ast_free(headdup);
+							if (ret == AST_DYNSTR_BUILD_FAILED) {
+								ast_log(LOG_WARNING, "Appending failed, aborting custom SIP tags\n");
+								break;
+							}
+						} else {
+							ast_debug(1, "Nothing to do for tag %s\n", headdup);
+							ast_free(headdup);
+						}
+					}
+				}
+			}
+
+			ast_channel_unlock(chan);
+		}
+		/* Now, finish it off, if needed */
+		if (ret != AST_DYNSTR_BUILD_FAILED && sip_cfg.from_tags_inside) {
+			ret = ast_str_append(&from, 0, ">;tag=%s", p->tag);
+		}
 	}
 	if (ret == AST_DYNSTR_BUILD_FAILED) {
 		/* We don't have an escape path from here... */
@@ -21936,6 +22067,8 @@
 	ast_cli(a->fd, "  Send RPID:              %s\n", AST_CLI_YESNO(ast_test_flag(&global_flags[0], SIP_SENDRPID)));
 	ast_cli(a->fd, "  Legacy userfield parse: %s\n", AST_CLI_YESNO(sip_cfg.legacy_useroption_parsing));
 	ast_cli(a->fd, "  Send Diversion:         %s\n", AST_CLI_YESNO(sip_cfg.send_diversion));
+	ast_cli(a->fd, "  Send OLI:               %s\n", AST_CLI_YESNO(sip_cfg.send_oli));
+	ast_cli(a->fd, "  From Tags Inside:       %s\n", AST_CLI_YESNO(sip_cfg.from_tags_inside));
 	ast_cli(a->fd, "  Caller ID:              %s\n", default_callerid);
 	if ((default_fromdomainport) && (default_fromdomainport != STANDARD_SIP_PORT)) {
 		ast_cli(a->fd, "  From: Domain:           %s:%d\n", default_fromdomain, default_fromdomainport);
@@ -23246,6 +23379,93 @@
 	return 0;
 }
 
+/*! \brief Read SIP tags from header (dialplan function) */
+static int func_tag_read(struct ast_channel *chan, const char *function, char *data, char *buf, size_t len)
+{
+	struct sip_pvt *p;
+	const char *content = NULL;
+	char *mutable_data = ast_strdupa(data);
+	AST_DECLARE_APP_ARGS(args,
+		AST_APP_ARG(tag);
+		AST_APP_ARG(header);
+		AST_APP_ARG(number);
+	);
+	int i, number, start = 0;
+	char *tagname = NULL;
+	char *tagfull = NULL;
+	char *tagvalue = NULL;
+
+	if (!chan) {
+		ast_log(LOG_WARNING, "No channel was provided to %s function.\n", function);
+		return -1;
+	}
+
+	if (ast_strlen_zero(data)) {
+		ast_log(LOG_WARNING, "This function requires a header name.\n");
+		return -1;
+	}
+
+	ast_channel_lock(chan);
+	if (!IS_SIP_TECH(ast_channel_tech(chan))) {
+		ast_log(LOG_WARNING, "This function can only be used on SIP channels.\n");
+		ast_channel_unlock(chan);
+		return -1;
+	}
+
+	AST_STANDARD_APP_ARGS(args, mutable_data);
+	if (!args.number) {
+		number = 1;
+	} else {
+		sscanf(args.number, "%30d", &number);
+		if (number < 1)
+			number = 1;
+	}
+
+	p = ast_channel_tech_pvt(chan);
+
+	/* If there is no private structure, this channel is no longer alive */
+	if (!p) {
+		ast_channel_unlock(chan);
+		return -1;
+	}
+
+	for (i = 0; i < number; i++) {
+		content = __get_header(&p->initreq, args.header, &start);
+	}
+
+	if (ast_strlen_zero(content)) {
+		ast_channel_unlock(chan);
+		return -1;
+	}
+
+	tagname = ast_strdupa(args.tag);
+	tagfull = ast_strdupa(content);
+
+	/* discard the first match, because it's not a tag. */
+	tagvalue = strsep(&tagfull, ";");
+	while (tagvalue && (tagvalue = strsep(&tagfull, ";"))) {
+		ast_debug(3, "%s: Looking for %s, found: %s\n", function, tagname, tagvalue);
+		if (!strncmp(tagname, tagvalue, strlen(tagname))) {
+			/* we found our tag */
+			const char *result = strchr(tagvalue, '=') + 1;
+			if (!result) {
+				continue;
+			}
+			ast_copy_string(buf, result, len);
+			ast_channel_unlock(chan);
+			return 0;
+		}
+	}
+
+	ast_channel_unlock(chan);
+	return 0;
+}
+
+static struct ast_custom_function sip_tag_function = {
+	.name = "SIP_TAG",
+	.read = func_tag_read,
+};
+
 /*! \brief Read SIP header (dialplan function) */
 static int func_header_read(struct ast_channel *chan, const char *function, char *data, char *buf, size_t len)
 {
@@ -32669,6 +32889,8 @@
 	sip_cfg.regextenonqualify = DEFAULT_REGEXTENONQUALIFY;
 	sip_cfg.legacy_useroption_parsing = DEFAULT_LEGACY_USEROPTION_PARSING;
 	sip_cfg.send_diversion = DEFAULT_SEND_DIVERSION;
+	sip_cfg.send_oli = DEFAULT_SEND_OLI;
+	sip_cfg.from_tags_inside = DEFAULT_FROM_TAGS_INSIDE;
 	sip_cfg.notifyringing = DEFAULT_NOTIFYRINGING;
 	sip_cfg.notifycid = DEFAULT_NOTIFYCID;
 	sip_cfg.notifyhold = FALSE;		/*!< Keep track of hold status for a peer */
@@ -32996,6 +33218,10 @@
 			sip_cfg.legacy_useroption_parsing = ast_true(v->value);
 		} else if (!strcasecmp(v->name, "send_diversion")) {
 			sip_cfg.send_diversion = ast_true(v->value);
+		} else if (!strcasecmp(v->name, "send_oli")) {
+			sip_cfg.send_oli = ast_true(v->value);
+		} else if (!strcasecmp(v->name, "from_tags_inside")) {
+			sip_cfg.from_tags_inside = ast_true(v->value);
 		} else if (!strcasecmp(v->name, "callerid")) {
 			ast_copy_string(default_callerid, v->value, sizeof(default_callerid));
 		} else if (!strcasecmp(v->name, "mwi_from")) {
@@ -34018,6 +34244,7 @@
 };
 
 static char *app_dtmfmode = "SIPDtmfMode";
+static char *app_sipaddtag = "SIPAddTag";
 static char *app_sipaddheader = "SIPAddHeader";
 static char *app_sipremoveheader = "SIPRemoveHeader";
 #ifdef TEST_FRAMEWORK
@@ -34078,6 +34305,46 @@
 	return 0;
 }
 
+/*! \brief Add a SIP tag to the From header in an outbound INVITE */
+static int sip_addtag(struct ast_channel *chan, const char *data)
+{
+	int no = 0;
+	int ok = FALSE;
+	char varbuf[30];
+	const char *inbuf = data;
+	char *subbuf;
+
+	if (ast_strlen_zero(inbuf)) {
+		ast_log(LOG_WARNING, "This application requires the argument: Tag\n");
+		return 0;
+	}
+	ast_channel_lock(chan);
+
+	/* Check for headers */
+	while (!ok && no <= 99) {
+		no++;
+		snprintf(varbuf, sizeof(varbuf), "__SIPADDTAG%.2d", no);
+
+		/* Compare without the leading underscores */
+		if ((pbx_builtin_getvar_helper(chan, (const char *) varbuf + 2) == (const char *) NULL)) {
+			ok = TRUE;
+		}
+	}
+	if (ok) {
+		size_t len = strlen(inbuf);
+		subbuf = ast_alloca(len + 1);
+		ast_get_encoded_str(inbuf, subbuf, len + 1);
+		pbx_builtin_setvar_helper(chan, varbuf, subbuf);
+		if (sipdebug) {
+			ast_debug(1, "SIP Tag added \"%s\" as %s\n", inbuf, varbuf);
+		}
+	} else {
+		ast_log(LOG_WARNING, "Too many SIP tags added, max 150\n");
+	}
+	ast_channel_unlock(chan);
+	return 0;
+}
+
 /*! \brief Add a SIP header to an outbound INVITE */
 static int sip_addheader(struct ast_channel *chan, const char *data)
 {
@@ -35618,6 +35885,7 @@
 
 	/* Register dialplan applications */
 	ast_register_application_xml(app_dtmfmode, sip_dtmfmode);
+	ast_register_application_xml(app_sipaddtag, sip_addtag);
 	ast_register_application_xml(app_sipaddheader, sip_addheader);
 	ast_register_application_xml(app_sipremoveheader, sip_removeheader);
 #ifdef TEST_FRAMEWORK
@@ -35625,6 +35893,7 @@
 #endif
 
 	/* Register dialplan functions */
+	ast_custom_function_register(&sip_tag_function);
 	ast_custom_function_register(&sip_header_function);
 	ast_custom_function_register(&sip_headers_function);
 	ast_custom_function_register(&sippeer_function);
@@ -35737,10 +36006,12 @@
 	ast_custom_function_unregister(&sippeer_function);
 	ast_custom_function_unregister(&sip_headers_function);
 	ast_custom_function_unregister(&sip_header_function);
+	ast_custom_function_unregister(&sip_tag_function);
 	ast_custom_function_unregister(&checksipdomain_function);
 
 	/* Unregister dial plan applications */
 	ast_unregister_application(app_dtmfmode);
+	ast_unregister_application(app_sipaddtag);
 	ast_unregister_application(app_sipaddheader);
 	ast_unregister_application(app_sipremoveheader);
 #ifdef TEST_FRAMEWORK
diff --git a/channels/sip/include/sip.h b/channels/sip/include/sip.h
index 18db352..a9116e2 100644
--- a/channels/sip/include/sip.h
+++ b/channels/sip/include/sip.h
@@ -226,6 +226,8 @@
 #define DEFAULT_REGEXTENONQUALIFY FALSE
 #define DEFAULT_LEGACY_USEROPTION_PARSING FALSE
 #define DEFAULT_SEND_DIVERSION TRUE
+#define DEFAULT_SEND_OLI FALSE
+#define DEFAULT_FROM_TAGS_INSIDE FALSE
 #define DEFAULT_T1MIN             100   /*!< 100 MS for minimal roundtrip time */
 #define DEFAULT_MAX_CALL_BITRATE (384)  /*!< Max bitrate for video */
 #ifndef DEFAULT_USERAGENT
@@ -759,6 +761,8 @@
 	int regextenonqualify;      /*!< Whether to add/remove regexten when qualifying peers */
 	int legacy_useroption_parsing; /*!< Whether to strip useroptions in URI via semicolons */
 	int send_diversion;	        /*!< Whether to Send SIP Diversion headers */
+	int send_oli;               /*!< Whether to Send ANI2/OLI */
+	int from_tags_inside;             /*!< Whether to place OLI inside the arrows or leave outside */
 	int matchexternaddrlocally;   /*!< Match externaddr/externhost setting against localnet setting */
 	char regcontext[AST_MAX_CONTEXT];  /*!< Context for auto-extensions */
 	char messagecontext[AST_MAX_CONTEXT];  /*!< Default context for out of dialog msgs. */
@@ -853,6 +857,7 @@
 /*! \brief Parameters to the transmit_invite function */
 struct sip_invite_param {
 	int addsipheaders;          /*!< Add extra SIP headers */
+	int addsiptags;             /*!< Add extra SIP tags to From header */
 	const char *uri_options;    /*!< URI options to add to the URI */
 	const char *vxml_url;       /*!< VXML url for Cisco phones */
 	char *auth;                 /*!< Authentication */
diff --git a/configs/samples/sip.conf.sample b/configs/samples/sip.conf.sample
index 4947754..0e0c277 100644
--- a/configs/samples/sip.conf.sample
+++ b/configs/samples/sip.conf.sample
@@ -542,6 +542,14 @@
                                                     ; is disabled, Asterisk won't send Diversion headers unless
                                                     ; they are added manually.
 
+;send_oli=no                    ; Default "no"      ; Asterisk can pass ANI2 or Originating Line Information (OLI)
+                                                    ; if desired. This will appear as isup-oli in the From header.
+
+;from_tags_inside=no            ; Default "no"      ; Some carriers expect isup-oli and other carrier tags to be 
+                                                    ; inside the arrow brackets rather than outside. This option 
+													; allows for compatability with these carriers. Custom SIP tags
+													; in the from header may be set using the SipAddTag application.
+
 ; The shrinkcallerid function removes '(', ' ', ')', non-trailing '.', and '-' not
 ; in square brackets.  For example, the caller id value 555.5555 becomes 5555555
 ; when this option is enabled.  Disabling this option results in no modification

-- 
To view, visit https://gerrit.asterisk.org/c/asterisk/+/16569
To unsubscribe, or for help writing mail filters, visit https://gerrit.asterisk.org/settings

Gerrit-Project: asterisk
Gerrit-Branch: master
Gerrit-Change-Id: I1e99a220295c03275827050147622a6c1f5a4be9
Gerrit-Change-Number: 16569
Gerrit-PatchSet: 1
Gerrit-Owner: N A <mail at interlinked.x10host.com>
Gerrit-MessageType: newchange
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.digium.com/pipermail/asterisk-code-review/attachments/20211007/93e1766d/attachment-0001.html>


More information about the asterisk-code-review mailing list