Newer
Older
}
cc_entry->core_id = monitor->core_id;
monitor_instance->suspension_entry->instance_data = cc_entry;
publish_type = SIP_PUBLISH_INITIAL;
} else {
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);
Kevin P. Fleming
committed
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);
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
/*
* \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;
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
/*! \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;
for (cur = table; cur->s; cur++) {
if (cur->x == x) {
/*! \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)
for (cur = table; cur->s; cur++) {
if (!strcasecmp(cur->s, s)) {
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;
Joshua Colp
committed
}
}
static const char *sip_reason_code_to_str(enum AST_REDIRECTING_REASON code)
{
if (code >= 0 && code < ARRAY_LEN(sip_reason_table)) {
return sip_reason_table[code].text;
Joshua Colp
committed
}
}
/*!
* \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) \
); \
ret = 1; \
} else if (peer->socket.type & SIP_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) \
); \
}\
(ret); \
})
/*! \brief
* duplicate a list of channel variables, \return the copy.
*/
static struct ast_variable *copy_vars(struct ast_variable *src)
Olle Johansson
committed
{
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;
Olle Johansson
committed
}
static void sip_tcptls_client_args_destructor(void *obj)
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;
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)
Joshua Colp
committed
if (!tcptls_session || !(th = ao2_alloc(sizeof(*th), sip_threadinfo_destructor))) {
Joshua Colp
committed
return NULL;
}
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 ? SIP_TRANSPORT_TLS: SIP_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;
ast_mutex_lock(&tcptls_session->lock);
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;
}
Russell Bryant
committed
/* goto tcptls_write_error should _NOT_ be used beyond this point */
ast_str_set(&packet->data, 0, "%s", (char *) buf);
packet->len = len;
Russell Bryant
committed
/* 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);
}
ast_mutex_unlock(&tcptls_session->lock);
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");
}
ast_mutex_unlock(&tcptls_session->lock);
/*! \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(NULL, tcptls_session);
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
/*! \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 SIP TCP thread management function
This function reads from the socket, parses the packet into a request
*/
static void *_sip_tcp_helper_thread(struct sip_pvt *pvt, struct ast_tcptls_session_instance *tcptls_session)
int res, cl, timeout = -1, authenticated = 0, flags, after_poll = 0, need_poll = 1;
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 ? SIP_TRANSPORT_TLS : SIP_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 ? "SSL" : "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;
Brett Bryant
committed
if (!(req.data = ast_str_create(SIP_MIN_PACKET))) {
}
if (!(reqcpy.data = ast_str_create(SIP_MIN_PACKET))) {
if(time(&start) == -1) {
ast_log(LOG_ERROR, "error executing time(): %s\n", strerror(errno));
goto cleanup;
}
for (;;) {
struct ast_str *str_save;
if (!tcptls_session->client && req.authenticated && !authenticated) {
authenticated = 1;
ast_atomic_fetchadd_int(&unauth_sessions, -1);
}
/* calculate the timeout for unauthenticated server sessions */
if (!tcptls_session->client && !authenticated ) {
if ((timeout = sip_check_authtimeout(start)) < 0) {
goto cleanup;
}
if (timeout == 0) {
ast_debug(2, "SIP %s server timed out\n", tcptls_session->ssl ? "SSL": "TCP");
goto cleanup;
}
} else {
timeout = -1;
}
res = ast_poll(fds, 2, timeout); /* polls for both socket and alert_pipe */
if (res < 0) {
ast_debug(2, "SIP %s server :: ast_wait_for_input returned %d\n", tcptls_session->ssl ? "SSL": "TCP", res);
goto cleanup;
} else if (res == 0) {
/* timeout */
ast_debug(2, "SIP %s server timed out\n", tcptls_session->ssl ? "SSL": "TCP");
goto cleanup;
Brett Bryant
committed
/* handle the socket event, check for both reads from the socket fd,
* and writes from alert_pipe fd */
if (fds[0].revents) { /* there is data on the socket to be read */
Brett Bryant
committed
/* clear request structure */
str_save = req.data;
memset(&req, 0, sizeof(req));
req.data = str_save;
ast_str_reset(req.data);
str_save = reqcpy.data;
memset(&reqcpy, 0, sizeof(reqcpy));
reqcpy.data = str_save;
ast_str_reset(reqcpy.data);
memset(buf, 0, sizeof(buf));
if (tcptls_session->ssl) {
set_socket_transport(&req.socket, SIP_TRANSPORT_TLS);
req.socket.port = htons(ourport_tls);
} else {
set_socket_transport(&req.socket, SIP_TRANSPORT_TCP);
req.socket.port = htons(ourport_tcp);
}
req.socket.fd = tcptls_session->fd;
/* Read in headers one line at a time */
while (ast_str_strlen(req.data) < 4 || strncmp(REQ_OFFSET_TO_STR(&req, data->used - 4), "\r\n\r\n", 4)) {
if (!tcptls_session->client && !authenticated ) {
if ((timeout = sip_check_authtimeout(start)) < 0) {
goto cleanup;
}
if (timeout == 0) {
ast_debug(2, "SIP %s server timed out\n", tcptls_session->ssl ? "SSL": "TCP");
goto cleanup;
}
} else {
timeout = -1;
}
/* special polling behavior is required for TLS
* sockets because of the buffering done in the
* TLS layer */
if (!tcptls_session->ssl || need_poll) {
need_poll = 0;
after_poll = 1;
res = ast_wait_for_input(tcptls_session->fd, timeout);
if (res < 0) {
ast_debug(2, "SIP TCP server :: ast_wait_for_input returned %d\n", res);
goto cleanup;
} else if (res == 0) {
/* timeout */
ast_debug(2, "SIP TCP server timed out\n");
goto cleanup;
}
ast_mutex_lock(&tcptls_session->lock);
if (!fgets(buf, sizeof(buf), tcptls_session->f)) {
ast_mutex_unlock(&tcptls_session->lock);
if (after_poll) {
goto cleanup;
} else {
need_poll = 1;
continue;
}
}
ast_mutex_unlock(&tcptls_session->lock);
ast_str_append(&req.data, 0, "%s", buf);
}
copy_request(&reqcpy, &req);
parse_request(&reqcpy);
/* In order to know how much to read, we need the content-length header */
if (sscanf(sip_get_header(&reqcpy, "Content-Length"), "%30d", &cl)) {
while (cl > 0) {
size_t bytes_read;
if (!tcptls_session->client && !authenticated ) {
if ((timeout = sip_check_authtimeout(start)) < 0) {
goto cleanup;
}
if (timeout == 0) {
ast_debug(2, "SIP %s server timed out\n", tcptls_session->ssl ? "SSL": "TCP");
goto cleanup;
}
} else {
timeout = -1;
}
if (!tcptls_session->ssl || need_poll) {
need_poll = 0;
after_poll = 1;
res = ast_wait_for_input(tcptls_session->fd, timeout);
if (res < 0) {
ast_debug(2, "SIP TCP server :: ast_wait_for_input returned %d\n", res);
goto cleanup;
} else if (res == 0) {
/* timeout */
ast_debug(2, "SIP TCP server timed out\n");
goto cleanup;
}
ast_mutex_lock(&tcptls_session->lock);
if (!(bytes_read = fread(buf, 1, MIN(sizeof(buf) - 1, cl), tcptls_session->f))) {
ast_mutex_unlock(&tcptls_session->lock);
if (after_poll) {
goto cleanup;
} else {
need_poll = 1;
continue;
}
}
buf[bytes_read] = '\0';
ast_mutex_unlock(&tcptls_session->lock);
cl -= strlen(buf);
ast_str_append(&req.data, 0, "%s", buf);
}
}
/*! \todo XXX If there's no Content-Length or if the content-length and what
we receive is not the same - we should generate an error */
req.socket.tcptls_session = tcptls_session;
handle_request_do(&req, &tcptls_session->remote_address);
}
if (fds[1].revents) { /* alert_pipe indicates there is data in the send queue to be sent */
enum sip_tcptls_alert alert;
struct tcptls_packet *packet;
Joshua Colp
committed
if (read(me->alert_pipe[0], &alert, sizeof(alert)) == -1) {
ast_log(LOG_ERROR, "read() failed: %s\n", strerror(errno));
continue;
}
switch (alert) {
case TCPTLS_ALERT_STOP:
goto cleanup;
case TCPTLS_ALERT_DATA:
ao2_lock(me);
if (!(packet = AST_LIST_REMOVE_HEAD(&me->packet_q, entry))) {
ast_log(LOG_WARNING, "TCPTLS thread alert_pipe indicated packet should be sent, but frame_q is empty");
}
Jason Parker
committed
if (ast_tcptls_server_write(tcptls_session, ast_str_buffer(packet->data), packet->len) == -1) {
ast_log(LOG_WARNING, "Failure to write to tcp/tls socket\n");
}
ao2_t_ref(packet, -1, "tcptls packet sent, this is no longer needed");
}
break;
default:
ast_log(LOG_ERROR, "Unknown tcptls thread alert '%d'\n", alert);
ast_debug(2, "Shutting down thread for %s server\n", tcptls_session->ssl ? "SSL" : "TCP");
if (tcptls_session && !tcptls_session->client && !authenticated) {
ast_atomic_fetchadd_int(&unauth_sessions, -1);
}
if (me) {
ao2_t_unlink(threadt, me, "Removing tcptls helper thread, thread is closing");
ao2_t_ref(me, -1, "Removing tcp_helper_threads threadinfo ref");
Mark Spencer
committed
}
deinit_req(&reqcpy);
deinit_req(&req);
/* if client, we own the parent session arguments and must decrement ref */
if (ca) {
ao2_t_ref(ca, -1, "closing tcptls thread, getting rid of client tcptls_session arguments");
}
if (tcptls_session) {
ast_mutex_lock(&tcptls_session->lock);
ast_tcptls_close_session_file(tcptls_session);
tcptls_session->parent = NULL;
ast_mutex_unlock(&tcptls_session->lock);
Olle Johansson
committed
ao2_ref(tcptls_session, -1);
tcptls_session = NULL;
}
return NULL;
Mark Spencer
committed
}
#define sip_ref_peer(arg1,arg2) _ref_peer((arg1),(arg2), __FILE__, __LINE__, __PRETTY_FUNCTION__)
#define sip_unref_peer(arg1,arg2) _unref_peer((arg1),(arg2), __FILE__, __LINE__, __PRETTY_FUNCTION__)
static struct sip_peer *_ref_peer(struct sip_peer *peer, char *tag, char *file, int line, const char *func)
if (peer)
__ao2_ref_debug(peer, 1, tag, file, line, func);
else
ast_log(LOG_ERROR, "Attempt to Ref a null peer pointer\n");
return peer;
static struct sip_peer *_unref_peer(struct sip_peer *peer, char *tag, char *file, int line, const char *func)
if (peer)
__ao2_ref_debug(peer, -1, tag, file, line, func);
return NULL;
/*!
* helper functions to unreference various types of objects.
* By handling them this way, we don't have to declare the
* destructor on each call, which removes the chance of errors.
*/
void *sip_unref_peer(struct sip_peer *peer, char *tag)
ao2_t_ref(peer, -1, tag);
return NULL;
}
struct sip_peer *sip_ref_peer(struct sip_peer *peer, char *tag)
{
ao2_t_ref(peer, 1, tag);
return peer;
}
#endif /* REF_DEBUG */
static void peer_sched_cleanup(struct sip_peer *peer)
{
if (peer->pokeexpire != -1) {
AST_SCHED_DEL_UNREF(sched, peer->pokeexpire,
sip_unref_peer(peer, "removing poke peer ref"));
}
if (peer->expire != -1) {
AST_SCHED_DEL_UNREF(sched, peer->expire,
sip_unref_peer(peer, "remove register expire ref"));
2872
2873
2874
2875
2876
2877
2878
2879
2880
2881
2882
2883
2884
2885
2886
2887
2888
2889
2890
2891
2892
2893
2894
2895
2896
2897
2898
2899
2900
2901
2902
2903
2904
2905
2906
2907
2908
2909
2910
2911
2912
2913
2914
2915
2916
2917
2918
2919
2920
}
}
typedef enum {
SIP_PEERS_MARKED,
SIP_PEERS_ALL,
} peer_unlink_flag_t;
/* this func is used with ao2_callback to unlink/delete all marked or linked
peers, depending on arg */
static int match_and_cleanup_peer_sched(void *peerobj, void *arg, int flags)
{
struct sip_peer *peer = peerobj;
peer_unlink_flag_t which = *(peer_unlink_flag_t *)arg;
if (which == SIP_PEERS_ALL || peer->the_mark) {
peer_sched_cleanup(peer);
return CMP_MATCH;
}
return 0;
}
static void unlink_peers_from_tables(peer_unlink_flag_t flag)
{
ao2_t_callback(peers, OBJ_NODATA | OBJ_UNLINK | OBJ_MULTIPLE,
match_and_cleanup_peer_sched, &flag, "initiating callback to remove marked peers");
ao2_t_callback(peers_by_ip, OBJ_NODATA | OBJ_UNLINK | OBJ_MULTIPLE,
match_and_cleanup_peer_sched, &flag, "initiating callback to remove marked peers");
}
/* \brief Unlink all marked peers from ao2 containers */
static void unlink_marked_peers_from_tables(void)
{
unlink_peers_from_tables(SIP_PEERS_MARKED);
}
static void unlink_all_peers_from_tables(void)
{
unlink_peers_from_tables(SIP_PEERS_ALL);
}
/* \brief Unlink single peer from all ao2 containers */
static void unlink_peer_from_tables(struct sip_peer *peer)
{
ao2_t_unlink(peers, peer, "ao2_unlink of peer from peers table");
if (!ast_sockaddr_isnull(&peer->addr)) {
ao2_t_unlink(peers_by_ip, peer, "ao2_unlink of peer from peers_by_ip table");
}
}
2922
2923
2924
2925
2926
2927
2928
2929
2930
2931
2932
2933
2934
2935
2936
2937
2938
2939
2940
2941
2942
2943
2944
2945
2946
/*! \brief maintain proper refcounts for a sip_pvt's outboundproxy
*
* This function sets pvt's outboundproxy pointer to the one referenced
* by the proxy parameter. Because proxy may be a refcounted object, and
* because pvt's old outboundproxy may also be a refcounted object, we need
* to maintain the proper refcounts.
*
* \param pvt The sip_pvt for which we wish to set the outboundproxy
* \param proxy The sip_proxy which we will point pvt towards.
* \return Returns void
*/
static void ref_proxy(struct sip_pvt *pvt, struct sip_proxy *proxy)
{
struct sip_proxy *old_obproxy = pvt->outboundproxy;
/* The sip_cfg.outboundproxy is statically allocated, and so
* we don't ever need to adjust refcounts for it
*/
if (proxy && proxy != &sip_cfg.outboundproxy) {
ao2_ref(proxy, +1);
}
pvt->outboundproxy = proxy;
if (old_obproxy && old_obproxy != &sip_cfg.outboundproxy) {
ao2_ref(old_obproxy, -1);
}
}
/*!
* \brief Unlink a dialog from the dialogs container, as well as any other places
* that it may be currently stored.
*
* \note A reference to the dialog must be held before calling this function, and this
* function does not release that reference.
*/
void dialog_unlink_all(struct sip_pvt *dialog)
struct ast_channel *owner;
dialog_ref(dialog, "Let's bump the count in the unlink so it doesn't accidentally become dead before we are done");
ao2_t_unlink(dialogs, dialog, "unlinking dialog via ao2_unlink");
Stefan Schmidt
committed
ao2_t_unlink(dialogs_needdestroy, dialog, "unlinking dialog_needdestroy via ao2_unlink");
ao2_t_unlink(dialogs_rtpcheck, dialog, "unlinking dialog_rtpcheck via ao2_unlink");
Olle Johansson
committed
/* Unlink us from the owner (channel) if we have one */
owner = sip_pvt_lock_full(dialog);
if (owner) {
ast_debug(1, "Detaching from channel %s\n", owner->name);
owner->tech_pvt = dialog_unref(owner->tech_pvt, "resetting channel dialog ptr in unlink_all");
ast_channel_unlock(owner);
ast_channel_unref(owner);
dialog->owner = NULL;
sip_pvt_unlock(dialog);
if (dialog->registry->call == dialog) {
dialog->registry->call = dialog_unref(dialog->registry->call, "nulling out the registry's call dialog field in unlink_all");
dialog->registry = registry_unref(dialog->registry, "delete dialog->registry");
}
if (dialog->stateid != -1) {
ast_extension_state_del(dialog->stateid, cb_extensionstate);
dialog->stateid = -1;
}
/* Remove link from peer to subscription of MWI */
if (dialog->relatedpeer && dialog->relatedpeer->mwipvt == dialog) {
dialog->relatedpeer->mwipvt = dialog_unref(dialog->relatedpeer->mwipvt, "delete ->relatedpeer->mwipvt");
}
if (dialog->relatedpeer && dialog->relatedpeer->call == dialog) {
dialog->relatedpeer->call = dialog_unref(dialog->relatedpeer->call, "unset the relatedpeer->call field in tandem with relatedpeer field itself");
/* remove all current packets in this dialog */
while((cp = dialog->packets)) {
dialog->packets = dialog->packets->next;
AST_SCHED_DEL(sched, cp->retransid);
dialog_unref(cp->owner, "remove all current packets in this dialog, and the pointer to the dialog too as part of __sip_destroy");
if (cp->data) {