Skip to content
Snippets Groups Projects
chan_sip.c 1.16 MiB
Newer Older
  • Learn to ignore specific revisions
  • 	return monitor_instance;
    
    static int find_sip_monitor_instance_by_subscription_pvt(void *obj, void *arg, int flags)
    
    	struct sip_monitor_instance *monitor_instance = obj;
    	return monitor_instance->subscription_pvt == arg ? CMP_MATCH | CMP_STOP : 0;
    }
    
    static int find_sip_monitor_instance_by_suspension_entry(void *obj, void *arg, int flags)
    {
    	struct sip_monitor_instance *monitor_instance = obj;
    	return monitor_instance->suspension_entry == arg ? CMP_MATCH | CMP_STOP : 0;
    }
    
    static int sip_cc_monitor_request_cc(struct ast_cc_monitor *monitor, int *available_timer_id);
    static int sip_cc_monitor_suspend(struct ast_cc_monitor *monitor);
    static int sip_cc_monitor_unsuspend(struct ast_cc_monitor *monitor);
    static int sip_cc_monitor_cancel_available_timer(struct ast_cc_monitor *monitor, int *sched_id);
    static void sip_cc_monitor_destructor(void *private_data);
    
    static struct ast_cc_monitor_callbacks sip_cc_monitor_callbacks = {
    	.type = "SIP",
    	.request_cc = sip_cc_monitor_request_cc,
    	.suspend = sip_cc_monitor_suspend,
    	.unsuspend = sip_cc_monitor_unsuspend,
    	.cancel_available_timer = sip_cc_monitor_cancel_available_timer,
    	.destructor = sip_cc_monitor_destructor,
    };
    
    static int sip_cc_monitor_request_cc(struct ast_cc_monitor *monitor, int *available_timer_id)
    {
    	struct sip_monitor_instance *monitor_instance = monitor->private_data;
    	enum ast_cc_service_type service = monitor->service_offered;
    	int when;
    
    	if (!monitor_instance) {
    		return -1;
    
    	if (!(monitor_instance->subscription_pvt = sip_alloc(NULL, NULL, 0, SIP_SUBSCRIBE, NULL, NULL))) {
    
    	when = service == AST_CC_CCBS ? ast_get_ccbs_available_timer(monitor->interface->config_params) :
    		ast_get_ccnr_available_timer(monitor->interface->config_params);
    
    	sip_pvt_lock(monitor_instance->subscription_pvt);
    
    	ast_set_flag(&monitor_instance->subscription_pvt->flags[0], SIP_OUTGOING);
    
    	create_addr(monitor_instance->subscription_pvt, monitor_instance->peername, 0, 1);
    
    Mark Michelson's avatar
    Mark Michelson committed
    	ast_sip_ouraddrfor(&monitor_instance->subscription_pvt->sa, &monitor_instance->subscription_pvt->ourip, monitor_instance->subscription_pvt);
    
    	monitor_instance->subscription_pvt->subscribed = CALL_COMPLETION;
    	monitor_instance->subscription_pvt->expiry = when;
    
    	transmit_invite(monitor_instance->subscription_pvt, SIP_SUBSCRIBE, FALSE, 2, monitor_instance->subscribe_uri);
    	sip_pvt_unlock(monitor_instance->subscription_pvt);
    
    	ao2_t_ref(monitor, +1, "Adding a ref to the monitor for the scheduler");
    	*available_timer_id = ast_sched_add(sched, when * 1000, ast_cc_available_timer_expire, monitor);
    	return 0;
    
    static int construct_pidf_body(enum sip_cc_publish_state state, char *pidf_body, size_t size, const char *presentity)
    
    	struct ast_str *body = ast_str_alloca(size);
    	char tuple_id[32];
    
    	generate_random_string(tuple_id, sizeof(tuple_id));
    
    	/* We'll make this a bare-bones pidf body. In state_notify_build_xml, the PIDF
    	 * body gets a lot more extra junk that isn't necessary, so we'll leave it out here.
    	 */
    	ast_str_append(&body, 0, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
    	/* XXX The entity attribute is currently set to the peer name associated with the
    	 * dialog. This is because we currently only call this function for call-completion
    	 * PUBLISH bodies. In such cases, the entity is completely disregarded. For other
    	 * event packages, it may be crucial to have a proper URI as the presentity so this
    	 * should be revisited as support is expanded.
    	 */
    	ast_str_append(&body, 0, "<presence xmlns=\"urn:ietf:params:xml:ns:pidf\" entity=\"%s\">\n", presentity);
    	ast_str_append(&body, 0, "<tuple id=\"%s\">\n", tuple_id);
    	ast_str_append(&body, 0, "<status><basic>%s</basic></status>\n", state == CC_OPEN ? "open" : "closed");
    	ast_str_append(&body, 0, "</tuple>\n");
    	ast_str_append(&body, 0, "</presence>\n");
    	ast_copy_string(pidf_body, ast_str_buffer(body), size);
    	return 0;
    
    static int sip_cc_monitor_suspend(struct ast_cc_monitor *monitor)
    
    	struct sip_monitor_instance *monitor_instance = monitor->private_data;
    	enum sip_publish_type publish_type;
    	struct cc_epa_entry *cc_entry;
    
    	if (!monitor_instance) {
    		return -1;
    	}
    
    	if (!monitor_instance->suspension_entry) {
    		/* We haven't yet allocated the suspension entry, so let's give it a shot */
    		if (!(monitor_instance->suspension_entry = create_epa_entry("call-completion", monitor_instance->peername))) {
    			ast_log(LOG_WARNING, "Unable to allocate sip EPA entry for call-completion\n");
    			ao2_ref(monitor_instance, -1);
    			return -1;
    
    		if (!(cc_entry = ast_calloc(1, sizeof(*cc_entry)))) {
    			ast_log(LOG_WARNING, "Unable to allocate space for instance data of EPA entry for call-completion\n");
    			ao2_ref(monitor_instance, -1);
    			return -1;
    		}
    		cc_entry->core_id = monitor->core_id;
    		monitor_instance->suspension_entry->instance_data = cc_entry;
    		publish_type = SIP_PUBLISH_INITIAL;
    
    		publish_type = SIP_PUBLISH_MODIFY;
    		cc_entry = monitor_instance->suspension_entry->instance_data;
    	}
    
    	cc_entry->current_state = CC_CLOSED;
    
    	if (ast_strlen_zero(monitor_instance->notify_uri)) {
    		/* If we have no set notify_uri, then what this means is that we have
    		 * not received a NOTIFY from this destination stating that he is
    		 * currently available.
    		 *
    		 * This situation can arise when the core calls the suspend callbacks
    		 * of multiple destinations. If one of the other destinations aside
    		 * from this one notified Asterisk that he is available, then there
    		 * is no reason to take any suspension action on this device. Rather,
    		 * we should return now and if we receive a NOTIFY while monitoring
    		 * is still "suspended" then we can immediately respond with the
    		 * proper PUBLISH to let this endpoint know what is going on.
    		 */
    		return 0;
    
    	construct_pidf_body(CC_CLOSED, monitor_instance->suspension_entry->body, sizeof(monitor_instance->suspension_entry->body), monitor_instance->peername);
    	return transmit_publish(monitor_instance->suspension_entry, publish_type, monitor_instance->notify_uri);
    }
    
    static int sip_cc_monitor_unsuspend(struct ast_cc_monitor *monitor)
    {
    	struct sip_monitor_instance *monitor_instance = monitor->private_data;
    	struct cc_epa_entry *cc_entry;
    
    	if (!monitor_instance) {
    		return -1;
    	}
    
    	ast_assert(monitor_instance->suspension_entry != NULL);
    
    	cc_entry = monitor_instance->suspension_entry->instance_data;
    	cc_entry->current_state = CC_OPEN;
    	if (ast_strlen_zero(monitor_instance->notify_uri)) {
    		/* This means we are being asked to unsuspend a call leg we never
    		 * sent a PUBLISH on. As such, there is no reason to send another
    		 * PUBLISH at this point either. We can just return instead.
    		 */
    		return 0;
    	}
    	construct_pidf_body(CC_OPEN, monitor_instance->suspension_entry->body, sizeof(monitor_instance->suspension_entry->body), monitor_instance->peername);
    	return transmit_publish(monitor_instance->suspension_entry, SIP_PUBLISH_MODIFY, monitor_instance->notify_uri);
    }
    
    static int sip_cc_monitor_cancel_available_timer(struct ast_cc_monitor *monitor, int *sched_id)
    {
    	if (*sched_id != -1) {
    		AST_SCHED_DEL(sched, *sched_id);
    		ao2_t_ref(monitor, -1, "Removing scheduler's reference to the monitor");
    	}
    	return 0;
    }
    
    static void sip_cc_monitor_destructor(void *private_data)
    {
    	struct sip_monitor_instance *monitor_instance = private_data;
    	ao2_unlink(sip_monitor_instances, monitor_instance);
    	ast_module_unref(ast_module_info->self);
    }
    
    static int sip_get_cc_information(struct sip_request *req, char *subscribe_uri, size_t size, enum ast_cc_service_type *service)
    {
    
    	char *call_info = ast_strdupa(sip_get_header(req, "Call-Info"));
    
    	char *uri;
    	char *purpose;
    	char *service_str;
    	static const char cc_purpose[] = "purpose=call-completion";
    	static const int cc_purpose_len = sizeof(cc_purpose) - 1;
    
    	if (ast_strlen_zero(call_info)) {
    		/* No Call-Info present. Definitely no CC offer */
    		return -1;
    	}
    
    	uri = strsep(&call_info, ";");
    
    	while ((purpose = strsep(&call_info, ";"))) {
    		if (!strncmp(purpose, cc_purpose, cc_purpose_len)) {
    			break;
    		}
    	}
    	if (!purpose) {
    		/* We didn't find the appropriate purpose= parameter. Oh well */
    		return -1;
    	}
    
    	/* Okay, call-completion has been offered. Let's figure out what type of service this is */
    	while ((service_str = strsep(&call_info, ";"))) {
    		if (!strncmp(service_str, "m=", 2)) {
    			break;
    
    	}
    	if (!service_str) {
    		/* So they didn't offer a particular service, We'll just go with CCBS since it really
    		 * doesn't matter anyway
    		 */
    		service_str = "BS";
    	} else {
    		/* We already determined that there is an "m=" so no need to check
    		 * the result of this strsep
    		 */
    		strsep(&service_str, "=");
    	}
    
    	if ((*service = service_string_to_service_type(service_str)) == AST_CC_NONE) {
    		/* Invalid service offered */
    		return -1;
    	}
    
    	ast_copy_string(subscribe_uri, get_in_brackets(uri), size);
    
    /*
     * \brief Determine what, if any, CC has been offered and queue a CC frame if possible
     *
     * After taking care of some formalities to be sure that this call is eligible for CC,
     * we first try to see if we can make use of native CC. We grab the information from
     * the passed-in sip_request (which is always a response to an INVITE). If we can
     * use native CC monitoring for the call, then so be it.
     *
     * If native cc monitoring is not possible or not supported, then we will instead attempt
     * to use generic monitoring. Falling back to generic from a failed attempt at using native
     * monitoring will only work if the monitor policy of the endpoint is "always"
     *
     * \param pvt The current dialog. Contains CC parameters for the endpoint
     * \param req The response to the INVITE we want to inspect
     * \param service The service to use if generic monitoring is to be used. For native
     * monitoring, we get the service from the SIP response itself
     */
    static void sip_handle_cc(struct sip_pvt *pvt, struct sip_request *req, enum ast_cc_service_type service)
    {
    	enum ast_cc_monitor_policies monitor_policy = ast_get_cc_monitor_policy(pvt->cc_params);
    	int core_id;
    	char interface_name[AST_CHANNEL_NAME];
    
    	if (monitor_policy == AST_CC_MONITOR_NEVER) {
    		/* Don't bother, just return */
    		return;
    
    	if ((core_id = ast_cc_get_current_core_id(pvt->owner)) == -1) {
    		/* For some reason, CC is invalid, so don't try it! */
    		return;
    
    	ast_channel_get_device_name(pvt->owner, interface_name, sizeof(interface_name));
    
    	if (monitor_policy == AST_CC_MONITOR_ALWAYS || monitor_policy == AST_CC_MONITOR_NATIVE) {
    		char subscribe_uri[SIPBUFSIZE];
    		char device_name[AST_CHANNEL_NAME];
    		enum ast_cc_service_type offered_service;
    		struct sip_monitor_instance *monitor_instance;
    		if (sip_get_cc_information(req, subscribe_uri, sizeof(subscribe_uri), &offered_service)) {
    			/* If CC isn't being offered to us, or for some reason the CC offer is
    			 * not formatted correctly, then it may still be possible to use generic
    			 * call completion since the monitor policy may be "always"
    			 */
    			goto generic;
    
    		ast_channel_get_device_name(pvt->owner, device_name, sizeof(device_name));
    		if (!(monitor_instance = sip_monitor_instance_init(core_id, subscribe_uri, pvt->peername, device_name))) {
    			/* Same deal. We can try using generic still */
    			goto generic;
    
    		/* We bump the refcount of chan_sip because once we queue this frame, the CC core
    		 * will have a reference to callbacks in this module. We decrement the module
    		 * refcount once the monitor destructor is called
    		 */
    		ast_module_ref(ast_module_info->self);
    		ast_queue_cc_frame(pvt->owner, "SIP", pvt->dialstring, offered_service, monitor_instance);
    		ao2_ref(monitor_instance, -1);
    		return;
    	}
    
    generic:
    	if (monitor_policy == AST_CC_MONITOR_GENERIC || monitor_policy == AST_CC_MONITOR_ALWAYS) {
    		ast_queue_cc_frame(pvt->owner, AST_CC_GENERIC_MONITOR_TYPE, interface_name, service, NULL);
    
    /*! \brief Working TLS connection configuration */
    static struct ast_tls_config sip_tls_cfg;
    
    /*! \brief Default TLS connection configuration */
    static struct ast_tls_config default_tls_cfg;
    
    /*! \brief The TCP server definition */
    static struct ast_tcptls_session_args sip_tcp_desc = {
    	.accept_fd = -1,
    	.master = AST_PTHREADT_NULL,
    	.tls_cfg = NULL,
    	.poll_timeout = -1,
    	.name = "SIP TCP server",
    	.accept_fn = ast_tcptls_server_root,
    	.worker_fn = sip_tcp_worker_fn,
    };
    
    /*! \brief The TCP/TLS server definition */
    static struct ast_tcptls_session_args sip_tls_desc = {
    	.accept_fd = -1,
    	.master = AST_PTHREADT_NULL,
    	.tls_cfg = &sip_tls_cfg,
    	.poll_timeout = -1,
    	.name = "SIP TLS server",
    	.accept_fn = ast_tcptls_server_root,
    	.worker_fn = sip_tcp_worker_fn,
    };
    
    /*! \brief Append to SIP dialog history
    	\return Always returns 0 */
    #define append_history(p, event, fmt , args... )	append_history_full(p, "%-15s " fmt, event, ## args)
    
    
    struct sip_pvt *dialog_ref_debug(struct sip_pvt *p, const char *tag, char *file, int line, const char *func)
    
    	if (p)
    #ifdef REF_DEBUG
    		__ao2_ref_debug(p, 1, tag, file, line, func);
    #else
    		ao2_ref(p, 1);
    #endif
    	else
    		ast_log(LOG_ERROR, "Attempt to Ref a null pointer\n");
    	return p;
    
    struct sip_pvt *dialog_unref_debug(struct sip_pvt *p, const char *tag, char *file, int line, const char *func)
    
    	if (p)
    #ifdef REF_DEBUG
    		__ao2_ref_debug(p, -1, tag, file, line, func);
    #else
    		ao2_ref(p, -1);
    #endif
    	return NULL;
    
    /*! \brief map from an integer value to a string.
     * If no match is found, return errorstring
    
    static const char *map_x_s(const struct _map_x_s *table, int x, const char *errorstring)
    
    	const struct _map_x_s *cur;
    
    
    Olle Johansson's avatar
    Olle Johansson committed
    	for (cur = table; cur->s; cur++) {
    		if (cur->x == x) {
    
    	return errorstring;
    
    /*! \brief map from a string to an integer value, case insensitive.
     * If no match is found, return errorvalue.
    
    static int map_s_x(const struct _map_x_s *table, const char *s, int errorvalue)
    
    	const struct _map_x_s *cur;
    
    Olle Johansson's avatar
    Olle Johansson committed
    	for (cur = table; cur->s; cur++) {
    		if (!strcasecmp(cur->s, s)) {
    
    	return errorvalue;
    }
    
    static enum AST_REDIRECTING_REASON sip_reason_str_to_code(const char *text)
    {
    	enum AST_REDIRECTING_REASON ast = AST_REDIRECTING_REASON_UNKNOWN;
    	int i;
    
    	for (i = 0; i < ARRAY_LEN(sip_reason_table); ++i) {
    		if (!strcasecmp(text, sip_reason_table[i].text)) {
    			ast = sip_reason_table[i].code;
    			break;
    
    static const char *sip_reason_code_to_str(struct ast_party_redirecting_reason *reason, int *table_lookup)
    
    	int code = reason->code;
    
    	/* If there's a specific string set, then we just
    	 * use it.
    	 */
    	if (!ast_strlen_zero(reason->str)) {
    		/* If we care about whether this can be found in
    		 * the table, then we need to check about that.
    		 */
    		if (table_lookup) {
    			/* If the string is literally "unknown" then don't bother with the lookup
    			 * because it can lead to a false negative.
    			 */
    			if (!strcasecmp(reason->str, "unknown") ||
    					sip_reason_str_to_code(reason->str) != AST_REDIRECTING_REASON_UNKNOWN) {
    				*table_lookup = TRUE;
    			} else {
    				*table_lookup = FALSE;
    			}
    		}
    		return reason->str;
    	}
    
    	if (table_lookup) {
    		*table_lookup = TRUE;
    	}
    
    
    	if (code >= 0 && code < ARRAY_LEN(sip_reason_table)) {
    		return sip_reason_table[code].text;
    
    	return "unknown";
    
    /*!
     * \brief generic function for determining if a correct transport is being
     * used to contact a peer
     *
     * this is done as a macro so that the "tmpl" var can be passed either a
     * sip_request or a sip_peer
     */
    #define check_request_transport(peer, tmpl) ({ \
    	int ret = 0; \
    	if (peer->socket.type == tmpl->socket.type) \
    		; \
    	else if (!(peer->transports & tmpl->socket.type)) {\
    		ast_log(LOG_ERROR, \
    			"'%s' is not a valid transport for '%s'. we only use '%s'! ending call.\n", \
    
    			sip_get_transport(tmpl->socket.type), peer->name, get_transport_list(peer->transports) \
    
    	} else if (peer->socket.type & AST_TRANSPORT_TLS) { \
    
    		ast_log(LOG_WARNING, \
    			"peer '%s' HAS NOT USED (OR SWITCHED TO) TLS in favor of '%s' (but this was allowed in sip.conf)!\n", \
    
    			peer->name, sip_get_transport(tmpl->socket.type) \
    
    		); \
    	} else { \
    		ast_debug(1, \
    			"peer '%s' has contacted us over %s even though we prefer %s.\n", \
    
    			peer->name, sip_get_transport(tmpl->socket.type), sip_get_transport(peer->socket.type) \
    
    /*! \brief
     * duplicate a list of channel variables, \return the copy.
     */
    static struct ast_variable *copy_vars(struct ast_variable *src)
    
    	struct ast_variable *res = NULL, *tmp, *v = NULL;
    
    	for (v = src ; v ; v = v->next) {
    		if ((tmp = ast_variable_new(v->name, v->value, v->file))) {
    			tmp->next = res;
    			res = tmp;
    		}
    	}
    	return res;
    
    static void tcptls_packet_destructor(void *obj)
    
    	struct tcptls_packet *packet = obj;
    
    	ast_free(packet->data);
    
    static void sip_tcptls_client_args_destructor(void *obj)
    
    Olle Johansson's avatar
    Olle Johansson committed
    {
    
    	struct ast_tcptls_session_args *args = obj;
    	if (args->tls_cfg) {
    		ast_free(args->tls_cfg->certfile);
    		ast_free(args->tls_cfg->pvtfile);
    		ast_free(args->tls_cfg->cipher);
    		ast_free(args->tls_cfg->cafile);
    		ast_free(args->tls_cfg->capath);
    
    	}
    	ast_free(args->tls_cfg);
    	ast_free((char *) args->name);
    
    static void sip_threadinfo_destructor(void *obj)
    
    	struct sip_threadinfo *th = obj;
    	struct tcptls_packet *packet;
    
    Olle Johansson's avatar
    Olle Johansson committed
    
    
    	if (th->alert_pipe[1] > -1) {
    		close(th->alert_pipe[0]);
    
    	if (th->alert_pipe[1] > -1) {
    		close(th->alert_pipe[1]);
    	}
    	th->alert_pipe[0] = th->alert_pipe[1] = -1;
    
    	while ((packet = AST_LIST_REMOVE_HEAD(&th->packet_q, entry))) {
    		ao2_t_ref(packet, -1, "thread destruction, removing packet from frame queue");
    
    	if (th->tcptls_session) {
    		ao2_t_ref(th->tcptls_session, -1, "remove tcptls_session for sip_threadinfo object");
    	}
    
    /*! \brief creates a sip_threadinfo object and links it into the threadt table. */
    static struct sip_threadinfo *sip_threadinfo_create(struct ast_tcptls_session_instance *tcptls_session, int transport)
    
    	struct sip_threadinfo *th;
    
    	if (!tcptls_session || !(th = ao2_alloc(sizeof(*th), sip_threadinfo_destructor))) {
    
    	th->alert_pipe[0] = th->alert_pipe[1] = -1;
    
    	if (pipe(th->alert_pipe) == -1) {
    		ao2_t_ref(th, -1, "Failed to open alert pipe on sip_threadinfo");
    		ast_log(LOG_ERROR, "Could not create sip alert pipe in tcptls thread, error %s\n", strerror(errno));
    		return NULL;
    
    	ao2_t_ref(tcptls_session, +1, "tcptls_session ref for sip_threadinfo object");
    	th->tcptls_session = tcptls_session;
    
    	th->type = transport ? transport : (tcptls_session->ssl ? AST_TRANSPORT_TLS: AST_TRANSPORT_TCP);
    
    	ao2_t_link(threadt, th, "Adding new tcptls helper thread");
    	ao2_t_ref(th, -1, "Decrementing threadinfo ref from alloc, only table ref remains");
    	return th;
    
    /*! \brief used to indicate to a tcptls thread that data is ready to be written */
    static int sip_tcptls_write(struct ast_tcptls_session_instance *tcptls_session, const void *buf, size_t len)
    
    	int res = len;
    	struct sip_threadinfo *th = NULL;
    	struct tcptls_packet *packet = NULL;
    	struct sip_threadinfo tmp = {
    		.tcptls_session = tcptls_session,
    	};
    	enum sip_tcptls_alert alert = TCPTLS_ALERT_DATA;
    
    	if (!tcptls_session) {
    		return XMIT_ERROR;
    
    	if ((tcptls_session->fd == -1) ||
    		!(th = ao2_t_find(threadt, &tmp, OBJ_POINTER, "ao2_find, getting sip_threadinfo in tcp helper thread")) ||
    		!(packet = ao2_alloc(sizeof(*packet), tcptls_packet_destructor)) ||
    		!(packet->data = ast_str_create(len))) {
    		goto tcptls_write_setup_error;
    	}
    
    	/* goto tcptls_write_error should _NOT_ be used beyond this point */
    	ast_str_set(&packet->data, 0, "%s", (char *) buf);
    	packet->len = len;
    
    	/* alert tcptls thread handler that there is a packet to be sent.
    	 * must lock the thread info object to guarantee control of the
    	 * packet queue */
    	ao2_lock(th);
    	if (write(th->alert_pipe[1], &alert, sizeof(alert)) == -1) {
    		ast_log(LOG_ERROR, "write() to alert pipe failed: %s\n", strerror(errno));
    		ao2_t_ref(packet, -1, "could not write to alert pipe, remove packet");
    		packet = NULL;
    		res = XMIT_ERROR;
    	} else { /* it is safe to queue the frame after issuing the alert when we hold the threadinfo lock */
    		AST_LIST_INSERT_TAIL(&th->packet_q, packet, entry);
    
    	ao2_unlock(th);
    
    	ao2_t_ref(th, -1, "In sip_tcptls_write, unref threadinfo object after finding it");
    	return res;
    
    tcptls_write_setup_error:
    	if (th) {
    		ao2_t_ref(th, -1, "In sip_tcptls_write, unref threadinfo obj, could not create packet");
    
    	if (packet) {
    		ao2_t_ref(packet, -1, "could not allocate packet's data");
    	}
    
    	return XMIT_ERROR;
    
    /*! \brief SIP TCP connection handler */
    static void *sip_tcp_worker_fn(void *data)
    
    	struct ast_tcptls_session_instance *tcptls_session = data;
    
    	return _sip_tcp_helper_thread(tcptls_session);
    
    /*! \brief SIP WebSocket connection handler */
    static void sip_websocket_callback(struct ast_websocket *session, struct ast_variable *parameters, struct ast_variable *headers)
    {
    	int res;
    
    	if (ast_websocket_set_nonblock(session)) {
    		goto end;
    	}
    
    
    	if (ast_websocket_set_timeout(session, sip_cfg.websocket_write_timeout)) {
    		goto end;
    	}
    
    
    	while ((res = ast_wait_for_input(ast_websocket_fd(session), -1)) > 0) {
    		char *payload;
    		uint64_t payload_len;
    		enum ast_websocket_opcode opcode;
    		int fragmented;
    
    		if (ast_websocket_read(session, &payload, &payload_len, &opcode, &fragmented)) {
    			/* We err on the side of caution and terminate the session if any error occurs */
    			break;
    		}
    
    		if (opcode == AST_WEBSOCKET_OPCODE_TEXT || opcode == AST_WEBSOCKET_OPCODE_BINARY) {
    			struct sip_request req = { 0, };
    
    
    			if (!(req.data = ast_str_create(payload_len + 1))) {
    
    				goto end;
    			}
    
    			if (ast_str_set(&req.data, -1, "%s", payload) == AST_DYNSTR_BUILD_FAILED) {
    				deinit_req(&req);
    				goto end;
    			}
    
    			req.socket.fd = ast_websocket_fd(session);
    
    			set_socket_transport(&req.socket, ast_websocket_is_secure(session) ? AST_TRANSPORT_WSS : AST_TRANSPORT_WS);
    
    			req.socket.ws_session = session;
    
    			handle_request_do(&req, ast_websocket_remote_address(session));
    			deinit_req(&req);
    
    		} else if (opcode == AST_WEBSOCKET_OPCODE_CLOSE) {
    			break;
    		}
    	}
    
    end:
    	ast_websocket_unref(session);
    }
    
    
    /*! \brief Check if the authtimeout has expired.
     * \param start the time when the session started
     *
     * \retval 0 the timeout has expired
     * \retval -1 error
     * \return the number of milliseconds until the timeout will expire
     */
    static int sip_check_authtimeout(time_t start)
    {
    	int timeout;
    	time_t now;
    	if(time(&now) == -1) {
    		ast_log(LOG_ERROR, "error executing time(): %s\n", strerror(errno));
    		return -1;
    	}
    
    	timeout = (authtimeout - (now - start)) * 1000;
    	if (timeout < 0) {
    		/* we have timed out */
    		return 0;
    	}
    
    	return timeout;
    }
    
    
    /*!
     * \brief Indication of a TCP message's integrity
     */
    enum message_integrity {
    	/*!
    	 * The message has an error in it with
    	 * regards to its Content-Length header
    	 */
    	MESSAGE_INVALID,
    	/*!
    	 * The message is incomplete
    	 */
    	MESSAGE_FRAGMENT,
    	/*!
    	 * The data contains a complete message
    	 * plus a fragment of another.
    	 */
    	MESSAGE_FRAGMENT_COMPLETE,
    	/*!
    	 * The message is complete
    	 */
    	MESSAGE_COMPLETE,
    };
    
    /*!
     * \brief
     * Get the content length from an unparsed SIP message
     *
     * \param message The unparsed SIP message headers
     * \return The value of the Content-Length header or -1 if message is invalid
     */
    static int read_raw_content_length(const char *message)
    {
    	char *content_length_str;
    
    	int content_length = -1;
    
    	struct ast_str *msg_copy;
    
    	/* Using a ast_str because lws2sws takes one of those */
    	if (!(msg_copy = ast_str_create(strlen(message) + 1))) {
    		return -1;
    	}
    	ast_str_set(&msg_copy, 0, "%s", message);
    
    
    	if (sip_cfg.pedanticsipchecking) {
    		lws2sws(msg_copy);
    	}
    
    
    	msg = ast_str_buffer(msg_copy);
    
    
    	/* Let's find a Content-Length header */
    
    	if ((content_length_str = strcasestr(msg, "\nContent-Length:"))) {
    		content_length_str += sizeof("\nContent-Length:") - 1;
    	} else if ((content_length_str = strcasestr(msg, "\nl:"))) {
    		content_length_str += sizeof("\nl:") - 1;
    	} else {
    
    		/* RFC 3261 18.3
    		 * "In the case of stream-oriented transports such as TCP, the Content-
    		 *  Length header field indicates the size of the body.  The Content-
    		 *  Length header field MUST be used with stream oriented transports."
    		 */
    
    	/* Double-check that this is a complete header */
    	if (!strchr(content_length_str, '\n')) {
    		goto done;
    
    	if (sscanf(content_length_str, "%30d", &content_length) != 1) {
    		content_length = -1;
    
    done:
    	ast_free(msg_copy);
    	return content_length;
    
    }
    
    /*!
     * \brief Check that a message received over TCP is a full message
     *
     * This will take the information read in and then determine if
     * 1) The message is a full SIP request
     * 2) The message is a partial SIP request
     * 3) The message contains a full SIP request along with another partial request
     * \param data The unparsed incoming SIP message.
     * \param request The resulting request with extra fragments removed.
     * \param overflow If the message contains more than a full request, this is the remainder of the message
     * \return The resulting integrity of the message
     */
    static enum message_integrity check_message_integrity(struct ast_str **request, struct ast_str **overflow)
    {
    
    	char *message = ast_str_buffer(*request);
    
    	char *body;
    	int content_length;
    
    	int message_len = ast_str_strlen(*request);
    
    	int body_len;
    
    	/* Important pieces to search for in a SIP request are \r\n\r\n. This
    	 * marks either
    	 * 1) The division between the headers and body
    	 * 2) The end of the SIP request
    	 */
    	body = strstr(message, "\r\n\r\n");
    	if (!body) {
    		/* This is clearly a partial message since we haven't reached an end
    		 * yet.
    		 */
    		return MESSAGE_FRAGMENT;
    	}
    	body += sizeof("\r\n\r\n") - 1;
    
    	body_len = message_len - (body - message);
    
    
    	body[-1] = '\0';
    	content_length = read_raw_content_length(message);
    	body[-1] = '\n';
    
    	if (content_length < 0) {
    		return MESSAGE_INVALID;
    	} else if (content_length == 0) {
    		/* We've definitely received an entire message. We need
    		 * to check if there's also a fragment of another message
    		 * in addition.
    		 */
    		if (body_len == 0) {
    			return MESSAGE_COMPLETE;
    		} else {
    			ast_str_append(overflow, 0, "%s", body);
    
    			ast_str_truncate(*request, message_len - body_len);
    
    			return MESSAGE_FRAGMENT_COMPLETE;
    		}
    	}
    	/* Positive content length. Let's see what sort of
    	 * message body we're dealing with.
    	 */
    	if (body_len < content_length) {
    		/* We don't have the full message body yet */
    		return MESSAGE_FRAGMENT;
    	} else if (body_len > content_length) {
    		/* We have the full message plus a fragment of a further
    		 * message
    		 */
    		ast_str_append(overflow, 0, "%s", body + content_length);
    
    		ast_str_truncate(*request, message_len - (body_len - content_length));
    
    		return MESSAGE_FRAGMENT_COMPLETE;
    	} else {
    		/* Yay! Full message with no extra content */
    		return MESSAGE_COMPLETE;
    	}
    }
    
    /*!
    
     * \brief Read SIP request or response from a TCP/TLS connection
    
     *
     * \param req The request structure to be filled in
    
     * \param tcptls_session The TCP/TLS connection from which to read
    
     * \retval -1 Failed to read data
     * \retval 0 Successfully read data
     */
    
    static int sip_tcptls_read(struct sip_request *req, struct ast_tcptls_session_instance *tcptls_session,
    
    		int authenticated, time_t start)
    {
    	enum message_integrity message_integrity = MESSAGE_FRAGMENT;
    
    	while (message_integrity == MESSAGE_FRAGMENT) {
    
    		if (ast_str_strlen(tcptls_session->overflow_buf) == 0) {
    			char readbuf[4097];
    			int timeout;
    			int res;
    			if (!tcptls_session->client && !authenticated) {
    				if ((timeout = sip_check_authtimeout(start)) < 0) {
    					return -1;
    				}
    
    				if (timeout == 0) {
    
    					ast_debug(2, "SIP TCP/TLS server timed out\n");
    
    					return -1;
    				}
    			} else {
    				timeout = -1;
    			}
    			res = ast_wait_for_input(tcptls_session->fd, timeout);
    			if (res < 0) {
    
    				ast_debug(2, "SIP TCP/TLS server :: ast_wait_for_input returned %d\n", res);
    
    				return -1;
    			} else if (res == 0) {
    
    				ast_debug(2, "SIP TCP/TLS server timed out\n");
    
    			res = ast_tcptls_server_read(tcptls_session, readbuf, sizeof(readbuf) - 1);
    
    				if (errno == EAGAIN || errno == EINTR) {
    					continue;
    				}
    				ast_debug(2, "SIP TCP/TLS server error when receiving data\n");
    
    				return -1;
    			} else if (res == 0) {
    
    				ast_debug(2, "SIP TCP/TLS server has shut down\n");
    
    				return -1;
    			}
    			readbuf[res] = '\0';
    			ast_str_append(&req->data, 0, "%s", readbuf);
    		} else {
    			ast_str_append(&req->data, 0, "%s", ast_str_buffer(tcptls_session->overflow_buf));
    			ast_str_reset(tcptls_session->overflow_buf);
    		}
    
    		datalen = ast_str_strlen(req->data);
    		if (datalen > SIP_MAX_PACKET_SIZE) {
    
    			ast_log(LOG_WARNING, "Rejecting TCP/TLS packet from '%s' because way too large: %zu\n",
    
    				ast_sockaddr_stringify(&tcptls_session->remote_address), datalen);
    			return -1;
    		}
    
    
    		message_integrity = check_message_integrity(&req->data, &tcptls_session->overflow_buf);
    	}
    
    	return 0;
    }
    
    
    /*! \brief SIP TCP thread management function
    	This function reads from the socket, parses the packet into a request
    */
    
    static void *_sip_tcp_helper_thread(struct ast_tcptls_session_instance *tcptls_session)
    
    	int res, timeout = -1, authenticated = 0, flags;
    
    	struct sip_request req = { 0, } , reqcpy = { 0, };
    	struct sip_threadinfo *me = NULL;
    	char buf[1024] = "";
    	struct pollfd fds[2] = { { 0 }, { 0 }, };
    	struct ast_tcptls_session_args *ca = NULL;
    
    	/* If this is a server session, then the connection has already been
    	 * setup. Check if the authlimit has been reached and if not create the
    	 * threadinfo object so we can access this thread for writing.
    	 *
    
    	 * if this is a client connection more work must be done.
    	 * 1. We own the parent session args for a client connection.  This pointer needs
    	 *    to be held on to so we can decrement it's ref count on thread destruction.
    	 * 2. The threadinfo object was created before this thread was launched, however
    	 *    it must be found within the threadt table.
    	 * 3. Last, the tcptls_session must be started.
    	 */
    	if (!tcptls_session->client) {
    
    		if (ast_atomic_fetchadd_int(&unauth_sessions, +1) >= authlimit) {
    			/* unauth_sessions is decremented in the cleanup code */
    			goto cleanup;
    		}
    
    		if ((flags = fcntl(tcptls_session->fd, F_GETFL)) == -1) {
    			ast_log(LOG_ERROR, "error setting socket to non blocking mode, fcntl() failed: %s\n", strerror(errno));
    			goto cleanup;
    		}
    
    		flags |= O_NONBLOCK;
    		if (fcntl(tcptls_session->fd, F_SETFL, flags) == -1) {
    			ast_log(LOG_ERROR, "error setting socket to non blocking mode, fcntl() failed: %s\n", strerror(errno));
    			goto cleanup;
    		}
    
    
    		if (!(me = sip_threadinfo_create(tcptls_session, tcptls_session->ssl ? AST_TRANSPORT_TLS : AST_TRANSPORT_TCP))) {
    
    			goto cleanup;
    		}
    		ao2_t_ref(me, +1, "Adding threadinfo ref for tcp_helper_thread");
    	} else {
    		struct sip_threadinfo tmp = {
    			.tcptls_session = tcptls_session,
    		};
    
    		if ((!(ca = tcptls_session->parent)) ||
    			(!(me = ao2_t_find(threadt, &tmp, OBJ_POINTER, "ao2_find, getting sip_threadinfo in tcp helper thread"))) ||
    			(!(tcptls_session = ast_tcptls_client_start(tcptls_session)))) {
    			goto cleanup;
    		}
    
    	flags = 1;
    	if (setsockopt(tcptls_session->fd, SOL_SOCKET, SO_KEEPALIVE, &flags, sizeof(flags))) {
    		ast_log(LOG_ERROR, "error enabling TCP keep-alives on sip socket: %s\n", strerror(errno));
    		goto cleanup;
    	}
    
    
    	me->threadid = pthread_self();
    
    	ast_debug(2, "Starting thread for %s server\n", tcptls_session->ssl ? "TLS" : "TCP");
    
    	/* set up pollfd to watch for reads on both the socket and the alert_pipe */
    	fds[0].fd = tcptls_session->fd;
    	fds[1].fd = me->alert_pipe[0];
    	fds[0].events = fds[1].events = POLLIN | POLLPRI;
    
    	if (!(req.data = ast_str_create(SIP_MIN_PACKET))) {
    
    	}
    	if (!(reqcpy.data = ast_str_create(SIP_MIN_PACKET))) {
    
    	if(time(&start) == -1) {