Skip to content
Snippets Groups Projects
chan_sip.c 586 KiB
Newer Older
  • Learn to ignore specific revisions
  • 			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 (option_debug > 2) {
    					if (flag)
    
    						ast_log(LOG_DEBUG, "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));
    
    						ast_log(LOG_DEBUG, "Sending reinvite on SIP '%s' - It's UDPTL soon redirected to us (IP %s)\n", p->callid, ast_inet_ntoa(p->ourip));
    
    				}
    				transmit_reinvite_with_t38_sdp(p);
    			} else if (!ast_test_flag(&p->flags[0], SIP_PENDINGBYE)) {
    				if (option_debug > 2) {
    					if (flag)
    
    						ast_log(LOG_DEBUG, "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));
    
    						ast_log(LOG_DEBUG, "Deferring reinvite on SIP '%s' - It's UDPTL will be redirected to us (IP %s)\n", p->callid, ast_inet_ntoa(p->ourip));
    
    				}
    				ast_set_flag(&p->flags[0], SIP_NEEDREINVITE);
    			}
    		}
    		/* Reset lastrtprx timer */
    		p->lastrtprx = p->lastrtptx = time(NULL);
    		ast_mutex_unlock(&p->lock);
    		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 (option_debug > 2) {
    			if (flag)
    
    				ast_log(LOG_DEBUG, "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));
    
    				ast_log(LOG_DEBUG, "Responding 200 OK on SIP '%s' - It's UDPTL soon redirected to us (IP %s)\n", p->callid, ast_inet_ntoa(p->ourip));
    
    		}
    		pvt->t38.state = T38_ENABLED;
    		p->t38.state = T38_ENABLED;
    		if (option_debug > 1) {
    			ast_log(LOG_DEBUG, "T38 changed state to %d on channel %s\n", pvt->t38.state, pvt->owner ? pvt->owner->name : "<none>");
    			ast_log(LOG_DEBUG, "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);
    		ast_mutex_unlock(&p->lock);
    		return 0;
    	}
    }
    
    
    
    /*! \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;
    
    
    	ast_mutex_lock(&p->lock);
    
    	if (!(p->rtp)) {
    		ast_mutex_unlock(&p->lock);
    		return AST_RTP_GET_FAILED;
    	}
    
    	*rtp = p->rtp;
    
    	if (ast_test_flag(&p->flags[0], SIP_CAN_REINVITE))
    		res = AST_RTP_TRY_NATIVE;
    
    
    	ast_mutex_unlock(&p->lock);
    
    /*! \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;
    
    
    	ast_mutex_lock(&p->lock);
    
    	if (!(p->rtp)) {
    		ast_mutex_unlock(&p->lock);
    		return AST_RTP_GET_FAILED;
    	}
    
    	*rtp = p->vrtp;
    
    	if (ast_test_flag(&p->flags[0], SIP_CAN_REINVITE))
    		res = AST_RTP_TRY_NATIVE;
    
    
    	ast_mutex_unlock(&p->lock);
    
    /*! \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, int codecs, int nat_active)
    
    {
    	struct sip_pvt *p;
    
    		return -1;
    	ast_mutex_lock(&p->lock);
    
    	if (ast_test_flag(&p->flags[0], SIP_ALREADYGONE)) {
    
    		/* If we're destroyed, don't bother */
    		ast_mutex_unlock(&p->lock);
    		return 0;
    	}
    
    
    	/* 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)) {
    		ast_mutex_unlock(&p->lock);
    		return 0;
    	}
    
    
    	if (rtp) 
    		changed |= ast_rtp_get_peer(rtp, &p->redirip);
    
    	else
    		memset(&p->redirip, 0, sizeof(p->redirip));
    	if (vrtp)
    
    		changed |= ast_rtp_get_peer(vrtp, &p->vredirip);
    
    	else
    		memset(&p->vredirip, 0, sizeof(p->vredirip));
    
    		p->redircodecs = codecs;
    		changed = 1;
    	}
    	if (changed && !ast_test_flag(&p->flags[0], SIP_GOTREFER)) {
    
    		if (chan->_state != AST_STATE_UP) {	/* We are in early state */
    			if (recordhistory)
    				append_history(p, "ExtInv", "Initial invite sent with remote bridge proposal.");
    			if (option_debug)
    
    				ast_log(LOG_DEBUG, "Early remote bridge setting SIP '%s' - Sending media to %s\n", p->callid, ast_inet_ntoa(rtp ? p->redirip.sin_addr : p->ourip));
    
    		} else if (!p->pendinginvite) {		/* We are up, and have no outstanding invite */
    
    				ast_log(LOG_DEBUG, "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));
    
    			transmit_reinvite_with_sdp(p);
    
    		} else if (!ast_test_flag(&p->flags[0], SIP_PENDINGBYE)) {
    
    				ast_log(LOG_DEBUG, "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));
    
    			/* 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);
    
    	ast_mutex_unlock(&p->lock);
    	return 0;
    
    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)
    {
    
    	char *mode;
    	if (data)
    		mode = (char *)data;
    	else {
    
    		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);
    
    		return 0;
    	}
    	ast_mutex_lock(&p->lock);
    	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_mutex_unlock(&p->lock);
    
    	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;
    
    	char varbuf[30];
    	char *inbuf = (char *) data;
    	
    	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);
    
    		if( (pbx_builtin_getvar_helper(chan, (const char *) varbuf) == (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, ":");
    	if (!extension) {
    		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, '@'))) {
    
    			char lhost[80], lport[80];
    			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);
    
    	/* this is all that we want to send to that SIP device */
    
    	ast_set_flag(&p->flags[0], SIP_ALREADYGONE);
    
    /*! \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);
    	} while(0));
    
    
    	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  reload: Part of Asterisk module interface */
    
    static struct ast_cli_entry  my_clis[] = {
    	{ { "sip", "notify", NULL }, sip_notify, "Send a notify packet to a SIP peer", notify_usage, complete_sipnotify },
    	{ { "sip", "show", "objects", NULL }, sip_show_objects, "Show all SIP object allocations", show_objects_usage },
    	{ { "sip", "show", "users", NULL }, sip_show_users, "Show defined SIP users", show_users_usage },
    	{ { "sip", "show", "user", NULL }, sip_show_user, "Show details on specific SIP user", show_user_usage, complete_sip_show_user },
    	{ { "sip", "show", "subscriptions", NULL }, sip_show_subscriptions, "Show active SIP subscriptions", show_subscriptions_usage},
    	{ { "sip", "show", "channels", NULL }, sip_show_channels, "Show active SIP channels", show_channels_usage},
    	{ { "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", "domains", NULL }, sip_show_domains, "List our local SIP domains.", show_domains_usage },
    
    	{ { "sip", "show", "settings", NULL }, sip_show_settings, "Show SIP global settings", show_settings_usage  },
    
    	{ { "sip", "debug", NULL }, sip_do_debug, "Enable SIP debugging", debug_usage },
    
    	{ { "sip", "debug", "ip", NULL }, sip_do_debug, "Enable SIP debugging on IP", debug_usage },
    	{ { "sip", "debug", "peer", NULL }, sip_do_debug, "Enable SIP debugging on Peername", debug_usage, complete_sip_debug_peer },
    	{ { "sip", "show", "peer", NULL }, sip_show_peer, "Show details on specific SIP peer", show_peer_usage, complete_sip_show_peer },
    	{ { "sip", "show", "peers", NULL }, sip_show_peers, "Show defined SIP peers", show_peers_usage },
    	{ { "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", "show", "inuse", NULL }, sip_show_inuse, "List all inuse/limits", show_inuse_usage },
    	{ { "sip", "show", "registry", NULL }, sip_show_registry, "Show SIP registration status", show_reg_usage },
    	{ { "sip", "history", NULL }, sip_do_history, "Enable SIP history", history_usage },
    	{ { "sip", "no", "history", NULL }, sip_no_history, "Disable SIP history", no_history_usage },
    	{ { "sip", "no", "debug", NULL }, sip_no_debug, "Disable SIP debugging", no_debug_usage },
    	{ { "sip", "reload", NULL }, sip_reload, "Reload SIP configuration", sip_reload_usage },
    };
    
    /*! \brief  load_module: 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 */
    
    
    	sched = sched_context_create();
    	if (!sched) {
    		ast_log(LOG_WARNING, "Unable to create schedule context\n");
    	}
    
    	io = io_context_create();
    	if (!io) {
    		ast_log(LOG_WARNING, "Unable to create I/O context\n");
    	}
    
    	sip_reloadreason = CHANNEL_MODULE_LOAD;
    	reload_config(sip_reloadreason);	/* Load the configuration from sip.conf */
    
    	/* 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");
    
    	/* Register all CLI functions for SIP */
    
    	ast_cli_register_multiple(my_clis, sizeof(my_clis)/ sizeof(my_clis[0]));
    
    	/* 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();
    
    	return 0;
    
    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);
    
    
    	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);
    
    	ast_unregister_application(app_dtmfmode);
    
    	ast_unregister_application(app_sipaddheader);
    
    	ast_cli_unregister_multiple(my_clis, sizeof(my_clis) / sizeof(my_clis[0]));
    
    	ast_rtp_proto_unregister(&sip_rtp);
    
    	ast_udptl_proto_unregister(&sip_udptl);
    
    
    	ast_manager_unregister("SIPpeers");
    	ast_manager_unregister("SIPshowpeer");
    
    	ast_mutex_lock(&iflock);
    	/* Hangup all interfaces if they have an owner */
    	for (p = iflist; 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);
    
    	ast_mutex_lock(&iflock);
    	/* Destroy all the interfaces and free their memory */
    	p = iflist;
    	while (p) {
    		pl = p;
    		p = p->next;
    		/* Free associated memory */
    		ast_mutex_destroy(&pl->lock);
    		if (pl->chanvars) {
    			ast_variables_destroy(pl->chanvars);
    			pl->chanvars = NULL;
    		}
    		free(pl);
    	}
    	iflist = NULL;
    	ast_mutex_unlock(&iflock);
    
    	/* 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,
    	       );