void *ast_tcptls_server_root(void *data) { struct ast_tcptls_session_args *desc = data; int fd; struct ast_sockaddr addr; struct ast_tcptls_session_instance *tcptls_session; pthread_t launched; for (;;) { int i, flags; if (desc->periodic_fn) { desc->periodic_fn(desc); } i = ast_wait_for_input(desc->accept_fd, desc->poll_timeout); if (i <= 0) { continue; } fd = ast_accept(desc->accept_fd, &addr); if (fd < 0) { if ((errno != EAGAIN) && (errno != EWOULDBLOCK) && (errno != EINTR) && (errno != ECONNABORTED)) { ast_log(LOG_ERROR, "Accept failed: %s\n", strerror(errno)); break; } continue; } tcptls_session = ao2_alloc(sizeof(*tcptls_session), session_instance_destructor); if (!tcptls_session) { ast_log(LOG_WARNING, "No memory for new session: %s\n", strerror(errno)); if (close(fd)) { ast_log(LOG_ERROR, "close() failed: %s\n", strerror(errno)); } continue; } tcptls_session->overflow_buf = ast_str_create(128); flags = fcntl(fd, F_GETFL); fcntl(fd, F_SETFL, flags & ~O_NONBLOCK); tcptls_session->stream = ast_iostream_from_fd(&fd); if (!tcptls_session->stream) { ast_log(LOG_WARNING, "No memory for new session iostream\n"); continue; } tcptls_session->parent = desc; ast_sockaddr_copy(&tcptls_session->remote_address, &addr); tcptls_session->client = 0; /* This thread is now the only place that controls the single ref to tcptls_session */ if (ast_pthread_create_detached_background(&launched, NULL, handle_tcptls_connection, tcptls_session)) { ast_log(LOG_ERROR, "Unable to launch helper thread: %s\n", strerror(errno)); ast_tcptls_close_session_file(tcptls_session); ao2_ref(tcptls_session, -1); } } return NULL; }
static void *http_root(void *data) { int fd; struct sockaddr_in sin; socklen_t sinlen; struct ast_http_server_instance *ser; pthread_t launched; pthread_attr_t attr; for (;;) { int flags; ast_wait_for_input(httpfd, -1); sinlen = sizeof(sin); fd = accept(httpfd, (struct sockaddr *)&sin, &sinlen); if (fd < 0) { if ((errno != EAGAIN) && (errno != EINTR)) ast_log(LOG_WARNING, "Accept failed: %s\n", strerror(errno)); continue; } if (ast_atomic_fetchadd_int(&session_count, +1) >= session_limit) { close(fd); continue; } ser = ast_calloc(1, sizeof(*ser)); if (!ser) { ast_log(LOG_WARNING, "No memory for new session: %s\n", strerror(errno)); close(fd); ast_atomic_fetchadd_int(&session_count, -1); continue; } flags = fcntl(fd, F_GETFL); fcntl(fd, F_SETFL, flags & ~O_NONBLOCK); ser->fd = fd; memcpy(&ser->requestor, &sin, sizeof(ser->requestor)); if ((ser->f = fdopen(ser->fd, "w+"))) { pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); if (ast_pthread_create_background(&launched, &attr, ast_httpd_helper_thread, ser)) { ast_log(LOG_WARNING, "Unable to launch helper thread: %s\n", strerror(errno)); fclose(ser->f); free(ser); ast_atomic_fetchadd_int(&session_count, -1); } pthread_attr_destroy(&attr); } else { ast_log(LOG_WARNING, "fdopen failed!\n"); close(ser->fd); free(ser); ast_atomic_fetchadd_int(&session_count, -1); } } return NULL; }
/*! \brief WebSocket connection handler. */ static void websocket_cb(struct ast_websocket *session, struct ast_variable *parameters, struct ast_variable *headers) { struct ast_taskprocessor *serializer; struct transport_create_data create_data; struct ws_transport *transport; struct transport_read_data read_data; if (ast_websocket_set_nonblock(session)) { ast_websocket_unref(session); return; } if (ast_websocket_set_timeout(session, get_write_timeout())) { ast_websocket_unref(session); return; } serializer = create_websocket_serializer(); if (!serializer) { ast_websocket_unref(session); return; } create_data.ws_session = session; if (ast_sip_push_task_synchronous(serializer, transport_create, &create_data)) { ast_log(LOG_ERROR, "Could not create WebSocket transport.\n"); ast_websocket_unref(session); return; } transport = create_data.transport; read_data.transport = transport; while (ast_wait_for_input(ast_websocket_fd(session), -1) > 0) { enum ast_websocket_opcode opcode; int fragmented; if (ast_websocket_read(session, &read_data.payload, &read_data.payload_len, &opcode, &fragmented)) { break; } if (opcode == AST_WEBSOCKET_OPCODE_TEXT || opcode == AST_WEBSOCKET_OPCODE_BINARY) { ast_sip_push_task_synchronous(serializer, transport_read, &read_data); } else if (opcode == AST_WEBSOCKET_OPCODE_CLOSE) { break; } } ast_sip_push_task_synchronous(serializer, transport_shutdown, transport); ast_taskprocessor_unreference(serializer); ast_websocket_unref(session); }
struct ast_json *ast_ari_websocket_session_read( struct ast_ari_websocket_session *session) { RAII_VAR(struct ast_json *, message, NULL, ast_json_unref); if (ast_websocket_fd(session->ws_session) < 0) { return NULL; } while (!message) { int res; char *payload; uint64_t payload_len; enum ast_websocket_opcode opcode; int fragmented; res = ast_wait_for_input( ast_websocket_fd(session->ws_session), -1); if (res <= 0) { ast_log(LOG_WARNING, "WebSocket poll error: %s\n", strerror(errno)); return NULL; } res = ast_websocket_read(session->ws_session, &payload, &payload_len, &opcode, &fragmented); if (res != 0) { ast_log(LOG_WARNING, "WebSocket read error: %s\n", strerror(errno)); return NULL; } switch (opcode) { case AST_WEBSOCKET_OPCODE_CLOSE: ast_debug(1, "WebSocket closed\n"); return NULL; case AST_WEBSOCKET_OPCODE_TEXT: message = ast_json_load_buf(payload, payload_len, NULL); if (message == NULL) { ast_log(LOG_WARNING, "WebSocket input failed to parse\n"); } break; default: /* Ignore all other message types */ break; } } return ast_json_ref(message); }
void *ast_tcptls_server_root(void *data) { struct ast_tcptls_session_args *desc = data; int fd; struct sockaddr_in sin; socklen_t sinlen; struct ast_tcptls_session_instance *tcptls_session; pthread_t launched; for (;;) { int i, flags; if (desc->periodic_fn) desc->periodic_fn(desc); i = ast_wait_for_input(desc->accept_fd, desc->poll_timeout); if (i <= 0) continue; sinlen = sizeof(sin); fd = accept(desc->accept_fd, (struct sockaddr *) &sin, &sinlen); if (fd < 0) { if ((errno != EAGAIN) && (errno != EINTR)) ast_log(LOG_WARNING, "Accept failed: %s\n", strerror(errno)); continue; } tcptls_session = ao2_alloc(sizeof(*tcptls_session), session_instance_destructor); if (!tcptls_session) { ast_log(LOG_WARNING, "No memory for new session: %s\n", strerror(errno)); close(fd); continue; } ast_mutex_init(&tcptls_session->lock); flags = fcntl(fd, F_GETFL); fcntl(fd, F_SETFL, flags & ~O_NONBLOCK); tcptls_session->fd = fd; tcptls_session->parent = desc; memcpy(&tcptls_session->remote_address, &sin, sizeof(tcptls_session->remote_address)); tcptls_session->client = 0; /* This thread is now the only place that controls the single ref to tcptls_session */ if (ast_pthread_create_detached_background(&launched, NULL, handle_tcptls_connection, tcptls_session)) { ast_log(LOG_WARNING, "Unable to launch helper thread: %s\n", strerror(errno)); close(tcptls_session->fd); ao2_ref(tcptls_session, -1); } } return NULL; }
static int dahdi_test_timer(void) { int fd; int x = 160; fd = open("/dev/dahdi/timer", O_RDWR); if (fd < 0) { return -1; } if (ioctl(fd, DAHDI_TIMERCONFIG, &x)) { ast_log(LOG_ERROR, "You have DAHDI built and drivers loaded, but the DAHDI timer test failed to set DAHDI_TIMERCONFIG to %d.\n" SEE_TIMING, x); close(fd); return -1; } if ((x = ast_wait_for_input(fd, 300)) < 0) { ast_log(LOG_ERROR, "You have DAHDI built and drivers loaded, but the DAHDI timer could not be polled during the DAHDI timer test.\n" SEE_TIMING); close(fd); return -1; } if (!x) { const char dahdi_timer_error[] = { "Asterisk has detected a problem with your DAHDI configuration and will shutdown for your protection. You have options:" "\n\t1. You only have to compile DAHDI support into Asterisk if you need it. One option is to recompile without DAHDI support." "\n\t2. You only have to load DAHDI drivers if you want to take advantage of DAHDI services. One option is to unload DAHDI modules if you don't need them." "\n\t3. If you need DAHDI services, you must correctly configure DAHDI." }; ast_log(LOG_ERROR, "%s\n" SEE_TIMING, dahdi_timer_error); usleep(100); close(fd); return -1; } close(fd); return 0; }
static void *http_root(void *data) { int fd; struct sockaddr_in sin; int sinlen; struct ast_http_server_instance *ser; pthread_t launched; pthread_attr_t attr; for (;;) { ast_wait_for_input(httpfd, -1); sinlen = sizeof(sin); fd = accept(httpfd, (struct sockaddr *)&sin, &sinlen); if (fd < 0) { if ((errno != EAGAIN) && (errno != EINTR)) ast_log(LOG_WARNING, "Accept failed: %s\n", strerror(errno)); continue; } ser = ast_calloc(1, sizeof(*ser)); if (ser) { ser->fd = fd; memcpy(&ser->requestor, &sin, sizeof(ser->requestor)); if ((ser->f = fdopen(ser->fd, "w+"))) { pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); if (ast_pthread_create(&launched, &attr, ast_httpd_helper_thread, ser)) { ast_log(LOG_WARNING, "Unable to launch helper thread: %s\n", strerror(errno)); fclose(ser->f); free(ser); } } else { ast_log(LOG_WARNING, "fdopen failed!\n"); close(ser->fd); free(ser); } } } return NULL; }
static int transport_websocket_recv(void *obj, char **data, unsigned int timeout_secs) { int res; int timeout_ms = timeout_secs * 1000; /* In Socket.io, 0 means forever... */ if (timeout_ms == 0) { timeout_ms = -1; } res = ast_wait_for_input(ast_websocket_fd(obj), timeout_ms); if (res < 0) { ast_log(LOG_WARNING, "WebSocket poll error: %s\n", strerror(errno)); return -1; } else if (res == 0) { return 0; } res = ast_websocket_read_string(obj, data); return res; }
int AST_OPTIONAL_API_NAME(ast_websocket_read)(struct ast_websocket *session, char **payload, uint64_t *payload_len, enum ast_websocket_opcode *opcode, int *fragmented) { char buf[MAXIMUM_FRAME_SIZE] = ""; size_t frame_size, expected = 2; *payload = NULL; *payload_len = 0; *fragmented = 0; /* We try to read in 14 bytes, which is the largest possible WebSocket header */ if ((frame_size = fread(&buf, 1, 14, session->f)) < 1) { return -1; } /* The minimum size for a WebSocket frame is 2 bytes */ if (frame_size < expected) { return -1; } *opcode = buf[0] & 0xf; if (*opcode == AST_WEBSOCKET_OPCODE_TEXT || *opcode == AST_WEBSOCKET_OPCODE_BINARY || *opcode == AST_WEBSOCKET_OPCODE_CONTINUATION || *opcode == AST_WEBSOCKET_OPCODE_PING || *opcode == AST_WEBSOCKET_OPCODE_PONG) { int fin = (buf[0] >> 7) & 1; int mask_present = (buf[1] >> 7) & 1; char *mask = NULL, *new_payload; size_t remaining; if (mask_present) { /* The mask should take up 4 bytes */ expected += 4; if (frame_size < expected) { /* Per the RFC 1009 means we received a message that was too large for us to process */ ast_websocket_close(session, 1009); return 0; } } /* Assume no extended length and no masking at the beginning */ *payload_len = buf[1] & 0x7f; *payload = &buf[2]; /* Determine if extended length is being used */ if (*payload_len == 126) { /* Use the next 2 bytes to get a uint16_t */ expected += 2; *payload += 2; if (frame_size < expected) { ast_websocket_close(session, 1009); return 0; } *payload_len = ntohs(get_unaligned_uint16(&buf[2])); } else if (*payload_len == 127) { /* Use the next 8 bytes to get a uint64_t */ expected += 8; *payload += 8; if (frame_size < expected) { ast_websocket_close(session, 1009); return 0; } *payload_len = ntohl(get_unaligned_uint64(&buf[2])); } /* If masking is present the payload currently points to the mask, so move it over 4 bytes to the actual payload */ if (mask_present) { mask = *payload; *payload += 4; } /* Determine how much payload we need to read in as we may have already read some in */ remaining = *payload_len - (frame_size - expected); /* If how much payload they want us to read in exceeds what we are capable of close the session, things * will fail no matter what most likely */ if (remaining > (MAXIMUM_FRAME_SIZE - frame_size)) { ast_websocket_close(session, 1009); return 0; } new_payload = *payload + (frame_size - expected); /* Read in the remaining payload */ while (remaining > 0) { size_t payload_read; /* Wait for data to come in */ if (ast_wait_for_input(session->fd, -1) <= 0) { *opcode = AST_WEBSOCKET_OPCODE_CLOSE; *payload = NULL; session->closing = 1; return 0; } /* If some sort of failure occurs notify the caller */ if ((payload_read = fread(new_payload, 1, remaining, session->f)) < 1) { return -1; } remaining -= payload_read; new_payload += payload_read; } /* If a mask is present unmask the payload */ if (mask_present) { unsigned int pos; for (pos = 0; pos < *payload_len; pos++) { (*payload)[pos] ^= mask[pos % 4]; } } if (!(new_payload = ast_realloc(session->payload, session->payload_len + *payload_len))) { *payload_len = 0; ast_websocket_close(session, 1009); return 0; } /* Per the RFC for PING we need to send back an opcode with the application data as received */ if (*opcode == AST_WEBSOCKET_OPCODE_PING) { ast_websocket_write(session, AST_WEBSOCKET_OPCODE_PONG, *payload, *payload_len); } session->payload = new_payload; memcpy(session->payload + session->payload_len, *payload, *payload_len); session->payload_len += *payload_len; if (!fin && session->reconstruct && (session->payload_len < session->reconstruct)) { /* If this is not a final message we need to defer returning it until later */ if (*opcode != AST_WEBSOCKET_OPCODE_CONTINUATION) { session->opcode = *opcode; } *opcode = AST_WEBSOCKET_OPCODE_CONTINUATION; *payload_len = 0; *payload = NULL; } else { if (*opcode == AST_WEBSOCKET_OPCODE_CONTINUATION) { if (!fin) { /* If this was not actually the final message tell the user it is fragmented so they can deal with it accordingly */ *fragmented = 1; } else { /* Final frame in multi-frame so push up the actual opcode */ *opcode = session->opcode; } } *payload_len = session->payload_len; *payload = session->payload; session->payload_len = 0; } } else if (*opcode == AST_WEBSOCKET_OPCODE_CLOSE) {
/*! * \internal * \brief fopencookie()/funopen() stream write function. * * \param cookie Stream control data. * \param buf Where to get data to write. * \param size Size of the buffer. * * \retval number of bytes written from buf. * \retval -1 on error. */ static HOOK_T tcptls_stream_write(void *cookie, const char *buf, LEN_T size) { struct ast_tcptls_stream *stream = cookie; struct timeval start; int ms; int res; int written; int remaining; if (!size) { /* You asked to write no data you wrote no data. */ return 0; } if (!stream || stream->fd == -1) { errno = EBADF; return -1; } if (stream->start.tv_sec) { start = stream->start; } else { start = ast_tvnow(); } #if defined(DO_SSL) if (stream->ssl) { written = 0; remaining = size; for (;;) { res = SSL_write(stream->ssl, buf + written, remaining); if (res == remaining) { /* Everything was written. */ return size; } if (0 < res) { /* Successfully wrote part of the buffer. Try to write the rest. */ written += res; remaining -= res; continue; } switch (SSL_get_error(stream->ssl, res)) { case SSL_ERROR_ZERO_RETURN: ast_debug(1, "TLS clean shutdown alert writing data\n"); if (written) { /* Report partial write. */ return written; } errno = EBADF; return -1; case SSL_ERROR_WANT_READ: ms = ast_remaining_ms(start, stream->timeout); if (!ms) { /* Report partial write. */ ast_debug(1, "TLS timeout writing data (want read)\n"); return written; } ast_wait_for_input(stream->fd, ms); break; case SSL_ERROR_WANT_WRITE: ms = ast_remaining_ms(start, stream->timeout); if (!ms) { /* Report partial write. */ ast_debug(1, "TLS timeout writing data (want write)\n"); return written; } ast_wait_for_output(stream->fd, ms); break; default: /* Undecoded SSL or transport error. */ ast_debug(1, "TLS transport or SSL error writing data\n"); if (written) { /* Report partial write. */ return written; } errno = EBADF; return -1; } } } #endif /* defined(DO_SSL) */ written = 0; remaining = size; for (;;) { res = write(stream->fd, buf + written, remaining); if (res == remaining) { /* Yay everything was written. */ return size; } if (0 < res) { /* Successfully wrote part of the buffer. Try to write the rest. */ written += res; remaining -= res; continue; } if (errno != EINTR && errno != EAGAIN) { /* Not a retryable error. */ ast_debug(1, "TCP socket error writing: %s\n", strerror(errno)); if (written) { return written; } return -1; } ms = ast_remaining_ms(start, stream->timeout); if (!ms) { /* Report partial write. */ ast_debug(1, "TCP timeout writing data\n"); return written; } ast_wait_for_output(stream->fd, ms); } }
/*! * \internal * \brief fopencookie()/funopen() stream read function. * * \param cookie Stream control data. * \param buf Where to put read data. * \param size Size of the buffer. * * \retval number of bytes put into buf. * \retval 0 on end of file. * \retval -1 on error. */ static HOOK_T tcptls_stream_read(void *cookie, char *buf, LEN_T size) { struct ast_tcptls_stream *stream = cookie; struct timeval start; int ms; int res; if (!size) { /* You asked for no data you got no data. */ return 0; } if (!stream || stream->fd == -1) { errno = EBADF; return -1; } if (stream->start.tv_sec) { start = stream->start; } else { start = ast_tvnow(); } #if defined(DO_SSL) if (stream->ssl) { for (;;) { res = SSL_read(stream->ssl, buf, size); if (0 < res) { /* We read some payload data. */ return res; } switch (SSL_get_error(stream->ssl, res)) { case SSL_ERROR_ZERO_RETURN: /* Report EOF for a shutdown */ ast_debug(1, "TLS clean shutdown alert reading data\n"); return 0; case SSL_ERROR_WANT_READ: if (!stream->exclusive_input) { /* We cannot wait for data now. */ errno = EAGAIN; return -1; } while ((ms = ast_remaining_ms(start, stream->timeout))) { res = ast_wait_for_input(stream->fd, ms); if (0 < res) { /* Socket is ready to be read. */ break; } if (res < 0) { if (errno == EINTR || errno == EAGAIN) { /* Try again. */ continue; } ast_debug(1, "TLS socket error waiting for read data: %s\n", strerror(errno)); return -1; } } break; case SSL_ERROR_WANT_WRITE: while ((ms = ast_remaining_ms(start, stream->timeout))) { res = ast_wait_for_output(stream->fd, ms); if (0 < res) { /* Socket is ready to be written. */ break; } if (res < 0) { if (errno == EINTR || errno == EAGAIN) { /* Try again. */ continue; } ast_debug(1, "TLS socket error waiting for write space: %s\n", strerror(errno)); return -1; } } break; default: /* Report EOF for an undecoded SSL or transport error. */ ast_debug(1, "TLS transport or SSL error reading data\n"); return 0; } if (!ms) { /* Report EOF for a timeout */ ast_debug(1, "TLS timeout reading data\n"); return 0; } } } #endif /* defined(DO_SSL) */ for (;;) { res = read(stream->fd, buf, size); if (0 <= res || !stream->exclusive_input) { /* Got data or we cannot wait for it. */ return res; } if (errno != EINTR && errno != EAGAIN) { /* Not a retryable error. */ ast_debug(1, "TCP socket error reading data: %s\n", strerror(errno)); return -1; } ms = ast_remaining_ms(start, stream->timeout); if (!ms) { /* Report EOF for a timeout */ ast_debug(1, "TCP timeout reading data\n"); return 0; } ast_wait_for_input(stream->fd, ms); } }