|
|
|
@ -2646,156 +2646,6 @@ static int sip_check_authtimeout(time_t start)
|
|
|
|
|
return timeout;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*!
|
|
|
|
|
* \brief Read a SIP request or response from a TLS connection
|
|
|
|
|
*
|
|
|
|
|
* Because TLS operations are hidden from view via a FILE handle, the
|
|
|
|
|
* logic for reading data is a bit complex, and we have to make periodic
|
|
|
|
|
* checks to be sure we aren't taking too long to perform the necessary
|
|
|
|
|
* action.
|
|
|
|
|
*
|
|
|
|
|
* \todo XXX This should be altered in the future not to use a FILE pointer
|
|
|
|
|
*
|
|
|
|
|
* \param req The request structure to fill in
|
|
|
|
|
* \param tcptls_session The TLS connection on which the data is being received
|
|
|
|
|
* \param authenticated A flag indicating whether authentication has occurred yet.
|
|
|
|
|
* This is only relevant in a server role.
|
|
|
|
|
* \param start The time at which we started attempting to read data. Used in
|
|
|
|
|
* determining if there has been a timeout.
|
|
|
|
|
* \param me Thread info. Used as a means of determining if the session needs to be stoppped.
|
|
|
|
|
* \retval -1 Failed to read data
|
|
|
|
|
* \retval 0 Succeeded in reading data
|
|
|
|
|
*/
|
|
|
|
|
static int sip_tls_read(struct sip_request *req, struct sip_request *reqcpy, struct ast_tcptls_session_instance *tcptls_session,
|
|
|
|
|
int authenticated, time_t start, struct sip_threadinfo *me)
|
|
|
|
|
{
|
|
|
|
|
int res, content_length, after_poll = 1, need_poll = 1;
|
|
|
|
|
size_t datalen = ast_str_strlen(req->data);
|
|
|
|
|
char buf[1024] = "";
|
|
|
|
|
int timeout = -1;
|
|
|
|
|
|
|
|
|
|
/* Read in headers one line at a time */
|
|
|
|
|
while (datalen < 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) {
|
|
|
|
|
ast_debug(2, "SIP TLS server failed to determine authentication timeout\n");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (timeout == 0) {
|
|
|
|
|
ast_debug(2, "SIP TLS server timed out\n");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
timeout = -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* special polling behavior is required for TLS
|
|
|
|
|
* sockets because of the buffering done in the
|
|
|
|
|
* TLS layer */
|
|
|
|
|
if (need_poll) {
|
|
|
|
|
need_poll = 0;
|
|
|
|
|
after_poll = 1;
|
|
|
|
|
res = ast_wait_for_input(tcptls_session->fd, timeout);
|
|
|
|
|
if (res < 0) {
|
|
|
|
|
ast_debug(2, "SIP TLS server :: ast_wait_for_input returned %d\n", res);
|
|
|
|
|
return -1;
|
|
|
|
|
} else if (res == 0) {
|
|
|
|
|
/* timeout */
|
|
|
|
|
ast_debug(2, "SIP TLS server timed out\n");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ao2_lock(tcptls_session);
|
|
|
|
|
if (!fgets(buf, sizeof(buf), tcptls_session->f)) {
|
|
|
|
|
ao2_unlock(tcptls_session);
|
|
|
|
|
if (after_poll) {
|
|
|
|
|
return -1;
|
|
|
|
|
} else {
|
|
|
|
|
need_poll = 1;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
ao2_unlock(tcptls_session);
|
|
|
|
|
after_poll = 0;
|
|
|
|
|
if (me->stop) {
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
ast_str_append(&req->data, 0, "%s", buf);
|
|
|
|
|
|
|
|
|
|
datalen = ast_str_strlen(req->data);
|
|
|
|
|
if (datalen > SIP_MAX_PACKET_SIZE) {
|
|
|
|
|
ast_log(LOG_WARNING, "Rejecting TLS packet from '%s' because way too large: %zu\n",
|
|
|
|
|
ast_sockaddr_stringify(&tcptls_session->remote_address), datalen);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
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", &content_length)) {
|
|
|
|
|
while (content_length > 0) {
|
|
|
|
|
size_t bytes_read;
|
|
|
|
|
if (!tcptls_session->client && !authenticated) {
|
|
|
|
|
if ((timeout = sip_check_authtimeout(start)) < 0) {
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (timeout == 0) {
|
|
|
|
|
ast_debug(2, "SIP TLS server timed out\n");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
timeout = -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (need_poll) {
|
|
|
|
|
need_poll = 0;
|
|
|
|
|
after_poll = 1;
|
|
|
|
|
res = ast_wait_for_input(tcptls_session->fd, timeout);
|
|
|
|
|
if (res < 0) {
|
|
|
|
|
ast_debug(2, "SIP TLS server :: ast_wait_for_input returned %d\n", res);
|
|
|
|
|
return -1;
|
|
|
|
|
} else if (res == 0) {
|
|
|
|
|
/* timeout */
|
|
|
|
|
ast_debug(2, "SIP TLS server timed out\n");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ao2_lock(tcptls_session);
|
|
|
|
|
if (!(bytes_read = fread(buf, 1, MIN(sizeof(buf) - 1, content_length), tcptls_session->f))) {
|
|
|
|
|
ao2_unlock(tcptls_session);
|
|
|
|
|
if (after_poll) {
|
|
|
|
|
return -1;
|
|
|
|
|
} else {
|
|
|
|
|
need_poll = 1;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
buf[bytes_read] = '\0';
|
|
|
|
|
ao2_unlock(tcptls_session);
|
|
|
|
|
after_poll = 0;
|
|
|
|
|
if (me->stop) {
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
content_length -= strlen(buf);
|
|
|
|
|
ast_str_append(&req->data, 0, "%s", buf);
|
|
|
|
|
|
|
|
|
|
datalen = ast_str_strlen(req->data);
|
|
|
|
|
if (datalen > SIP_MAX_PACKET_SIZE) {
|
|
|
|
|
ast_log(LOG_WARNING, "Rejecting TLS packet from '%s' because way too large: %zu\n",
|
|
|
|
|
ast_sockaddr_stringify(&tcptls_session->remote_address), datalen);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
/*! \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 */
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*!
|
|
|
|
|
* \brief Indication of a TCP message's integrity
|
|
|
|
|
*/
|
|
|
|
@ -2949,14 +2799,14 @@ static enum message_integrity check_message_integrity(struct ast_str **request,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*!
|
|
|
|
|
* \brief Read SIP request or response from a TCP connection
|
|
|
|
|
* \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 connection from which to read
|
|
|
|
|
* \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_tcp_read(struct sip_request *req, struct ast_tcptls_session_instance *tcptls_session,
|
|
|
|
|
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;
|
|
|
|
@ -2974,7 +2824,7 @@ static int sip_tcp_read(struct sip_request *req, struct ast_tcptls_session_insta
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (timeout == 0) {
|
|
|
|
|
ast_debug(2, "SIP TCP server timed out\n");
|
|
|
|
|
ast_debug(2, "SIP TCP/TLS server timed out\n");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
@ -2982,19 +2832,22 @@ static int sip_tcp_read(struct sip_request *req, struct ast_tcptls_session_insta
|
|
|
|
|
}
|
|
|
|
|
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);
|
|
|
|
|
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 server timed out\n");
|
|
|
|
|
ast_debug(2, "SIP TCP/TLS server timed out\n");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
res = recv(tcptls_session->fd, readbuf, sizeof(readbuf) - 1, 0);
|
|
|
|
|
res = ast_tcptls_server_read(tcptls_session, readbuf, sizeof(readbuf) - 1);
|
|
|
|
|
if (res < 0) {
|
|
|
|
|
ast_debug(2, "SIP TCP server error when receiving data\n");
|
|
|
|
|
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 server has shut down\n");
|
|
|
|
|
ast_debug(2, "SIP TCP/TLS server has shut down\n");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
readbuf[res] = '\0';
|
|
|
|
@ -3003,10 +2856,10 @@ static int sip_tcp_read(struct sip_request *req, struct ast_tcptls_session_insta
|
|
|
|
|
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 packet from '%s' because way too large: %zu\n",
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
@ -3172,12 +3025,8 @@ static void *_sip_tcp_helper_thread(struct ast_tcptls_session_instance *tcptls_s
|
|
|
|
|
req.socket.port = htons(ourport_tcp);
|
|
|
|
|
}
|
|
|
|
|
req.socket.fd = tcptls_session->fd;
|
|
|
|
|
if (tcptls_session->ssl) {
|
|
|
|
|
res = sip_tls_read(&req, &reqcpy, tcptls_session, authenticated, start, me);
|
|
|
|
|
} else {
|
|
|
|
|
res = sip_tcp_read(&req, tcptls_session, authenticated, start);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
res = sip_tcptls_read(&req, tcptls_session, authenticated, start);
|
|
|
|
|
if (res < 0) {
|
|
|
|
|
goto cleanup;
|
|
|
|
|
}
|
|
|
|
|