Merge qio 2017/10/04 v1
-----BEGIN PGP SIGNATURE----- iQIcBAABCAAGBQJZ1NKoAAoJEL6G67QVEE/fJ0oP/RMt6ZuCcAgR369Bp4tZRcp4 Z58Dhr48pjWmSrXINxambx3I+W3OlHo+5x4JxDWa9+VeKlmSEiZqWkzEa1TsHi7h YLVjuTkGO9wDYU/CnLprwDTNtxZ7ppt9immPG+6MsnqSIGBeCAKCKs7gXK85EHzz pRbpb5MCf0duUjExQPYSrNGIT4KVjKEj1G9QAZM80tTg/I/FxxWc7OXV3fZtl3cY kblLCMc0kahnQAYLDzL9bjgrvx7D8u8xd0BZBTaNz7gZ0sh7f99ad0KCcFyVpLoG YSm/GbuvbrylX0h5HXKalo3RTantCE+JuGunT/KN4Q8ba0SvbVttROr+5EDbRy1y UIKfM5RtLpj6P72ySLBrcPE2WX/Kw7ItB5D2ayu+fO1TWhPa5S58k8XuMv5G72Ny hxcx6Wd/bD0SzdBfZHvd7gfc4VMSXo2XjjRbcHlPa1nRIluFzsHLFug82+MgNL6z D5Vw1pW+6QCq9bZK6wsuMRKP+BxISg4BfH8zsEe+CstovOKEgTMr3HxLSj6jCFPN Ta7t+JnIieTTrne/g/upecbbHDXjDPA2qavOBjcePR262T5OeCjVYiOgrEw2xomC xcTHg/kwAVgc9G85pORtEuZK5gCgD2dKdnODgaJKv4VZHk+I9wBUKOihUqBgA2WW iHenAJzXZcFRYKFQzyNr =/rNZ -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/berrange/tags/pull-qio-2017-10-04-1' into staging Merge qio 2017/10/04 v1 # gpg: Signature made Wed 04 Oct 2017 13:23:04 BST # gpg: using RSA key 0xBE86EBB415104FDF # gpg: Good signature from "Daniel P. Berrange <dan@berrange.com>" # gpg: aka "Daniel P. Berrange <berrange@redhat.com>" # Primary key fingerprint: DAF3 A6FD B26B 6291 2D0E 8E3F BE86 EBB4 1510 4FDF * remotes/berrange/tags/pull-qio-2017-10-04-1: io: add trace events for websockets frame handling io: Attempt to send websocket close messages to client io: Reply to ping frames io: Ignore websocket PING and PONG frames io: Allow empty websocket payload io: Add support for fragmented websocket binary frames io: Small updates in preparation for websocket changes ui: Always remove an old VNC channel watch before adding a new one io: use case insensitive check for Connection & Upgrade websock headers io: include full error message in websocket handshake trace io: send proper HTTP response for websocket errors Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
1fdc4c5d82
@ -60,11 +60,13 @@ struct QIOChannelWebsock {
|
||||
Buffer encoutput;
|
||||
Buffer rawinput;
|
||||
Buffer rawoutput;
|
||||
Buffer ping_reply;
|
||||
size_t payload_remain;
|
||||
QIOChannelWebsockMask mask;
|
||||
guint io_tag;
|
||||
Error *io_err;
|
||||
gboolean io_eof;
|
||||
uint8_t opcode;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -25,6 +25,8 @@
|
||||
#include "crypto/hash.h"
|
||||
#include "trace.h"
|
||||
|
||||
#include <time.h>
|
||||
|
||||
|
||||
/* Max amount to allow in rawinput/rawoutput buffers */
|
||||
#define QIO_CHANNEL_WEBSOCK_MAX_BUFFER 8192
|
||||
@ -44,13 +46,40 @@
|
||||
#define QIO_CHANNEL_WEBSOCK_CONNECTION_UPGRADE "Upgrade"
|
||||
#define QIO_CHANNEL_WEBSOCK_UPGRADE_WEBSOCKET "websocket"
|
||||
|
||||
#define QIO_CHANNEL_WEBSOCK_HANDSHAKE_RESPONSE \
|
||||
#define QIO_CHANNEL_WEBSOCK_HANDSHAKE_RES_COMMON \
|
||||
"Server: QEMU VNC\r\n" \
|
||||
"Date: %s\r\n"
|
||||
|
||||
#define QIO_CHANNEL_WEBSOCK_HANDSHAKE_RES_OK \
|
||||
"HTTP/1.1 101 Switching Protocols\r\n" \
|
||||
QIO_CHANNEL_WEBSOCK_HANDSHAKE_RES_COMMON \
|
||||
"Upgrade: websocket\r\n" \
|
||||
"Connection: Upgrade\r\n" \
|
||||
"Sec-WebSocket-Accept: %s\r\n" \
|
||||
"Sec-WebSocket-Protocol: binary\r\n" \
|
||||
"\r\n"
|
||||
#define QIO_CHANNEL_WEBSOCK_HANDSHAKE_RES_NOT_FOUND \
|
||||
"HTTP/1.1 404 Not Found\r\n" \
|
||||
QIO_CHANNEL_WEBSOCK_HANDSHAKE_RES_COMMON \
|
||||
"Connection: close\r\n" \
|
||||
"\r\n"
|
||||
#define QIO_CHANNEL_WEBSOCK_HANDSHAKE_RES_BAD_REQUEST \
|
||||
"HTTP/1.1 400 Bad Request\r\n" \
|
||||
QIO_CHANNEL_WEBSOCK_HANDSHAKE_RES_COMMON \
|
||||
"Connection: close\r\n" \
|
||||
"Sec-WebSocket-Version: " \
|
||||
QIO_CHANNEL_WEBSOCK_SUPPORTED_VERSION \
|
||||
"\r\n"
|
||||
#define QIO_CHANNEL_WEBSOCK_HANDSHAKE_RES_SERVER_ERR \
|
||||
"HTTP/1.1 500 Internal Server Error\r\n" \
|
||||
QIO_CHANNEL_WEBSOCK_HANDSHAKE_RES_COMMON \
|
||||
"Connection: close\r\n" \
|
||||
"\r\n"
|
||||
#define QIO_CHANNEL_WEBSOCK_HANDSHAKE_RES_TOO_LARGE \
|
||||
"HTTP/1.1 403 Request Entity Too Large\r\n" \
|
||||
QIO_CHANNEL_WEBSOCK_HANDSHAKE_RES_COMMON \
|
||||
"Connection: close\r\n" \
|
||||
"\r\n"
|
||||
#define QIO_CHANNEL_WEBSOCK_HANDSHAKE_DELIM "\r\n"
|
||||
#define QIO_CHANNEL_WEBSOCK_HANDSHAKE_END "\r\n\r\n"
|
||||
#define QIO_CHANNEL_WEBSOCK_SUPPORTED_VERSION "13"
|
||||
@ -81,13 +110,12 @@
|
||||
/* Magic 7-bit length to indicate use of 64-bit payload length */
|
||||
#define QIO_CHANNEL_WEBSOCK_PAYLOAD_LEN_MAGIC_64_BIT 127
|
||||
|
||||
/* Bitmasks & shifts for accessing header fields */
|
||||
/* Bitmasks for accessing header fields */
|
||||
#define QIO_CHANNEL_WEBSOCK_HEADER_FIELD_FIN 0x80
|
||||
#define QIO_CHANNEL_WEBSOCK_HEADER_FIELD_OPCODE 0x0f
|
||||
#define QIO_CHANNEL_WEBSOCK_HEADER_FIELD_HAS_MASK 0x80
|
||||
#define QIO_CHANNEL_WEBSOCK_HEADER_FIELD_PAYLOAD_LEN 0x7f
|
||||
#define QIO_CHANNEL_WEBSOCK_HEADER_SHIFT_FIN 7
|
||||
#define QIO_CHANNEL_WEBSOCK_HEADER_SHIFT_HAS_MASK 7
|
||||
#define QIO_CHANNEL_WEBSOCK_CONTROL_OPCODE_MASK 0x8
|
||||
|
||||
typedef struct QIOChannelWebsockHeader QIOChannelWebsockHeader;
|
||||
|
||||
@ -123,8 +151,55 @@ enum {
|
||||
QIO_CHANNEL_WEBSOCK_OPCODE_PONG = 0xA
|
||||
};
|
||||
|
||||
static void qio_channel_websock_handshake_send_res(QIOChannelWebsock *ioc,
|
||||
const char *resmsg,
|
||||
...)
|
||||
{
|
||||
va_list vargs;
|
||||
char *response;
|
||||
size_t responselen;
|
||||
|
||||
va_start(vargs, resmsg);
|
||||
response = g_strdup_vprintf(resmsg, vargs);
|
||||
responselen = strlen(response);
|
||||
buffer_reserve(&ioc->encoutput, responselen);
|
||||
buffer_append(&ioc->encoutput, response, responselen);
|
||||
va_end(vargs);
|
||||
}
|
||||
|
||||
static gchar *qio_channel_websock_date_str(void)
|
||||
{
|
||||
struct tm tm;
|
||||
time_t now = time(NULL);
|
||||
char datebuf[128];
|
||||
|
||||
gmtime_r(&now, &tm);
|
||||
|
||||
strftime(datebuf, sizeof(datebuf), "%a, %d %b %Y %H:%M:%S GMT", &tm);
|
||||
|
||||
return g_strdup(datebuf);
|
||||
}
|
||||
|
||||
static void qio_channel_websock_handshake_send_res_err(QIOChannelWebsock *ioc,
|
||||
const char *resdata)
|
||||
{
|
||||
char *date = qio_channel_websock_date_str();
|
||||
qio_channel_websock_handshake_send_res(ioc, resdata, date);
|
||||
g_free(date);
|
||||
}
|
||||
|
||||
enum {
|
||||
QIO_CHANNEL_WEBSOCK_STATUS_NORMAL = 1000,
|
||||
QIO_CHANNEL_WEBSOCK_STATUS_PROTOCOL_ERR = 1002,
|
||||
QIO_CHANNEL_WEBSOCK_STATUS_INVALID_DATA = 1003,
|
||||
QIO_CHANNEL_WEBSOCK_STATUS_POLICY = 1008,
|
||||
QIO_CHANNEL_WEBSOCK_STATUS_TOO_LARGE = 1009,
|
||||
QIO_CHANNEL_WEBSOCK_STATUS_SERVER_ERR = 1011,
|
||||
};
|
||||
|
||||
static size_t
|
||||
qio_channel_websock_extract_headers(char *buffer,
|
||||
qio_channel_websock_extract_headers(QIOChannelWebsock *ioc,
|
||||
char *buffer,
|
||||
QIOChannelWebsockHTTPHeader *hdrs,
|
||||
size_t nhdrsalloc,
|
||||
Error **errp)
|
||||
@ -145,7 +220,7 @@ qio_channel_websock_extract_headers(char *buffer,
|
||||
nl = strstr(buffer, QIO_CHANNEL_WEBSOCK_HANDSHAKE_DELIM);
|
||||
if (!nl) {
|
||||
error_setg(errp, "Missing HTTP header delimiter");
|
||||
return 0;
|
||||
goto bad_request;
|
||||
}
|
||||
*nl = '\0';
|
||||
|
||||
@ -158,18 +233,20 @@ qio_channel_websock_extract_headers(char *buffer,
|
||||
|
||||
if (!g_str_equal(buffer, QIO_CHANNEL_WEBSOCK_HTTP_METHOD)) {
|
||||
error_setg(errp, "Unsupported HTTP method %s", buffer);
|
||||
return 0;
|
||||
goto bad_request;
|
||||
}
|
||||
|
||||
buffer = tmp + 1;
|
||||
tmp = strchr(buffer, ' ');
|
||||
if (!tmp) {
|
||||
error_setg(errp, "Missing HTTP version delimiter");
|
||||
return 0;
|
||||
goto bad_request;
|
||||
}
|
||||
*tmp = '\0';
|
||||
|
||||
if (!g_str_equal(buffer, QIO_CHANNEL_WEBSOCK_HTTP_PATH)) {
|
||||
qio_channel_websock_handshake_send_res_err(
|
||||
ioc, QIO_CHANNEL_WEBSOCK_HANDSHAKE_RES_NOT_FOUND);
|
||||
error_setg(errp, "Unexpected HTTP path %s", buffer);
|
||||
return 0;
|
||||
}
|
||||
@ -178,7 +255,7 @@ qio_channel_websock_extract_headers(char *buffer,
|
||||
|
||||
if (!g_str_equal(buffer, QIO_CHANNEL_WEBSOCK_HTTP_VERSION)) {
|
||||
error_setg(errp, "Unsupported HTTP version %s", buffer);
|
||||
return 0;
|
||||
goto bad_request;
|
||||
}
|
||||
|
||||
buffer = nl + strlen(QIO_CHANNEL_WEBSOCK_HANDSHAKE_DELIM);
|
||||
@ -203,7 +280,7 @@ qio_channel_websock_extract_headers(char *buffer,
|
||||
sep = strchr(buffer, ':');
|
||||
if (!sep) {
|
||||
error_setg(errp, "Malformed HTTP header");
|
||||
return 0;
|
||||
goto bad_request;
|
||||
}
|
||||
*sep = '\0';
|
||||
sep++;
|
||||
@ -213,7 +290,7 @@ qio_channel_websock_extract_headers(char *buffer,
|
||||
|
||||
if (nhdrs >= nhdrsalloc) {
|
||||
error_setg(errp, "Too many HTTP headers");
|
||||
return 0;
|
||||
goto bad_request;
|
||||
}
|
||||
|
||||
hdr = &hdrs[nhdrs++];
|
||||
@ -231,6 +308,11 @@ qio_channel_websock_extract_headers(char *buffer,
|
||||
} while (nl != NULL);
|
||||
|
||||
return nhdrs;
|
||||
|
||||
bad_request:
|
||||
qio_channel_websock_handshake_send_res_err(
|
||||
ioc, QIO_CHANNEL_WEBSOCK_HANDSHAKE_RES_BAD_REQUEST);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char *
|
||||
@ -250,14 +332,14 @@ qio_channel_websock_find_header(QIOChannelWebsockHTTPHeader *hdrs,
|
||||
}
|
||||
|
||||
|
||||
static int qio_channel_websock_handshake_send_response(QIOChannelWebsock *ioc,
|
||||
const char *key,
|
||||
Error **errp)
|
||||
static void qio_channel_websock_handshake_send_res_ok(QIOChannelWebsock *ioc,
|
||||
const char *key,
|
||||
Error **errp)
|
||||
{
|
||||
char combined_key[QIO_CHANNEL_WEBSOCK_CLIENT_KEY_LEN +
|
||||
QIO_CHANNEL_WEBSOCK_GUID_LEN + 1];
|
||||
char *accept = NULL, *response = NULL;
|
||||
size_t responselen;
|
||||
char *accept = NULL;
|
||||
char *date = qio_channel_websock_date_str();
|
||||
|
||||
g_strlcpy(combined_key, key, QIO_CHANNEL_WEBSOCK_CLIENT_KEY_LEN + 1);
|
||||
g_strlcat(combined_key, QIO_CHANNEL_WEBSOCK_GUID,
|
||||
@ -271,105 +353,108 @@ static int qio_channel_websock_handshake_send_response(QIOChannelWebsock *ioc,
|
||||
QIO_CHANNEL_WEBSOCK_GUID_LEN,
|
||||
&accept,
|
||||
errp) < 0) {
|
||||
return -1;
|
||||
qio_channel_websock_handshake_send_res_err(
|
||||
ioc, QIO_CHANNEL_WEBSOCK_HANDSHAKE_RES_SERVER_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
response = g_strdup_printf(QIO_CHANNEL_WEBSOCK_HANDSHAKE_RESPONSE, accept);
|
||||
responselen = strlen(response);
|
||||
buffer_reserve(&ioc->encoutput, responselen);
|
||||
buffer_append(&ioc->encoutput, response, responselen);
|
||||
qio_channel_websock_handshake_send_res(
|
||||
ioc, QIO_CHANNEL_WEBSOCK_HANDSHAKE_RES_OK, date, accept);
|
||||
|
||||
g_free(date);
|
||||
g_free(accept);
|
||||
g_free(response);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qio_channel_websock_handshake_process(QIOChannelWebsock *ioc,
|
||||
char *buffer,
|
||||
Error **errp)
|
||||
static void qio_channel_websock_handshake_process(QIOChannelWebsock *ioc,
|
||||
char *buffer,
|
||||
Error **errp)
|
||||
{
|
||||
QIOChannelWebsockHTTPHeader hdrs[32];
|
||||
size_t nhdrs = G_N_ELEMENTS(hdrs);
|
||||
const char *protocols = NULL, *version = NULL, *key = NULL,
|
||||
*host = NULL, *connection = NULL, *upgrade = NULL;
|
||||
|
||||
nhdrs = qio_channel_websock_extract_headers(buffer, hdrs, nhdrs, errp);
|
||||
nhdrs = qio_channel_websock_extract_headers(ioc, buffer, hdrs, nhdrs, errp);
|
||||
if (!nhdrs) {
|
||||
return -1;
|
||||
return;
|
||||
}
|
||||
|
||||
protocols = qio_channel_websock_find_header(
|
||||
hdrs, nhdrs, QIO_CHANNEL_WEBSOCK_HEADER_PROTOCOL);
|
||||
if (!protocols) {
|
||||
error_setg(errp, "Missing websocket protocol header data");
|
||||
return -1;
|
||||
goto bad_request;
|
||||
}
|
||||
|
||||
version = qio_channel_websock_find_header(
|
||||
hdrs, nhdrs, QIO_CHANNEL_WEBSOCK_HEADER_VERSION);
|
||||
if (!version) {
|
||||
error_setg(errp, "Missing websocket version header data");
|
||||
return -1;
|
||||
goto bad_request;
|
||||
}
|
||||
|
||||
key = qio_channel_websock_find_header(
|
||||
hdrs, nhdrs, QIO_CHANNEL_WEBSOCK_HEADER_KEY);
|
||||
if (!key) {
|
||||
error_setg(errp, "Missing websocket key header data");
|
||||
return -1;
|
||||
goto bad_request;
|
||||
}
|
||||
|
||||
host = qio_channel_websock_find_header(
|
||||
hdrs, nhdrs, QIO_CHANNEL_WEBSOCK_HEADER_HOST);
|
||||
if (!host) {
|
||||
error_setg(errp, "Missing websocket host header data");
|
||||
return -1;
|
||||
goto bad_request;
|
||||
}
|
||||
|
||||
connection = qio_channel_websock_find_header(
|
||||
hdrs, nhdrs, QIO_CHANNEL_WEBSOCK_HEADER_CONNECTION);
|
||||
if (!connection) {
|
||||
error_setg(errp, "Missing websocket connection header data");
|
||||
return -1;
|
||||
goto bad_request;
|
||||
}
|
||||
|
||||
upgrade = qio_channel_websock_find_header(
|
||||
hdrs, nhdrs, QIO_CHANNEL_WEBSOCK_HEADER_UPGRADE);
|
||||
if (!upgrade) {
|
||||
error_setg(errp, "Missing websocket upgrade header data");
|
||||
return -1;
|
||||
goto bad_request;
|
||||
}
|
||||
|
||||
if (!g_strrstr(protocols, QIO_CHANNEL_WEBSOCK_PROTOCOL_BINARY)) {
|
||||
error_setg(errp, "No '%s' protocol is supported by client '%s'",
|
||||
QIO_CHANNEL_WEBSOCK_PROTOCOL_BINARY, protocols);
|
||||
return -1;
|
||||
goto bad_request;
|
||||
}
|
||||
|
||||
if (!g_str_equal(version, QIO_CHANNEL_WEBSOCK_SUPPORTED_VERSION)) {
|
||||
error_setg(errp, "Version '%s' is not supported by client '%s'",
|
||||
QIO_CHANNEL_WEBSOCK_SUPPORTED_VERSION, version);
|
||||
return -1;
|
||||
goto bad_request;
|
||||
}
|
||||
|
||||
if (strlen(key) != QIO_CHANNEL_WEBSOCK_CLIENT_KEY_LEN) {
|
||||
error_setg(errp, "Key length '%zu' was not as expected '%d'",
|
||||
strlen(key), QIO_CHANNEL_WEBSOCK_CLIENT_KEY_LEN);
|
||||
return -1;
|
||||
goto bad_request;
|
||||
}
|
||||
|
||||
if (!g_strrstr(connection, QIO_CHANNEL_WEBSOCK_CONNECTION_UPGRADE)) {
|
||||
if (strcasecmp(connection, QIO_CHANNEL_WEBSOCK_CONNECTION_UPGRADE) != 0) {
|
||||
error_setg(errp, "No connection upgrade requested '%s'", connection);
|
||||
return -1;
|
||||
goto bad_request;
|
||||
}
|
||||
|
||||
if (!g_str_equal(upgrade, QIO_CHANNEL_WEBSOCK_UPGRADE_WEBSOCKET)) {
|
||||
if (strcasecmp(upgrade, QIO_CHANNEL_WEBSOCK_UPGRADE_WEBSOCKET) != 0) {
|
||||
error_setg(errp, "Incorrect upgrade method '%s'", upgrade);
|
||||
return -1;
|
||||
goto bad_request;
|
||||
}
|
||||
|
||||
return qio_channel_websock_handshake_send_response(ioc, key, errp);
|
||||
qio_channel_websock_handshake_send_res_ok(ioc, key, errp);
|
||||
return;
|
||||
|
||||
bad_request:
|
||||
qio_channel_websock_handshake_send_res_err(
|
||||
ioc, QIO_CHANNEL_WEBSOCK_HANDSHAKE_RES_BAD_REQUEST);
|
||||
}
|
||||
|
||||
static int qio_channel_websock_handshake_read(QIOChannelWebsock *ioc,
|
||||
@ -393,20 +478,20 @@ static int qio_channel_websock_handshake_read(QIOChannelWebsock *ioc,
|
||||
QIO_CHANNEL_WEBSOCK_HANDSHAKE_END);
|
||||
if (!handshake_end) {
|
||||
if (ioc->encinput.offset >= 4096) {
|
||||
qio_channel_websock_handshake_send_res_err(
|
||||
ioc, QIO_CHANNEL_WEBSOCK_HANDSHAKE_RES_TOO_LARGE);
|
||||
error_setg(errp,
|
||||
"End of headers not found in first 4096 bytes");
|
||||
return -1;
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
*handshake_end = '\0';
|
||||
|
||||
if (qio_channel_websock_handshake_process(ioc,
|
||||
(char *)ioc->encinput.buffer,
|
||||
errp) < 0) {
|
||||
return -1;
|
||||
}
|
||||
qio_channel_websock_handshake_process(ioc,
|
||||
(char *)ioc->encinput.buffer,
|
||||
errp);
|
||||
|
||||
buffer_advance(&ioc->encinput,
|
||||
handshake_end - (char *)ioc->encinput.buffer +
|
||||
@ -430,7 +515,7 @@ static gboolean qio_channel_websock_handshake_send(QIOChannel *ioc,
|
||||
&err);
|
||||
|
||||
if (ret < 0) {
|
||||
trace_qio_channel_websock_handshake_fail(ioc);
|
||||
trace_qio_channel_websock_handshake_fail(ioc, error_get_pretty(err));
|
||||
qio_task_set_error(task, err);
|
||||
qio_task_complete(task);
|
||||
return FALSE;
|
||||
@ -438,8 +523,16 @@ static gboolean qio_channel_websock_handshake_send(QIOChannel *ioc,
|
||||
|
||||
buffer_advance(&wioc->encoutput, ret);
|
||||
if (wioc->encoutput.offset == 0) {
|
||||
trace_qio_channel_websock_handshake_complete(ioc);
|
||||
qio_task_complete(task);
|
||||
if (wioc->io_err) {
|
||||
trace_qio_channel_websock_handshake_fail(
|
||||
ioc, error_get_pretty(wioc->io_err));
|
||||
qio_task_set_error(task, wioc->io_err);
|
||||
wioc->io_err = NULL;
|
||||
qio_task_complete(task);
|
||||
} else {
|
||||
trace_qio_channel_websock_handshake_complete(ioc);
|
||||
qio_task_complete(task);
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
trace_qio_channel_websock_handshake_pending(ioc, G_IO_OUT);
|
||||
@ -458,7 +551,12 @@ static gboolean qio_channel_websock_handshake_io(QIOChannel *ioc,
|
||||
|
||||
ret = qio_channel_websock_handshake_read(wioc, &err);
|
||||
if (ret < 0) {
|
||||
trace_qio_channel_websock_handshake_fail(ioc);
|
||||
/*
|
||||
* We only take this path on a fatal I/O error reading from
|
||||
* client connection, as most of the time we have an
|
||||
* HTTP 4xx err response to send instead
|
||||
*/
|
||||
trace_qio_channel_websock_handshake_fail(ioc, error_get_pretty(err));
|
||||
qio_task_set_error(task, err);
|
||||
qio_task_complete(task);
|
||||
return FALSE;
|
||||
@ -469,6 +567,10 @@ static gboolean qio_channel_websock_handshake_io(QIOChannel *ioc,
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (err) {
|
||||
error_propagate(&wioc->io_err, err);
|
||||
}
|
||||
|
||||
trace_qio_channel_websock_handshake_reply(ioc);
|
||||
qio_channel_add_watch(
|
||||
wioc->master,
|
||||
@ -480,7 +582,9 @@ static gboolean qio_channel_websock_handshake_io(QIOChannel *ioc,
|
||||
}
|
||||
|
||||
|
||||
static void qio_channel_websock_encode(QIOChannelWebsock *ioc)
|
||||
static void qio_channel_websock_encode_buffer(QIOChannelWebsock *ioc,
|
||||
Buffer *output,
|
||||
uint8_t opcode, Buffer *buffer)
|
||||
{
|
||||
size_t header_size;
|
||||
union {
|
||||
@ -488,39 +592,66 @@ static void qio_channel_websock_encode(QIOChannelWebsock *ioc)
|
||||
QIOChannelWebsockHeader ws;
|
||||
} header;
|
||||
|
||||
if (!ioc->rawoutput.offset) {
|
||||
return;
|
||||
}
|
||||
|
||||
header.ws.b0 = (1 << QIO_CHANNEL_WEBSOCK_HEADER_SHIFT_FIN) |
|
||||
(QIO_CHANNEL_WEBSOCK_OPCODE_BINARY_FRAME &
|
||||
QIO_CHANNEL_WEBSOCK_HEADER_FIELD_OPCODE);
|
||||
if (ioc->rawoutput.offset <
|
||||
QIO_CHANNEL_WEBSOCK_PAYLOAD_LEN_THRESHOLD_7_BIT) {
|
||||
header.ws.b1 = (uint8_t)ioc->rawoutput.offset;
|
||||
header.ws.b0 = QIO_CHANNEL_WEBSOCK_HEADER_FIELD_FIN |
|
||||
(opcode & QIO_CHANNEL_WEBSOCK_HEADER_FIELD_OPCODE);
|
||||
if (buffer->offset < QIO_CHANNEL_WEBSOCK_PAYLOAD_LEN_THRESHOLD_7_BIT) {
|
||||
header.ws.b1 = (uint8_t)buffer->offset;
|
||||
header_size = QIO_CHANNEL_WEBSOCK_HEADER_LEN_7_BIT;
|
||||
} else if (ioc->rawoutput.offset <
|
||||
} else if (buffer->offset <
|
||||
QIO_CHANNEL_WEBSOCK_PAYLOAD_LEN_THRESHOLD_16_BIT) {
|
||||
header.ws.b1 = QIO_CHANNEL_WEBSOCK_PAYLOAD_LEN_MAGIC_16_BIT;
|
||||
header.ws.u.s16.l16 = cpu_to_be16((uint16_t)ioc->rawoutput.offset);
|
||||
header.ws.u.s16.l16 = cpu_to_be16((uint16_t)buffer->offset);
|
||||
header_size = QIO_CHANNEL_WEBSOCK_HEADER_LEN_16_BIT;
|
||||
} else {
|
||||
header.ws.b1 = QIO_CHANNEL_WEBSOCK_PAYLOAD_LEN_MAGIC_64_BIT;
|
||||
header.ws.u.s64.l64 = cpu_to_be64(ioc->rawoutput.offset);
|
||||
header.ws.u.s64.l64 = cpu_to_be64(buffer->offset);
|
||||
header_size = QIO_CHANNEL_WEBSOCK_HEADER_LEN_64_BIT;
|
||||
}
|
||||
header_size -= QIO_CHANNEL_WEBSOCK_HEADER_LEN_MASK;
|
||||
|
||||
buffer_reserve(&ioc->encoutput, header_size + ioc->rawoutput.offset);
|
||||
buffer_append(&ioc->encoutput, header.buf, header_size);
|
||||
buffer_append(&ioc->encoutput, ioc->rawoutput.buffer,
|
||||
ioc->rawoutput.offset);
|
||||
trace_qio_channel_websock_encode(ioc, opcode, header_size, buffer->offset);
|
||||
buffer_reserve(output, header_size + buffer->offset);
|
||||
buffer_append(output, header.buf, header_size);
|
||||
buffer_append(output, buffer->buffer, buffer->offset);
|
||||
}
|
||||
|
||||
|
||||
static void qio_channel_websock_encode(QIOChannelWebsock *ioc)
|
||||
{
|
||||
if (!ioc->rawoutput.offset) {
|
||||
return;
|
||||
}
|
||||
qio_channel_websock_encode_buffer(
|
||||
ioc, &ioc->encoutput, QIO_CHANNEL_WEBSOCK_OPCODE_BINARY_FRAME,
|
||||
&ioc->rawoutput);
|
||||
buffer_reset(&ioc->rawoutput);
|
||||
}
|
||||
|
||||
|
||||
static ssize_t qio_channel_websock_decode_header(QIOChannelWebsock *ioc,
|
||||
Error **errp)
|
||||
static ssize_t qio_channel_websock_write_wire(QIOChannelWebsock *, Error **);
|
||||
|
||||
|
||||
static void qio_channel_websock_write_close(QIOChannelWebsock *ioc,
|
||||
uint16_t code, const char *reason)
|
||||
{
|
||||
buffer_reserve(&ioc->rawoutput, 2 + (reason ? strlen(reason) : 0));
|
||||
*(uint16_t *)(ioc->rawoutput.buffer + ioc->rawoutput.offset) =
|
||||
cpu_to_be16(code);
|
||||
ioc->rawoutput.offset += 2;
|
||||
if (reason) {
|
||||
buffer_append(&ioc->rawoutput, reason, strlen(reason));
|
||||
}
|
||||
qio_channel_websock_encode_buffer(
|
||||
ioc, &ioc->encoutput, QIO_CHANNEL_WEBSOCK_OPCODE_CLOSE,
|
||||
&ioc->rawoutput);
|
||||
buffer_reset(&ioc->rawoutput);
|
||||
qio_channel_websock_write_wire(ioc, NULL);
|
||||
qio_channel_shutdown(ioc->master, QIO_CHANNEL_SHUTDOWN_BOTH, NULL);
|
||||
}
|
||||
|
||||
|
||||
static int qio_channel_websock_decode_header(QIOChannelWebsock *ioc,
|
||||
Error **errp)
|
||||
{
|
||||
unsigned char opcode, fin, has_mask;
|
||||
size_t header_size;
|
||||
@ -532,6 +663,9 @@ static ssize_t qio_channel_websock_decode_header(QIOChannelWebsock *ioc,
|
||||
error_setg(errp,
|
||||
"Decoding header but %zu bytes of payload remain",
|
||||
ioc->payload_remain);
|
||||
qio_channel_websock_write_close(
|
||||
ioc, QIO_CHANNEL_WEBSOCK_STATUS_SERVER_ERR,
|
||||
"internal server error");
|
||||
return -1;
|
||||
}
|
||||
if (ioc->encinput.offset < QIO_CHANNEL_WEBSOCK_HEADER_LEN_7_BIT) {
|
||||
@ -539,33 +673,57 @@ static ssize_t qio_channel_websock_decode_header(QIOChannelWebsock *ioc,
|
||||
return QIO_CHANNEL_ERR_BLOCK;
|
||||
}
|
||||
|
||||
fin = (header->b0 & QIO_CHANNEL_WEBSOCK_HEADER_FIELD_FIN) >>
|
||||
QIO_CHANNEL_WEBSOCK_HEADER_SHIFT_FIN;
|
||||
fin = header->b0 & QIO_CHANNEL_WEBSOCK_HEADER_FIELD_FIN;
|
||||
opcode = header->b0 & QIO_CHANNEL_WEBSOCK_HEADER_FIELD_OPCODE;
|
||||
has_mask = (header->b1 & QIO_CHANNEL_WEBSOCK_HEADER_FIELD_HAS_MASK) >>
|
||||
QIO_CHANNEL_WEBSOCK_HEADER_SHIFT_HAS_MASK;
|
||||
has_mask = header->b1 & QIO_CHANNEL_WEBSOCK_HEADER_FIELD_HAS_MASK;
|
||||
payload_len = header->b1 & QIO_CHANNEL_WEBSOCK_HEADER_FIELD_PAYLOAD_LEN;
|
||||
|
||||
/* Save or restore opcode. */
|
||||
if (opcode) {
|
||||
ioc->opcode = opcode;
|
||||
} else {
|
||||
opcode = ioc->opcode;
|
||||
}
|
||||
|
||||
trace_qio_channel_websock_header_partial_decode(ioc, payload_len,
|
||||
fin, opcode, (int)has_mask);
|
||||
|
||||
if (opcode == QIO_CHANNEL_WEBSOCK_OPCODE_CLOSE) {
|
||||
/* disconnect */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Websocket frame sanity check:
|
||||
* * Websocket fragmentation is not supported.
|
||||
* * All websockets frames sent by a client have to be masked.
|
||||
* * Only binary encoding is supported.
|
||||
* * Fragmentation is only supported for binary frames.
|
||||
* * All frames sent by a client MUST be masked.
|
||||
* * Only binary and ping/pong encoding is supported.
|
||||
*/
|
||||
if (!fin) {
|
||||
error_setg(errp, "websocket fragmentation is not supported");
|
||||
return -1;
|
||||
if (opcode != QIO_CHANNEL_WEBSOCK_OPCODE_BINARY_FRAME) {
|
||||
error_setg(errp, "only binary websocket frames may be fragmented");
|
||||
qio_channel_websock_write_close(
|
||||
ioc, QIO_CHANNEL_WEBSOCK_STATUS_POLICY ,
|
||||
"only binary frames may be fragmented");
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
if (opcode != QIO_CHANNEL_WEBSOCK_OPCODE_BINARY_FRAME &&
|
||||
opcode != QIO_CHANNEL_WEBSOCK_OPCODE_CLOSE &&
|
||||
opcode != QIO_CHANNEL_WEBSOCK_OPCODE_PING &&
|
||||
opcode != QIO_CHANNEL_WEBSOCK_OPCODE_PONG) {
|
||||
error_setg(errp, "unsupported opcode: %#04x; only binary, close, "
|
||||
"ping, and pong websocket frames are supported", opcode);
|
||||
qio_channel_websock_write_close(
|
||||
ioc, QIO_CHANNEL_WEBSOCK_STATUS_INVALID_DATA ,
|
||||
"only binary, close, ping, and pong frames are supported");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
if (!has_mask) {
|
||||
error_setg(errp, "websocket frames must be masked");
|
||||
return -1;
|
||||
}
|
||||
if (opcode != QIO_CHANNEL_WEBSOCK_OPCODE_BINARY_FRAME) {
|
||||
error_setg(errp, "only binary websocket frames are supported");
|
||||
error_setg(errp, "client websocket frames must be masked");
|
||||
qio_channel_websock_write_close(
|
||||
ioc, QIO_CHANNEL_WEBSOCK_STATUS_PROTOCOL_ERR,
|
||||
"client frames must be masked");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -573,6 +731,12 @@ static ssize_t qio_channel_websock_decode_header(QIOChannelWebsock *ioc,
|
||||
ioc->payload_remain = payload_len;
|
||||
header_size = QIO_CHANNEL_WEBSOCK_HEADER_LEN_7_BIT;
|
||||
ioc->mask = header->u.m;
|
||||
} else if (opcode & QIO_CHANNEL_WEBSOCK_CONTROL_OPCODE_MASK) {
|
||||
error_setg(errp, "websocket control frame is too large");
|
||||
qio_channel_websock_write_close(
|
||||
ioc, QIO_CHANNEL_WEBSOCK_STATUS_PROTOCOL_ERR,
|
||||
"control frame is too large");
|
||||
return -1;
|
||||
} else if (payload_len == QIO_CHANNEL_WEBSOCK_PAYLOAD_LEN_MAGIC_16_BIT &&
|
||||
ioc->encinput.offset >= QIO_CHANNEL_WEBSOCK_HEADER_LEN_16_BIT) {
|
||||
ioc->payload_remain = be16_to_cpu(header->u.s16.l16);
|
||||
@ -588,54 +752,90 @@ static ssize_t qio_channel_websock_decode_header(QIOChannelWebsock *ioc,
|
||||
return QIO_CHANNEL_ERR_BLOCK;
|
||||
}
|
||||
|
||||
trace_qio_channel_websock_header_full_decode(
|
||||
ioc, header_size, ioc->payload_remain, ioc->mask.u);
|
||||
buffer_advance(&ioc->encinput, header_size);
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static ssize_t qio_channel_websock_decode_payload(QIOChannelWebsock *ioc,
|
||||
Error **errp)
|
||||
static int qio_channel_websock_decode_payload(QIOChannelWebsock *ioc,
|
||||
Error **errp)
|
||||
{
|
||||
size_t i;
|
||||
size_t payload_len;
|
||||
size_t payload_len = 0;
|
||||
uint32_t *payload32;
|
||||
|
||||
if (!ioc->payload_remain) {
|
||||
error_setg(errp,
|
||||
"Decoding payload but no bytes of payload remain");
|
||||
if (ioc->payload_remain) {
|
||||
/* If we aren't at the end of the payload, then drop
|
||||
* off the last bytes, so we're always multiple of 4
|
||||
* for purpose of unmasking, except at end of payload
|
||||
*/
|
||||
if (ioc->encinput.offset < ioc->payload_remain) {
|
||||
/* Wait for the entire payload before processing control frames
|
||||
* because the payload will most likely be echoed back. */
|
||||
if (ioc->opcode & QIO_CHANNEL_WEBSOCK_CONTROL_OPCODE_MASK) {
|
||||
return QIO_CHANNEL_ERR_BLOCK;
|
||||
}
|
||||
payload_len = ioc->encinput.offset - (ioc->encinput.offset % 4);
|
||||
} else {
|
||||
payload_len = ioc->payload_remain;
|
||||
}
|
||||
if (payload_len == 0) {
|
||||
return QIO_CHANNEL_ERR_BLOCK;
|
||||
}
|
||||
|
||||
ioc->payload_remain -= payload_len;
|
||||
|
||||
/* unmask frame */
|
||||
/* process 1 frame (32 bit op) */
|
||||
payload32 = (uint32_t *)ioc->encinput.buffer;
|
||||
for (i = 0; i < payload_len / 4; i++) {
|
||||
payload32[i] ^= ioc->mask.u;
|
||||
}
|
||||
/* process the remaining bytes (if any) */
|
||||
for (i *= 4; i < payload_len; i++) {
|
||||
ioc->encinput.buffer[i] ^= ioc->mask.c[i % 4];
|
||||
}
|
||||
}
|
||||
|
||||
trace_qio_channel_websock_payload_decode(
|
||||
ioc, ioc->opcode, ioc->payload_remain);
|
||||
|
||||
if (ioc->opcode == QIO_CHANNEL_WEBSOCK_OPCODE_BINARY_FRAME) {
|
||||
if (payload_len) {
|
||||
/* binary frames are passed on */
|
||||
buffer_reserve(&ioc->rawinput, payload_len);
|
||||
buffer_append(&ioc->rawinput, ioc->encinput.buffer, payload_len);
|
||||
}
|
||||
} else if (ioc->opcode == QIO_CHANNEL_WEBSOCK_OPCODE_CLOSE) {
|
||||
/* close frames are echoed back */
|
||||
error_setg(errp, "websocket closed by peer");
|
||||
if (payload_len) {
|
||||
/* echo client status */
|
||||
qio_channel_websock_encode_buffer(
|
||||
ioc, &ioc->encoutput, QIO_CHANNEL_WEBSOCK_OPCODE_CLOSE,
|
||||
&ioc->encinput);
|
||||
qio_channel_websock_write_wire(ioc, NULL);
|
||||
qio_channel_shutdown(ioc->master, QIO_CHANNEL_SHUTDOWN_BOTH, NULL);
|
||||
} else {
|
||||
/* send our own status */
|
||||
qio_channel_websock_write_close(
|
||||
ioc, QIO_CHANNEL_WEBSOCK_STATUS_NORMAL, "peer requested close");
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
} else if (ioc->opcode == QIO_CHANNEL_WEBSOCK_OPCODE_PING) {
|
||||
/* ping frames produce an immediate reply */
|
||||
buffer_reset(&ioc->ping_reply);
|
||||
qio_channel_websock_encode_buffer(
|
||||
ioc, &ioc->ping_reply, QIO_CHANNEL_WEBSOCK_OPCODE_PONG,
|
||||
&ioc->encinput);
|
||||
} /* pong frames are ignored */
|
||||
|
||||
/* If we aren't at the end of the payload, then drop
|
||||
* off the last bytes, so we're always multiple of 4
|
||||
* for purpose of unmasking, except at end of payload
|
||||
*/
|
||||
if (ioc->encinput.offset < ioc->payload_remain) {
|
||||
payload_len = ioc->encinput.offset - (ioc->encinput.offset % 4);
|
||||
} else {
|
||||
payload_len = ioc->payload_remain;
|
||||
if (payload_len) {
|
||||
buffer_advance(&ioc->encinput, payload_len);
|
||||
}
|
||||
if (payload_len == 0) {
|
||||
return QIO_CHANNEL_ERR_BLOCK;
|
||||
}
|
||||
|
||||
ioc->payload_remain -= payload_len;
|
||||
|
||||
/* unmask frame */
|
||||
/* process 1 frame (32 bit op) */
|
||||
payload32 = (uint32_t *)ioc->encinput.buffer;
|
||||
for (i = 0; i < payload_len / 4; i++) {
|
||||
payload32[i] ^= ioc->mask.u;
|
||||
}
|
||||
/* process the remaining bytes (if any) */
|
||||
for (i *= 4; i < payload_len; i++) {
|
||||
ioc->encinput.buffer[i] ^= ioc->mask.c[i % 4];
|
||||
}
|
||||
|
||||
buffer_reserve(&ioc->rawinput, payload_len);
|
||||
buffer_append(&ioc->rawinput, ioc->encinput.buffer, payload_len);
|
||||
buffer_advance(&ioc->encinput, payload_len);
|
||||
return payload_len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@ -688,6 +888,7 @@ static void qio_channel_websock_finalize(Object *obj)
|
||||
buffer_free(&ioc->encoutput);
|
||||
buffer_free(&ioc->rawinput);
|
||||
buffer_free(&ioc->rawoutput);
|
||||
buffer_free(&ioc->ping_reply);
|
||||
object_unref(OBJECT(ioc->master));
|
||||
if (ioc->io_tag) {
|
||||
g_source_remove(ioc->io_tag);
|
||||
@ -715,8 +916,8 @@ static ssize_t qio_channel_websock_read_wire(QIOChannelWebsock *ioc,
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
if (ret == 0 &&
|
||||
ioc->encinput.offset == 0) {
|
||||
if (ret == 0 && ioc->encinput.offset == 0) {
|
||||
ioc->io_eof = TRUE;
|
||||
return 0;
|
||||
}
|
||||
ioc->encinput.offset += ret;
|
||||
@ -728,10 +929,6 @@ static ssize_t qio_channel_websock_read_wire(QIOChannelWebsock *ioc,
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
if (ret == 0) {
|
||||
ioc->io_eof = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ret = qio_channel_websock_decode_payload(ioc, errp);
|
||||
@ -748,7 +945,13 @@ static ssize_t qio_channel_websock_write_wire(QIOChannelWebsock *ioc,
|
||||
{
|
||||
ssize_t ret;
|
||||
ssize_t done = 0;
|
||||
qio_channel_websock_encode(ioc);
|
||||
|
||||
/* ping replies take priority over binary data */
|
||||
if (!ioc->ping_reply.offset) {
|
||||
qio_channel_websock_encode(ioc);
|
||||
} else if (!ioc->encoutput.offset) {
|
||||
buffer_move_empty(&ioc->encoutput, &ioc->ping_reply);
|
||||
}
|
||||
|
||||
while (ioc->encoutput.offset > 0) {
|
||||
ret = qio_channel_write(ioc->master,
|
||||
@ -823,7 +1026,7 @@ static void qio_channel_websock_set_watch(QIOChannelWebsock *ioc)
|
||||
return;
|
||||
}
|
||||
|
||||
if (ioc->encoutput.offset) {
|
||||
if (ioc->encoutput.offset || ioc->ping_reply.offset) {
|
||||
cond |= G_IO_OUT;
|
||||
}
|
||||
if (ioc->encinput.offset < QIO_CHANNEL_WEBSOCK_MAX_BUFFER &&
|
||||
@ -985,6 +1188,7 @@ static int qio_channel_websock_close(QIOChannel *ioc,
|
||||
{
|
||||
QIOChannelWebsock *wioc = QIO_CHANNEL_WEBSOCK(ioc);
|
||||
|
||||
trace_qio_channel_websock_close(ioc);
|
||||
return qio_channel_close(wioc->master, errp);
|
||||
}
|
||||
|
||||
@ -996,14 +1200,12 @@ struct QIOChannelWebsockSource {
|
||||
};
|
||||
|
||||
static gboolean
|
||||
qio_channel_websock_source_prepare(GSource *source,
|
||||
gint *timeout)
|
||||
qio_channel_websock_source_check(GSource *source)
|
||||
{
|
||||
QIOChannelWebsockSource *wsource = (QIOChannelWebsockSource *)source;
|
||||
GIOCondition cond = 0;
|
||||
*timeout = -1;
|
||||
|
||||
if (wsource->wioc->rawinput.offset) {
|
||||
if (wsource->wioc->rawinput.offset || wsource->wioc->io_eof) {
|
||||
cond |= G_IO_IN;
|
||||
}
|
||||
if (wsource->wioc->rawoutput.offset < QIO_CHANNEL_WEBSOCK_MAX_BUFFER) {
|
||||
@ -1014,19 +1216,11 @@ qio_channel_websock_source_prepare(GSource *source,
|
||||
}
|
||||
|
||||
static gboolean
|
||||
qio_channel_websock_source_check(GSource *source)
|
||||
qio_channel_websock_source_prepare(GSource *source,
|
||||
gint *timeout)
|
||||
{
|
||||
QIOChannelWebsockSource *wsource = (QIOChannelWebsockSource *)source;
|
||||
GIOCondition cond = 0;
|
||||
|
||||
if (wsource->wioc->rawinput.offset) {
|
||||
cond |= G_IO_IN;
|
||||
}
|
||||
if (wsource->wioc->rawoutput.offset < QIO_CHANNEL_WEBSOCK_MAX_BUFFER) {
|
||||
cond |= G_IO_OUT;
|
||||
}
|
||||
|
||||
return cond & wsource->condition;
|
||||
*timeout = -1;
|
||||
return qio_channel_websock_source_check(source);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
@ -1036,17 +1230,9 @@ qio_channel_websock_source_dispatch(GSource *source,
|
||||
{
|
||||
QIOChannelFunc func = (QIOChannelFunc)callback;
|
||||
QIOChannelWebsockSource *wsource = (QIOChannelWebsockSource *)source;
|
||||
GIOCondition cond = 0;
|
||||
|
||||
if (wsource->wioc->rawinput.offset) {
|
||||
cond |= G_IO_IN;
|
||||
}
|
||||
if (wsource->wioc->rawoutput.offset < QIO_CHANNEL_WEBSOCK_MAX_BUFFER) {
|
||||
cond |= G_IO_OUT;
|
||||
}
|
||||
|
||||
return (*func)(QIO_CHANNEL(wsource->wioc),
|
||||
(cond & wsource->condition),
|
||||
qio_channel_websock_source_check(source),
|
||||
user_data);
|
||||
}
|
||||
|
||||
|
@ -46,8 +46,13 @@ qio_channel_websock_new_server(void *ioc, void *master) "Websock new client ioc=
|
||||
qio_channel_websock_handshake_start(void *ioc) "Websock handshake start ioc=%p"
|
||||
qio_channel_websock_handshake_pending(void *ioc, int status) "Websock handshake pending ioc=%p status=%d"
|
||||
qio_channel_websock_handshake_reply(void *ioc) "Websock handshake reply ioc=%p"
|
||||
qio_channel_websock_handshake_fail(void *ioc) "Websock handshake fail ioc=%p"
|
||||
qio_channel_websock_handshake_fail(void *ioc, const char *msg) "Websock handshake fail ioc=%p err=%s"
|
||||
qio_channel_websock_handshake_complete(void *ioc) "Websock handshake complete ioc=%p"
|
||||
qio_channel_websock_header_partial_decode(void *ioc, size_t payloadlen, unsigned char fin, unsigned char opcode, unsigned char has_mask) "Websocket header decoded ioc=%p payload-len=%zu fin=0x%x opcode=0x%x has_mask=0x%x"
|
||||
qio_channel_websock_header_full_decode(void *ioc, size_t headerlen, size_t payloadlen, uint32_t mask) "Websocket header decoded ioc=%p header-len=%zu payload-len=%zu mask=0x%x"
|
||||
qio_channel_websock_payload_decode(void *ioc, uint8_t opcode, size_t payload_remain) "Websocket header decoded ioc=%p opcode=0x%x payload-remain=%zu"
|
||||
qio_channel_websock_encode(void *ioc, uint8_t opcode, size_t payloadlen, size_t headerlen) "Websocket encoded ioc=%p opcode=0x%x header-len=%zu payload-len=%zu"
|
||||
qio_channel_websock_close(void *ioc) "Websocket close ioc=%p"
|
||||
|
||||
# io/channel-command.c
|
||||
qio_channel_command_new_pid(void *ioc, int writefd, int readfd, int pid) "Command new pid ioc=%p writefd=%d readfd=%d pid=%d"
|
||||
|
@ -75,6 +75,9 @@ static void vnc_tls_handshake_done(QIOTask *task,
|
||||
vnc_client_error(vs);
|
||||
error_free(err);
|
||||
} else {
|
||||
if (vs->ioc_tag) {
|
||||
g_source_remove(vs->ioc_tag);
|
||||
}
|
||||
vs->ioc_tag = qio_channel_add_watch(
|
||||
vs->ioc, G_IO_IN | G_IO_OUT, vnc_client_io, vs, NULL);
|
||||
start_auth_vencrypt_subauth(vs);
|
||||
|
@ -37,6 +37,9 @@ static void vncws_tls_handshake_done(QIOTask *task,
|
||||
error_free(err);
|
||||
} else {
|
||||
VNC_DEBUG("TLS handshake complete, starting websocket handshake\n");
|
||||
if (vs->ioc_tag) {
|
||||
g_source_remove(vs->ioc_tag);
|
||||
}
|
||||
vs->ioc_tag = qio_channel_add_watch(
|
||||
QIO_CHANNEL(vs->ioc), G_IO_IN, vncws_handshake_io, vs, NULL);
|
||||
}
|
||||
@ -97,6 +100,9 @@ static void vncws_handshake_done(QIOTask *task,
|
||||
} else {
|
||||
VNC_DEBUG("Websock handshake complete, starting VNC protocol\n");
|
||||
vnc_start_protocol(vs);
|
||||
if (vs->ioc_tag) {
|
||||
g_source_remove(vs->ioc_tag);
|
||||
}
|
||||
vs->ioc_tag = qio_channel_add_watch(
|
||||
vs->ioc, G_IO_IN, vnc_client_io, vs, NULL);
|
||||
}
|
||||
|
4
ui/vnc.c
4
ui/vnc.c
@ -1122,6 +1122,7 @@ static void vnc_disconnect_start(VncState *vs)
|
||||
vnc_set_share_mode(vs, VNC_SHARE_MODE_DISCONNECTED);
|
||||
if (vs->ioc_tag) {
|
||||
g_source_remove(vs->ioc_tag);
|
||||
vs->ioc_tag = 0;
|
||||
}
|
||||
qio_channel_close(vs->ioc, NULL);
|
||||
vs->disconnecting = TRUE;
|
||||
@ -2934,6 +2935,9 @@ static void vnc_connect(VncDisplay *vd, QIOChannelSocket *sioc,
|
||||
VNC_DEBUG("New client on socket %p\n", vs->sioc);
|
||||
update_displaychangelistener(&vd->dcl, VNC_REFRESH_INTERVAL_BASE);
|
||||
qio_channel_set_blocking(vs->ioc, false, NULL);
|
||||
if (vs->ioc_tag) {
|
||||
g_source_remove(vs->ioc_tag);
|
||||
}
|
||||
if (websocket) {
|
||||
vs->websocket = 1;
|
||||
if (vd->tlscreds) {
|
||||
|
Loading…
Reference in New Issue
Block a user