Newer
Older
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 (chan->tech != &sip_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);
} else if (!strcasecmp(mode,"rfc2833")) {
ast_clear_flag(&p->flags[0], SIP_DTMF);
ast_set_flag(&p->flags[0], SIP_DTMF_RFC2833);
} else if (!strcasecmp(mode,"inband")) {
ast_clear_flag(&p->flags[0], SIP_DTMF);
ast_set_flag(&p->flags[0], SIP_DTMF_INBAND);
} else
ast_log(LOG_WARNING, "I don't know about this dtmf mode: %s\n",mode);
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_log(LOG_DEBUG,"SIP Header added \"%s\" as %s\n", inbuf, varbuf);
} 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 = strstr(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, 32000); /* Make sure we stop send this reply. */
/* hangup here */
return -1;
}
/*! \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;
/*! \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);
if (iterator->pokeexpire > -1)
ast_sched_del(sched, iterator->pokeexpire);
ms += 100;
iterator->pokeexpire = ast_sched_add(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);
if (iterator->expire > -1)
ast_sched_del(sched, iterator->expire);
ms += regspacing;
iterator->expire = ast_sched_add(sched, ms, sip_reregister, iterator);
ASTOBJ_UNLOCK(iterator);
} while (0)
);
static int sip_do_reload(enum channelreloadreason reason)
Olle Johansson
committed
if (option_debug > 3)
ast_log(LOG_DEBUG, "--------------- SIP reload started\n");
clear_realm_authentication(authl);
clear_sip_domains();
/* First, destroy all outstanding registry calls */
/* This is needed, since otherwise active registry entries will not be destroyed */
ASTOBJ_CONTAINER_TRAVERSE(®l, 1, do {
ASTOBJ_RDLOCK(iterator);
if (iterator->call) {
if (option_debug > 2)
ast_log(LOG_DEBUG, "Destroying active SIP dialog for registry %s@%s\n", iterator->username, iterator->hostname);
/* This will also remove references to the registry */
sip_destroy(iterator->call);
}
ASTOBJ_UNLOCK(iterator);
Olle Johansson
committed
/* Then, actually destroy users and registry */
ASTOBJ_CONTAINER_DESTROYALL(&userl, sip_destroy_user);
Olle Johansson
committed
if (option_debug > 3)
ast_log(LOG_DEBUG, "--------------- Done destroying user list\n");
ASTOBJ_CONTAINER_DESTROYALL(®l, sip_registry_destroy);
Olle Johansson
committed
if (option_debug > 3)
ast_log(LOG_DEBUG, "--------------- Done destroying registry list\n");
ASTOBJ_CONTAINER_MARKALL(&peerl);
reload_config(reason);
Olle Johansson
committed
/* Prune peers who still are supposed to be deleted */
ASTOBJ_CONTAINER_PRUNE_MARKED(&peerl, sip_destroy_peer);
Olle Johansson
committed
if (option_debug > 3)
ast_log(LOG_DEBUG, "--------------- 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();
Olle Johansson
committed
if (option_debug > 3)
ast_log(LOG_DEBUG, "--------------- SIP reload done\n");
/*! \brief Force reload of module from cli */
static int sip_reload(int fd, int argc, char *argv[])
{
ast_mutex_lock(&sip_reload_lock);
ast_verbose("Previous SIP reload not yet done\n");
if (fd)
sip_reloadreason = CHANNEL_CLI_RELOAD;
else
sip_reloadreason = CHANNEL_MODULE_RELOAD;
}
ast_mutex_unlock(&sip_reload_lock);
restart_monitor();
return 0;
}
/*! \brief Part of Asterisk module interface */
static int reload(void)
{
return sip_reload(0, 0, NULL);
}
/*! \brief SIP Cli commands definition */
static struct ast_cli_entry cli_sip[] = {
{ { "sip", "show", "channels", NULL },
sip_show_channels, "List active SIP channels",
{ { "sip", "show", "domains", NULL },
sip_show_domains, "List our local SIP domains.",
{ { "sip", "show", "inuse", NULL },
sip_show_inuse, "List all inuse/limits",
{ { "sip", "show", "objects", NULL },
sip_show_objects, "List all SIP object allocations",
{ { "sip", "show", "peers", NULL },
sip_show_peers, "List defined SIP peers",
{ { "sip", "show", "registry", NULL },
sip_show_registry, "List SIP registration status",
sip_show_settings, "Show SIP global settings",
{ { "sip", "show", "subscriptions", NULL },
sip_show_subscriptions, "List active SIP subscriptions",
{ { "sip", "show", "users", NULL },
sip_show_users, "List defined SIP users",
17314
17315
17316
17317
17318
17319
17320
17321
17322
17323
17324
17325
17326
17327
17328
17329
17330
17331
17332
17333
17334
17335
17336
17337
17338
17339
17340
17341
17342
17343
17344
17345
17346
{ { "sip", "notify", NULL },
sip_notify, "Send a notify packet to a SIP peer",
notify_usage, complete_sipnotify },
{ { "sip", "show", "channel", NULL },
sip_show_channel, "Show detailed SIP channel info",
show_channel_usage, complete_sipch },
{ { "sip", "show", "history", NULL },
sip_show_history, "Show SIP dialog history",
show_history_usage, complete_sipch },
{ { "sip", "show", "peer", NULL },
sip_show_peer, "Show details on specific SIP peer",
show_peer_usage, complete_sip_show_peer },
{ { "sip", "show", "user", NULL },
sip_show_user, "Show details on specific SIP user",
show_user_usage, complete_sip_show_user },
{ { "sip", "prune", "realtime", NULL },
sip_prune_realtime, "Prune cached Realtime object(s)",
prune_realtime_usage },
{ { "sip", "prune", "realtime", "peer", NULL },
sip_prune_realtime, "Prune cached Realtime peer(s)",
prune_realtime_usage, complete_sip_prune_realtime_peer },
{ { "sip", "prune", "realtime", "user", NULL },
sip_prune_realtime, "Prune cached Realtime user(s)",
prune_realtime_usage, complete_sip_prune_realtime_user },
{ { "sip", "set", "debug", NULL },
sip_do_debug, "Enable SIP debugging",
debug_usage },
{ { "sip", "set", "debug", "ip", NULL },
sip_do_debug, "Enable SIP debugging on IP",
debug_usage },
{ { "sip", "set", "debug", "peer", NULL },
sip_do_debug, "Enable SIP debugging on Peername",
debug_usage, complete_sip_debug_peer },
{ { "sip", "set", "debug", "off", NULL },
sip_no_debug, "Disable SIP debugging",
{ { "sip", "history", NULL },
sip_do_history, "Enable SIP history",
history_usage },
{ { "sip", "history", "off", NULL },
sip_no_history, "Disable SIP history",
{ { "sip", "reload", NULL },
sip_reload, "Reload SIP configuration",
sip_reload_usage },
Kevin P. Fleming
committed
};
/*! \brief PBX load module - initialization */
static int load_module(void)
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;
/* 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, manager_sip_show_peers,
"List SIP peers (text format)", mandescr_show_peers);
ast_manager_register2("SIPshowpeer", EVENT_FLAG_SYSTEM, manager_sip_show_peer,
"Show SIP peer (text format)", mandescr_show_peer);
Kevin P. Fleming
committed
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)
/* 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");
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)) {
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);
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Session Initiation Protocol (SIP)",
.load = load_module,
.unload = unload_module,
.reload = reload,
);