mirror of https://github.com/xqemu/xqemu.git
Merge qio 2017/02/27 v2
-----BEGIN PGP SIGNATURE----- iQIcBAABCAAGBQJYuEO3AAoJEL6G67QVEE/fLqwP/1i42tJZf/HUL6oHfA+5iOZk JVGzxUsdafHdg+XU5BXLHFiWtx/QCct0rOIbGz0tLQWRqf2cMNCk8qeECD94yB2B pqOZWZXFBjHXCqQ/giEff1gtri52fz8+jBBKR60yEdDRjMvlGpupq0tWQhuzwd4b Jf2Q++/ZdT+2WTyd1Uzt3DRIAyQcr7kHQ3ZD6bj7gaOWVMopxa6628wXKF0ecSpE ZEtt2fxuhM0TO8LdNxeBUL31ZYaXkKv6xAg0ynS38GVObRg22WPpuWsLrP8hVdYk ZQctNxSeGte+yl9uWTRs0NzEEXq1PnBC+k6ylcc9B8TFgQvaeMHJjAtskuA3jtGT 8KhDM2W6jaHUPIpA2H5LzgxBJSim9gsSYszWsXCSTgqOK2t5PTShx9xC9xvOZg9z EG66LJ84AyBF/J5clq0M7qRu54iW+HRtavRiKB0kX8Swcbkf0cuyI8AEon/ZYbg3 5mY7ttO95hgS1wpCrshrymyC+AqyJReV9xCZ8yc3VtZ9g6RWn8+1gt5GczZEJcTi BcI0PKlEeVLfQd5CVhVcNtEqORnTX/E2cNI+FMBVhSM2ZVWvPpHssmYlvlrPFErG Uf15cL1DmSRUlIMhYGYgrJh08rJjp7COM8Q7TRnImfXZ0pH7S1kIlVzGmC+4HHNH Ob/QGEwB1yvBiENbqi0L =bTpM -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/berrange/tags/pull-qio-2017-02-27-2' into staging Merge qio 2017/02/27 v2 # gpg: Signature made Thu 02 Mar 2017 16:09:27 GMT # 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-02-27-2: io: fully parse & validate HTTP headers for websocket protocol handshake tests: fix leaks in test-io-channel-command io: fix decoding when multiple websockets frames arrive at once Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
1ec2dca691
|
@ -33,11 +33,16 @@
|
|||
#define QIO_CHANNEL_WEBSOCK_GUID "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
|
||||
#define QIO_CHANNEL_WEBSOCK_GUID_LEN strlen(QIO_CHANNEL_WEBSOCK_GUID)
|
||||
|
||||
#define QIO_CHANNEL_WEBSOCK_HEADER_PROTOCOL "Sec-WebSocket-Protocol"
|
||||
#define QIO_CHANNEL_WEBSOCK_HEADER_VERSION "Sec-WebSocket-Version"
|
||||
#define QIO_CHANNEL_WEBSOCK_HEADER_KEY "Sec-WebSocket-Key"
|
||||
#define QIO_CHANNEL_WEBSOCK_HEADER_PROTOCOL "sec-websocket-protocol"
|
||||
#define QIO_CHANNEL_WEBSOCK_HEADER_VERSION "sec-websocket-version"
|
||||
#define QIO_CHANNEL_WEBSOCK_HEADER_KEY "sec-websocket-key"
|
||||
#define QIO_CHANNEL_WEBSOCK_HEADER_UPGRADE "upgrade"
|
||||
#define QIO_CHANNEL_WEBSOCK_HEADER_HOST "host"
|
||||
#define QIO_CHANNEL_WEBSOCK_HEADER_CONNECTION "connection"
|
||||
|
||||
#define QIO_CHANNEL_WEBSOCK_PROTOCOL_BINARY "binary"
|
||||
#define QIO_CHANNEL_WEBSOCK_CONNECTION_UPGRADE "Upgrade"
|
||||
#define QIO_CHANNEL_WEBSOCK_UPGRADE_WEBSOCKET "websocket"
|
||||
|
||||
#define QIO_CHANNEL_WEBSOCK_HANDSHAKE_RESPONSE \
|
||||
"HTTP/1.1 101 Switching Protocols\r\n" \
|
||||
|
@ -49,6 +54,9 @@
|
|||
#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"
|
||||
#define QIO_CHANNEL_WEBSOCK_HTTP_METHOD "GET"
|
||||
#define QIO_CHANNEL_WEBSOCK_HTTP_PATH "/"
|
||||
#define QIO_CHANNEL_WEBSOCK_HTTP_VERSION "HTTP/1.1"
|
||||
|
||||
/* The websockets packet header is variable length
|
||||
* depending on the size of the payload... */
|
||||
|
@ -99,6 +107,13 @@ struct QEMU_PACKED QIOChannelWebsockHeader {
|
|||
} u;
|
||||
};
|
||||
|
||||
typedef struct QIOChannelWebsockHTTPHeader QIOChannelWebsockHTTPHeader;
|
||||
|
||||
struct QIOChannelWebsockHTTPHeader {
|
||||
char *name;
|
||||
char *value;
|
||||
};
|
||||
|
||||
enum {
|
||||
QIO_CHANNEL_WEBSOCK_OPCODE_CONTINUATION = 0x0,
|
||||
QIO_CHANNEL_WEBSOCK_OPCODE_TEXT_FRAME = 0x1,
|
||||
|
@ -108,25 +123,130 @@ enum {
|
|||
QIO_CHANNEL_WEBSOCK_OPCODE_PONG = 0xA
|
||||
};
|
||||
|
||||
static char *qio_channel_websock_handshake_entry(const char *handshake,
|
||||
size_t handshake_len,
|
||||
const char *name)
|
||||
static size_t
|
||||
qio_channel_websock_extract_headers(char *buffer,
|
||||
QIOChannelWebsockHTTPHeader *hdrs,
|
||||
size_t nhdrsalloc,
|
||||
Error **errp)
|
||||
{
|
||||
char *begin, *end, *ret = NULL;
|
||||
char *line = g_strdup_printf("%s%s: ",
|
||||
QIO_CHANNEL_WEBSOCK_HANDSHAKE_DELIM,
|
||||
name);
|
||||
begin = g_strstr_len(handshake, handshake_len, line);
|
||||
if (begin != NULL) {
|
||||
begin += strlen(line);
|
||||
end = g_strstr_len(begin, handshake_len - (begin - handshake),
|
||||
QIO_CHANNEL_WEBSOCK_HANDSHAKE_DELIM);
|
||||
if (end != NULL) {
|
||||
ret = g_strndup(begin, end - begin);
|
||||
char *nl, *sep, *tmp;
|
||||
size_t nhdrs = 0;
|
||||
|
||||
/*
|
||||
* First parse the HTTP protocol greeting of format:
|
||||
*
|
||||
* $METHOD $PATH $VERSION
|
||||
*
|
||||
* e.g.
|
||||
*
|
||||
* GET / HTTP/1.1
|
||||
*/
|
||||
|
||||
nl = strstr(buffer, QIO_CHANNEL_WEBSOCK_HANDSHAKE_DELIM);
|
||||
if (!nl) {
|
||||
error_setg(errp, "Missing HTTP header delimiter");
|
||||
return 0;
|
||||
}
|
||||
*nl = '\0';
|
||||
|
||||
tmp = strchr(buffer, ' ');
|
||||
if (!tmp) {
|
||||
error_setg(errp, "Missing HTTP path delimiter");
|
||||
return 0;
|
||||
}
|
||||
*tmp = '\0';
|
||||
|
||||
if (!g_str_equal(buffer, QIO_CHANNEL_WEBSOCK_HTTP_METHOD)) {
|
||||
error_setg(errp, "Unsupported HTTP method %s", buffer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
buffer = tmp + 1;
|
||||
tmp = strchr(buffer, ' ');
|
||||
if (!tmp) {
|
||||
error_setg(errp, "Missing HTTP version delimiter");
|
||||
return 0;
|
||||
}
|
||||
*tmp = '\0';
|
||||
|
||||
if (!g_str_equal(buffer, QIO_CHANNEL_WEBSOCK_HTTP_PATH)) {
|
||||
error_setg(errp, "Unexpected HTTP path %s", buffer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
buffer = tmp + 1;
|
||||
|
||||
if (!g_str_equal(buffer, QIO_CHANNEL_WEBSOCK_HTTP_VERSION)) {
|
||||
error_setg(errp, "Unsupported HTTP version %s", buffer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
buffer = nl + strlen(QIO_CHANNEL_WEBSOCK_HANDSHAKE_DELIM);
|
||||
|
||||
/*
|
||||
* Now parse all the header fields of format
|
||||
*
|
||||
* $NAME: $VALUE
|
||||
*
|
||||
* e.g.
|
||||
*
|
||||
* Cache-control: no-cache
|
||||
*/
|
||||
do {
|
||||
QIOChannelWebsockHTTPHeader *hdr;
|
||||
|
||||
nl = strstr(buffer, QIO_CHANNEL_WEBSOCK_HANDSHAKE_DELIM);
|
||||
if (nl) {
|
||||
*nl = '\0';
|
||||
}
|
||||
|
||||
sep = strchr(buffer, ':');
|
||||
if (!sep) {
|
||||
error_setg(errp, "Malformed HTTP header");
|
||||
return 0;
|
||||
}
|
||||
*sep = '\0';
|
||||
sep++;
|
||||
while (*sep == ' ') {
|
||||
sep++;
|
||||
}
|
||||
|
||||
if (nhdrs >= nhdrsalloc) {
|
||||
error_setg(errp, "Too many HTTP headers");
|
||||
return 0;
|
||||
}
|
||||
|
||||
hdr = &hdrs[nhdrs++];
|
||||
hdr->name = buffer;
|
||||
hdr->value = sep;
|
||||
|
||||
/* Canonicalize header name for easier identification later */
|
||||
for (tmp = hdr->name; *tmp; tmp++) {
|
||||
*tmp = g_ascii_tolower(*tmp);
|
||||
}
|
||||
|
||||
if (nl) {
|
||||
buffer = nl + strlen(QIO_CHANNEL_WEBSOCK_HANDSHAKE_DELIM);
|
||||
}
|
||||
} while (nl != NULL);
|
||||
|
||||
return nhdrs;
|
||||
}
|
||||
|
||||
static const char *
|
||||
qio_channel_websock_find_header(QIOChannelWebsockHTTPHeader *hdrs,
|
||||
size_t nhdrs,
|
||||
const char *name)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < nhdrs; i++) {
|
||||
if (g_str_equal(hdrs[i].name, name)) {
|
||||
return hdrs[i].value;
|
||||
}
|
||||
}
|
||||
g_free(line);
|
||||
return ret;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
@ -166,58 +286,90 @@ static int qio_channel_websock_handshake_send_response(QIOChannelWebsock *ioc,
|
|||
}
|
||||
|
||||
static int qio_channel_websock_handshake_process(QIOChannelWebsock *ioc,
|
||||
const char *line,
|
||||
size_t size,
|
||||
char *buffer,
|
||||
Error **errp)
|
||||
{
|
||||
int ret = -1;
|
||||
char *protocols = qio_channel_websock_handshake_entry(
|
||||
line, size, QIO_CHANNEL_WEBSOCK_HEADER_PROTOCOL);
|
||||
char *version = qio_channel_websock_handshake_entry(
|
||||
line, size, QIO_CHANNEL_WEBSOCK_HEADER_VERSION);
|
||||
char *key = qio_channel_websock_handshake_entry(
|
||||
line, size, QIO_CHANNEL_WEBSOCK_HEADER_KEY);
|
||||
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);
|
||||
if (!nhdrs) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
protocols = qio_channel_websock_find_header(
|
||||
hdrs, nhdrs, QIO_CHANNEL_WEBSOCK_HEADER_PROTOCOL);
|
||||
if (!protocols) {
|
||||
error_setg(errp, "Missing websocket protocol header data");
|
||||
goto cleanup;
|
||||
return -1;
|
||||
}
|
||||
|
||||
version = qio_channel_websock_find_header(
|
||||
hdrs, nhdrs, QIO_CHANNEL_WEBSOCK_HEADER_VERSION);
|
||||
if (!version) {
|
||||
error_setg(errp, "Missing websocket version header data");
|
||||
goto cleanup;
|
||||
return -1;
|
||||
}
|
||||
|
||||
key = qio_channel_websock_find_header(
|
||||
hdrs, nhdrs, QIO_CHANNEL_WEBSOCK_HEADER_KEY);
|
||||
if (!key) {
|
||||
error_setg(errp, "Missing websocket key header data");
|
||||
goto cleanup;
|
||||
return -1;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
goto cleanup;
|
||||
return -1;
|
||||
}
|
||||
|
||||
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);
|
||||
goto cleanup;
|
||||
return -1;
|
||||
}
|
||||
|
||||
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);
|
||||
goto cleanup;
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = qio_channel_websock_handshake_send_response(ioc, key, errp);
|
||||
if (!g_strrstr(connection, QIO_CHANNEL_WEBSOCK_CONNECTION_UPGRADE)) {
|
||||
error_setg(errp, "No connection upgrade requested '%s'", connection);
|
||||
return -1;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
g_free(protocols);
|
||||
g_free(version);
|
||||
g_free(key);
|
||||
return ret;
|
||||
if (!g_str_equal(upgrade, QIO_CHANNEL_WEBSOCK_UPGRADE_WEBSOCKET)) {
|
||||
error_setg(errp, "Incorrect upgrade method '%s'", upgrade);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return qio_channel_websock_handshake_send_response(ioc, key, errp);
|
||||
}
|
||||
|
||||
static int qio_channel_websock_handshake_read(QIOChannelWebsock *ioc,
|
||||
|
@ -248,10 +400,10 @@ static int qio_channel_websock_handshake_read(QIOChannelWebsock *ioc,
|
|||
return 0;
|
||||
}
|
||||
}
|
||||
*handshake_end = '\0';
|
||||
|
||||
if (qio_channel_websock_handshake_process(ioc,
|
||||
(char *)ioc->encinput.buffer,
|
||||
ioc->encinput.offset,
|
||||
errp) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
@ -570,21 +722,24 @@ static ssize_t qio_channel_websock_read_wire(QIOChannelWebsock *ioc,
|
|||
ioc->encinput.offset += ret;
|
||||
}
|
||||
|
||||
if (ioc->payload_remain == 0) {
|
||||
ret = qio_channel_websock_decode_header(ioc, errp);
|
||||
while (ioc->encinput.offset != 0) {
|
||||
if (ioc->payload_remain == 0) {
|
||||
ret = qio_channel_websock_decode_header(ioc, errp);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
if (ret == 0) {
|
||||
ioc->io_eof = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ret = qio_channel_websock_decode_payload(ioc, errp);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
if (ret == 0) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
ret = qio_channel_websock_decode_payload(ioc, errp);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
return ret;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
|
@ -642,9 +797,6 @@ static gboolean qio_channel_websock_flush(QIOChannel *ioc,
|
|||
if (ret < 0) {
|
||||
goto cleanup;
|
||||
}
|
||||
if (ret == 0) {
|
||||
wioc->io_eof = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
cleanup:
|
||||
|
|
Loading…
Reference in New Issue