Newer
Older
if (reinvite) { /* If we are handling sending re-invite to the other side of the bridge */
/*! \note The SIP_CAN_REINVITE flag is for RTP media redirects,
not really T38 re-invites which are different. In this
case it's used properly, to see if we can reinvite over
NAT
*/
if (ast_test_flag(&p->flags[0], SIP_CAN_REINVITE) && ast_test_flag(&pvt->flags[0], SIP_CAN_REINVITE)) {
ast_udptl_get_peer(pvt->udptl, &p->udptlredirip);
flag =1;
} else {
memset(&p->udptlredirip, 0, sizeof(p->udptlredirip));
}
if (!ast_test_flag(&p->flags[0], SIP_GOTREFER)) {
if (!p->pendinginvite) {
if (flag)
ast_debug(3, "Sending reinvite on SIP '%s' - It's UDPTL soon redirected to IP %s:%d\n", p->callid, ast_inet_ntoa(p->udptlredirip.sin_addr), ntohs(p->udptlredirip.sin_port));
else
ast_debug(3, "Sending reinvite on SIP '%s' - It's UDPTL soon redirected to us (IP %s)\n", p->callid, ast_inet_ntoa(p->ourip.sin_addr));
transmit_reinvite_with_sdp(p, TRUE);
} else if (!ast_test_flag(&p->flags[0], SIP_PENDINGBYE)) {
if (flag)
ast_debug(3, "Deferring reinvite on SIP '%s' - It's UDPTL will be redirected to IP %s:%d\n", p->callid, ast_inet_ntoa(p->udptlredirip.sin_addr), ntohs(p->udptlredirip.sin_port));
else
ast_debug(3, "Deferring reinvite on SIP '%s' - It's UDPTL will be redirected to us (IP %s)\n", p->callid, ast_inet_ntoa(p->ourip.sin_addr));
ast_set_flag(&p->flags[0], SIP_NEEDREINVITE);
}
}
/* Reset lastrtprx timer */
p->lastrtprx = p->lastrtptx = time(NULL);
sip_pvt_unlock(p);
return 0;
} else { /* If we are handling sending 200 OK to the other side of the bridge */
if (ast_test_flag(&p->flags[0], SIP_CAN_REINVITE) && ast_test_flag(&pvt->flags[0], SIP_CAN_REINVITE)) {
ast_udptl_get_peer(pvt->udptl, &p->udptlredirip);
flag = 1;
} else {
memset(&p->udptlredirip, 0, sizeof(p->udptlredirip));
}
if (flag)
ast_debug(3, "Responding 200 OK on SIP '%s' - It's UDPTL soon redirected to IP %s:%d\n", p->callid, ast_inet_ntoa(p->udptlredirip.sin_addr), ntohs(p->udptlredirip.sin_port));
else
ast_debug(3, "Responding 200 OK on SIP '%s' - It's UDPTL soon redirected to us (IP %s)\n", p->callid, ast_inet_ntoa(p->ourip.sin_addr));
pvt->t38.state = T38_ENABLED;
p->t38.state = T38_ENABLED;
ast_debug(2, "T38 changed state to %d on channel %s\n", pvt->t38.state, pvt->owner ? pvt->owner->name : "<none>");
ast_debug(2, "T38 changed state to %d on channel %s\n", p->t38.state, chan ? chan->name : "<none>");
transmit_response_with_t38_sdp(p, "200 OK", &p->initreq, XMIT_CRITICAL);
p->lastrtprx = p->lastrtptx = time(NULL);
sip_pvt_unlock(p);
return 0;
}
}
/*! \brief Returns null if we can't reinvite audio (part of RTP interface) */
Joshua Colp
committed
static enum ast_rtp_get_result sip_get_rtp_peer(struct ast_channel *chan, struct ast_rtp **rtp)
Joshua Colp
committed
struct sip_pvt *p = NULL;
enum ast_rtp_get_result res = AST_RTP_TRY_PARTIAL;
if (!(p = chan->tech_pvt))
return AST_RTP_GET_FAILED;
sip_pvt_lock(p);
Joshua Colp
committed
if (!(p->rtp)) {
sip_pvt_unlock(p);
Joshua Colp
committed
return AST_RTP_GET_FAILED;
}
*rtp = p->rtp;
if (ast_rtp_getnat(*rtp) && !ast_test_flag(&p->flags[0], SIP_CAN_REINVITE_NAT))
res = AST_RTP_TRY_PARTIAL;
else if (ast_test_flag(&p->flags[0], SIP_CAN_REINVITE))
Joshua Colp
committed
res = AST_RTP_TRY_NATIVE;
else if (ast_test_flag(&global_jbconf, AST_JB_FORCED))
res = AST_RTP_GET_FAILED;
Joshua Colp
committed
sip_pvt_unlock(p);
Joshua Colp
committed
return res;
/*! \brief Returns null if we can't reinvite video (part of RTP interface) */
Joshua Colp
committed
static enum ast_rtp_get_result sip_get_vrtp_peer(struct ast_channel *chan, struct ast_rtp **rtp)
Joshua Colp
committed
struct sip_pvt *p = NULL;
enum ast_rtp_get_result res = AST_RTP_TRY_PARTIAL;
if (!(p = chan->tech_pvt))
return AST_RTP_GET_FAILED;
sip_pvt_lock(p);
Joshua Colp
committed
if (!(p->vrtp)) {
sip_pvt_unlock(p);
Joshua Colp
committed
return AST_RTP_GET_FAILED;
}
*rtp = p->vrtp;
if (ast_test_flag(&p->flags[0], SIP_CAN_REINVITE))
res = AST_RTP_TRY_NATIVE;
sip_pvt_unlock(p);
Joshua Colp
committed
return res;
}
19109
19110
19111
19112
19113
19114
19115
19116
19117
19118
19119
19120
19121
19122
19123
19124
19125
19126
19127
19128
19129
19130
19131
19132
19133
/*! \brief Returns null if we can't reinvite text (part of RTP interface) */
static enum ast_rtp_get_result sip_get_trtp_peer(struct ast_channel *chan, struct ast_rtp **rtp)
{
struct sip_pvt *p = NULL;
enum ast_rtp_get_result res = AST_RTP_TRY_PARTIAL;
if (!(p = chan->tech_pvt))
return AST_RTP_GET_FAILED;
sip_pvt_lock(p);
if (!(p->trtp)) {
sip_pvt_unlock(p);
return AST_RTP_GET_FAILED;
}
*rtp = p->trtp;
if (ast_test_flag(&p->flags[0], SIP_CAN_REINVITE))
res = AST_RTP_TRY_NATIVE;
sip_pvt_unlock(p);
return res;
}
/*! \brief Set the RTP peer for this call */
static int sip_set_rtp_peer(struct ast_channel *chan, struct ast_rtp *rtp, struct ast_rtp *vrtp, struct ast_rtp *trtp, int codecs, int nat_active)
int changed = 0;
p = chan->tech_pvt;
if (!p)
/* Disable early RTP bridge */
if (chan->_state != AST_STATE_UP && !global_directrtpsetup) /* We are in early state */
return 0;
sip_pvt_lock(p);
if (p->alreadygone) {
/* If we're destroyed, don't bother */
sip_pvt_unlock(p);
return 0;
}
Kevin P. Fleming
committed
/* if this peer cannot handle reinvites of the media stream to devices
that are known to be behind a NAT, then stop the process now
*/
if (nat_active && !ast_test_flag(&p->flags[0], SIP_CAN_REINVITE_NAT)) {
sip_pvt_unlock(p);
Kevin P. Fleming
committed
return 0;
}
changed |= ast_rtp_get_peer(rtp, &p->redirip);
} else if (p->redirip.sin_addr.s_addr || ntohs(p->redirip.sin_port) != 0) {
memset(&p->redirip, 0, sizeof(p->redirip));
changed = 1;
}
if (vrtp) {
changed |= ast_rtp_get_peer(vrtp, &p->vredirip);
} else if (p->vredirip.sin_addr.s_addr || ntohs(p->vredirip.sin_port) != 0) {
memset(&p->vredirip, 0, sizeof(p->vredirip));
if (trtp) {
changed |= ast_rtp_get_peer(trtp, &p->tredirip);
} else if (p->tredirip.sin_addr.s_addr || ntohs(p->tredirip.sin_port) != 0) {
memset(&p->tredirip, 0, sizeof(p->tredirip));
changed = 1;
}
if (codecs) {
if ((p->redircodecs != codecs)) {
p->redircodecs = codecs;
changed = 1;
}
if ((p->capability & codecs) != p->capability) {
p->jointcapability &= codecs;
p->capability &= codecs;
changed = 1;
}
if (changed && !ast_test_flag(&p->flags[0], SIP_GOTREFER) && !ast_test_flag(&p->flags[0], SIP_DEFER_BYE_ON_TRANSFER)) {
if (chan->_state != AST_STATE_UP) { /* We are in early state */
append_history(p, "ExtInv", "Initial invite sent with remote bridge proposal.");
ast_debug(1, "Early remote bridge setting SIP '%s' - Sending media to %s\n", p->callid, ast_inet_ntoa(rtp ? p->redirip.sin_addr : p->ourip.sin_addr));
} else if (!p->pendinginvite) { /* We are up, and have no outstanding invite */
ast_debug(3, "Sending reinvite on SIP '%s' - It's audio soon redirected to IP %s\n", p->callid, ast_inet_ntoa(rtp ? p->redirip.sin_addr : p->ourip.sin_addr));
transmit_reinvite_with_sdp(p, FALSE);
} else if (!ast_test_flag(&p->flags[0], SIP_PENDINGBYE)) {
ast_debug(3, "Deferring reinvite on SIP '%s' - It's audio will be redirected to IP %s\n", p->callid, ast_inet_ntoa(rtp ? p->redirip.sin_addr : p->ourip.sin_addr));
/* We have a pending Invite. Send re-invite when we're done with the invite */
ast_set_flag(&p->flags[0], SIP_NEEDREINVITE);
p->lastrtprx = p->lastrtptx = time(NULL);
sip_pvt_unlock(p);
static char *synopsis_dtmfmode = "Change the dtmfmode for a SIP call";
static char *descrip_dtmfmode = " SIPDtmfMode(inband|info|rfc2833): Changes the dtmfmode for a SIP call\n";
Olle Johansson
committed
static char *app_sipaddheader = "SIPAddHeader";
static char *synopsis_sipaddheader = "Add a SIP header to the outbound call";
static char *descrip_sipaddheader = ""
" SIPAddHeader(Header: Content):\n"
Olle Johansson
committed
"Adds a header to a SIP call placed with DIAL.\n"
"Remember to user the X-header if you are adding non-standard SIP\n"
"headers, like \"X-Asterisk-Accountcode:\". Use this with care.\n"
"Adding the wrong headers may jeopardize the SIP dialog.\n"
"Always returns 0\n";
/*! \brief Set the DTMFmode for an outbound SIP call (application) */
static int sip_dtmfmode(struct ast_channel *chan, void *data)
{
struct sip_pvt *p;
char *mode = data;
if (!data) {
ast_log(LOG_WARNING, "This application requires the argument: info, inband, rfc2833\n");
if (!IS_SIP_TECH(chan->tech)) {
ast_log(LOG_WARNING, "Call this application only on SIP incoming calls\n");
return 0;
}
p = chan->tech_pvt;
sip_pvt_lock(p);
if (!strcasecmp(mode,"info")) {
ast_clear_flag(&p->flags[0], SIP_DTMF);
ast_set_flag(&p->flags[0], SIP_DTMF_INFO);
p->jointnoncodeccapability &= ~AST_RTP_DTMF;
} else if (!strcasecmp(mode,"shortinfo")) {
ast_clear_flag(&p->flags[0], SIP_DTMF);
ast_set_flag(&p->flags[0], SIP_DTMF_SHORTINFO);
p->jointnoncodeccapability &= ~AST_RTP_DTMF;
} else if (!strcasecmp(mode,"rfc2833")) {
ast_clear_flag(&p->flags[0], SIP_DTMF);
ast_set_flag(&p->flags[0], SIP_DTMF_RFC2833);
p->jointnoncodeccapability |= AST_RTP_DTMF;
} else if (!strcasecmp(mode,"inband")) {
ast_clear_flag(&p->flags[0], SIP_DTMF);
ast_set_flag(&p->flags[0], SIP_DTMF_INBAND);
p->jointnoncodeccapability &= ~AST_RTP_DTMF;
} else
ast_log(LOG_WARNING, "I don't know about this dtmf mode: %s\n",mode);
if (p->rtp)
ast_rtp_setdtmf(p->rtp, ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_RFC2833);
if (ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_INBAND) {
if (!p->vad) {
p->vad = ast_dsp_new();
ast_dsp_set_features(p->vad, DSP_FEATURE_DTMF_DETECT);
} else {
if (p->vad) {
ast_dsp_free(p->vad);
p->vad = NULL;
}
sip_pvt_unlock(p);
/*! \brief Add a SIP header to an outbound INVITE */
Olle Johansson
committed
static int sip_addheader(struct ast_channel *chan, void *data)
{
int no = 0;
Olle Johansson
committed
char varbuf[30];
char *inbuf = data;
Olle Johansson
committed
if (ast_strlen_zero(inbuf)) {
ast_log(LOG_WARNING, "This application requires the argument: Header\n");
return 0;
}
Olle Johansson
committed
/* Check for headers */
while (!ok && no <= 50) {
no++;
snprintf(varbuf, sizeof(varbuf), "_SIPADDHEADER%.2d", no);
/* Compare without the leading underscore */
if( (pbx_builtin_getvar_helper(chan, (const char *) varbuf + 1) == (const char *) NULL) )
Olle Johansson
committed
}
if (ok) {
pbx_builtin_setvar_helper (chan, varbuf, inbuf);
if (sipdebug)
ast_debug(1,"SIP Header added \"%s\" as %s\n", inbuf, varbuf);
Olle Johansson
committed
} else {
ast_log(LOG_WARNING, "Too many SIP headers added, max 50\n");
}
Olle Johansson
committed
return 0;
}
/*! \brief Transfer call before connect with a 302 redirect
\note Called by the transfer() dialplan application through the sip_transfer()
pbx interface function if the call is in ringing state
\todo Fix this function so that we wait for reply to the REFER and
react to errors, denials or other issues the other end might have.
Mark Spencer
committed
static int sip_sipredirect(struct sip_pvt *p, const char *dest)
{
char *cdest;
char *extension, *host, *port;
char tmp[80];
Russell Bryant
committed
cdest = ast_strdupa(dest);
extension = strsep(&cdest, "@");
host = strsep(&cdest, ":");
port = strsep(&cdest, ":");
if (ast_strlen_zero(extension)) {
ast_log(LOG_ERROR, "Missing mandatory argument: extension\n");
return 0;
}
/* we'll issue the redirect message here */
if (!host) {
char *localtmp;
Olle Johansson
committed
Kevin P. Fleming
committed
ast_copy_string(tmp, get_header(&p->initreq, "To"), sizeof(tmp));
Russell Bryant
committed
if (ast_strlen_zero(tmp)) {
ast_log(LOG_ERROR, "Cannot retrieve the 'To' header from the original SIP request!\n");
return 0;
}
if ((localtmp = strcasestr(tmp, "sip:")) && (localtmp = strchr(localtmp, '@'))) {
Russell Bryant
committed
char lhost[80], lport[80];
Olle Johansson
committed
Russell Bryant
committed
memset(lhost, 0, sizeof(lhost));
memset(lport, 0, sizeof(lport));
/* This is okey because lhost and lport are as big as tmp */
sscanf(localtmp, "%[^<>:; ]:%[^<>:; ]", lhost, lport);
Russell Bryant
committed
if (ast_strlen_zero(lhost)) {
ast_log(LOG_ERROR, "Can't find the host address\n");
return 0;
}
Russell Bryant
committed
host = ast_strdupa(lhost);
Russell Bryant
committed
if (!ast_strlen_zero(lport)) {
Russell Bryant
committed
port = ast_strdupa(lport);
Russell Bryant
committed
}
ast_string_field_build(p, our_contact, "Transfer <sip:%s@%s%s%s>", extension, host, port ? ":" : "", port ? port : "");
transmit_response_reliable(p, "302 Moved Temporarily", &p->initreq);
sip_scheddestroy(p, SIP_TRANS_TIMEOUT); /* Make sure we stop send this reply. */
/*! \brief Return SIP UA's codec (part of the RTP interface) */
static int sip_get_codec(struct ast_channel *chan)
{
struct sip_pvt *p = chan->tech_pvt;
return p->peercapability ? p->peercapability : p->capability;
/*! \brief Send a poke to all known peers
Space them out 100 ms apart
XXX We might have a cool algorithm for this or use random - any suggestions?
*/
static void sip_poke_all_peers(void)
{
int ms = 0;
if (!speerobjs) /* No peers, just give up */
return;
ASTOBJ_CONTAINER_TRAVERSE(&peerl, 1, do {
ASTOBJ_WRLOCK(iterator);
ms += 100;
iterator->pokeexpire = ast_sched_replace(iterator->pokeexpire,
sched, ms, sip_poke_peer_s, iterator);
ASTOBJ_UNLOCK(iterator);
} while (0)
);
}
/*! \brief Send all known registrations */
static void sip_send_all_registers(void)
{
int regspacing;
if (!regobjs)
return;
regspacing = default_expiry * 1000/regobjs;
if (regspacing > 100)
regspacing = 100;
ms = regspacing;
ASTOBJ_CONTAINER_TRAVERSE(®l, 1, do {
ASTOBJ_WRLOCK(iterator);
ms += regspacing;
iterator->expire = ast_sched_replace(iterator->expire,
sched, ms, sip_reregister, iterator);
ASTOBJ_UNLOCK(iterator);
} while (0)
);
static int sip_do_reload(enum channelreloadreason reason)
reload_config(reason);
Olle Johansson
committed
/* Prune peers who still are supposed to be deleted */
ASTOBJ_CONTAINER_PRUNE_MARKED(&peerl, sip_destroy_peer);
ast_debug(4, "--------------- Done destroying pruned peers\n");
Olle Johansson
committed
/* Send qualify (OPTIONS) to all peers */
Olle Johansson
committed
/* Register with all services */
sip_send_all_registers();
ast_debug(4, "--------------- SIP reload done\n");
Olle Johansson
committed
/*! \brief Force reload of module from cli */
static char *sip_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
switch (cmd) {
case CLI_INIT:
e->command = "sip reload";
e->usage =
"Usage: sip reload\n"
" Reloads SIP configuration from sip.conf\n";
return NULL;
case CLI_GENERATE:
return NULL;
}
ast_mutex_lock(&sip_reload_lock);
ast_verbose("Previous SIP reload not yet done\n");
Joshua Colp
committed
sip_reloadreason = (a && a->fd) ? CHANNEL_CLI_RELOAD : CHANNEL_MODULE_RELOAD;
ast_mutex_unlock(&sip_reload_lock);
restart_monitor();
/*! \brief Part of Asterisk module interface */
static int reload(void)
if (sip_reload(0, 0, NULL))
return 0;
return 1;
/*! \brief SIP Cli commands definition */
static struct ast_cli_entry cli_sip[] = {
Jason Parker
committed
19486
19487
19488
19489
19490
19491
19492
19493
19494
19495
19496
19497
19498
19499
19500
19501
19502
19503
19504
AST_CLI_DEFINE(sip_show_channels, "List active SIP channels/subscriptions"),
AST_CLI_DEFINE(sip_show_domains, "List our local SIP domains."),
AST_CLI_DEFINE(sip_show_inuse, "List all inuse/limits"),
AST_CLI_DEFINE(sip_show_objects, "List all SIP object allocations"),
AST_CLI_DEFINE(sip_show_peers, "List defined SIP peers"),
AST_CLI_DEFINE(sip_show_registry, "List SIP registration status"),
AST_CLI_DEFINE(sip_unregister, "Unregister (force expiration) a SIP peer from the registery\n"),
AST_CLI_DEFINE(sip_show_settings, "Show SIP global settings"),
AST_CLI_DEFINE(sip_notify, "Send a notify packet to a SIP peer"),
AST_CLI_DEFINE(sip_show_channel, "Show detailed SIP channel info"),
AST_CLI_DEFINE(sip_show_history, "Show SIP dialog history"),
AST_CLI_DEFINE(sip_show_peer, "Show details on specific SIP peer"),
AST_CLI_DEFINE(sip_show_users, "List defined SIP users"),
AST_CLI_DEFINE(sip_show_user, "Show details on specific SIP user"),
AST_CLI_DEFINE(sip_prune_realtime, "Prune cached Realtime users/peers"),
AST_CLI_DEFINE(sip_do_debug, "Enable/Disable SIP debugging"),
AST_CLI_DEFINE(sip_do_history, "Enable SIP history"),
AST_CLI_DEFINE(sip_no_history, "Disable SIP history"),
AST_CLI_DEFINE(sip_reload, "Reload SIP configuration"),
Kevin P. Fleming
committed
};
/*! \brief PBX load module - initialization */
static int load_module(void)
Olle Johansson
committed
ast_verbose("SIP channel loading...\n");
ASTOBJ_CONTAINER_INIT(&userl); /* User object list */
ASTOBJ_CONTAINER_INIT(&peerl); /* Peer object list */
ASTOBJ_CONTAINER_INIT(®l); /* Registry object list */
if (!(sched = sched_context_create())) {
ast_log(LOG_ERROR, "Unable to create scheduler context\n");
return AST_MODULE_LOAD_FAILURE;
if (!(io = io_context_create())) {
ast_log(LOG_ERROR, "Unable to create I/O context\n");
sched_context_destroy(sched);
return AST_MODULE_LOAD_FAILURE;
sip_reloadreason = CHANNEL_MODULE_LOAD;
if(reload_config(sip_reloadreason)) /* Load the configuration from sip.conf */
return AST_MODULE_LOAD_DECLINE;
/* Prepare the version that does not require DTMF BEGIN frames.
Kevin P. Fleming
committed
* We need to use tricks such as memcpy and casts because the variable
* has const fields.
*/
memcpy(&sip_tech_info, &sip_tech, sizeof(sip_tech));
Kevin P. Fleming
committed
memset((void *) &sip_tech_info.send_digit_begin, 0, sizeof(sip_tech_info.send_digit_begin));
/* Make sure we can register our sip channel type */
if (ast_channel_register(&sip_tech)) {
ast_log(LOG_ERROR, "Unable to register channel type 'SIP'\n");
io_context_destroy(io);
sched_context_destroy(sched);
return AST_MODULE_LOAD_FAILURE;
/* Register all CLI functions for SIP */
ast_cli_register_multiple(cli_sip, sizeof(cli_sip)/ sizeof(struct ast_cli_entry));
Kevin P. Fleming
committed
/* Tell the RTP subdriver that we're here */
ast_rtp_proto_register(&sip_rtp);
Kevin P. Fleming
committed
/* Tell the UDPTL subdriver that we're here */
ast_udptl_proto_register(&sip_udptl);
/* Register dialplan applications */
ast_register_application(app_dtmfmode, sip_dtmfmode, synopsis_dtmfmode, descrip_dtmfmode);
Olle Johansson
committed
ast_register_application(app_sipaddheader, sip_addheader, synopsis_sipaddheader, descrip_sipaddheader);
/* Register dialplan functions */
ast_custom_function_register(&sip_header_function);
ast_custom_function_register(&sippeer_function);
Kevin P. Fleming
committed
ast_custom_function_register(&sipchaninfo_function);
ast_custom_function_register(&checksipdomain_function);
/* Register manager commands */
ast_manager_register2("SIPpeers", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, manager_sip_show_peers,
"List SIP peers (text format)", mandescr_show_peers);
ast_manager_register2("SIPshowpeer", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, manager_sip_show_peer,
"Show SIP peer (text format)", mandescr_show_peer);
ast_manager_register2("SIPshowregistry", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, manager_show_registry,
"Show SIP registrations (text format)", mandescr_show_registry);
sip_poke_all_peers();
sip_send_all_registers();
/* And start the monitor for the first time */
restart_monitor();
return AST_MODULE_LOAD_SUCCESS;
static int unload_module(void)
struct ast_context *con;
/* First, take us out of the channel type list */
ast_channel_unregister(&sip_tech);
Kevin P. Fleming
committed
ast_custom_function_unregister(&sipchaninfo_function);
ast_custom_function_unregister(&sippeer_function);
Kevin P. Fleming
committed
ast_custom_function_unregister(&sip_header_function);
ast_custom_function_unregister(&checksipdomain_function);
Kevin P. Fleming
committed
/* Unregister dial plan applications */
Olle Johansson
committed
ast_unregister_application(app_sipaddheader);
ast_cli_unregister_multiple(cli_sip, sizeof(cli_sip) / sizeof(struct ast_cli_entry));
/* Disconnect from the RTP subsystem */
ast_rtp_proto_unregister(&sip_rtp);
ast_udptl_proto_unregister(&sip_udptl);
ast_manager_unregister("SIPpeers");
ast_manager_unregister("SIPshowpeer");
ast_manager_unregister("SIPshowregistry");
Olle Johansson
committed
/* Hangup all dialogs if they have an owner */
for (p = dialoglist; p ; p = p->next) {
if (p->owner)
ast_softhangup(p->owner, AST_SOFTHANGUP_APPUNLOAD);
ast_mutex_lock(&monlock);
if (monitor_thread && (monitor_thread != AST_PTHREADT_STOP) && (monitor_thread != AST_PTHREADT_NULL)) {
pthread_cancel(monitor_thread);
pthread_kill(monitor_thread, SIGURG);
pthread_join(monitor_thread, NULL);
monitor_thread = AST_PTHREADT_STOP;
ast_mutex_unlock(&monlock);
Olle Johansson
committed
/* Destroy all the dialogs and free their memory */
p = dialoglist;
while (p) {
pl = p;
p = p->next;
__sip_destroy(pl, TRUE, TRUE);
}
Olle Johansson
committed
dialoglist = NULL;
Mark Spencer
committed
/* Free memory for local network address mask */
ASTOBJ_CONTAINER_DESTROYALL(&userl, sip_destroy_user);
ASTOBJ_CONTAINER_DESTROY(&userl);
ASTOBJ_CONTAINER_DESTROYALL(&peerl, sip_destroy_peer);
ASTOBJ_CONTAINER_DESTROY(&peerl);
ASTOBJ_CONTAINER_DESTROYALL(®l, sip_registry_destroy);
ASTOBJ_CONTAINER_DESTROY(®l);
clear_realm_authentication(authl);
clear_sip_domains();
close(sipsock);
sched_context_destroy(sched);
con = ast_context_find(used_context);
if (con)
ast_context_destroy(con, "SIP");
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Session Initiation Protocol (SIP)",
.load = load_module,
.unload = unload_module,
.reload = reload,
);