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

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