qemu

Форк
0
/
channel-websock.c 
1338 строк · 42.4 Кб
1
/*
2
 * QEMU I/O channels driver websockets
3
 *
4
 * Copyright (c) 2015 Red Hat, Inc.
5
 *
6
 * This library is free software; you can redistribute it and/or
7
 * modify it under the terms of the GNU Lesser General Public
8
 * License as published by the Free Software Foundation; either
9
 * version 2.1 of the License, or (at your option) any later version.
10
 *
11
 * This library is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
 * Lesser General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU Lesser General Public
17
 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
18
 *
19
 */
20

21
#include "qemu/osdep.h"
22
#include "qapi/error.h"
23
#include "qemu/bswap.h"
24
#include "io/channel-websock.h"
25
#include "crypto/hash.h"
26
#include "trace.h"
27
#include "qemu/iov.h"
28
#include "qemu/module.h"
29

30
/* Max amount to allow in rawinput/encoutput buffers */
31
#define QIO_CHANNEL_WEBSOCK_MAX_BUFFER 8192
32

33
#define QIO_CHANNEL_WEBSOCK_CLIENT_KEY_LEN 24
34
#define QIO_CHANNEL_WEBSOCK_GUID "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
35
#define QIO_CHANNEL_WEBSOCK_GUID_LEN (sizeof(QIO_CHANNEL_WEBSOCK_GUID) - 1)
36

37
#define QIO_CHANNEL_WEBSOCK_HEADER_PROTOCOL "sec-websocket-protocol"
38
#define QIO_CHANNEL_WEBSOCK_HEADER_VERSION "sec-websocket-version"
39
#define QIO_CHANNEL_WEBSOCK_HEADER_KEY "sec-websocket-key"
40
#define QIO_CHANNEL_WEBSOCK_HEADER_UPGRADE "upgrade"
41
#define QIO_CHANNEL_WEBSOCK_HEADER_HOST "host"
42
#define QIO_CHANNEL_WEBSOCK_HEADER_CONNECTION "connection"
43

44
#define QIO_CHANNEL_WEBSOCK_PROTOCOL_BINARY "binary"
45
#define QIO_CHANNEL_WEBSOCK_CONNECTION_UPGRADE "Upgrade"
46
#define QIO_CHANNEL_WEBSOCK_UPGRADE_WEBSOCKET "websocket"
47

48
#define QIO_CHANNEL_WEBSOCK_HANDSHAKE_RES_COMMON \
49
    "Server: QEMU VNC\r\n"                       \
50
    "Date: %s\r\n"
51

52
#define QIO_CHANNEL_WEBSOCK_HANDSHAKE_WITH_PROTO_RES_OK \
53
    "HTTP/1.1 101 Switching Protocols\r\n"              \
54
    QIO_CHANNEL_WEBSOCK_HANDSHAKE_RES_COMMON            \
55
    "Upgrade: websocket\r\n"                            \
56
    "Connection: Upgrade\r\n"                           \
57
    "Sec-WebSocket-Accept: %s\r\n"                      \
58
    "Sec-WebSocket-Protocol: binary\r\n"                \
59
    "\r\n"
60
#define QIO_CHANNEL_WEBSOCK_HANDSHAKE_RES_OK    \
61
    "HTTP/1.1 101 Switching Protocols\r\n"      \
62
    QIO_CHANNEL_WEBSOCK_HANDSHAKE_RES_COMMON    \
63
    "Upgrade: websocket\r\n"                    \
64
    "Connection: Upgrade\r\n"                   \
65
    "Sec-WebSocket-Accept: %s\r\n"              \
66
    "\r\n"
67
#define QIO_CHANNEL_WEBSOCK_HANDSHAKE_RES_NOT_FOUND \
68
    "HTTP/1.1 404 Not Found\r\n"                    \
69
    QIO_CHANNEL_WEBSOCK_HANDSHAKE_RES_COMMON        \
70
    "Connection: close\r\n"                         \
71
    "\r\n"
72
#define QIO_CHANNEL_WEBSOCK_HANDSHAKE_RES_BAD_REQUEST \
73
    "HTTP/1.1 400 Bad Request\r\n"                    \
74
    QIO_CHANNEL_WEBSOCK_HANDSHAKE_RES_COMMON          \
75
    "Connection: close\r\n"                           \
76
    "Sec-WebSocket-Version: "                         \
77
    QIO_CHANNEL_WEBSOCK_SUPPORTED_VERSION             \
78
    "\r\n"
79
#define QIO_CHANNEL_WEBSOCK_HANDSHAKE_RES_SERVER_ERR \
80
    "HTTP/1.1 500 Internal Server Error\r\n"         \
81
    QIO_CHANNEL_WEBSOCK_HANDSHAKE_RES_COMMON         \
82
    "Connection: close\r\n"                          \
83
    "\r\n"
84
#define QIO_CHANNEL_WEBSOCK_HANDSHAKE_RES_TOO_LARGE  \
85
    "HTTP/1.1 403 Request Entity Too Large\r\n"      \
86
    QIO_CHANNEL_WEBSOCK_HANDSHAKE_RES_COMMON         \
87
    "Connection: close\r\n"                          \
88
    "\r\n"
89
#define QIO_CHANNEL_WEBSOCK_HANDSHAKE_DELIM "\r\n"
90
#define QIO_CHANNEL_WEBSOCK_HANDSHAKE_END "\r\n\r\n"
91
#define QIO_CHANNEL_WEBSOCK_SUPPORTED_VERSION "13"
92
#define QIO_CHANNEL_WEBSOCK_HTTP_METHOD "GET"
93
#define QIO_CHANNEL_WEBSOCK_HTTP_PATH "/"
94
#define QIO_CHANNEL_WEBSOCK_HTTP_VERSION "HTTP/1.1"
95

96
/* The websockets packet header is variable length
97
 * depending on the size of the payload... */
98

99
/* ...length when using 7-bit payload length */
100
#define QIO_CHANNEL_WEBSOCK_HEADER_LEN_7_BIT 6
101
/* ...length when using 16-bit payload length */
102
#define QIO_CHANNEL_WEBSOCK_HEADER_LEN_16_BIT 8
103
/* ...length when using 64-bit payload length */
104
#define QIO_CHANNEL_WEBSOCK_HEADER_LEN_64_BIT 14
105

106
/* Length of the optional data mask field in header */
107
#define QIO_CHANNEL_WEBSOCK_HEADER_LEN_MASK 4
108

109
/* Maximum length that can fit in 7-bit payload size */
110
#define QIO_CHANNEL_WEBSOCK_PAYLOAD_LEN_THRESHOLD_7_BIT 126
111
/* Maximum length that can fit in 16-bit payload size */
112
#define QIO_CHANNEL_WEBSOCK_PAYLOAD_LEN_THRESHOLD_16_BIT 65536
113

114
/* Magic 7-bit length to indicate use of 16-bit payload length */
115
#define QIO_CHANNEL_WEBSOCK_PAYLOAD_LEN_MAGIC_16_BIT 126
116
/* Magic 7-bit length to indicate use of 64-bit payload length */
117
#define QIO_CHANNEL_WEBSOCK_PAYLOAD_LEN_MAGIC_64_BIT 127
118

119
/* Bitmasks for accessing header fields */
120
#define QIO_CHANNEL_WEBSOCK_HEADER_FIELD_FIN 0x80
121
#define QIO_CHANNEL_WEBSOCK_HEADER_FIELD_OPCODE 0x0f
122
#define QIO_CHANNEL_WEBSOCK_HEADER_FIELD_HAS_MASK 0x80
123
#define QIO_CHANNEL_WEBSOCK_HEADER_FIELD_PAYLOAD_LEN 0x7f
124
#define QIO_CHANNEL_WEBSOCK_CONTROL_OPCODE_MASK 0x8
125

126
typedef struct QIOChannelWebsockHeader QIOChannelWebsockHeader;
127

128
struct QEMU_PACKED QIOChannelWebsockHeader {
129
    unsigned char b0;
130
    unsigned char b1;
131
    union {
132
        struct QEMU_PACKED {
133
            uint16_t l16;
134
            QIOChannelWebsockMask m16;
135
        } s16;
136
        struct QEMU_PACKED {
137
            uint64_t l64;
138
            QIOChannelWebsockMask m64;
139
        } s64;
140
        QIOChannelWebsockMask m;
141
    } u;
142
};
143

144
typedef struct QIOChannelWebsockHTTPHeader QIOChannelWebsockHTTPHeader;
145

146
struct QIOChannelWebsockHTTPHeader {
147
    char *name;
148
    char *value;
149
};
150

151
enum {
152
    QIO_CHANNEL_WEBSOCK_OPCODE_CONTINUATION = 0x0,
153
    QIO_CHANNEL_WEBSOCK_OPCODE_TEXT_FRAME = 0x1,
154
    QIO_CHANNEL_WEBSOCK_OPCODE_BINARY_FRAME = 0x2,
155
    QIO_CHANNEL_WEBSOCK_OPCODE_CLOSE = 0x8,
156
    QIO_CHANNEL_WEBSOCK_OPCODE_PING = 0x9,
157
    QIO_CHANNEL_WEBSOCK_OPCODE_PONG = 0xA
158
};
159

160
static void G_GNUC_PRINTF(2, 3)
161
qio_channel_websock_handshake_send_res(QIOChannelWebsock *ioc,
162
                                       const char *resmsg,
163
                                       ...)
164
{
165
    va_list vargs;
166
    char *response;
167
    size_t responselen;
168

169
    va_start(vargs, resmsg);
170
    response = g_strdup_vprintf(resmsg, vargs);
171
    responselen = strlen(response);
172
    buffer_reserve(&ioc->encoutput, responselen);
173
    buffer_append(&ioc->encoutput, response, responselen);
174
    g_free(response);
175
    va_end(vargs);
176
}
177

178
static gchar *qio_channel_websock_date_str(void)
179
{
180
    g_autoptr(GDateTime) now = g_date_time_new_now_utc();
181

182
    return g_date_time_format(now, "%a, %d %b %Y %H:%M:%S GMT");
183
}
184

185
static void qio_channel_websock_handshake_send_res_err(QIOChannelWebsock *ioc,
186
                                                       const char *resdata)
187
{
188
    char *date = qio_channel_websock_date_str();
189
    qio_channel_websock_handshake_send_res(ioc, resdata, date);
190
    g_free(date);
191
}
192

193
enum {
194
    QIO_CHANNEL_WEBSOCK_STATUS_NORMAL = 1000,
195
    QIO_CHANNEL_WEBSOCK_STATUS_PROTOCOL_ERR = 1002,
196
    QIO_CHANNEL_WEBSOCK_STATUS_INVALID_DATA = 1003,
197
    QIO_CHANNEL_WEBSOCK_STATUS_POLICY = 1008,
198
    QIO_CHANNEL_WEBSOCK_STATUS_TOO_LARGE = 1009,
199
    QIO_CHANNEL_WEBSOCK_STATUS_SERVER_ERR = 1011,
200
};
201

202
static size_t
203
qio_channel_websock_extract_headers(QIOChannelWebsock *ioc,
204
                                    char *buffer,
205
                                    QIOChannelWebsockHTTPHeader *hdrs,
206
                                    size_t nhdrsalloc,
207
                                    Error **errp)
208
{
209
    char *nl, *sep, *tmp;
210
    size_t nhdrs = 0;
211

212
    /*
213
     * First parse the HTTP protocol greeting of format:
214
     *
215
     *   $METHOD $PATH $VERSION
216
     *
217
     * e.g.
218
     *
219
     *   GET / HTTP/1.1
220
     */
221

222
    nl = strstr(buffer, QIO_CHANNEL_WEBSOCK_HANDSHAKE_DELIM);
223
    if (!nl) {
224
        error_setg(errp, "Missing HTTP header delimiter");
225
        goto bad_request;
226
    }
227
    *nl = '\0';
228
    trace_qio_channel_websock_http_greeting(ioc, buffer);
229

230
    tmp = strchr(buffer, ' ');
231
    if (!tmp) {
232
        error_setg(errp, "Missing HTTP path delimiter");
233
        return 0;
234
    }
235
    *tmp = '\0';
236

237
    if (!g_str_equal(buffer, QIO_CHANNEL_WEBSOCK_HTTP_METHOD)) {
238
        error_setg(errp, "Unsupported HTTP method %s", buffer);
239
        goto bad_request;
240
    }
241

242
    buffer = tmp + 1;
243
    tmp = strchr(buffer, ' ');
244
    if (!tmp) {
245
        error_setg(errp, "Missing HTTP version delimiter");
246
        goto bad_request;
247
    }
248
    *tmp = '\0';
249

250
    if (!g_str_equal(buffer, QIO_CHANNEL_WEBSOCK_HTTP_PATH)) {
251
        qio_channel_websock_handshake_send_res_err(
252
            ioc, QIO_CHANNEL_WEBSOCK_HANDSHAKE_RES_NOT_FOUND);
253
        error_setg(errp, "Unexpected HTTP path %s", buffer);
254
        return 0;
255
    }
256

257
    buffer = tmp + 1;
258

259
    if (!g_str_equal(buffer, QIO_CHANNEL_WEBSOCK_HTTP_VERSION)) {
260
        error_setg(errp, "Unsupported HTTP version %s", buffer);
261
        goto bad_request;
262
    }
263

264
    buffer = nl + strlen(QIO_CHANNEL_WEBSOCK_HANDSHAKE_DELIM);
265

266
    /*
267
     * Now parse all the header fields of format
268
     *
269
     *   $NAME: $VALUE
270
     *
271
     * e.g.
272
     *
273
     *   Cache-control: no-cache
274
     */
275
    do {
276
        QIOChannelWebsockHTTPHeader *hdr;
277

278
        nl = strstr(buffer, QIO_CHANNEL_WEBSOCK_HANDSHAKE_DELIM);
279
        if (nl) {
280
            *nl = '\0';
281
        }
282

283
        sep = strchr(buffer, ':');
284
        if (!sep) {
285
            error_setg(errp, "Malformed HTTP header");
286
            goto bad_request;
287
        }
288
        *sep = '\0';
289
        sep++;
290
        while (*sep == ' ') {
291
            sep++;
292
        }
293

294
        if (nhdrs >= nhdrsalloc) {
295
            error_setg(errp, "Too many HTTP headers");
296
            goto bad_request;
297
        }
298

299
        hdr = &hdrs[nhdrs++];
300
        hdr->name = buffer;
301
        hdr->value = sep;
302

303
        /* Canonicalize header name for easier identification later */
304
        for (tmp = hdr->name; *tmp; tmp++) {
305
            *tmp = g_ascii_tolower(*tmp);
306
        }
307

308
        if (nl) {
309
            buffer = nl + strlen(QIO_CHANNEL_WEBSOCK_HANDSHAKE_DELIM);
310
        }
311
    } while (nl != NULL);
312

313
    return nhdrs;
314

315
 bad_request:
316
    qio_channel_websock_handshake_send_res_err(
317
        ioc, QIO_CHANNEL_WEBSOCK_HANDSHAKE_RES_BAD_REQUEST);
318
    return 0;
319
}
320

321
static const char *
322
qio_channel_websock_find_header(QIOChannelWebsockHTTPHeader *hdrs,
323
                                size_t nhdrs,
324
                                const char *name)
325
{
326
    size_t i;
327

328
    for (i = 0; i < nhdrs; i++) {
329
        if (g_str_equal(hdrs[i].name, name)) {
330
            return hdrs[i].value;
331
        }
332
    }
333

334
    return NULL;
335
}
336

337

338
static void qio_channel_websock_handshake_send_res_ok(QIOChannelWebsock *ioc,
339
                                                      const char *key,
340
                                                      const bool use_protocols,
341
                                                      Error **errp)
342
{
343
    char combined_key[QIO_CHANNEL_WEBSOCK_CLIENT_KEY_LEN +
344
                      QIO_CHANNEL_WEBSOCK_GUID_LEN + 1];
345
    char *accept = NULL;
346
    char *date = NULL;
347

348
    g_strlcpy(combined_key, key, QIO_CHANNEL_WEBSOCK_CLIENT_KEY_LEN + 1);
349
    g_strlcat(combined_key, QIO_CHANNEL_WEBSOCK_GUID,
350
              QIO_CHANNEL_WEBSOCK_CLIENT_KEY_LEN +
351
              QIO_CHANNEL_WEBSOCK_GUID_LEN + 1);
352

353
    /* hash and encode it */
354
    if (qcrypto_hash_base64(QCRYPTO_HASH_ALG_SHA1,
355
                            combined_key,
356
                            QIO_CHANNEL_WEBSOCK_CLIENT_KEY_LEN +
357
                            QIO_CHANNEL_WEBSOCK_GUID_LEN,
358
                            &accept,
359
                            errp) < 0) {
360
        qio_channel_websock_handshake_send_res_err(
361
            ioc, QIO_CHANNEL_WEBSOCK_HANDSHAKE_RES_SERVER_ERR);
362
        return;
363
    }
364

365
    date = qio_channel_websock_date_str();
366
    if (use_protocols) {
367
            qio_channel_websock_handshake_send_res(
368
                ioc, QIO_CHANNEL_WEBSOCK_HANDSHAKE_WITH_PROTO_RES_OK,
369
                date, accept);
370
    } else {
371
            qio_channel_websock_handshake_send_res(
372
                ioc, QIO_CHANNEL_WEBSOCK_HANDSHAKE_RES_OK, date, accept);
373
    }
374

375
    g_free(date);
376
    g_free(accept);
377
}
378

379
static void qio_channel_websock_handshake_process(QIOChannelWebsock *ioc,
380
                                                  char *buffer,
381
                                                  Error **errp)
382
{
383
    QIOChannelWebsockHTTPHeader hdrs[32];
384
    size_t nhdrs = G_N_ELEMENTS(hdrs);
385
    const char *protocols = NULL, *version = NULL, *key = NULL,
386
        *host = NULL, *connection = NULL, *upgrade = NULL;
387
    char **connectionv;
388
    bool upgraded = false;
389
    size_t i;
390

391
    nhdrs = qio_channel_websock_extract_headers(ioc, buffer, hdrs, nhdrs, errp);
392
    if (!nhdrs) {
393
        return;
394
    }
395

396
    protocols = qio_channel_websock_find_header(
397
        hdrs, nhdrs, QIO_CHANNEL_WEBSOCK_HEADER_PROTOCOL);
398

399
    version = qio_channel_websock_find_header(
400
        hdrs, nhdrs, QIO_CHANNEL_WEBSOCK_HEADER_VERSION);
401
    if (!version) {
402
        error_setg(errp, "Missing websocket version header data");
403
        goto bad_request;
404
    }
405

406
    key = qio_channel_websock_find_header(
407
        hdrs, nhdrs, QIO_CHANNEL_WEBSOCK_HEADER_KEY);
408
    if (!key) {
409
        error_setg(errp, "Missing websocket key header data");
410
        goto bad_request;
411
    }
412

413
    host = qio_channel_websock_find_header(
414
        hdrs, nhdrs, QIO_CHANNEL_WEBSOCK_HEADER_HOST);
415
    if (!host) {
416
        error_setg(errp, "Missing websocket host header data");
417
        goto bad_request;
418
    }
419

420
    connection = qio_channel_websock_find_header(
421
        hdrs, nhdrs, QIO_CHANNEL_WEBSOCK_HEADER_CONNECTION);
422
    if (!connection) {
423
        error_setg(errp, "Missing websocket connection header data");
424
        goto bad_request;
425
    }
426

427
    upgrade = qio_channel_websock_find_header(
428
        hdrs, nhdrs, QIO_CHANNEL_WEBSOCK_HEADER_UPGRADE);
429
    if (!upgrade) {
430
        error_setg(errp, "Missing websocket upgrade header data");
431
        goto bad_request;
432
    }
433

434
    trace_qio_channel_websock_http_request(ioc, protocols, version,
435
                                           host, connection, upgrade, key);
436

437
    if (protocols) {
438
            if (!g_strrstr(protocols, QIO_CHANNEL_WEBSOCK_PROTOCOL_BINARY)) {
439
                error_setg(errp, "No '%s' protocol is supported by client '%s'",
440
                           QIO_CHANNEL_WEBSOCK_PROTOCOL_BINARY, protocols);
441
                goto bad_request;
442
            }
443
    }
444

445
    if (!g_str_equal(version, QIO_CHANNEL_WEBSOCK_SUPPORTED_VERSION)) {
446
        error_setg(errp, "Version '%s' is not supported by client '%s'",
447
                   QIO_CHANNEL_WEBSOCK_SUPPORTED_VERSION, version);
448
        goto bad_request;
449
    }
450

451
    if (strlen(key) != QIO_CHANNEL_WEBSOCK_CLIENT_KEY_LEN) {
452
        error_setg(errp, "Key length '%zu' was not as expected '%d'",
453
                   strlen(key), QIO_CHANNEL_WEBSOCK_CLIENT_KEY_LEN);
454
        goto bad_request;
455
    }
456

457
    connectionv = g_strsplit(connection, ",", 0);
458
    for (i = 0; connectionv != NULL && connectionv[i] != NULL; i++) {
459
        g_strstrip(connectionv[i]);
460
        if (strcasecmp(connectionv[i],
461
                       QIO_CHANNEL_WEBSOCK_CONNECTION_UPGRADE) == 0) {
462
            upgraded = true;
463
        }
464
    }
465
    g_strfreev(connectionv);
466
    if (!upgraded) {
467
        error_setg(errp, "No connection upgrade requested '%s'", connection);
468
        goto bad_request;
469
    }
470

471
    if (strcasecmp(upgrade, QIO_CHANNEL_WEBSOCK_UPGRADE_WEBSOCKET) != 0) {
472
        error_setg(errp, "Incorrect upgrade method '%s'", upgrade);
473
        goto bad_request;
474
    }
475

476
    qio_channel_websock_handshake_send_res_ok(ioc, key, !!protocols, errp);
477
    return;
478

479
 bad_request:
480
    qio_channel_websock_handshake_send_res_err(
481
        ioc, QIO_CHANNEL_WEBSOCK_HANDSHAKE_RES_BAD_REQUEST);
482
}
483

484
static int qio_channel_websock_handshake_read(QIOChannelWebsock *ioc,
485
                                              Error **errp)
486
{
487
    char *handshake_end;
488
    ssize_t ret;
489
    /* Typical HTTP headers from novnc are 512 bytes, so limiting
490
     * total header size to 4096 is easily enough. */
491
    size_t want = 4096 - ioc->encinput.offset;
492
    buffer_reserve(&ioc->encinput, want);
493
    ret = qio_channel_read(ioc->master,
494
                           (char *)buffer_end(&ioc->encinput), want, errp);
495
    if (ret < 0) {
496
        return -1;
497
    }
498
    ioc->encinput.offset += ret;
499

500
    handshake_end = g_strstr_len((char *)ioc->encinput.buffer,
501
                                 ioc->encinput.offset,
502
                                 QIO_CHANNEL_WEBSOCK_HANDSHAKE_END);
503
    if (!handshake_end) {
504
        if (ioc->encinput.offset >= 4096) {
505
            qio_channel_websock_handshake_send_res_err(
506
                ioc, QIO_CHANNEL_WEBSOCK_HANDSHAKE_RES_TOO_LARGE);
507
            error_setg(errp,
508
                       "End of headers not found in first 4096 bytes");
509
            return 1;
510
        } else if (ret == 0) {
511
            error_setg(errp,
512
                       "End of headers not found before connection closed");
513
            return -1;
514
        }
515
        return 0;
516
    }
517
    *handshake_end = '\0';
518

519
    qio_channel_websock_handshake_process(ioc,
520
                                          (char *)ioc->encinput.buffer,
521
                                          errp);
522

523
    buffer_advance(&ioc->encinput,
524
                   handshake_end - (char *)ioc->encinput.buffer +
525
                   strlen(QIO_CHANNEL_WEBSOCK_HANDSHAKE_END));
526
    return 1;
527
}
528

529
static gboolean qio_channel_websock_handshake_send(QIOChannel *ioc,
530
                                                   GIOCondition condition,
531
                                                   gpointer user_data)
532
{
533
    QIOTask *task = user_data;
534
    QIOChannelWebsock *wioc = QIO_CHANNEL_WEBSOCK(
535
        qio_task_get_source(task));
536
    Error *err = NULL;
537
    ssize_t ret;
538

539
    ret = qio_channel_write(wioc->master,
540
                            (char *)wioc->encoutput.buffer,
541
                            wioc->encoutput.offset,
542
                            &err);
543

544
    if (ret < 0) {
545
        trace_qio_channel_websock_handshake_fail(ioc, error_get_pretty(err));
546
        qio_task_set_error(task, err);
547
        qio_task_complete(task);
548
        return FALSE;
549
    }
550

551
    buffer_advance(&wioc->encoutput, ret);
552
    if (wioc->encoutput.offset == 0) {
553
        if (wioc->io_err) {
554
            trace_qio_channel_websock_handshake_fail(
555
                ioc, error_get_pretty(wioc->io_err));
556
            qio_task_set_error(task, wioc->io_err);
557
            wioc->io_err = NULL;
558
            qio_task_complete(task);
559
        } else {
560
            trace_qio_channel_websock_handshake_complete(ioc);
561
            qio_task_complete(task);
562
        }
563
        return FALSE;
564
    }
565
    trace_qio_channel_websock_handshake_pending(ioc, G_IO_OUT);
566
    return TRUE;
567
}
568

569
static gboolean qio_channel_websock_handshake_io(QIOChannel *ioc,
570
                                                 GIOCondition condition,
571
                                                 gpointer user_data)
572
{
573
    QIOTask *task = user_data;
574
    QIOChannelWebsock *wioc = QIO_CHANNEL_WEBSOCK(
575
        qio_task_get_source(task));
576
    Error *err = NULL;
577
    int ret;
578

579
    ret = qio_channel_websock_handshake_read(wioc, &err);
580
    if (ret < 0) {
581
        /*
582
         * We only take this path on a fatal I/O error reading from
583
         * client connection, as most of the time we have an
584
         * HTTP 4xx err response to send instead
585
         */
586
        trace_qio_channel_websock_handshake_fail(ioc, error_get_pretty(err));
587
        qio_task_set_error(task, err);
588
        qio_task_complete(task);
589
        return FALSE;
590
    }
591
    if (ret == 0) {
592
        trace_qio_channel_websock_handshake_pending(ioc, G_IO_IN);
593
        /* need more data still */
594
        return TRUE;
595
    }
596

597
    error_propagate(&wioc->io_err, err);
598

599
    trace_qio_channel_websock_handshake_reply(ioc);
600
    qio_channel_add_watch(
601
        wioc->master,
602
        G_IO_OUT,
603
        qio_channel_websock_handshake_send,
604
        task,
605
        NULL);
606
    return FALSE;
607
}
608

609

610
static void qio_channel_websock_encode(QIOChannelWebsock *ioc,
611
                                       uint8_t opcode,
612
                                       const struct iovec *iov,
613
                                       size_t niov,
614
                                       size_t size)
615
{
616
    size_t header_size;
617
    size_t i;
618
    union {
619
        char buf[QIO_CHANNEL_WEBSOCK_HEADER_LEN_64_BIT];
620
        QIOChannelWebsockHeader ws;
621
    } header;
622

623
    assert(size <= iov_size(iov, niov));
624

625
    header.ws.b0 = QIO_CHANNEL_WEBSOCK_HEADER_FIELD_FIN |
626
        (opcode & QIO_CHANNEL_WEBSOCK_HEADER_FIELD_OPCODE);
627
    if (size < QIO_CHANNEL_WEBSOCK_PAYLOAD_LEN_THRESHOLD_7_BIT) {
628
        header.ws.b1 = (uint8_t)size;
629
        header_size = QIO_CHANNEL_WEBSOCK_HEADER_LEN_7_BIT;
630
    } else if (size < QIO_CHANNEL_WEBSOCK_PAYLOAD_LEN_THRESHOLD_16_BIT) {
631
        header.ws.b1 = QIO_CHANNEL_WEBSOCK_PAYLOAD_LEN_MAGIC_16_BIT;
632
        header.ws.u.s16.l16 = cpu_to_be16((uint16_t)size);
633
        header_size = QIO_CHANNEL_WEBSOCK_HEADER_LEN_16_BIT;
634
    } else {
635
        header.ws.b1 = QIO_CHANNEL_WEBSOCK_PAYLOAD_LEN_MAGIC_64_BIT;
636
        header.ws.u.s64.l64 = cpu_to_be64(size);
637
        header_size = QIO_CHANNEL_WEBSOCK_HEADER_LEN_64_BIT;
638
    }
639
    header_size -= QIO_CHANNEL_WEBSOCK_HEADER_LEN_MASK;
640

641
    trace_qio_channel_websock_encode(ioc, opcode, header_size, size);
642
    buffer_reserve(&ioc->encoutput, header_size + size);
643
    buffer_append(&ioc->encoutput, header.buf, header_size);
644
    for (i = 0; i < niov && size != 0; i++) {
645
        size_t want = iov[i].iov_len;
646
        if (want > size) {
647
            want = size;
648
        }
649
        buffer_append(&ioc->encoutput, iov[i].iov_base, want);
650
        size -= want;
651
    }
652
}
653

654

655
static ssize_t qio_channel_websock_write_wire(QIOChannelWebsock *, Error **);
656

657

658
static void qio_channel_websock_write_close(QIOChannelWebsock *ioc,
659
                                            uint16_t code, const char *reason)
660
{
661
    struct iovec iov[2] = {
662
        { .iov_base = &code, .iov_len = sizeof(code) },
663
    };
664
    size_t niov = 1;
665
    size_t size = iov[0].iov_len;
666

667
    cpu_to_be16s(&code);
668

669
    if (reason) {
670
        iov[1].iov_base = (void *)reason;
671
        iov[1].iov_len = strlen(reason);
672
        size += iov[1].iov_len;
673
        niov++;
674
    }
675
    qio_channel_websock_encode(ioc, QIO_CHANNEL_WEBSOCK_OPCODE_CLOSE,
676
                               iov, niov, size);
677
    qio_channel_websock_write_wire(ioc, NULL);
678
    qio_channel_shutdown(ioc->master, QIO_CHANNEL_SHUTDOWN_BOTH, NULL);
679
}
680

681

682
static int qio_channel_websock_decode_header(QIOChannelWebsock *ioc,
683
                                             Error **errp)
684
{
685
    unsigned char opcode, fin, has_mask;
686
    size_t header_size;
687
    size_t payload_len;
688
    QIOChannelWebsockHeader *header =
689
        (QIOChannelWebsockHeader *)ioc->encinput.buffer;
690

691
    if (ioc->payload_remain) {
692
        error_setg(errp,
693
                   "Decoding header but %zu bytes of payload remain",
694
                   ioc->payload_remain);
695
        qio_channel_websock_write_close(
696
            ioc, QIO_CHANNEL_WEBSOCK_STATUS_SERVER_ERR,
697
            "internal server error");
698
        return -1;
699
    }
700
    if (ioc->encinput.offset < QIO_CHANNEL_WEBSOCK_HEADER_LEN_7_BIT) {
701
        /* header not complete */
702
        return QIO_CHANNEL_ERR_BLOCK;
703
    }
704

705
    fin = header->b0 & QIO_CHANNEL_WEBSOCK_HEADER_FIELD_FIN;
706
    opcode = header->b0 & QIO_CHANNEL_WEBSOCK_HEADER_FIELD_OPCODE;
707
    has_mask = header->b1 & QIO_CHANNEL_WEBSOCK_HEADER_FIELD_HAS_MASK;
708
    payload_len = header->b1 & QIO_CHANNEL_WEBSOCK_HEADER_FIELD_PAYLOAD_LEN;
709

710
    /* Save or restore opcode. */
711
    if (opcode) {
712
        ioc->opcode = opcode;
713
    } else {
714
        opcode = ioc->opcode;
715
    }
716

717
    trace_qio_channel_websock_header_partial_decode(ioc, payload_len,
718
                                                    fin, opcode, (int)has_mask);
719

720
    if (opcode == QIO_CHANNEL_WEBSOCK_OPCODE_CLOSE) {
721
        /* disconnect */
722
        return 0;
723
    }
724

725
    /* Websocket frame sanity check:
726
     * * Fragmentation is only supported for binary frames.
727
     * * All frames sent by a client MUST be masked.
728
     * * Only binary and ping/pong encoding is supported.
729
     */
730
    if (!fin) {
731
        if (opcode != QIO_CHANNEL_WEBSOCK_OPCODE_BINARY_FRAME) {
732
            error_setg(errp, "only binary websocket frames may be fragmented");
733
            qio_channel_websock_write_close(
734
                ioc, QIO_CHANNEL_WEBSOCK_STATUS_POLICY ,
735
                "only binary frames may be fragmented");
736
            return -1;
737
        }
738
    } else {
739
        if (opcode != QIO_CHANNEL_WEBSOCK_OPCODE_BINARY_FRAME &&
740
            opcode != QIO_CHANNEL_WEBSOCK_OPCODE_CLOSE &&
741
            opcode != QIO_CHANNEL_WEBSOCK_OPCODE_PING &&
742
            opcode != QIO_CHANNEL_WEBSOCK_OPCODE_PONG) {
743
            error_setg(errp, "unsupported opcode: 0x%04x; only binary, close, "
744
                       "ping, and pong websocket frames are supported", opcode);
745
            qio_channel_websock_write_close(
746
                ioc, QIO_CHANNEL_WEBSOCK_STATUS_INVALID_DATA ,
747
                "only binary, close, ping, and pong frames are supported");
748
            return -1;
749
        }
750
    }
751
    if (!has_mask) {
752
        error_setg(errp, "client websocket frames must be masked");
753
        qio_channel_websock_write_close(
754
            ioc, QIO_CHANNEL_WEBSOCK_STATUS_PROTOCOL_ERR,
755
            "client frames must be masked");
756
        return -1;
757
    }
758

759
    if (payload_len < QIO_CHANNEL_WEBSOCK_PAYLOAD_LEN_MAGIC_16_BIT) {
760
        ioc->payload_remain = payload_len;
761
        header_size = QIO_CHANNEL_WEBSOCK_HEADER_LEN_7_BIT;
762
        ioc->mask = header->u.m;
763
    } else if (opcode & QIO_CHANNEL_WEBSOCK_CONTROL_OPCODE_MASK) {
764
        error_setg(errp, "websocket control frame is too large");
765
        qio_channel_websock_write_close(
766
            ioc, QIO_CHANNEL_WEBSOCK_STATUS_PROTOCOL_ERR,
767
            "control frame is too large");
768
        return -1;
769
    } else if (payload_len == QIO_CHANNEL_WEBSOCK_PAYLOAD_LEN_MAGIC_16_BIT &&
770
               ioc->encinput.offset >= QIO_CHANNEL_WEBSOCK_HEADER_LEN_16_BIT) {
771
        ioc->payload_remain = be16_to_cpu(header->u.s16.l16);
772
        header_size = QIO_CHANNEL_WEBSOCK_HEADER_LEN_16_BIT;
773
        ioc->mask = header->u.s16.m16;
774
    } else if (payload_len == QIO_CHANNEL_WEBSOCK_PAYLOAD_LEN_MAGIC_64_BIT &&
775
               ioc->encinput.offset >= QIO_CHANNEL_WEBSOCK_HEADER_LEN_64_BIT) {
776
        ioc->payload_remain = be64_to_cpu(header->u.s64.l64);
777
        header_size = QIO_CHANNEL_WEBSOCK_HEADER_LEN_64_BIT;
778
        ioc->mask = header->u.s64.m64;
779
    } else {
780
        /* header not complete */
781
        return QIO_CHANNEL_ERR_BLOCK;
782
    }
783

784
    trace_qio_channel_websock_header_full_decode(
785
        ioc, header_size, ioc->payload_remain, ioc->mask.u);
786
    buffer_advance(&ioc->encinput, header_size);
787
    return 0;
788
}
789

790

791
static int qio_channel_websock_decode_payload(QIOChannelWebsock *ioc,
792
                                              Error **errp)
793
{
794
    size_t i;
795
    size_t payload_len = 0;
796
    uint32_t *payload32;
797

798
    if (ioc->payload_remain) {
799
        /* If we aren't at the end of the payload, then drop
800
         * off the last bytes, so we're always multiple of 4
801
         * for purpose of unmasking, except at end of payload
802
         */
803
        if (ioc->encinput.offset < ioc->payload_remain) {
804
            /* Wait for the entire payload before processing control frames
805
             * because the payload will most likely be echoed back. */
806
            if (ioc->opcode & QIO_CHANNEL_WEBSOCK_CONTROL_OPCODE_MASK) {
807
                return QIO_CHANNEL_ERR_BLOCK;
808
            }
809
            payload_len = ioc->encinput.offset - (ioc->encinput.offset % 4);
810
        } else {
811
            payload_len = ioc->payload_remain;
812
        }
813
        if (payload_len == 0) {
814
            return QIO_CHANNEL_ERR_BLOCK;
815
        }
816

817
        ioc->payload_remain -= payload_len;
818

819
        /* unmask frame */
820
        /* process 1 frame (32 bit op) */
821
        payload32 = (uint32_t *)ioc->encinput.buffer;
822
        for (i = 0; i < payload_len / 4; i++) {
823
            payload32[i] ^= ioc->mask.u;
824
        }
825
        /* process the remaining bytes (if any) */
826
        for (i *= 4; i < payload_len; i++) {
827
            ioc->encinput.buffer[i] ^= ioc->mask.c[i % 4];
828
        }
829
    }
830

831
    trace_qio_channel_websock_payload_decode(
832
        ioc, ioc->opcode, ioc->payload_remain);
833

834
    if (ioc->opcode == QIO_CHANNEL_WEBSOCK_OPCODE_BINARY_FRAME) {
835
        if (payload_len) {
836
            /* binary frames are passed on */
837
            buffer_reserve(&ioc->rawinput, payload_len);
838
            buffer_append(&ioc->rawinput, ioc->encinput.buffer, payload_len);
839
        }
840
    } else if (ioc->opcode == QIO_CHANNEL_WEBSOCK_OPCODE_CLOSE) {
841
        /* close frames are echoed back */
842
        error_setg(errp, "websocket closed by peer");
843
        if (payload_len) {
844
            /* echo client status */
845
            struct iovec iov = { .iov_base = ioc->encinput.buffer,
846
                                 .iov_len = ioc->encinput.offset };
847
            qio_channel_websock_encode(ioc, QIO_CHANNEL_WEBSOCK_OPCODE_CLOSE,
848
                                       &iov, 1, iov.iov_len);
849
            qio_channel_websock_write_wire(ioc, NULL);
850
            qio_channel_shutdown(ioc->master, QIO_CHANNEL_SHUTDOWN_BOTH, NULL);
851
        } else {
852
            /* send our own status */
853
            qio_channel_websock_write_close(
854
                ioc, QIO_CHANNEL_WEBSOCK_STATUS_NORMAL, "peer requested close");
855
        }
856
        return -1;
857
    } else if (ioc->opcode == QIO_CHANNEL_WEBSOCK_OPCODE_PING) {
858
        /* ping frames produce an immediate reply, as long as we've not still
859
         * got a previous pong queued, in which case we drop the new pong */
860
        if (ioc->pong_remain == 0) {
861
            struct iovec iov = { .iov_base = ioc->encinput.buffer,
862
                                 .iov_len = ioc->encinput.offset };
863
            qio_channel_websock_encode(ioc, QIO_CHANNEL_WEBSOCK_OPCODE_PONG,
864
                                       &iov, 1, iov.iov_len);
865
            ioc->pong_remain = ioc->encoutput.offset;
866
        }
867
    }   /* pong frames are ignored */
868

869
    if (payload_len) {
870
        buffer_advance(&ioc->encinput, payload_len);
871
    }
872
    return 0;
873
}
874

875

876
QIOChannelWebsock *
877
qio_channel_websock_new_server(QIOChannel *master)
878
{
879
    QIOChannelWebsock *wioc;
880
    QIOChannel *ioc;
881

882
    wioc = QIO_CHANNEL_WEBSOCK(object_new(TYPE_QIO_CHANNEL_WEBSOCK));
883
    ioc = QIO_CHANNEL(wioc);
884

885
    wioc->master = master;
886
    ioc->follow_coroutine_ctx = master->follow_coroutine_ctx;
887
    if (qio_channel_has_feature(master, QIO_CHANNEL_FEATURE_SHUTDOWN)) {
888
        qio_channel_set_feature(ioc, QIO_CHANNEL_FEATURE_SHUTDOWN);
889
    }
890
    object_ref(OBJECT(master));
891

892
    trace_qio_channel_websock_new_server(wioc, master);
893
    return wioc;
894
}
895

896
void qio_channel_websock_handshake(QIOChannelWebsock *ioc,
897
                                   QIOTaskFunc func,
898
                                   gpointer opaque,
899
                                   GDestroyNotify destroy)
900
{
901
    QIOTask *task;
902

903
    task = qio_task_new(OBJECT(ioc),
904
                        func,
905
                        opaque,
906
                        destroy);
907

908
    trace_qio_channel_websock_handshake_start(ioc);
909
    trace_qio_channel_websock_handshake_pending(ioc, G_IO_IN);
910
    qio_channel_add_watch(ioc->master,
911
                          G_IO_IN,
912
                          qio_channel_websock_handshake_io,
913
                          task,
914
                          NULL);
915
}
916

917

918
static void qio_channel_websock_finalize(Object *obj)
919
{
920
    QIOChannelWebsock *ioc = QIO_CHANNEL_WEBSOCK(obj);
921

922
    buffer_free(&ioc->encinput);
923
    buffer_free(&ioc->encoutput);
924
    buffer_free(&ioc->rawinput);
925
    object_unref(OBJECT(ioc->master));
926
    if (ioc->io_tag) {
927
        g_source_remove(ioc->io_tag);
928
    }
929
    if (ioc->io_err) {
930
        error_free(ioc->io_err);
931
    }
932
}
933

934

935
static ssize_t qio_channel_websock_read_wire(QIOChannelWebsock *ioc,
936
                                             Error **errp)
937
{
938
    ssize_t ret;
939

940
    if (ioc->encinput.offset < 4096) {
941
        size_t want = 4096 - ioc->encinput.offset;
942

943
        buffer_reserve(&ioc->encinput, want);
944
        ret = qio_channel_read(ioc->master,
945
                               (char *)ioc->encinput.buffer +
946
                               ioc->encinput.offset,
947
                               want,
948
                               errp);
949
        if (ret < 0) {
950
            return ret;
951
        }
952
        if (ret == 0 && ioc->encinput.offset == 0) {
953
            ioc->io_eof = TRUE;
954
            return 0;
955
        }
956
        ioc->encinput.offset += ret;
957
    }
958

959
    while (ioc->encinput.offset != 0) {
960
        if (ioc->payload_remain == 0) {
961
            ret = qio_channel_websock_decode_header(ioc, errp);
962
            if (ret < 0) {
963
                return ret;
964
            }
965
        }
966

967
        ret = qio_channel_websock_decode_payload(ioc, errp);
968
        if (ret < 0) {
969
            return ret;
970
        }
971
    }
972
    return 1;
973
}
974

975

976
static ssize_t qio_channel_websock_write_wire(QIOChannelWebsock *ioc,
977
                                              Error **errp)
978
{
979
    ssize_t ret;
980
    ssize_t done = 0;
981

982
    while (ioc->encoutput.offset > 0) {
983
        ret = qio_channel_write(ioc->master,
984
                                (char *)ioc->encoutput.buffer,
985
                                ioc->encoutput.offset,
986
                                errp);
987
        if (ret < 0) {
988
            if (ret == QIO_CHANNEL_ERR_BLOCK &&
989
                done > 0) {
990
                return done;
991
            } else {
992
                return ret;
993
            }
994
        }
995
        buffer_advance(&ioc->encoutput, ret);
996
        done += ret;
997
        if (ioc->pong_remain < ret) {
998
            ioc->pong_remain = 0;
999
        } else {
1000
            ioc->pong_remain -= ret;
1001
        }
1002
    }
1003
    return done;
1004
}
1005

1006

1007
static void qio_channel_websock_flush_free(gpointer user_data)
1008
{
1009
    QIOChannelWebsock *wioc = QIO_CHANNEL_WEBSOCK(user_data);
1010
    object_unref(OBJECT(wioc));
1011
}
1012

1013
static void qio_channel_websock_set_watch(QIOChannelWebsock *ioc);
1014

1015
static gboolean qio_channel_websock_flush(QIOChannel *ioc,
1016
                                          GIOCondition condition,
1017
                                          gpointer user_data)
1018
{
1019
    QIOChannelWebsock *wioc = QIO_CHANNEL_WEBSOCK(user_data);
1020
    ssize_t ret;
1021

1022
    if (condition & G_IO_OUT) {
1023
        ret = qio_channel_websock_write_wire(wioc, &wioc->io_err);
1024
        if (ret < 0) {
1025
            goto cleanup;
1026
        }
1027
    }
1028

1029
    if (condition & G_IO_IN) {
1030
        ret = qio_channel_websock_read_wire(wioc, &wioc->io_err);
1031
        if (ret < 0) {
1032
            goto cleanup;
1033
        }
1034
    }
1035

1036
 cleanup:
1037
    qio_channel_websock_set_watch(wioc);
1038
    return FALSE;
1039
}
1040

1041

1042
static void qio_channel_websock_unset_watch(QIOChannelWebsock *ioc)
1043
{
1044
    if (ioc->io_tag) {
1045
        g_source_remove(ioc->io_tag);
1046
        ioc->io_tag = 0;
1047
    }
1048
}
1049

1050
static void qio_channel_websock_set_watch(QIOChannelWebsock *ioc)
1051
{
1052
    GIOCondition cond = 0;
1053

1054
    qio_channel_websock_unset_watch(ioc);
1055

1056
    if (ioc->io_err) {
1057
        return;
1058
    }
1059

1060
    if (ioc->encoutput.offset) {
1061
        cond |= G_IO_OUT;
1062
    }
1063
    if (ioc->encinput.offset < QIO_CHANNEL_WEBSOCK_MAX_BUFFER &&
1064
        !ioc->io_eof) {
1065
        cond |= G_IO_IN;
1066
    }
1067

1068
    if (cond) {
1069
        object_ref(OBJECT(ioc));
1070
        ioc->io_tag =
1071
            qio_channel_add_watch(ioc->master,
1072
                                  cond,
1073
                                  qio_channel_websock_flush,
1074
                                  ioc,
1075
                                  qio_channel_websock_flush_free);
1076
    }
1077
}
1078

1079

1080
static ssize_t qio_channel_websock_readv(QIOChannel *ioc,
1081
                                         const struct iovec *iov,
1082
                                         size_t niov,
1083
                                         int **fds,
1084
                                         size_t *nfds,
1085
                                         int flags,
1086
                                         Error **errp)
1087
{
1088
    QIOChannelWebsock *wioc = QIO_CHANNEL_WEBSOCK(ioc);
1089
    size_t i;
1090
    ssize_t got = 0;
1091
    ssize_t ret;
1092

1093
    if (wioc->io_err) {
1094
        error_propagate(errp, error_copy(wioc->io_err));
1095
        return -1;
1096
    }
1097

1098
    if (!wioc->rawinput.offset) {
1099
        ret = qio_channel_websock_read_wire(QIO_CHANNEL_WEBSOCK(ioc), errp);
1100
        if (ret < 0) {
1101
            return ret;
1102
        }
1103
    }
1104

1105
    for (i = 0 ; i < niov ; i++) {
1106
        size_t want = iov[i].iov_len;
1107
        if (want > (wioc->rawinput.offset - got)) {
1108
            want = (wioc->rawinput.offset - got);
1109
        }
1110

1111
        memcpy(iov[i].iov_base,
1112
               wioc->rawinput.buffer + got,
1113
               want);
1114
        got += want;
1115

1116
        if (want < iov[i].iov_len) {
1117
            break;
1118
        }
1119
    }
1120

1121
    buffer_advance(&wioc->rawinput, got);
1122
    qio_channel_websock_set_watch(wioc);
1123
    return got;
1124
}
1125

1126

1127
static ssize_t qio_channel_websock_writev(QIOChannel *ioc,
1128
                                          const struct iovec *iov,
1129
                                          size_t niov,
1130
                                          int *fds,
1131
                                          size_t nfds,
1132
                                          int flags,
1133
                                          Error **errp)
1134
{
1135
    QIOChannelWebsock *wioc = QIO_CHANNEL_WEBSOCK(ioc);
1136
    ssize_t want = iov_size(iov, niov);
1137
    ssize_t avail;
1138
    ssize_t ret;
1139

1140
    if (wioc->io_err) {
1141
        error_propagate(errp, error_copy(wioc->io_err));
1142
        return -1;
1143
    }
1144

1145
    if (wioc->io_eof) {
1146
        error_setg(errp, "%s", "Broken pipe");
1147
        return -1;
1148
    }
1149

1150
    avail = wioc->encoutput.offset >= QIO_CHANNEL_WEBSOCK_MAX_BUFFER ?
1151
        0 : (QIO_CHANNEL_WEBSOCK_MAX_BUFFER - wioc->encoutput.offset);
1152
    if (want > avail) {
1153
        want = avail;
1154
    }
1155

1156
    if (want) {
1157
        qio_channel_websock_encode(wioc,
1158
                                   QIO_CHANNEL_WEBSOCK_OPCODE_BINARY_FRAME,
1159
                                   iov, niov, want);
1160
    }
1161

1162
    /* Even if want == 0, we'll try write_wire in case there's
1163
     * pending data we could usefully flush out
1164
     */
1165
    ret = qio_channel_websock_write_wire(wioc, errp);
1166
    if (ret < 0 &&
1167
        ret != QIO_CHANNEL_ERR_BLOCK) {
1168
        qio_channel_websock_unset_watch(wioc);
1169
        return -1;
1170
    }
1171

1172
    qio_channel_websock_set_watch(wioc);
1173

1174
    if (want == 0) {
1175
        return QIO_CHANNEL_ERR_BLOCK;
1176
    }
1177

1178
    return want;
1179
}
1180

1181
static int qio_channel_websock_set_blocking(QIOChannel *ioc,
1182
                                            bool enabled,
1183
                                            Error **errp)
1184
{
1185
    QIOChannelWebsock *wioc = QIO_CHANNEL_WEBSOCK(ioc);
1186

1187
    qio_channel_set_blocking(wioc->master, enabled, errp);
1188
    return 0;
1189
}
1190

1191
static void qio_channel_websock_set_delay(QIOChannel *ioc,
1192
                                          bool enabled)
1193
{
1194
    QIOChannelWebsock *tioc = QIO_CHANNEL_WEBSOCK(ioc);
1195

1196
    qio_channel_set_delay(tioc->master, enabled);
1197
}
1198

1199
static void qio_channel_websock_set_cork(QIOChannel *ioc,
1200
                                         bool enabled)
1201
{
1202
    QIOChannelWebsock *tioc = QIO_CHANNEL_WEBSOCK(ioc);
1203

1204
    qio_channel_set_cork(tioc->master, enabled);
1205
}
1206

1207
static int qio_channel_websock_shutdown(QIOChannel *ioc,
1208
                                        QIOChannelShutdown how,
1209
                                        Error **errp)
1210
{
1211
    QIOChannelWebsock *tioc = QIO_CHANNEL_WEBSOCK(ioc);
1212

1213
    return qio_channel_shutdown(tioc->master, how, errp);
1214
}
1215

1216
static int qio_channel_websock_close(QIOChannel *ioc,
1217
                                     Error **errp)
1218
{
1219
    QIOChannelWebsock *wioc = QIO_CHANNEL_WEBSOCK(ioc);
1220

1221
    trace_qio_channel_websock_close(ioc);
1222
    return qio_channel_close(wioc->master, errp);
1223
}
1224

1225
typedef struct QIOChannelWebsockSource QIOChannelWebsockSource;
1226
struct QIOChannelWebsockSource {
1227
    GSource parent;
1228
    QIOChannelWebsock *wioc;
1229
    GIOCondition condition;
1230
};
1231

1232
static gboolean
1233
qio_channel_websock_source_check(GSource *source)
1234
{
1235
    QIOChannelWebsockSource *wsource = (QIOChannelWebsockSource *)source;
1236
    GIOCondition cond = 0;
1237

1238
    if (wsource->wioc->rawinput.offset) {
1239
        cond |= G_IO_IN;
1240
    }
1241
    if (wsource->wioc->encoutput.offset < QIO_CHANNEL_WEBSOCK_MAX_BUFFER) {
1242
        cond |= G_IO_OUT;
1243
    }
1244
    if (wsource->wioc->io_eof) {
1245
        cond |= G_IO_HUP;
1246
    }
1247
    if (wsource->wioc->io_err) {
1248
        cond |= G_IO_ERR;
1249
    }
1250

1251
    return cond & wsource->condition;
1252
}
1253

1254
static gboolean
1255
qio_channel_websock_source_prepare(GSource *source,
1256
                                   gint *timeout)
1257
{
1258
    *timeout = -1;
1259
    return qio_channel_websock_source_check(source);
1260
}
1261

1262
static gboolean
1263
qio_channel_websock_source_dispatch(GSource *source,
1264
                                    GSourceFunc callback,
1265
                                    gpointer user_data)
1266
{
1267
    QIOChannelFunc func = (QIOChannelFunc)callback;
1268
    QIOChannelWebsockSource *wsource = (QIOChannelWebsockSource *)source;
1269

1270
    return (*func)(QIO_CHANNEL(wsource->wioc),
1271
                   qio_channel_websock_source_check(source),
1272
                   user_data);
1273
}
1274

1275
static void
1276
qio_channel_websock_source_finalize(GSource *source)
1277
{
1278
    QIOChannelWebsockSource *ssource = (QIOChannelWebsockSource *)source;
1279

1280
    object_unref(OBJECT(ssource->wioc));
1281
}
1282

1283
GSourceFuncs qio_channel_websock_source_funcs = {
1284
    qio_channel_websock_source_prepare,
1285
    qio_channel_websock_source_check,
1286
    qio_channel_websock_source_dispatch,
1287
    qio_channel_websock_source_finalize
1288
};
1289

1290
static GSource *qio_channel_websock_create_watch(QIOChannel *ioc,
1291
                                                 GIOCondition condition)
1292
{
1293
    QIOChannelWebsock *wioc = QIO_CHANNEL_WEBSOCK(ioc);
1294
    QIOChannelWebsockSource *ssource;
1295
    GSource *source;
1296

1297
    source = g_source_new(&qio_channel_websock_source_funcs,
1298
                          sizeof(QIOChannelWebsockSource));
1299
    ssource = (QIOChannelWebsockSource *)source;
1300

1301
    ssource->wioc = wioc;
1302
    object_ref(OBJECT(wioc));
1303

1304
    ssource->condition = condition;
1305

1306
    qio_channel_websock_set_watch(wioc);
1307
    return source;
1308
}
1309

1310
static void qio_channel_websock_class_init(ObjectClass *klass,
1311
                                           void *class_data G_GNUC_UNUSED)
1312
{
1313
    QIOChannelClass *ioc_klass = QIO_CHANNEL_CLASS(klass);
1314

1315
    ioc_klass->io_writev = qio_channel_websock_writev;
1316
    ioc_klass->io_readv = qio_channel_websock_readv;
1317
    ioc_klass->io_set_blocking = qio_channel_websock_set_blocking;
1318
    ioc_klass->io_set_cork = qio_channel_websock_set_cork;
1319
    ioc_klass->io_set_delay = qio_channel_websock_set_delay;
1320
    ioc_klass->io_close = qio_channel_websock_close;
1321
    ioc_klass->io_shutdown = qio_channel_websock_shutdown;
1322
    ioc_klass->io_create_watch = qio_channel_websock_create_watch;
1323
}
1324

1325
static const TypeInfo qio_channel_websock_info = {
1326
    .parent = TYPE_QIO_CHANNEL,
1327
    .name = TYPE_QIO_CHANNEL_WEBSOCK,
1328
    .instance_size = sizeof(QIOChannelWebsock),
1329
    .instance_finalize = qio_channel_websock_finalize,
1330
    .class_init = qio_channel_websock_class_init,
1331
};
1332

1333
static void qio_channel_websock_register_types(void)
1334
{
1335
    type_register_static(&qio_channel_websock_info);
1336
}
1337

1338
type_init(qio_channel_websock_register_types);
1339

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.