Skip to content
Snippets Groups Projects
chan_sip.c 1.16 MiB
Newer Older
  • Learn to ignore specific revisions
  • 	int res;
    
    #define CHECK_RESULTS(in, expected_res, expected_start, expected_len)	do {	\
    		input = (in);						\
    		res = get_in_brackets_const(input, &start, &len);	\
    		if ((expected_res) != res) {				\
    			ast_test_status_update(test, "Unexpected result: %d != %d\n", expected_res, res); \
    			return AST_TEST_FAIL;				\
    		}							\
    		if ((expected_start) != start) {			\
    			const char *e = expected_start ? expected_start : "(null)"; \
    			const char *a = start ? start : "(null)";	\
    			ast_test_status_update(test, "Unexpected start: %s != %s\n", e, a); \
    			return AST_TEST_FAIL;				\
    		}							\
    		if ((expected_len) != len) {				\
    			ast_test_status_update(test, "Unexpected len: %d != %d\n", expected_len, len); \
    			return AST_TEST_FAIL;				\
    		}							\
    	} while(0)
    
    	switch (cmd) {
    	case TEST_INIT:
    		info->name = __func__;
    		info->category = "/channels/chan_sip/";
    		info->summary = "get_in_brackets_const test";
    		info->description =
    			"Tests the get_in_brackets_const function";
    		return AST_TEST_NOT_RUN;
    	case TEST_EXECUTE:
    		break;
    	}
    
    	CHECK_RESULTS("", 1, NULL, -1);
    	CHECK_RESULTS("normal <test>", 0, input + 8, 4);
    	CHECK_RESULTS("\"normal\" <test>", 0, input + 10, 4);
    	CHECK_RESULTS("not normal <test", -1, NULL, -1);
    	CHECK_RESULTS("\"yes < really\" <test>", 0, input + 16, 4);
    	CHECK_RESULTS("\"even > this\" <test>", 0, input + 15, 4);
    	CHECK_RESULTS("<sip:id1@10.10.10.10;lr>", 0, input + 1, 22);
    	CHECK_RESULTS("<sip:id1@10.10.10.10;lr>, <sip:id1@10.10.10.20;lr>", 0, input + 1, 22);
    	CHECK_RESULTS("<sip:id1,id2@10.10.10.10;lr>", 0, input + 1, 26);
    	CHECK_RESULTS("<sip:id1@10., <sip:id2@10.10.10.10;lr>", 0, input + 1, 36);
    	CHECK_RESULTS("\"quoted text\" <sip:dlg1@10.10.10.10;lr>", 0, input + 15, 23);
    
    	return AST_TEST_PASS;
    }
    
    
    #endif
    
    #define DATA_EXPORT_SIP_PEER(MEMBER)				\
    	MEMBER(sip_peer, name, AST_DATA_STRING)			\
    	MEMBER(sip_peer, secret, AST_DATA_PASSWORD)		\
    	MEMBER(sip_peer, md5secret, AST_DATA_PASSWORD)		\
    	MEMBER(sip_peer, remotesecret, AST_DATA_PASSWORD)	\
    	MEMBER(sip_peer, context, AST_DATA_STRING)		\
    	MEMBER(sip_peer, subscribecontext, AST_DATA_STRING)	\
    	MEMBER(sip_peer, username, AST_DATA_STRING)		\
    	MEMBER(sip_peer, accountcode, AST_DATA_STRING)		\
    	MEMBER(sip_peer, tohost, AST_DATA_STRING)		\
    	MEMBER(sip_peer, regexten, AST_DATA_STRING)		\
    	MEMBER(sip_peer, fromuser, AST_DATA_STRING)		\
    	MEMBER(sip_peer, fromdomain, AST_DATA_STRING)		\
    	MEMBER(sip_peer, fullcontact, AST_DATA_STRING)		\
    	MEMBER(sip_peer, cid_num, AST_DATA_STRING)		\
    	MEMBER(sip_peer, cid_name, AST_DATA_STRING)		\
    	MEMBER(sip_peer, vmexten, AST_DATA_STRING)		\
    	MEMBER(sip_peer, language, AST_DATA_STRING)		\
    	MEMBER(sip_peer, mohinterpret, AST_DATA_STRING)		\
    	MEMBER(sip_peer, mohsuggest, AST_DATA_STRING)		\
    	MEMBER(sip_peer, parkinglot, AST_DATA_STRING)		\
    	MEMBER(sip_peer, useragent, AST_DATA_STRING)		\
    	MEMBER(sip_peer, mwi_from, AST_DATA_STRING)		\
    	MEMBER(sip_peer, engine, AST_DATA_STRING)		\
    	MEMBER(sip_peer, unsolicited_mailbox, AST_DATA_STRING)	\
    	MEMBER(sip_peer, is_realtime, AST_DATA_BOOLEAN)		\
    	MEMBER(sip_peer, host_dynamic, AST_DATA_BOOLEAN)	\
    	MEMBER(sip_peer, autoframing, AST_DATA_BOOLEAN)		\
    
    Kinsey Moore's avatar
    Kinsey Moore committed
    	MEMBER(sip_peer, inuse, AST_DATA_INTEGER)		\
    	MEMBER(sip_peer, ringing, AST_DATA_INTEGER)		\
    	MEMBER(sip_peer, onhold, AST_DATA_INTEGER)		\
    
    	MEMBER(sip_peer, call_limit, AST_DATA_INTEGER)		\
    	MEMBER(sip_peer, t38_maxdatagram, AST_DATA_INTEGER)	\
    	MEMBER(sip_peer, maxcallbitrate, AST_DATA_INTEGER)	\
    	MEMBER(sip_peer, rtptimeout, AST_DATA_SECONDS)		\
    	MEMBER(sip_peer, rtpholdtimeout, AST_DATA_SECONDS)	\
    	MEMBER(sip_peer, rtpkeepalive, AST_DATA_SECONDS)	\
    	MEMBER(sip_peer, lastms, AST_DATA_MILLISECONDS)		\
    	MEMBER(sip_peer, maxms, AST_DATA_MILLISECONDS)		\
    	MEMBER(sip_peer, qualifyfreq, AST_DATA_MILLISECONDS)	\
    	MEMBER(sip_peer, timer_t1, AST_DATA_MILLISECONDS)	\
    
    	MEMBER(sip_peer, timer_b, AST_DATA_MILLISECONDS)	\
    	MEMBER(sip_peer, description, AST_DATA_STRING)
    
    
    AST_DATA_STRUCTURE(sip_peer, DATA_EXPORT_SIP_PEER);
    
    static int peers_data_provider_get(const struct ast_data_search *search,
    	struct ast_data *data_root)
    {
    	struct sip_peer *peer;
    	struct ao2_iterator i;
    	struct ast_data *data_peer, *data_peer_mailboxes = NULL, *data_peer_mailbox, *enum_node;
    	struct ast_data *data_sip_options;
    	int total_mailboxes, x;
    	struct sip_mailbox *mailbox;
    
    	i = ao2_iterator_init(peers, 0);
    	while ((peer = ao2_iterator_next(&i))) {
    		ao2_lock(peer);
    
    		data_peer = ast_data_add_node(data_root, "peer");
    		if (!data_peer) {
    			ao2_unlock(peer);
    			ao2_ref(peer, -1);
    			continue;
    		}
    
    		ast_data_add_structure(sip_peer, data_peer, peer);
    
    		/* transfer mode */
    		enum_node = ast_data_add_node(data_peer, "allowtransfer");
    		if (!enum_node) {
    
    			ao2_unlock(peer);
    			ao2_ref(peer, -1);
    
    			continue;
    		}
    		ast_data_add_str(enum_node, "text", transfermode2str(peer->allowtransfer));
    		ast_data_add_int(enum_node, "value", peer->allowtransfer);
    
    		/* transports */
    		ast_data_add_str(data_peer, "transports", get_transport_list(peer->transports));
    
    		/* peer type */
    		if ((peer->type & SIP_TYPE_USER) && (peer->type & SIP_TYPE_PEER)) {
    			ast_data_add_str(data_peer, "type", "friend");
    		} else if (peer->type & SIP_TYPE_PEER) {
    			ast_data_add_str(data_peer, "type", "peer");
    		} else if (peer->type & SIP_TYPE_USER) {
    			ast_data_add_str(data_peer, "type", "user");
    		}
    
    		/* mailboxes */
    		total_mailboxes = 0;
    		AST_LIST_TRAVERSE(&peer->mailboxes, mailbox, entry) {
    			if (!total_mailboxes) {
    				data_peer_mailboxes = ast_data_add_node(data_peer, "mailboxes");
    				if (!data_peer_mailboxes) {
    					break;
    				}
    				total_mailboxes++;
    			}
    
    			data_peer_mailbox = ast_data_add_node(data_peer_mailboxes, "mailbox");
    			if (!data_peer_mailbox) {
    				continue;
    			}
    
    			ast_data_add_str(data_peer_mailbox, "id", mailbox->id);
    
    		}
    
    		/* amaflags */
    		enum_node = ast_data_add_node(data_peer, "amaflags");
    		if (!enum_node) {
    
    			ao2_unlock(peer);
    			ao2_ref(peer, -1);
    
    			continue;
    		}
    		ast_data_add_int(enum_node, "value", peer->amaflags);
    
    		ast_data_add_str(enum_node, "text", ast_channel_amaflags2string(peer->amaflags));
    
    
    		/* sip options */
    		data_sip_options = ast_data_add_node(data_peer, "sipoptions");
    		if (!data_sip_options) {
    
    			ao2_unlock(peer);
    			ao2_ref(peer, -1);
    
    			continue;
    		}
    		for (x = 0 ; x < ARRAY_LEN(sip_options); x++) {
    			ast_data_add_bool(data_sip_options, sip_options[x].text, peer->sipoptions & sip_options[x].id);
    		}
    
    		/* callingpres */
    		enum_node = ast_data_add_node(data_peer, "callingpres");
    		if (!enum_node) {
    
    			ao2_unlock(peer);
    			ao2_ref(peer, -1);
    
    			continue;
    		}
    		ast_data_add_int(enum_node, "value", peer->callingpres);
    		ast_data_add_str(enum_node, "text", ast_describe_caller_presentation(peer->callingpres));
    
    		/* codecs */
    
    		ast_data_add_codecs(data_peer, "codecs", peer->caps);
    
    
    		if (!ast_data_search_match(search, data_peer)) {
    			ast_data_remove_node(data_root, data_peer);
    		}
    
    		ao2_unlock(peer);
    		ao2_ref(peer, -1);
    	}
    	ao2_iterator_destroy(&i);
    
    	return 0;
    }
    
    static const struct ast_data_handler peers_data_provider = {
    	.version = AST_DATA_HANDLER_VERSION,
    	.get = peers_data_provider_get
    };
    
    static const struct ast_data_entry sip_data_providers[] = {
    	AST_DATA_ENTRY("asterisk/channel/sip/peers", &peers_data_provider),
    };
    
    
    static const struct ast_sip_api_tech chan_sip_api_provider = {
    	.version = AST_SIP_API_VERSION,
    	.name = "chan_sip",
    	.sipinfo_send = sipinfo_send,
    };
    
    
    Andrew Latham's avatar
    Andrew Latham committed
    /*!
     * \brief Load the module
     *
     * Module loading including tests for configuration or dependencies.
     * This function can return AST_MODULE_LOAD_FAILURE, AST_MODULE_LOAD_DECLINE,
     * or AST_MODULE_LOAD_SUCCESS. If a dependency or environment variable fails
    
     * tests return AST_MODULE_LOAD_FAILURE. If the module can not load the
     * configuration file or other non-critical problem return
    
    Andrew Latham's avatar
    Andrew Latham committed
     * AST_MODULE_LOAD_DECLINE. On success return AST_MODULE_LOAD_SUCCESS.
     */
    
    static int load_module(void)
    
    	ast_verbose("SIP channel loading...\n");
    
    	if (STASIS_MESSAGE_TYPE_INIT(session_timeout_type)) {
    
    		return AST_MODULE_LOAD_FAILURE;
    	}
    
    
    	if (!(sip_tech.capabilities = ast_format_cap_alloc(0))) {
    
    	if (ast_sip_api_provider_register(&chan_sip_api_provider)) {
    
    		return AST_MODULE_LOAD_FAILURE;
    	}
    
    
    	/* the fact that ao2_containers can't resize automatically is a major worry! */
    
    Olle Johansson's avatar
    Olle Johansson committed
    	/* if the number of objects gets above MAX_XXX_BUCKETS, things will slow down */
    
    	peers = ao2_t_container_alloc(HASH_PEER_SIZE, peer_hash_cb, peer_cmp_cb, "allocate peers");
    	peers_by_ip = ao2_t_container_alloc(HASH_PEER_SIZE, peer_iphash_cb, peer_ipcmp_cb, "allocate peers_by_ip");
    	dialogs = ao2_t_container_alloc(HASH_DIALOG_SIZE, dialog_hash_cb, dialog_cmp_cb, "allocate dialogs");
    
    	dialogs_needdestroy = ao2_t_container_alloc(1, NULL, NULL, "allocate dialogs_needdestroy");
    
    	dialogs_rtpcheck = ao2_t_container_alloc(HASH_DIALOG_SIZE, dialog_hash_cb, dialog_cmp_cb, "allocate dialogs for rtpchecks");
    
    	threadt = ao2_t_container_alloc(HASH_DIALOG_SIZE, threadt_hash_cb, threadt_cmp_cb, "allocate threadt table");
    
    	if (!peers || !peers_by_ip || !dialogs || !dialogs_needdestroy || !dialogs_rtpcheck
    		|| !threadt) {
    		ast_log(LOG_ERROR, "Unable to create primary SIP container(s)\n");
    
    		return AST_MODULE_LOAD_FAILURE;
    	}
    
    	if (!(sip_cfg.caps = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT))) {
    		unload_module();
    
    	ast_format_cap_append_by_type(sip_tech.capabilities, AST_MEDIA_TYPE_AUDIO);
    
    	registry_list = ao2_t_container_alloc(HASH_REGISTRY_SIZE, registry_hash_cb, registry_cmp_cb, "allocate registry_list");
    	subscription_mwi_list = ao2_t_container_alloc_list(AO2_ALLOC_OPT_LOCK_MUTEX,
    		AO2_CONTAINER_ALLOC_OPT_INSERT_BEGIN, NULL, NULL, "allocate subscription_mwi_list");
    
    	if (!(sched = ast_sched_context_create())) {
    
    		ast_log(LOG_ERROR, "Unable to create scheduler context\n");
    
    	if (!(io = io_context_create())) {
    		ast_log(LOG_ERROR, "Unable to create I/O context\n");
    
    	sip_reloadreason = CHANNEL_MODULE_LOAD;
    
    	can_parse_xml = sip_is_xml_parsable();
    
    	if (reload_config(sip_reloadreason)) {	/* Load the configuration from sip.conf */
    
    		return AST_MODULE_LOAD_DECLINE;
    
    Olle Johansson's avatar
    Olle Johansson committed
    	}
    
    	/* Initialize bogus peer. Can be done first after reload_config() */
    	if (!(bogus_peer = temp_peer("(bogus_peer)"))) {
    		ast_log(LOG_ERROR, "Unable to create bogus_peer for authentication\n");
    
    		return AST_MODULE_LOAD_FAILURE;
    	}
    	/* Make sure the auth will always fail. */
    	ast_string_field_set(bogus_peer, md5secret, BOGUS_PEER_MD5SECRET);
    	ast_clear_flag(&bogus_peer->flags[0], SIP_INSECURE);
    
    
    	/* 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));
    
    	if (ast_msg_tech_register(&sip_msg_tech)) {
    
    		return AST_MODULE_LOAD_FAILURE;
    	}
    
    
    	/* 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");
    
    #ifdef TEST_FRAMEWORK
    	AST_TEST_REGISTER(test_sip_peers_get);
    
    	AST_TEST_REGISTER(test_sip_mwi_subscribe_parse);
    
    	AST_TEST_REGISTER(test_tcp_message_fragmentation);
    
    	AST_TEST_REGISTER(get_in_brackets_const_test);
    
    #endif
    
    	/* Register AstData providers */
    	ast_data_register_multiple(sip_data_providers, ARRAY_LEN(sip_data_providers));
    
    
    	/* Register all CLI functions for SIP */
    
    	ast_cli_register_multiple(cli_sip, ARRAY_LEN(cli_sip));
    
    	/* Tell the RTP engine about our RTP glue */
    	ast_rtp_glue_register(&sip_rtp_glue);
    
    
    	/* Register dialplan applications */
    
    	ast_register_application_xml(app_dtmfmode, sip_dtmfmode);
    	ast_register_application_xml(app_sipaddheader, sip_addheader);
    
    	ast_register_application_xml(app_sipremoveheader, sip_removeheader);
    
    #ifdef TEST_FRAMEWORK
    	ast_register_application_xml(app_sipsendcustominfo, sip_sendcustominfo);
    #endif
    
    
    	/* Register dialplan functions */
    	ast_custom_function_register(&sip_header_function);
    	ast_custom_function_register(&sippeer_function);
    
    	ast_custom_function_register(&checksipdomain_function);
    
    	ast_manager_register_xml("SIPpeers", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, manager_sip_show_peers);
    	ast_manager_register_xml("SIPshowpeer", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, manager_sip_show_peer);
    	ast_manager_register_xml("SIPqualifypeer", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, manager_sip_qualify_peer);
    	ast_manager_register_xml("SIPshowregistry", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, manager_show_registry);
    	ast_manager_register_xml("SIPnotify", EVENT_FLAG_SYSTEM, manager_sipnotify);
    
    	ast_manager_register_xml("SIPpeerstatus", EVENT_FLAG_SYSTEM, manager_sip_peer_status);
    
    	sip_poke_all_peers();
    	sip_keepalive_all_peers();
    
    	sip_send_all_registers();
    
    	initialize_escs();
    
    	if (sip_epa_register(&cc_epa_static_data)) {
    
    		return AST_MODULE_LOAD_DECLINE;
    	}
    
    
    	if (sip_reqresp_parser_init() == -1) {
    		ast_log(LOG_ERROR, "Unable to initialize the SIP request and response parser\n");
    
    		return AST_MODULE_LOAD_DECLINE;
    	}
    
    
    	if (can_parse_xml) {
    		/* SIP CC agents require the ability to parse XML PIDF bodies
    		 * in incoming PUBLISH requests
    		 */
    		if (ast_cc_agent_register(&sip_cc_agent_callbacks)) {
    
    			return AST_MODULE_LOAD_DECLINE;
    		}
    	}
    	if (ast_cc_monitor_register(&sip_cc_monitor_callbacks)) {
    
    		return AST_MODULE_LOAD_DECLINE;
    	}
    	if (!(sip_monitor_instances = ao2_container_alloc(37, sip_monitor_instance_hash_fn, sip_monitor_instance_cmp_fn))) {
    
    		return AST_MODULE_LOAD_DECLINE;
    	}
    
    	/* And start the monitor for the first time */
    	restart_monitor();
    
    
    	ast_realtime_require_field(ast_check_realtime("sipregs") ? "sipregs" : "sippeers",
    		"name", RQ_CHAR, 10,
    
    		"ipaddr", RQ_CHAR, INET6_ADDRSTRLEN - 1,
    
    		"useragent", RQ_CHAR, 20,
    
    		"lastms", RQ_INTEGER4, 11,
    
    	ast_websocket_add_protocol("sip", sip_websocket_callback);
    
    
    /*! \brief PBX unload module API */
    
    static int unload_module(void)
    
    	struct sip_threadinfo *th;
    
    	ast_sip_api_provider_unregister();
    
    
    	ast_websocket_remove_protocol("sip", sip_websocket_callback);
    
    
    	network_change_stasis_unsubscribe();
    	acl_change_event_stasis_unsubscribe();
    
    	/* First, take us out of the channel type list */
    	ast_channel_unregister(&sip_tech);
    
    
    	ast_msg_tech_unregister(&sip_msg_tech);
    
    
    	/* Unregister dial plan functions */
    
    	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);
    
    	ast_unregister_application(app_sipremoveheader);
    
    	ast_unregister_application(app_sipsendcustominfo);
    
    
    	AST_TEST_UNREGISTER(test_sip_peers_get);
    
    	AST_TEST_UNREGISTER(test_sip_mwi_subscribe_parse);
    
    	AST_TEST_UNREGISTER(test_tcp_message_fragmentation);
    
    	AST_TEST_UNREGISTER(get_in_brackets_const_test);
    
    #endif
    	/* Unregister all the AstData providers */
    	ast_data_unregister(NULL);
    
    
    	/* Unregister CLI commands */
    
    	ast_cli_unregister_multiple(cli_sip, ARRAY_LEN(cli_sip));
    
    	/* Disconnect from RTP engine */
    	ast_rtp_glue_unregister(&sip_rtp_glue);
    
    
    	/* Unregister AMI actions */
    
    	ast_manager_unregister("SIPpeers");
    	ast_manager_unregister("SIPshowpeer");
    
    	ast_manager_unregister("SIPqualifypeer");
    
    	ast_manager_unregister("SIPshowregistry");
    
    	ast_manager_unregister("SIPnotify");
    
    	ast_manager_unregister("SIPpeerstatus");
    
    	/* Kill TCP/TLS server threads */
    
    Olle Johansson's avatar
    Olle Johansson committed
    	if (sip_tcp_desc.master) {
    
    		ast_tcptls_server_stop(&sip_tcp_desc);
    
    Olle Johansson's avatar
    Olle Johansson committed
    	}
    	if (sip_tls_desc.master) {
    
    		ast_tcptls_server_stop(&sip_tls_desc);
    
    Olle Johansson's avatar
    Olle Johansson committed
    	}
    
    	ast_ssl_teardown(sip_tls_desc.tls_cfg);
    
    
    	/* Kill all existing TCP/TLS threads */
    
    	i = ao2_iterator_init(threadt, 0);
    	while ((th = ao2_t_iterator_next(&i, "iterate through tcp threads for 'sip show tcp'"))) {
    
    		pthread_t thread = th->threadid;
    		th->stop = 1;
    		pthread_kill(thread, SIGURG);
    
    		ao2_t_ref(th, -1, "decrement ref from iterator");
    
    	/* Hangup all dialogs if they have an owner */
    
    	i = ao2_iterator_init(dialogs, 0);
    	while ((p = ao2_t_iterator_next(&i, "iterate thru dialogs"))) {
    
    		if (p->owner)
    			ast_softhangup(p->owner, AST_SOFTHANGUP_APPUNLOAD);
    
    		ao2_t_ref(p, -1, "toss dialog ptr from iterator_next");
    
    	unlink_all_peers_from_tables();
    
    
    	if (monitor_thread && (monitor_thread != AST_PTHREADT_STOP) && (monitor_thread != AST_PTHREADT_NULL)) {
    
    		pthread_t th = monitor_thread;
    		monitor_thread = AST_PTHREADT_STOP;
    		pthread_cancel(th);
    		pthread_kill(th, SIGURG);
    		ast_mutex_unlock(&monlock);
    		pthread_join(th, NULL);
    	} else {
    		monitor_thread = AST_PTHREADT_STOP;
    		ast_mutex_unlock(&monlock);
    
    	/* Destroy all the dialogs and free their memory */
    
    	i = ao2_iterator_init(dialogs, 0);
    	while ((p = ao2_t_iterator_next(&i, "iterate thru dialogs"))) {
    
    		ao2_t_ref(p, -1, "throw away iterator result");
    
    	/* Free memory for local network address mask */
    
    	ast_free_ha(localaddr);
    
    	ast_mutex_lock(&authl_lock);
    	if (authl) {
    
    		ao2_t_cleanup(authl, "Removing global authentication");
    
    		authl = NULL;
    	}
    	ast_mutex_unlock(&authl_lock);
    
    	sip_epa_unregister_all();
    
    	ast_free(default_tls_cfg.certfile);
    	ast_free(default_tls_cfg.pvtfile);
    	ast_free(default_tls_cfg.cipher);
    	ast_free(default_tls_cfg.cafile);
    	ast_free(default_tls_cfg.capath);
    
    	cleanup_all_regs();
    
    	ao2_cleanup(registry_list);
    
    	{
    		struct ao2_iterator iter;
    		struct sip_subscription_mwi *iterator;
    
    		iter = ao2_iterator_init(subscription_mwi_list, 0);
    		while ((iterator = ao2_t_iterator_next(&iter, "unload_module iter"))) {
    			ao2_lock(iterator);
    			if (iterator->dnsmgr) {
    				ast_dnsmgr_release(iterator->dnsmgr);
    				iterator->dnsmgr = NULL;
    				ao2_t_ref(iterator, -1, "dnsmgr release");
    			}
    			ao2_unlock(iterator);
    			ao2_t_ref(iterator, -1, "unload_module iter");
    
    		ao2_iterator_destroy(&iter);
    	}
    	ao2_cleanup(subscription_mwi_list);
    
    	/*
    	 * Wait awhile for the TCP/TLS thread container to become empty.
    	 *
    	 * XXX This is a hack, but the worker threads cannot be created
    	 * joinable.  They can die on their own and remove themselves
    	 * from the container thus resulting in a huge memory leak.
    	 */
    	wait_count = 1000;
    	while (ao2_container_count(threadt) && --wait_count) {
    		sched_yield();
    	}
    	if (!wait_count) {
    		ast_debug(2, "TCP/TLS thread container did not become empty :(\n");
    	}
    
    
    	ao2_t_cleanup(bogus_peer, "unref the bogus_peer");
    
    	ao2_t_cleanup(peers, "unref the peers table");
    	ao2_t_cleanup(peers_by_ip, "unref the peers_by_ip table");
    	ao2_t_cleanup(dialogs, "unref the dialogs table");
    	ao2_t_cleanup(dialogs_needdestroy, "unref dialogs_needdestroy");
    	ao2_t_cleanup(dialogs_rtpcheck, "unref dialogs_rtpcheck");
    	ao2_t_cleanup(threadt, "unref the thread table");
    	ao2_t_cleanup(sip_monitor_instances, "unref the sip_monitor_instances table");
    
    	sip_cfg.contact_acl = ast_free_acl_list(sip_cfg.contact_acl);
    
    Kevin Harwell's avatar
    Kevin Harwell committed
    	if (sipsock_read_id) {
    		ast_io_remove(io, sipsock_read_id);
    		sipsock_read_id = NULL;
    	}
    
    Kevin Harwell's avatar
    Kevin Harwell committed
    	io_context_destroy(io);
    
    	ast_sched_context_destroy(sched);
    
    	con = ast_context_find(used_context);
    
    Olle Johansson's avatar
    Olle Johansson committed
    	if (con) {
    
    Olle Johansson's avatar
    Olle Johansson committed
    	}
    
    	ast_unload_realtime("sipregs");
    	ast_unload_realtime("sippeers");
    
    	ast_cc_monitor_unregister(&sip_cc_monitor_callbacks);
    	ast_cc_agent_unregister(&sip_cc_agent_callbacks);
    
    	sip_reqresp_parser_exit();
    
    Kevin Harwell's avatar
    Kevin Harwell committed
    	if (notify_types) {
    		ast_config_destroy(notify_types);
    		notify_types = NULL;
    	}
    
    
    	ao2_cleanup(sip_tech.capabilities);
    	sip_tech.capabilities = NULL;
    	ao2_cleanup(sip_cfg.caps);
    	sip_cfg.caps = NULL;
    
    	STASIS_MESSAGE_TYPE_CLEANUP(session_timeout_type);
    
    
    AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Session Initiation Protocol (SIP)",
    
    		.load = load_module,
    		.unload = unload_module,
    		.reload = reload,
    
    		.nonoptreq = "res_crypto,res_http_websocket",