Skip to content
Snippets Groups Projects
chan_sip.c 620 KiB
Newer Older
  • Learn to ignore specific revisions
  • "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)
    {
    
    		ast_log(LOG_WARNING, "This application requires the argument: info, inband, rfc2833\n");
    
    		return 0;
    	}
    
    	ast_channel_lock(chan);
    
    	if (chan->tech != &sip_tech) {
    
    		ast_log(LOG_WARNING, "Call this application only on SIP incoming calls\n");
    
    		ast_channel_unlock(chan);
    
    		ast_channel_unlock(chan);
    
    	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;
    
    	ast_channel_unlock(chan);
    
    /*! \brief Add a SIP header to an outbound INVITE */
    
    static int sip_addheader(struct ast_channel *chan, void *data)
    {
    	int no = 0;
    
    	int ok = FALSE;
    
    	
    	if (ast_strlen_zero(inbuf)) {
    		ast_log(LOG_WARNING, "This application requires the argument: Header\n");
    		return 0;
    	}
    
    	ast_channel_lock(chan);
    
    
    	/* 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) )
    
    			ok = TRUE;
    
    	}
    	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");
    	}
    
    	ast_channel_unlock(chan);
    
    /*! \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.
    
    Olle Johansson's avatar
    Olle Johansson committed
     */
    
    static int sip_sipredirect(struct sip_pvt *p, const char *dest)
    
    {
    	char *cdest;
    	char *extension, *host, *port;
    	char tmp[80];
    
    	extension = strsep(&cdest, "@");
    	host = strsep(&cdest, ":");
    	port = strsep(&cdest, ":");
    
    		ast_log(LOG_ERROR, "Missing mandatory argument: extension\n");
    		return 0;
    	}
    
    	/* we'll issue the redirect message here */
    	if (!host) {
    		char *localtmp;
    
    		ast_copy_string(tmp, get_header(&p->initreq, "To"), sizeof(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, '@'))) {
    
    			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);
    
    				ast_log(LOG_ERROR, "Can't find the host address\n");
    				return 0;
    			}
    
    	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. */
    
    /*! \brief Return SIP UA's codec (part of the RTP interface) */
    
    static int sip_get_codec(struct ast_channel *chan)
    {
    
    /*! \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);
    
    /*! \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(&regl, 1, do {
    
    		ASTOBJ_WRLOCK(iterator);
    
    		if (iterator->expire > -1)
    			ast_sched_del(sched, iterator->expire);
    
    		iterator->expire = ast_sched_add(sched, ms, sip_reregister, iterator);
    
    		ASTOBJ_UNLOCK(iterator);
    	} while (0)
    	);
    
    /*! \brief Reload module */
    
    static int sip_do_reload(enum channelreloadreason reason)
    
    	if (option_debug > 3)
    		ast_log(LOG_DEBUG, "--------------- SIP reload started\n");
    
    
    	clear_realm_authentication(authl);
    
    	/* First, destroy all outstanding registry calls */
    	/* This is needed, since otherwise active registry entries will not be destroyed */
    	ASTOBJ_CONTAINER_TRAVERSE(&regl, 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);
    
    	ASTOBJ_CONTAINER_DESTROYALL(&userl, sip_destroy_user);
    
    	if (option_debug > 3)
    		ast_log(LOG_DEBUG, "--------------- Done destroying user list\n");
    
    	ASTOBJ_CONTAINER_DESTROYALL(&regl, sip_registry_destroy);
    
    	if (option_debug > 3)
    		ast_log(LOG_DEBUG, "--------------- Done destroying registry list\n");
    
    	ASTOBJ_CONTAINER_MARKALL(&peerl);
    
    	/* Prune peers who still are supposed to be deleted */
    	ASTOBJ_CONTAINER_PRUNE_MARKED(&peerl, sip_destroy_peer);
    
    	if (option_debug > 3)
    		ast_log(LOG_DEBUG, "--------------- Done destroying pruned peers\n");
    
    	sip_poke_all_peers();
    
    	sip_send_all_registers();
    
    	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);
    
    	if (sip_reloading) 
    
    		ast_verbose("Previous SIP reload not yet done\n");
    
    		sip_reloading = TRUE;
    
    		if (fd)
    			sip_reloadreason = CHANNEL_CLI_RELOAD;
    		else
    			sip_reloadreason = CHANNEL_MODULE_RELOAD;
    	}
    
    	ast_mutex_unlock(&sip_reload_lock);
    	restart_monitor();
    
    /*! \brief  Part of Asterisk module interface */
    
    /*! \brief SIP Cli commands definition */
    
    static struct ast_cli_entry cli_sip[] = {
    
    	{ { "sip", "show", "channels", NULL },
    
    	sip_show_channels, "List active SIP channels",
    
    	show_channels_usage },
    
    	{ { "sip", "show", "domains", NULL },
    
    	sip_show_domains, "List our local SIP domains.",
    
    	show_domains_usage },
    
    	{ { "sip", "show", "inuse", NULL },
    
    	sip_show_inuse, "List all inuse/limits",
    
    	show_inuse_usage },
    
    	{ { "sip", "show", "objects", NULL },
    
    	sip_show_objects, "List all SIP object allocations",
    
    	show_objects_usage },
    
    	{ { "sip", "show", "peers", NULL },
    
    	sip_show_peers, "List defined SIP peers",
    
    	show_peers_usage },
    
    	{ { "sip", "show", "registry", NULL },
    
    	sip_show_registry, "List SIP registration status",
    
    	show_reg_usage },
    
    Olle Johansson's avatar
    Olle Johansson committed
    	{ { "sip", "show", "settings", NULL },
    
    	sip_show_settings, "Show SIP global settings",
    
    	show_settings_usage },
    
    	{ { "sip", "show", "subscriptions", NULL },
    
    	sip_show_subscriptions, "List active SIP subscriptions",
    
    	show_subscriptions_usage },
    
    	{ { "sip", "show", "users", NULL },
    
    	sip_show_users, "List defined SIP users",
    
    	show_users_usage },
    
    
    	{ { "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",
    
    	no_debug_usage },
    
    
    	{ { "sip", "history", NULL },
    	sip_do_history, "Enable SIP history",
    	history_usage },
    
    
    	{ { "sip", "history", "off", NULL },
    
    	sip_no_history, "Disable SIP history",
    
    	no_history_usage },
    
    
    	{ { "sip", "reload", NULL },
    	sip_reload, "Reload SIP configuration",
    	sip_reload_usage },
    
    /*! \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(&regl);	/* 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));
    
    	/* Tell the RTP subdriver that we're here */
    
    	ast_rtp_proto_register(&sip_rtp);
    
    	/* 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);
    
    	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);
    
    	ast_custom_function_register(&sipchaninfo_function);
    
    	ast_custom_function_register(&checksipdomain_function);
    
    	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);
    
    	sip_send_all_registers();
    	
    	/* And start the monitor for the first time */
    	restart_monitor();
    
    
    /*! \brief PBX unload module API */
    
    static int unload_module(void)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	struct sip_pvt *p, *pl;
    
    	/* First, take us out of the channel type list */
    	ast_channel_unregister(&sip_tech);
    
    
    	/* Unregister dial plan functions */
    
    	ast_custom_function_unregister(&sipchaninfo_function);
    
    	ast_custom_function_unregister(&sippeer_function);
    
    	ast_custom_function_unregister(&sip_header_function);
    
    	ast_custom_function_unregister(&checksipdomain_function);
    
    	/* Unregister dial plan applications */
    
    	ast_unregister_application(app_dtmfmode);
    
    	ast_unregister_application(app_sipaddheader);
    
    	/* Unregister CLI commands */
    
    	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);
    
    	/* Disconnect from UDPTL */
    
    	ast_udptl_proto_unregister(&sip_udptl);
    
    
    	/* Unregister AMI actions */
    
    	ast_manager_unregister("SIPpeers");
    	ast_manager_unregister("SIPshowpeer");
    
    	dialoglist_lock();
    
    	/* 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);
    
    	dialoglist_unlock();
    
    	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);
    
    	dialoglist_lock();
    
    	/* Destroy all the dialogs and free their memory */
    	p = dialoglist;
    
    		__sip_destroy(pl, TRUE, TRUE);
    
    	dialoglist_unlock();
    
    	/* Free memory for local network address mask */
    
    	ast_free_ha(localaddr);
    
    
    	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(&regl, sip_registry_destroy);
    
    	ASTOBJ_CONTAINER_DESTROY(&regl);
    
    
    	clear_realm_authentication(authl);
    
    	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,
    	       );