<html>
<head>
<base href="https://wiki.asterisk.org/wiki">
<link rel="stylesheet" href="/wiki/s/en/2176/25/9/_/styles/combined.css?spaceKey=AST&forWysiwyg=true" type="text/css">
</head>
<body style="background: white;" bgcolor="white" class="email-body">
<div id="pageContent">
<div id="notificationFormat">
<div class="wiki-content">
<div class="email">
<h2><a href="https://wiki.asterisk.org/wiki/display/AST/Writing+a+SIP+Session+Supplement">Writing a SIP Session Supplement</a></h2>
<h4>Page <b>added</b> by <a href="https://wiki.asterisk.org/wiki/display/~mmichelson">Mark Michelson</a>
</h4>
<br/>
<div class="notificationGreySide">
<p>If you are unfamiliar with what a SIP session supplement is, please see <a href="/wiki/display/AST/How+to+extend+SIP+functionality+in+Asterisk#HowtoextendSIPfunctionalityinAsterisk-SIPsessionsupplement">this</a> for an explanation.</p>
<div>
<ul>
<li><a href='#WritingaSIPSessionSupplement-Problemoverview'>Problem overview</a></li>
<li><a href='#WritingaSIPSessionSupplement-Methodofsolution'>Method of solution</a></li>
<li><a href='#WritingaSIPSessionSupplement-Creatingthesupplement'>Creating the supplement</a></li>
<ul>
<li><a href='#WritingaSIPSessionSupplement-Basemodulecode'>Base module code</a></li>
<ul>
<li><a href='#WritingaSIPSessionSupplement-Initialdirectives'>Initial directives</a></li>
<li><a href='#WritingaSIPSessionSupplement-Moduleboilerplate'>Module boilerplate</a></li>
<li><a href='#WritingaSIPSessionSupplement-Thesessionsupplement'>The session supplement</a></li>
</ul>
<li><a href='#WritingaSIPSessionSupplement-Fillinginthesupplementcallback'>Filling in the supplement callback</a></li>
<ul>
<li><a href='#WritingaSIPSessionSupplement-VariableDeclarations'>Variable Declarations</a></li>
<li><a href='#WritingaSIPSessionSupplement-Requireheaderhandling'>Require header handling</a></li>
<li><a href='#WritingaSIPSessionSupplement-AnswerModeheaderhandling'>Answer-Mode header handling</a></li>
</ul>
<li><a href='#WritingaSIPSessionSupplement-Adjustments'>Adjustments</a></li>
<ul>
<li><a href='#WritingaSIPSessionSupplement-Usingthe%7B%7BSIPAUTOANSWER%7D%7Dchannelvariable'>Using the <tt>SIP_AUTO_ANSWER</tt> channel variable</a></li>
<li><a href='#WritingaSIPSessionSupplement-Afinaladjustment'>A final adjustment</a></li>
</ul>
</ul>
<li><a href='#WritingaSIPSessionSupplement-FinishedSupplement'>Finished Supplement</a></li>
</ul></div>
<h1><a name="WritingaSIPSessionSupplement-Problemoverview"></a>Problem overview</h1>
<p>For our tutorial, we will be implementing (a simple version of) <a href="http://tools.ietf.org/html/rfc5373" class="external-link" rel="nofollow">RFC 5373</a>. This RFC defines a way to indicate to a user agent that it should automatically respond to the incoming INVITE with a 200 OK response. For this tutorial, we will be implementing the UAC version of this so that Asterisk can request that outgoing calls are automatically answered by the recipient.</p>
<h1><a name="WritingaSIPSessionSupplement-Methodofsolution"></a>Method of solution</h1>
<p>In order to solve this problem, we will write a simple session supplement that is capable of adding information to outgoing INVITE requests to add appropriate headers to request that the recipient of the INVITE automatically answers the call. The headers will be added if there exists a channel variable called <tt>SIP_AUTO_ANSWER</tt> on the outbound channel.</p>
<h1><a name="WritingaSIPSessionSupplement-Creatingthesupplement"></a>Creating the supplement</h1>
<p>Let's start by creating a new file called <tt>res_sip_auto_answer.c</tt> and place it in the <tt>res</tt> directory of the Asterisk source. Placing the file there will automatically result in a loadable module being built when a <tt>make</tt> command is run.</p>
<h2><a name="WritingaSIPSessionSupplement-Basemodulecode"></a>Base module code</h2>
<p>Let's consider what we need to do for this feature to work. All we have to do is conditionally add headers to outbound INVITE requests. We don't need to do anything with inbound requests or responses and we don't need to do anything with outbound responses. Given that, let's go ahead and define the session supplement we're going to use:</p>
<div class="code panel" style="border-width: 1px;"><div class="codeHeader panelHeader" style="border-bottom-width: 1px;"><b>res_sip_auto_answer.c</b></div><div class="codeContent panelContent">
<pre class="theme: Confluence; brush: c; gutter: false">#include "asterisk.h"
#include <pjsip.h>
#include <pjlib.h>
#include "asterisk/res_sip_session.h"
#include "asterisk/module.h"
/*** MODULEINFO
<depend>pjproject</depend>
<depend>res_sip_session</depend>
***/
static void auto_answer_outgoing_request(struct ast_sip_session *session, pjsip_tx_data *tdata)
{
/* STUB */
}
static struct ast_sip_session_supplement auto_answer_supplement = {
.method = "INVITE",
.outgoing_request = auto_answer_outgoing_request,
};
static int load_module(void)
{
if (ast_sip_session_register_supplement(&auto_answer_supplement)) {
return AST_MODULE_LOAD_DECLINE;
}
return AST_MODULE_LOAD_SUCCESS;
}
static int unload_module(void)
{
ast_sip_session_unregister_supplement(&auto_answer_supplement));
return 0;
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "SIP Auto Answer Support",
.load = load_module,
.unload = unload_module,
.load_pri = AST_MODPRI_APP_DEPEND,
);</pre>
</div></div>
<h3><a name="WritingaSIPSessionSupplement-Initialdirectives"></a>Initial directives</h3>
<p>Let's go into deeper detail about what we have just written. Let's start at the top:</p>
<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="theme: Confluence; brush: c; gutter: false">#include "asterisk.h"
#include <pjsip.h>
#include <pjlib.h>
#include "asterisk/res_sip_session.h"
#include "asterisk/module.h"
/*** MODULEINFO
<depend>pjproject</depend>
<depend>res_sip_session</depend>
***/</pre>
</div></div>
<p>The <tt>#includes</tt> here grab the necessary headers we will need. All code in Asterisk starts by including <tt>asterisk.h</tt>. After that, we will need the <tt>pjsip.h</tt> and <tt>pjlib.h</tt> files in order to make use of PJSIP functions. We will use these later in the tutorial. The inclusion of <tt>asterisk/res_sip_session.h</tt> is what allows us to be able to create a session supplement. Finally, the inclusion of <tt>asterisk/module.h</tt> is necessary since our file is going to be a loadable module in Asterisk.</p>
<p>The <tt>MODULEINFO</tt> block is used by the menuselect system in Asterisk in order to allow for the module to only be selectable if the necessary dependencies are met. In our case, we need the PJPROJECT library to be present, and we need <tt>res_sip_session</tt> to be built as well, since it supplies the API for using session supplements.</p>
<h3><a name="WritingaSIPSessionSupplement-Moduleboilerplate"></a>Module boilerplate</h3>
<p>Next let's jump down to the bottom of the file:</p>
<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="theme: Confluence; brush: c; gutter: false">static int load_module(void)
{
if (ast_sip_session_register_supplement(&auto_answer_supplement)) {
return AST_MODULE_LOAD_DECLINE;
}
return AST_MODULE_LOAD_SUCCESS;
}
static int unload_module(void)
{
ast_sip_session_unregister_supplement(&auto_answer_supplement));
return 0;
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "SIP Auto Answer Support",
.load = load_module,
.unload = unload_module,
.load_pri = AST_MODPRI_APP_DEPEND,
);</pre>
</div></div>
<p>We will not go into a lot of detail about the module-specific code here since it is covered in a tutorial <a href="/wiki/display/AST/Modules" title="Modules">here</a>. However, notice that when the module loads, it registers the <tt>auto_answer_supplement</tt> with <tt>res_sip_session</tt> and when the module unloads, it unregisters the <tt>auto_answer_supplement</tt>.</p>
<h3><a name="WritingaSIPSessionSupplement-Thesessionsupplement"></a>The session supplement</h3>
<p>Now let's have a look at the important part of the code:</p>
<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="theme: Confluence; brush: c; gutter: false">static void auto_answer_outgoing_request(struct ast_sip_session *session, pjsip_tx_data *tdata)
{
/* STUB */
}
static struct ast_sip_session_supplement auto_answer_supplement = {
.method = "INVITE",
.outgoing_request = auto_answer_outgoing_request,
};</pre>
</div></div>
<p>Let's start with the bottom struct declaration. It defines a session supplement called <tt>auto_answer_supplement</tt>. For this supplement, the important fields to fill out are the <tt>method</tt> and the <tt>outgoing_request</tt> fields. By setting <tt>.method = "INVITE"</tt> it tells <tt>res_sip_session</tt> only to call into this supplement on INVITEs and not for other methods. If the method had been left empty, then the supplement would be called into for all SIP method types. We also set <tt>.outgoing_request</tt>. This makes it so that on outgoing SIP requests, our method will be called. Combined with the earlier <tt>method</tt> setting, it means that our session will only be called into for outgoing INVITE requests. Other session supplement fields are as follows:</p>
<ul>
        <li><tt>outgoing_response</tt>: The specified callback is called when an outgoing SIP response is sent on the session.</li>
        <li><tt>incoming_request</tt>: The specified callback is called when an incoming SIP request arrives on the session.</li>
        <li><tt>incoming_response</tt>: The specified callback is called when an incoming SIP response arrives on the session.</li>
        <li><tt>priority</tt>: This is mostly applicable to session supplements that wish to act on the initial incoming INVITE in a session. This will help determine when the supplements are called. Supplements with lower numbered priority are called before those with higher numbers. The most common use for this is to ensure that the supplement is called either before or after the <tt>ast_channel</tt> associated with the session is created.</li>
</ul>
<p>Our <tt>outgoing_request</tt> callback currently is just a stub. The next thing we are going to do is try to start filling it in. We'll start by unconditionally adding the necessary auto answer headers to the outbound INVITE. For the rest of this documentation, we will focus solely on the <tt>auto_answer_outgoing_request</tt> function.</p>
<h2><a name="WritingaSIPSessionSupplement-Fillinginthesupplementcallback"></a>Filling in the supplement callback</h2>
<p>Let's take a look at where we are currently with our callback:</p>
<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="theme: Confluence; brush: c; gutter: false">static void auto_answer_outgoing_request(struct ast_sip_session *session, pjsip_tx_data *tdata)
{
/* STUB */
}</pre>
</div></div>
<p>Let's go over the function in a little more detail. The first parameter to this callback is our SIP session. This contains information such as the associated <tt>ast_channel</tt> structure as well as other session-specific details. The <tt>tdata</tt> parameter is a PJSIP outgoing SIP message structure. In this case, we know the <tt>tdata</tt> is an outbound INVITE request due to the constraints of our supplement.</p>
<p>Let's start by adding the necessary headers to the outgoing INVITE. According to RFC 5373, there are two headers we should be adding:</p>
<ul>
        <li>Require: answermode</li>
        <li>Answer-Mode: auto</li>
</ul>
<p>The top one requires that the UAS supports the "answermode" option. A UAS that does not support this option will likely reject the call. The second header tells the UAS that it should automatically answer the call instead of awaiting interaction from the UAS's UI (e.g. having a person answer a ringing phone).</p>
<p>So let's add these headers:</p>
<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="theme: Confluence; brush: c; gutter: false">static void auto_answer_outgoing_request(struct ast_sip_session *session, pjsip_tx_data *tdata)
{
static const pj_str_t answer_mode_name = { "Answer-Mode", 11 };
static const pj_str_t answer_mode_value = { "auto", 4 };
static const pj_str_t require_value = { "answermode", 10 };
pjsip_generic_string_hdr *answer_mode;
pjsip_require_hdr *require;
/* Let's add the require header. There could already be a require header present in the
* request. If so, then we just need to add "answermode" to the list of requirements. Otherwise,
* we need to create a require header and add "answermode" to it.
*/
require = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_REQUIRE, NULL);
if (!require) {
require = pjsip_require_hdr_create(tdata->pool);
pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr *) require);
}
pj_strdup(tdata->pool, &require->values[require->count++], &require_value);
/* Now we can add the Answer-Mode header. This is easier since nothing else should be adding this
* header to the message before we get it.
*/
answer_mode = pjsip_generic_hdr_create(tdata->pool, &answer_mode_name, &answer_mode_value);
pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr *) answer_mode);
}</pre>
</div></div>
<h3><a name="WritingaSIPSessionSupplement-VariableDeclarations"></a>Variable Declarations</h3>
<p>Now we have some content! Let's go into it in more detail, starting from the top:</p>
<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="theme: Confluence; brush: c; gutter: false">static const pj_str_t answer_mode_name = { "Answer-Mode", 11 };
static const pj_str_t answer_mode_value = { "auto", 4 };
static const pj_str_t require_value = { "answermode", 10 };
pjsip_generic_string_hdr *answer_mode;
pjsip_require_hdr *require;</pre>
</div></div>
<p>First is to declare the parameters we will need. The names should be self-evident here, but in case it's not clear, we have created the name and value of the Answer-Mode header, as well as the header itself. Since PJSIP does not know about the Answer-Mode header, we just use a generic string header for it. We also have defined the value we need to place in the Require header and the Require header itself.</p>
<h3><a name="WritingaSIPSessionSupplement-Requireheaderhandling"></a>Require header handling</h3>
<p>Next, let's have a look at what we are doing with the Require header:</p>
<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="theme: Confluence; brush: c; gutter: false">require = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_REQUIRE, NULL);
if (!require) {
require = pjsip_require_hdr_create(tdata->pool);
pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr *) require);
}
pj_cstr(&require->values[require->count++], &require_value);</pre>
</div></div>
<p>First we try to see if a Require header already exists in the INVITE request. If it does not, then we create a new Require header and add it to the INVITE. Finally, we modify the header by adding the <tt>require_value</tt> to the last spot in the array of values for the header and incrementing the number of members in the array.</p>
<h3><a name="WritingaSIPSessionSupplement-AnswerModeheaderhandling"></a>Answer-Mode header handling</h3>
<p>Next, let's have a look at what we are doing with the Auto-Answer header:</p>
<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="theme: Confluence; brush: c; gutter: false">answer_mode = pjsip_generic_hdr_create(tdata->pool, &answer_mode_name, &answer_mode_value);
pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr *) answer_mode);</pre>
</div></div>
<p>Since we don't expect anyone else to be adding an Auto-Answer header to the outbound request, we simply create a new generic string header with the appropriate name and value and then add it to the outgoing message.</p>
<h2><a name="WritingaSIPSessionSupplement-Adjustments"></a>Adjustments</h2>
<h3><a name="WritingaSIPSessionSupplement-Usingthe%7B%7BSIPAUTOANSWER%7D%7Dchannelvariable"></a>Using the <tt>SIP_AUTO_ANSWER</tt> channel variable</h3>
<p>At this point, we have a simple session supplement written, but we don't actually want to add the auto-answer information to every single outgoing INVITE. Instead, we want to do so based on the presence of the SIP_AUTO_ANSWER channel variable on the outbound channel. Let's modify the code to do this. We will insert the code just before our Require header handling:</p>
<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="theme: Confluence; brush: c; gutter: false">...
pjsip_require_hdr *require;
int add_auto_answer;
ast_channel_lock(session->channel);
add_auto_answer = ast_true(pbx_builtin_getvar_helper(session->channel, "SIP_AUTO_ANSWER"));
ast_channel_unlock(session->channel);
if (!add_auto_answer) {
return;
}
/* Let's add the require header. There could already be a require header present in the
...</pre>
</div></div>
<p>With this new code, we'll check the <tt>SIP_AUTO_ANSWER</tt> channel variable to see if it tells us we should add auto-answer headers. The <tt>ast_true</tt> function checks that the header checks a string's value for words like "yes", "on", or "1" in order to be sure that it is intentional for the auto-answer feature to be invoked. The <tt>pbx_builtin_getvar_helper</tt> function requires that the channel is locked while it is called and the value returned by it is used.</p>
<h3><a name="WritingaSIPSessionSupplement-Afinaladjustment"></a>A final adjustment</h3>
<p>So now we have code that will conditionally add the auto-answer headers. We only have one final change to make. Think about when this callback is called. It's called on outbound INVITE requests. The thing is, that means it will be called both for the initial outbound INVITE for a session and it will be called on reinvites as well. Auto-answer does not pertain to reinvites, so we should add code to ensure that we only attempt to add the headers for initial outbound INVITEs.</p>
<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="theme: Confluence; brush: c; gutter: false">...
int add_auto_answer;
if (session->inv_session->state >= PJSIP_INV_CONFIRMED) {
return;
}
ast_channel_lock(session->channel);
...</pre>
</div></div>
<p>The <tt>session->inv_session</tt> is a PJSIP structure that keeps up with details of the underlying INVITE dialog. The <tt>PJSIP_INV_CONFIRMED</tt> state indicates that the initial INVITE transaction has completed. Therefore, if the state is here or beyond, then this outbound request must be a reinvite.</p>
<h1><a name="WritingaSIPSessionSupplement-FinishedSupplement"></a>Finished Supplement</h1>
<p>At this point, we are finished. So let's put it all together and see what we have:</p>
<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="theme: Confluence; brush: c; gutter: false">#include "asterisk.h"
#include <pjsip.h>
#include <pjlib.h>
#include "asterisk/res_sip_session.h"
#include "asterisk/module.h"
#include "asterisk/pbx.h"
/*** MODULEINFO
<depend>pjproject</depend>
<depend>res_sip_session</depend>
***/
static void auto_answer_outgoing_request(struct ast_sip_session *session, pjsip_tx_data *tdata)
{
static const pj_str_t answer_mode_name = { "Answer-Mode", 11 };
static const pj_str_t answer_mode_value = { "auto", 4 };
static const pj_str_t require_value = { "answermode", 10 };
pjsip_generic_string_hdr *answer_mode;
pjsip_require_hdr *require;
int add_auto_answer;
if (session->inv_session->state >= PJSIP_INV_CONFIRMED) {
return;
}
ast_channel_lock(session->channel);
add_auto_answer = ast_true(pbx_builtin_getvar_helper(session->channel, "SIP_AUTO_ANSWER"));
ast_channel_unlock(session->channel);
if (!add_auto_answer) {
return;
}
/* Let's add the require header. There could already be a require header present in the
* request. If so, then we just need to add "answermode" to the list of requirements. Otherwise,
* we need to create a require header and add "answermode" to it.
*/
require = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_REQUIRE, NULL);
if (!require) {
require = pjsip_require_hdr_create(tdata->pool);
pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr *) require);
}
pj_strdup(tdata->pool, &require->values[require->count++], &require_value);
/* Now we can add the Answer-Mode header. This is easier since nothing else should be adding this
* header to the message before we get it.
*/
answer_mode = pjsip_generic_hdr_create(tdata->pool, &answer_mode_name, &answer_mode_value);
pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr *) answer_mode);
}
static struct ast_sip_session_supplement auto_answer_supplement = {
.method = "INVITE",
.outgoing_request = auto_answer_outgoing_request,
};
static int load_module(void)
{
if (ast_sip_session_register_supplement(&auto_answer_supplement)) {
return AST_MODULE_LOAD_DECLINE;
}
return AST_MODULE_LOAD_SUCCESS;
}
static int unload_module(void)
{
ast_sip_session_unregister_supplement(&auto_answer_supplement));
return 0;
}
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "SIP Auto Answer Support",
.load = load_module,
.unload = unload_module,
.load_pri = AST_MODPRI_APP_DEPEND,
);</pre>
</div></div>
<p>And there you have it. In approximately 80 lines of code, you've added an Asterisk module that can modify outgoing INVITE requests!</p>
</div>
<div id="commentsSection" class="wiki-content pageSection">
<div style="float: right;" class="grey">
<a href="https://wiki.asterisk.org/wiki/users/removespacenotification.action?spaceKey=AST">Stop watching space</a>
<span style="padding: 0px 5px;">|</span>
<a href="https://wiki.asterisk.org/wiki/users/editmyemailsettings.action">Change email notification preferences</a>
</div>
<a href="https://wiki.asterisk.org/wiki/display/AST/Writing+a+SIP+Session+Supplement">View Online</a>
|
<a href="https://wiki.asterisk.org/wiki/display/AST/Writing+a+SIP+Session+Supplement?showComments=true&showCommentArea=true#addcomment">Add Comment</a>
</div>
</div>
</div>
</div>
</div>
</body>
</html>