Skip to content
Snippets Groups Projects
chan_sip.c 699 KiB
Newer Older
  • Learn to ignore specific revisions
  • 	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);
    
    		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);
    
    /*! \brief Returns null if we can't reinvite audio (part of RTP interface) */
    
    static enum ast_rtp_get_result sip_get_rtp_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;
    
    
    	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))
    
    	else if (ast_test_flag(&global_jbconf, AST_JB_FORCED))
    		res = AST_RTP_GET_FAILED;
    
    /*! \brief Returns null if we can't reinvite video (part of RTP interface) */
    
    static enum ast_rtp_get_result sip_get_vrtp_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;
    
    		return AST_RTP_GET_FAILED;
    	}
    
    	*rtp = p->vrtp;
    
    	if (ast_test_flag(&p->flags[0], SIP_CAN_REINVITE))
    		res = AST_RTP_TRY_NATIVE;
    
    
    /*! \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)
    
    {
    	struct sip_pvt *p;
    
    
    	/* Disable early RTP bridge  */
    	if (chan->_state != AST_STATE_UP && !global_directrtpsetup) 	/* We are in early state */
    		return 0;
    
    
    		/* If we're destroyed, don't bother */
    
    
    	/* 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)) {
    
    		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);	
    
    	/* Reset lastrtprx timer */
    
    	p->lastrtprx = p->lastrtptx = time(NULL);
    
    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";
    
    static char *app_dtmfmode = "SIPDtmfMode";
    
    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"
    
    "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 (!IS_SIP_TECH(chan->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);
    
    		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;
    
    	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_debug(1,"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 = strcasestr(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, SIP_TRANS_TIMEOUT);	/* Make sure we stop send this reply. */
    
    	sip_alreadygone(p);
    
    /*! \brief Return SIP UA's codec (part of the RTP interface) */
    
    static int sip_get_codec(struct ast_channel *chan)
    {
    
    	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);
    
    		iterator->pokeexpire = ast_sched_replace(iterator->pokeexpire, 
    			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);
    
    		iterator->expire = ast_sched_replace(iterator->expire, 
    			sched, ms, sip_reregister, iterator);
    
    		ASTOBJ_UNLOCK(iterator);
    	} while (0)
    	);
    
    /*! \brief Reload module */
    
    static int sip_do_reload(enum channelreloadreason reason)
    
    	/* 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");
    
    	sip_poke_all_peers();
    
    	sip_send_all_registers();
    
    	ast_debug(4, "--------------- SIP reload done\n");
    
    /*! \brief Force reload of module from cli */
    
    Jason Parker's avatar
    Jason Parker committed
    static char *sip_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
    
    Jason Parker's avatar
    Jason Parker committed
    	
    	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);
    
    	if (sip_reloading) 
    
    		ast_verbose("Previous SIP reload not yet done\n");
    
    		sip_reloading = TRUE;
    
    		sip_reloadreason = (a && a->fd) ? CHANNEL_CLI_RELOAD : CHANNEL_MODULE_RELOAD;
    
    	ast_mutex_unlock(&sip_reload_lock);
    	restart_monitor();
    
    Jason Parker's avatar
    Jason Parker committed
    	return CLI_SUCCESS;
    
    /*! \brief  Part of Asterisk module interface */
    
    Jason Parker's avatar
    Jason Parker committed
    	if (sip_reload(0, 0, NULL))
    		return 0;
    	return 1;
    
    /*! \brief SIP Cli commands definition */
    
    static struct ast_cli_entry cli_sip[] = {
    
    	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"),
    
    /*! \brief PBX load module - initialization */
    
    static int load_module(void)
    
    	ast_verbose("SIP channel loading...\n");
    
    	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;
    
    	/* Prepare the version that does not require DTMF BEGIN frames.
    
    	 * 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));
    
    	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));
    
    	/* 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 | 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_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");
    
    	ast_manager_unregister("SIPshowregistry");
    
    	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();
    
    	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);
    
    	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);
    
    	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,
    	       );