chan_sip: Replace sip_tls_read() and resolve the large SDP poll issue.
authorRichard Mudgett <rmudgett@digium.com>
Thu, 7 Aug 2014 21:58:38 +0000 (21:58 +0000)
committerRichard Mudgett <rmudgett@digium.com>
Thu, 7 Aug 2014 21:58:38 +0000 (21:58 +0000)
Replace sip_tls_read() and sip_tcp_read() with a single function and
resolve the poll/wait issue with large SDP payloads.

ASTERISK-18345 #close
Reported by: Stephane Chazelas
Patches:
      tcptls_pollv4.diff (license #5835) patch uploaded by Elazar Broad

Review: https://reviewboard.asterisk.org/r/3882/
........

Merged revisions 420434 from http://svn.asterisk.org/svn/asterisk/branches/1.8
........

Merged revisions 420435 from http://svn.asterisk.org/svn/asterisk/branches/11
........

Merged revisions 420436 from http://svn.asterisk.org/svn/asterisk/branches/12

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@420437 65c4cc65-6c06-0410-ace0-fbb531ad65f3

channels/chan_sip.c

index 884c284..d313ca0 100644 (file)
@@ -2695,156 +2695,6 @@ static int sip_check_authtimeout(time_t start)
 }
 
 /*!
- * \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
  */
 enum message_integrity {
@@ -2997,14 +2847,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;
@@ -3022,7 +2872,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 {
@@ -3030,19 +2880,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';
@@ -3054,7 +2907,7 @@ static int sip_tcp_read(struct sip_request *req, struct ast_tcptls_session_insta
 
                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;
                }
@@ -3220,12 +3073,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;
                        }