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"
26 #include "qemu/sockets.h"
28 static int vncws_start_tls_handshake(VncState
*vs
)
30 int ret
= gnutls_handshake(vs
->tls
.session
);
33 if (!gnutls_error_is_fatal(ret
)) {
34 VNC_DEBUG("Handshake interrupted (blocking)\n");
35 if (!gnutls_record_get_direction(vs
->tls
.session
)) {
36 qemu_set_fd_handler(vs
->csock
, vncws_tls_handshake_io
,
39 qemu_set_fd_handler(vs
->csock
, NULL
, vncws_tls_handshake_io
,
44 VNC_DEBUG("Handshake failed %s\n", gnutls_strerror(ret
));
49 if (vs
->vd
->tls
.x509verify
) {
50 if (vnc_tls_validate_certificate(vs
) < 0) {
51 VNC_DEBUG("Client verification failed\n");
55 VNC_DEBUG("Client verification passed\n");
59 VNC_DEBUG("Handshake done, switching to TLS data mode\n");
60 qemu_set_fd_handler(vs
->csock
, vncws_handshake_read
, NULL
, vs
);
65 void vncws_tls_handshake_io(void *opaque
)
67 VncState
*vs
= (VncState
*)opaque
;
69 if (!vs
->tls
.session
) {
70 VNC_DEBUG("TLS Websocket setup\n");
71 if (vnc_tls_client_setup(vs
, vs
->vd
->tls
.x509cert
!= NULL
) < 0) {
75 VNC_DEBUG("Handshake IO continue\n");
76 vncws_start_tls_handshake(vs
);
78 #endif /* CONFIG_VNC_TLS */
80 void vncws_handshake_read(void *opaque
)
82 VncState
*vs
= opaque
;
83 uint8_t *handshake_end
;
85 /* Typical HTTP headers from novnc are 512 bytes, so limiting
86 * total header size to 4096 is easily enough. */
87 size_t want
= 4096 - vs
->ws_input
.offset
;
88 buffer_reserve(&vs
->ws_input
, want
);
89 ret
= vnc_client_read_buf(vs
, buffer_end(&vs
->ws_input
), want
);
92 if (vs
->csock
== -1) {
93 vnc_disconnect_finish(vs
);
97 vs
->ws_input
.offset
+= ret
;
99 handshake_end
= (uint8_t *)g_strstr_len((char *)vs
->ws_input
.buffer
,
100 vs
->ws_input
.offset
, WS_HANDSHAKE_END
);
102 qemu_set_fd_handler(vs
->csock
, vnc_client_read
, NULL
, vs
);
103 vncws_process_handshake(vs
, vs
->ws_input
.buffer
, vs
->ws_input
.offset
);
104 buffer_advance(&vs
->ws_input
, handshake_end
- vs
->ws_input
.buffer
+
105 strlen(WS_HANDSHAKE_END
));
106 } else if (vs
->ws_input
.offset
>= 4096) {
107 VNC_DEBUG("End of headers not found in first 4096 bytes\n");
108 vnc_client_error(vs
);
113 long vnc_client_read_ws(VncState
*vs
)
117 size_t payload_size
, header_size
;
118 VNC_DEBUG("Read websocket %p size %zd offset %zd\n", vs
->ws_input
.buffer
,
119 vs
->ws_input
.capacity
, vs
->ws_input
.offset
);
120 buffer_reserve(&vs
->ws_input
, 4096);
121 ret
= vnc_client_read_buf(vs
, buffer_end(&vs
->ws_input
), 4096);
125 vs
->ws_input
.offset
+= ret
;
128 /* consume as much of ws_input buffer as possible */
130 if (vs
->ws_payload_remain
== 0) {
131 err
= vncws_decode_frame_header(&vs
->ws_input
,
133 &vs
->ws_payload_remain
,
134 &vs
->ws_payload_mask
);
139 buffer_advance(&vs
->ws_input
, header_size
);
141 if (vs
->ws_payload_remain
!= 0) {
142 err
= vncws_decode_frame_payload(&vs
->ws_input
,
143 &vs
->ws_payload_remain
,
144 &vs
->ws_payload_mask
,
155 buffer_reserve(&vs
->input
, payload_size
);
156 buffer_append(&vs
->input
, payload
, payload_size
);
158 buffer_advance(&vs
->ws_input
, payload_size
);
160 } while (vs
->ws_input
.offset
> 0);
165 long vnc_client_write_ws(VncState
*vs
)
168 VNC_DEBUG("Write WS: Pending output %p size %zd offset %zd\n",
169 vs
->output
.buffer
, vs
->output
.capacity
, vs
->output
.offset
);
170 vncws_encode_frame(&vs
->ws_output
, vs
->output
.buffer
, vs
->output
.offset
);
171 buffer_reset(&vs
->output
);
172 ret
= vnc_client_write_buf(vs
, vs
->ws_output
.buffer
, vs
->ws_output
.offset
);
177 buffer_advance(&vs
->ws_output
, ret
);
179 if (vs
->ws_output
.offset
== 0) {
180 qemu_set_fd_handler(vs
->csock
, vnc_client_read
, NULL
, vs
);
186 static char *vncws_extract_handshake_entry(const char *handshake
,
187 size_t handshake_len
, const char *name
)
189 char *begin
, *end
, *ret
= NULL
;
190 char *line
= g_strdup_printf("%s%s: ", WS_HANDSHAKE_DELIM
, name
);
191 begin
= g_strstr_len(handshake
, handshake_len
, line
);
193 begin
+= strlen(line
);
194 end
= g_strstr_len(begin
, handshake_len
- (begin
- handshake
),
197 ret
= g_strndup(begin
, end
- begin
);
204 static void vncws_send_handshake_response(VncState
*vs
, const char* key
)
206 char combined_key
[WS_CLIENT_KEY_LEN
+ WS_GUID_LEN
+ 1];
207 char *accept
= NULL
, *response
= NULL
;
210 g_strlcpy(combined_key
, key
, WS_CLIENT_KEY_LEN
+ 1);
211 g_strlcat(combined_key
, WS_GUID
, WS_CLIENT_KEY_LEN
+ WS_GUID_LEN
+ 1);
213 /* hash and encode it */
214 if (qcrypto_hash_base64(QCRYPTO_HASH_ALG_SHA1
,
216 WS_CLIENT_KEY_LEN
+ WS_GUID_LEN
,
219 VNC_DEBUG("Hashing Websocket combined key failed %s\n",
220 error_get_pretty(err
));
222 vnc_client_error(vs
);
226 response
= g_strdup_printf(WS_HANDSHAKE
, accept
);
227 vnc_client_write_buf(vs
, (const uint8_t *)response
, strlen(response
));
236 void vncws_process_handshake(VncState
*vs
, uint8_t *line
, size_t size
)
238 char *protocols
= vncws_extract_handshake_entry((const char *)line
, size
,
239 "Sec-WebSocket-Protocol");
240 char *version
= vncws_extract_handshake_entry((const char *)line
, size
,
241 "Sec-WebSocket-Version");
242 char *key
= vncws_extract_handshake_entry((const char *)line
, size
,
243 "Sec-WebSocket-Key");
245 if (protocols
&& version
&& key
246 && g_strrstr(protocols
, "binary")
247 && !strcmp(version
, WS_SUPPORTED_VERSION
)
248 && strlen(key
) == WS_CLIENT_KEY_LEN
) {
249 vncws_send_handshake_response(vs
, key
);
251 VNC_DEBUG("Defective Websockets header or unsupported protocol\n");
252 vnc_client_error(vs
);
260 void vncws_encode_frame(Buffer
*output
, const void *payload
,
261 const size_t payload_size
)
263 size_t header_size
= 0;
264 unsigned char opcode
= WS_OPCODE_BINARY_FRAME
;
266 char buf
[WS_HEAD_MAX_LEN
];
274 header
.ws
.b0
= 0x80 | (opcode
& 0x0f);
275 if (payload_size
<= 125) {
276 header
.ws
.b1
= (uint8_t)payload_size
;
278 } else if (payload_size
< 65536) {
280 header
.ws
.u
.s16
.l16
= cpu_to_be16((uint16_t)payload_size
);
284 header
.ws
.u
.s64
.l64
= cpu_to_be64(payload_size
);
288 buffer_reserve(output
, header_size
+ payload_size
);
289 buffer_append(output
, header
.buf
, header_size
);
290 buffer_append(output
, payload
, payload_size
);
293 int vncws_decode_frame_header(Buffer
*input
,
295 size_t *payload_remain
,
296 WsMask
*payload_mask
)
298 unsigned char opcode
= 0, fin
= 0, has_mask
= 0;
300 WsHeader
*header
= (WsHeader
*)input
->buffer
;
302 if (input
->offset
< WS_HEAD_MIN_LEN
+ 4) {
303 /* header not complete */
307 fin
= (header
->b0
& 0x80) >> 7;
308 opcode
= header
->b0
& 0x0f;
309 has_mask
= (header
->b1
& 0x80) >> 7;
310 payload_len
= header
->b1
& 0x7f;
312 if (opcode
== WS_OPCODE_CLOSE
) {
317 /* Websocket frame sanity check:
318 * * Websocket fragmentation is not supported.
319 * * All websockets frames sent by a client have to be masked.
320 * * Only binary encoding is supported.
322 if (!fin
|| !has_mask
|| opcode
!= WS_OPCODE_BINARY_FRAME
) {
323 VNC_DEBUG("Received faulty/unsupported Websocket frame\n");
327 if (payload_len
< 126) {
328 *payload_remain
= payload_len
;
330 *payload_mask
= header
->u
.m
;
331 } else if (payload_len
== 126 && input
->offset
>= 8) {
332 *payload_remain
= be16_to_cpu(header
->u
.s16
.l16
);
334 *payload_mask
= header
->u
.s16
.m16
;
335 } else if (payload_len
== 127 && input
->offset
>= 14) {
336 *payload_remain
= be64_to_cpu(header
->u
.s64
.l64
);
338 *payload_mask
= header
->u
.s64
.m64
;
340 /* header not complete */
347 int vncws_decode_frame_payload(Buffer
*input
,
348 size_t *payload_remain
, WsMask
*payload_mask
,
349 uint8_t **payload
, size_t *payload_size
)
354 *payload
= input
->buffer
;
355 /* If we aren't at the end of the payload, then drop
356 * off the last bytes, so we're always multiple of 4
357 * for purpose of unmasking, except at end of payload
359 if (input
->offset
< *payload_remain
) {
360 *payload_size
= input
->offset
- (input
->offset
% 4);
362 *payload_size
= *payload_remain
;
364 if (*payload_size
== 0) {
367 *payload_remain
-= *payload_size
;
370 /* process 1 frame (32 bit op) */
371 payload32
= (uint32_t *)(*payload
);
372 for (i
= 0; i
< *payload_size
/ 4; i
++) {
373 payload32
[i
] ^= payload_mask
->u
;
375 /* process the remaining bytes (if any) */
376 for (i
*= 4; i
< *payload_size
; i
++) {
377 (*payload
)[i
] ^= payload_mask
->c
[i
% 4];