Blame SOURCES/kvm-io-Attempt-to-send-websocket-close-messages-to-clien.patch

9bac43
From b3f0cf18f87238075db4091fbfa63921852aeca6 Mon Sep 17 00:00:00 2001
9bac43
From: "Daniel P. Berrange" <berrange@redhat.com>
9bac43
Date: Wed, 20 Dec 2017 17:56:52 +0100
9bac43
Subject: [PATCH 12/42] io: Attempt to send websocket close messages to client
9bac43
9bac43
RH-Author: Daniel P. Berrange <berrange@redhat.com>
9bac43
Message-id: <20171220175702.29663-11-berrange@redhat.com>
9bac43
Patchwork-id: 78463
9bac43
O-Subject: [RHV-7.5 qemu-kvm-rhev PATCH v2 10/20] io: Attempt to send websocket close messages to client
9bac43
Bugzilla: 1518649
9bac43
RH-Acked-by: John Snow <jsnow@redhat.com>
9bac43
RH-Acked-by: Jeffrey Cody <jcody@redhat.com>
9bac43
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
9bac43
9bac43
From: Brandon Carpenter <brandon.carpenter@cypherpath.com>
9bac43
9bac43
Make a best effort attempt to close websocket connections according to
9bac43
the RFC. Sends the close message, as room permits in the socket buffer,
9bac43
and immediately closes the socket.
9bac43
9bac43
Signed-off-by: Brandon Carpenter <brandon.carpenter@cypherpath.com>
9bac43
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
9bac43
(cherry picked from commit 530ca60c16c83435d4becc9916d74fa43e003815)
9bac43
Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
9bac43
---
9bac43
 io/channel-websock.c | 68 +++++++++++++++++++++++++++++++++++++++++++++++++---
9bac43
 1 file changed, 65 insertions(+), 3 deletions(-)
9bac43
9bac43
diff --git a/io/channel-websock.c b/io/channel-websock.c
9bac43
index b6fc0c9..3195eb2 100644
9bac43
--- a/io/channel-websock.c
9bac43
+++ b/io/channel-websock.c
9bac43
@@ -188,6 +188,15 @@ static void qio_channel_websock_handshake_send_res_err(QIOChannelWebsock *ioc,
9bac43
     g_free(date);
9bac43
 }
9bac43
 
9bac43
+enum {
9bac43
+    QIO_CHANNEL_WEBSOCK_STATUS_NORMAL = 1000,
9bac43
+    QIO_CHANNEL_WEBSOCK_STATUS_PROTOCOL_ERR = 1002,
9bac43
+    QIO_CHANNEL_WEBSOCK_STATUS_INVALID_DATA = 1003,
9bac43
+    QIO_CHANNEL_WEBSOCK_STATUS_POLICY = 1008,
9bac43
+    QIO_CHANNEL_WEBSOCK_STATUS_TOO_LARGE = 1009,
9bac43
+    QIO_CHANNEL_WEBSOCK_STATUS_SERVER_ERR = 1011,
9bac43
+};
9bac43
+
9bac43
 static size_t
9bac43
 qio_channel_websock_extract_headers(QIOChannelWebsock *ioc,
9bac43
                                     char *buffer,
9bac43
@@ -617,6 +626,27 @@ static void qio_channel_websock_encode(QIOChannelWebsock *ioc)
9bac43
 }
9bac43
 
9bac43
 
9bac43
+static ssize_t qio_channel_websock_write_wire(QIOChannelWebsock *, Error **);
9bac43
+
9bac43
+
9bac43
+static void qio_channel_websock_write_close(QIOChannelWebsock *ioc,
9bac43
+                                            uint16_t code, const char *reason)
9bac43
+{
9bac43
+    buffer_reserve(&ioc->rawoutput, 2 + (reason ? strlen(reason) : 0));
9bac43
+    *(uint16_t *)(ioc->rawoutput.buffer + ioc->rawoutput.offset) =
9bac43
+        cpu_to_be16(code);
9bac43
+    ioc->rawoutput.offset += 2;
9bac43
+    if (reason) {
9bac43
+        buffer_append(&ioc->rawoutput, reason, strlen(reason));
9bac43
+    }
9bac43
+    qio_channel_websock_encode_buffer(
9bac43
+        &ioc->encoutput, QIO_CHANNEL_WEBSOCK_OPCODE_CLOSE, &ioc->rawoutput);
9bac43
+    buffer_reset(&ioc->rawoutput);
9bac43
+    qio_channel_websock_write_wire(ioc, NULL);
9bac43
+    qio_channel_shutdown(ioc->master, QIO_CHANNEL_SHUTDOWN_BOTH, NULL);
9bac43
+}
9bac43
+
9bac43
+
9bac43
 static int qio_channel_websock_decode_header(QIOChannelWebsock *ioc,
9bac43
                                              Error **errp)
9bac43
 {
9bac43
@@ -630,6 +660,9 @@ static int qio_channel_websock_decode_header(QIOChannelWebsock *ioc,
9bac43
         error_setg(errp,
9bac43
                    "Decoding header but %zu bytes of payload remain",
9bac43
                    ioc->payload_remain);
9bac43
+        qio_channel_websock_write_close(
9bac43
+            ioc, QIO_CHANNEL_WEBSOCK_STATUS_SERVER_ERR,
9bac43
+            "internal server error");
9bac43
         return -1;
9bac43
     }
9bac43
     if (ioc->encinput.offset < QIO_CHANNEL_WEBSOCK_HEADER_LEN_7_BIT) {
9bac43
@@ -662,19 +695,29 @@ static int qio_channel_websock_decode_header(QIOChannelWebsock *ioc,
9bac43
     if (!fin) {
9bac43
         if (opcode != QIO_CHANNEL_WEBSOCK_OPCODE_BINARY_FRAME) {
9bac43
             error_setg(errp, "only binary websocket frames may be fragmented");
9bac43
+            qio_channel_websock_write_close(
9bac43
+                ioc, QIO_CHANNEL_WEBSOCK_STATUS_POLICY ,
9bac43
+                "only binary frames may be fragmented");
9bac43
             return -1;
9bac43
         }
9bac43
     } else {
9bac43
         if (opcode != QIO_CHANNEL_WEBSOCK_OPCODE_BINARY_FRAME &&
9bac43
+            opcode != QIO_CHANNEL_WEBSOCK_OPCODE_CLOSE &&
9bac43
             opcode != QIO_CHANNEL_WEBSOCK_OPCODE_PING &&
9bac43
             opcode != QIO_CHANNEL_WEBSOCK_OPCODE_PONG) {
9bac43
-            error_setg(errp, "unsupported opcode: %#04x; only binary, ping, "
9bac43
-                             "and pong websocket frames are supported", opcode);
9bac43
+            error_setg(errp, "unsupported opcode: %#04x; only binary, close, "
9bac43
+                       "ping, and pong websocket frames are supported", opcode);
9bac43
+            qio_channel_websock_write_close(
9bac43
+                ioc, QIO_CHANNEL_WEBSOCK_STATUS_INVALID_DATA ,
9bac43
+                "only binary, close, ping, and pong frames are supported");
9bac43
             return -1;
9bac43
         }
9bac43
     }
9bac43
     if (!has_mask) {
9bac43
         error_setg(errp, "client websocket frames must be masked");
9bac43
+        qio_channel_websock_write_close(
9bac43
+            ioc, QIO_CHANNEL_WEBSOCK_STATUS_PROTOCOL_ERR,
9bac43
+            "client frames must be masked");
9bac43
         return -1;
9bac43
     }
9bac43
 
9bac43
@@ -684,6 +727,9 @@ static int qio_channel_websock_decode_header(QIOChannelWebsock *ioc,
9bac43
         ioc->mask = header->u.m;
9bac43
     } else if (opcode & QIO_CHANNEL_WEBSOCK_CONTROL_OPCODE_MASK) {
9bac43
         error_setg(errp, "websocket control frame is too large");
9bac43
+        qio_channel_websock_write_close(
9bac43
+            ioc, QIO_CHANNEL_WEBSOCK_STATUS_PROTOCOL_ERR,
9bac43
+            "control frame is too large");
9bac43
         return -1;
9bac43
     } else if (payload_len == QIO_CHANNEL_WEBSOCK_PAYLOAD_LEN_MAGIC_16_BIT &&
9bac43
                ioc->encinput.offset >= QIO_CHANNEL_WEBSOCK_HEADER_LEN_16_BIT) {
9bac43
@@ -701,7 +747,7 @@ static int qio_channel_websock_decode_header(QIOChannelWebsock *ioc,
9bac43
     }
9bac43
 
9bac43
     buffer_advance(&ioc->encinput, header_size);
9bac43
-    return 1;
9bac43
+    return 0;
9bac43
 }
9bac43
 
9bac43
 
9bac43
@@ -751,6 +797,22 @@ static int qio_channel_websock_decode_payload(QIOChannelWebsock *ioc,
9bac43
             buffer_reserve(&ioc->rawinput, payload_len);
9bac43
             buffer_append(&ioc->rawinput, ioc->encinput.buffer, payload_len);
9bac43
         }
9bac43
+    } else if (ioc->opcode == QIO_CHANNEL_WEBSOCK_OPCODE_CLOSE) {
9bac43
+        /* close frames are echoed back */
9bac43
+        error_setg(errp, "websocket closed by peer");
9bac43
+        if (payload_len) {
9bac43
+            /* echo client status */
9bac43
+            qio_channel_websock_encode_buffer(
9bac43
+                &ioc->encoutput, QIO_CHANNEL_WEBSOCK_OPCODE_CLOSE,
9bac43
+                &ioc->encinput);
9bac43
+            qio_channel_websock_write_wire(ioc, NULL);
9bac43
+            qio_channel_shutdown(ioc->master, QIO_CHANNEL_SHUTDOWN_BOTH, NULL);
9bac43
+        } else {
9bac43
+            /* send our own status */
9bac43
+            qio_channel_websock_write_close(
9bac43
+                ioc, QIO_CHANNEL_WEBSOCK_STATUS_NORMAL, "peer requested close");
9bac43
+        }
9bac43
+        return -1;
9bac43
     } else if (ioc->opcode == QIO_CHANNEL_WEBSOCK_OPCODE_PING) {
9bac43
         /* ping frames produce an immediate reply */
9bac43
         buffer_reset(&ioc->ping_reply);
9bac43
-- 
9bac43
1.8.3.1
9bac43