]> git.ipfire.org Git - thirdparty/asterisk.git/commitdiff
websocket: Consider pending SSL data when waiting for socket input
authorSean Bright <sean.bright@gmail.com>
Tue, 26 Nov 2019 19:24:10 +0000 (14:24 -0500)
committerSean Bright <sean.bright@gmail.com>
Thu, 2 Jan 2020 21:51:28 +0000 (15:51 -0600)
When TLS is in use, checking the readiness of the underlying FD is insufficient
for determining if there is data available to be read. So before polling the
FD, check if there is any buffered data in the TLS layer and use that first.

ASTERISK-28562 #close
Reported by: Robert Sutton

Change-Id: I95fcb3e2004700d5cf8e5ee04943f0115b15e10d

include/asterisk/http_websocket.h
include/asterisk/iostream.h
main/iostream.c
res/res_http_websocket.c
res/res_pjsip_transport_websocket.c

index 2180ef46bd6d5f65759a3a75c9155f37f8a5c679..6fd2d0a6c572b7e6a17b1c0da0c50d538ef6bd30 100644 (file)
@@ -337,6 +337,20 @@ AST_OPTIONAL_API(void, ast_websocket_unref, (struct ast_websocket *session), {re
  */
 AST_OPTIONAL_API(int, ast_websocket_fd, (struct ast_websocket *session), { errno = ENOSYS; return -1;});
 
+/*!
+ * \brief Wait for the WebSocket session to be ready to be read.
+ * \since 16.8.0
+ * \since 17.2.0
+ *
+ * \param session Pointer to the WebSocket session
+ * \param timeout the number of milliseconds to wait
+ *
+ * \retval -1 if error occurred
+ * \retval 0 if the timeout expired
+ * \retval 1 if the WebSocket session is ready for reading
+ */
+AST_OPTIONAL_API(int, ast_websocket_wait_for_input, (struct ast_websocket *session, int timeout), { errno = ENOSYS; return -1; });
+
 /*!
  * \brief Get the remote address for a WebSocket connected session.
  *
index 17376eae1e4ed62a1572c456d80e0607470b1138..602fefbab34b7ae44a6f55948aeb306b274ff883 100644 (file)
@@ -126,6 +126,20 @@ void ast_iostream_set_exclusive_input(struct ast_iostream *stream, int exclusive
  */
 int ast_iostream_get_fd(struct ast_iostream *stream);
 
+/*!
+ * \brief Wait for input on the iostream's file descriptor
+ * \since 16.8.0
+ * \since 17.2.0
+ *
+ * \param stream A pointer to an iostream
+ * \param timeout the number of milliseconds to wait
+ *
+ * \retval -1 if error occurred
+ * \retval 0 if the timeout expired
+ * \retval 1 if the stream is ready for reading
+ */
+int ast_iostream_wait_for_input(struct ast_iostream *stream, int timeout);
+
 /*!
  * \brief Make an iostream non-blocking.
  *
index 15131c09ffd113ffebacf256d1faf2779afad148..d060b6d6d445cda68d6ce6968ad4ce8b4b15b9fe 100644 (file)
@@ -86,6 +86,20 @@ int ast_iostream_get_fd(struct ast_iostream *stream)
        return stream->fd;
 }
 
+int ast_iostream_wait_for_input(struct ast_iostream *stream, int timeout)
+{
+#if defined(DO_SSL)
+       /* Because SSL is read in blocks, it's possible that the last time we read we
+          got more than we asked for and it is now buffered inside OpenSSL. If that
+          is the case, calling ast_wait_for_input() will block until the fd is ready
+          for reading again, which might never happen. */
+       if (stream->ssl && SSL_pending(stream->ssl)) {
+               return 1;
+       }
+#endif
+       return ast_wait_for_input(stream->fd, timeout);
+}
+
 void ast_iostream_nonblock(struct ast_iostream *stream)
 {
        ast_fd_set_flags(stream->fd, O_NONBLOCK);
index e79066b8948dde1bdd663d9ca885ebf94a70c045..63fccdda0e58a01221ce6c054d09567c84fa5296 100644 (file)
@@ -427,6 +427,11 @@ int AST_OPTIONAL_API_NAME(ast_websocket_fd)(struct ast_websocket *session)
        return session->closing ? -1 : ast_iostream_get_fd(session->stream);
 }
 
+int AST_OPTIONAL_API_NAME(ast_websocket_wait_for_input)(struct ast_websocket *session, int timeout)
+{
+       return session->closing ? -1 : ast_iostream_wait_for_input(session->stream, timeout);
+}
+
 struct ast_sockaddr * AST_OPTIONAL_API_NAME(ast_websocket_remote_address)(struct ast_websocket *session)
 {
        return &session->remote_address;
@@ -545,8 +550,8 @@ static inline int ws_safe_read(struct ast_websocket *session, char *buf, size_t
                                break;
                        }
                }
-               if (ast_wait_for_input(ast_iostream_get_fd(session->stream), 1000) < 0) {
-                       ast_log(LOG_ERROR, "ast_wait_for_input returned err: %s\n", strerror(errno));
+               if (ast_iostream_wait_for_input(session->stream, 1000) < 0) {
+                       ast_log(LOG_ERROR, "ast_iostream_wait_for_input returned err: %s\n", strerror(errno));
                        *opcode = AST_WEBSOCKET_OPCODE_CLOSE;
                        session->closing = 1;
                        ao2_unlock(session);
@@ -974,7 +979,7 @@ static void websocket_echo_callback(struct ast_websocket *session, struct ast_va
                goto end;
        }
 
-       while ((res = ast_wait_for_input(ast_websocket_fd(session), -1)) > 0) {
+       while ((res = ast_websocket_wait_for_input(session, -1)) > 0) {
                char *payload;
                uint64_t payload_len;
                enum ast_websocket_opcode opcode;
index 6383f6810fdaa004b2cf902caadbad117afbf8dd..4f47a8ccb542ad607e789a6f0387e506f6810df7 100644 (file)
@@ -392,7 +392,7 @@ static void websocket_cb(struct ast_websocket *session, struct ast_variable *par
        transport = create_data.transport;
        read_data.transport = transport;
 
-       while (ast_wait_for_input(ast_websocket_fd(session), -1) > 0) {
+       while (ast_websocket_wait_for_input(session, -1) > 0) {
                enum ast_websocket_opcode opcode;
                int fragmented;