2 * QEMU VNC display driver: Websockets support
4 * Copyright (C) 2010 Joel Martin
5 * Copyright (C) 2012 Tim Hardeck
7 * This is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This software is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this software; if not, see <http://www.gnu.org/licenses/>.
22 #include "qemu/main-loop.h"
23 #include "crypto/hash.h"
25 static int vncws_start_tls_handshake(VncState
*vs
)
29 if (qcrypto_tls_session_handshake(vs
->tls
, &err
) < 0) {
33 switch (qcrypto_tls_session_get_handshake_status(vs
->tls
)) {
34 case QCRYPTO_TLS_HANDSHAKE_COMPLETE
:
35 VNC_DEBUG("Handshake done, checking credentials\n");
36 if (qcrypto_tls_session_check_credentials(vs
->tls
, &err
) < 0) {
39 VNC_DEBUG("Client verification passed, starting TLS I/O\n");
40 qemu_set_fd_handler(vs
->csock
, vncws_handshake_read
, NULL
, vs
);
43 case QCRYPTO_TLS_HANDSHAKE_RECVING
:
44 VNC_DEBUG("Handshake interrupted (blocking read)\n");
45 qemu_set_fd_handler(vs
->csock
, vncws_tls_handshake_io
, NULL
, vs
);
48 case QCRYPTO_TLS_HANDSHAKE_SENDING
:
49 VNC_DEBUG("Handshake interrupted (blocking write)\n");
50 qemu_set_fd_handler(vs
->csock
, NULL
, vncws_tls_handshake_io
, vs
);
57 VNC_DEBUG("Handshake failed %s\n", error_get_pretty(err
));
63 void vncws_tls_handshake_io(void *opaque
)
65 VncState
*vs
= (VncState
*)opaque
;
68 vs
->tls
= qcrypto_tls_session_new(vs
->vd
->tlscreds
,
71 QCRYPTO_TLS_CREDS_ENDPOINT_SERVER
,
74 VNC_DEBUG("Failed to setup TLS %s\n",
75 error_get_pretty(err
));
81 qcrypto_tls_session_set_callbacks(vs
->tls
,
86 VNC_DEBUG("Start TLS WS handshake process\n");
87 vncws_start_tls_handshake(vs
);
90 void vncws_handshake_read(void *opaque
)
92 VncState
*vs
= opaque
;
93 uint8_t *handshake_end
;
95 /* Typical HTTP headers from novnc are 512 bytes, so limiting
96 * total header size to 4096 is easily enough. */
97 size_t want
= 4096 - vs
->ws_input
.offset
;
98 buffer_reserve(&vs
->ws_input
, want
);
99 ret
= vnc_client_read_buf(vs
, buffer_end(&vs
->ws_input
), want
);
102 if (vs
->csock
== -1) {
103 vnc_disconnect_finish(vs
);
107 vs
->ws_input
.offset
+= ret
;
109 handshake_end
= (uint8_t *)g_strstr_len((char *)vs
->ws_input
.buffer
,
110 vs
->ws_input
.offset
, WS_HANDSHAKE_END
);
112 qemu_set_fd_handler(vs
->csock
, vnc_client_read
, NULL
, vs
);
113 vncws_process_handshake(vs
, vs
->ws_input
.buffer
, vs
->ws_input
.offset
);
114 buffer_advance(&vs
->ws_input
, handshake_end
- vs
->ws_input
.buffer
+
115 strlen(WS_HANDSHAKE_END
));
116 } else if (vs
->ws_input
.offset
>= 4096) {
117 VNC_DEBUG("End of headers not found in first 4096 bytes\n");
118 vnc_client_error(vs
);
123 long vnc_client_read_ws(VncState
*vs
)
127 size_t payload_size
, header_size
;
128 VNC_DEBUG("Read websocket %p size %zd offset %zd\n", vs
->ws_input
.buffer
,
129 vs
->ws_input
.capacity
, vs
->ws_input
.offset
);
130 buffer_reserve(&vs
->ws_input
, 4096);
131 ret
= vnc_client_read_buf(vs
, buffer_end(&vs
->ws_input
), 4096);
135 vs
->ws_input
.offset
+= ret
;
138 /* consume as much of ws_input buffer as possible */
140 if (vs
->ws_payload_remain
== 0) {
141 err
= vncws_decode_frame_header(&vs
->ws_input
,
143 &vs
->ws_payload_remain
,
144 &vs
->ws_payload_mask
);
149 buffer_advance(&vs
->ws_input
, header_size
);
151 if (vs
->ws_payload_remain
!= 0) {
152 err
= vncws_decode_frame_payload(&vs
->ws_input
,
153 &vs
->ws_payload_remain
,
154 &vs
->ws_payload_mask
,
165 buffer_reserve(&vs
->input
, payload_size
);
166 buffer_append(&vs
->input
, payload
, payload_size
);
168 buffer_advance(&vs
->ws_input
, payload_size
);
170 } while (vs
->ws_input
.offset
> 0);
175 long vnc_client_write_ws(VncState
*vs
)
178 VNC_DEBUG("Write WS: Pending output %p size %zd offset %zd\n",
179 vs
->output
.buffer
, vs
->output
.capacity
, vs
->output
.offset
);
180 vncws_encode_frame(&vs
->ws_output
, vs
->output
.buffer
, vs
->output
.offset
);
181 buffer_reset(&vs
->output
);
182 ret
= vnc_client_write_buf(vs
, vs
->ws_output
.buffer
, vs
->ws_output
.offset
);
187 buffer_advance(&vs
->ws_output
, ret
);
189 if (vs
->ws_output
.offset
== 0) {
190 qemu_set_fd_handler(vs
->csock
, vnc_client_read
, NULL
, vs
);
196 static char *vncws_extract_handshake_entry(const char *handshake
,
197 size_t handshake_len
, const char *name
)
199 char *begin
, *end
, *ret
= NULL
;
200 char *line
= g_strdup_printf("%s%s: ", WS_HANDSHAKE_DELIM
, name
);
201 begin
= g_strstr_len(handshake
, handshake_len
, line
);
203 begin
+= strlen(line
);
204 end
= g_strstr_len(begin
, handshake_len
- (begin
- handshake
),
207 ret
= g_strndup(begin
, end
- begin
);
214 static void vncws_send_handshake_response(VncState
*vs
, const char* key
)
216 char combined_key
[WS_CLIENT_KEY_LEN
+ WS_GUID_LEN
+ 1];
217 char *accept
= NULL
, *response
= NULL
;
220 g_strlcpy(combined_key
, key
, WS_CLIENT_KEY_LEN
+ 1);
221 g_strlcat(combined_key
, WS_GUID
, WS_CLIENT_KEY_LEN
+ WS_GUID_LEN
+ 1);
223 /* hash and encode it */
224 if (qcrypto_hash_base64(QCRYPTO_HASH_ALG_SHA1
,
226 WS_CLIENT_KEY_LEN
+ WS_GUID_LEN
,
229 VNC_DEBUG("Hashing Websocket combined key failed %s\n",
230 error_get_pretty(err
));
232 vnc_client_error(vs
);
236 response
= g_strdup_printf(WS_HANDSHAKE
, accept
);
237 vnc_client_write_buf(vs
, (const uint8_t *)response
, strlen(response
));
246 void vncws_process_handshake(VncState
*vs
, uint8_t *line
, size_t size
)
248 char *protocols
= vncws_extract_handshake_entry((const char *)line
, size
,
249 "Sec-WebSocket-Protocol");
250 char *version
= vncws_extract_handshake_entry((const char *)line
, size
,
251 "Sec-WebSocket-Version");
252 char *key
= vncws_extract_handshake_entry((const char *)line
, size
,
253 "Sec-WebSocket-Key");
255 if (protocols
&& version
&& key
256 && g_strrstr(protocols
, "binary")
257 && !strcmp(version
, WS_SUPPORTED_VERSION
)
258 && strlen(key
) == WS_CLIENT_KEY_LEN
) {
259 vncws_send_handshake_response(vs
, key
);
261 VNC_DEBUG("Defective Websockets header or unsupported protocol\n");
262 vnc_client_error(vs
);
270 void vncws_encode_frame(Buffer
*output
, const void *payload
,
271 const size_t payload_size
)
273 size_t header_size
= 0;
274 unsigned char opcode
= WS_OPCODE_BINARY_FRAME
;
276 char buf
[WS_HEAD_MAX_LEN
];
284 header
.ws
.b0
= 0x80 | (opcode
& 0x0f);
285 if (payload_size
<= 125) {
286 header
.ws
.b1
= (uint8_t)payload_size
;
288 } else if (payload_size
< 65536) {
290 header
.ws
.u
.s16
.l16
= cpu_to_be16((uint16_t)payload_size
);
294 header
.ws
.u
.s64
.l64
= cpu_to_be64(payload_size
);
298 buffer_reserve(output
, header_size
+ payload_size
);
299 buffer_append(output
, header
.buf
, header_size
);
300 buffer_append(output
, payload
, payload_size
);
303 int vncws_decode_frame_header(Buffer
*input
,
305 size_t *payload_remain
,
306 WsMask
*payload_mask
)
308 unsigned char opcode
= 0, fin
= 0, has_mask
= 0;
310 WsHeader
*header
= (WsHeader
*)input
->buffer
;
312 if (input
->offset
< WS_HEAD_MIN_LEN
+ 4) {
313 /* header not complete */
317 fin
= (header
->b0
& 0x80) >> 7;
318 opcode
= header
->b0
& 0x0f;
319 has_mask
= (header
->b1
& 0x80) >> 7;
320 payload_len
= header
->b1
& 0x7f;
322 if (opcode
== WS_OPCODE_CLOSE
) {
327 /* Websocket frame sanity check:
328 * * Websocket fragmentation is not supported.
329 * * All websockets frames sent by a client have to be masked.
330 * * Only binary encoding is supported.
332 if (!fin
|| !has_mask
|| opcode
!= WS_OPCODE_BINARY_FRAME
) {
333 VNC_DEBUG("Received faulty/unsupported Websocket frame\n");
337 if (payload_len
< 126) {
338 *payload_remain
= payload_len
;
340 *payload_mask
= header
->u
.m
;
341 } else if (payload_len
== 126 && input
->offset
>= 8) {
342 *payload_remain
= be16_to_cpu(header
->u
.s16
.l16
);
344 *payload_mask
= header
->u
.s16
.m16
;
345 } else if (payload_len
== 127 && input
->offset
>= 14) {
346 *payload_remain
= be64_to_cpu(header
->u
.s64
.l64
);
348 *payload_mask
= header
->u
.s64
.m64
;
350 /* header not complete */
357 int vncws_decode_frame_payload(Buffer
*input
,
358 size_t *payload_remain
, WsMask
*payload_mask
,
359 uint8_t **payload
, size_t *payload_size
)
364 *payload
= input
->buffer
;
365 /* If we aren't at the end of the payload, then drop
366 * off the last bytes, so we're always multiple of 4
367 * for purpose of unmasking, except at end of payload
369 if (input
->offset
< *payload_remain
) {
370 *payload_size
= input
->offset
- (input
->offset
% 4);
372 *payload_size
= *payload_remain
;
374 if (*payload_size
== 0) {
377 *payload_remain
-= *payload_size
;
380 /* process 1 frame (32 bit op) */
381 payload32
= (uint32_t *)(*payload
);
382 for (i
= 0; i
< *payload_size
/ 4; i
++) {
383 payload32
[i
] ^= payload_mask
->u
;
385 /* process the remaining bytes (if any) */
386 for (i
*= 4; i
< *payload_size
; i
++) {
387 (*payload
)[i
] ^= payload_mask
->c
[i
% 4];