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"
25 #include "qemu/sockets.h"
27 static int vncws_start_tls_handshake(VncState
*vs
)
29 int ret
= gnutls_handshake(vs
->tls
.session
);
32 if (!gnutls_error_is_fatal(ret
)) {
33 VNC_DEBUG("Handshake interrupted (blocking)\n");
34 if (!gnutls_record_get_direction(vs
->tls
.session
)) {
35 qemu_set_fd_handler(vs
->csock
, vncws_tls_handshake_io
,
38 qemu_set_fd_handler(vs
->csock
, NULL
, vncws_tls_handshake_io
,
43 VNC_DEBUG("Handshake failed %s\n", gnutls_strerror(ret
));
48 if (vs
->vd
->tls
.x509verify
) {
49 if (vnc_tls_validate_certificate(vs
) < 0) {
50 VNC_DEBUG("Client verification failed\n");
54 VNC_DEBUG("Client verification passed\n");
58 VNC_DEBUG("Handshake done, switching to TLS data mode\n");
59 qemu_set_fd_handler2(vs
->csock
, NULL
, vncws_handshake_read
, NULL
, vs
);
64 void vncws_tls_handshake_io(void *opaque
)
66 VncState
*vs
= (VncState
*)opaque
;
68 if (!vs
->tls
.session
) {
69 VNC_DEBUG("TLS Websocket setup\n");
70 if (vnc_tls_client_setup(vs
, vs
->vd
->tls
.x509cert
!= NULL
) < 0) {
74 VNC_DEBUG("Handshake IO continue\n");
75 vncws_start_tls_handshake(vs
);
77 #endif /* CONFIG_VNC_TLS */
79 void vncws_handshake_read(void *opaque
)
81 VncState
*vs
= opaque
;
82 uint8_t *handshake_end
;
84 /* Typical HTTP headers from novnc are 512 bytes, so limiting
85 * total header size to 4096 is easily enough. */
86 size_t want
= 4096 - vs
->ws_input
.offset
;
87 buffer_reserve(&vs
->ws_input
, want
);
88 ret
= vnc_client_read_buf(vs
, buffer_end(&vs
->ws_input
), want
);
91 if (vs
->csock
== -1) {
92 vnc_disconnect_finish(vs
);
96 vs
->ws_input
.offset
+= ret
;
98 handshake_end
= (uint8_t *)g_strstr_len((char *)vs
->ws_input
.buffer
,
99 vs
->ws_input
.offset
, WS_HANDSHAKE_END
);
101 qemu_set_fd_handler2(vs
->csock
, NULL
, vnc_client_read
, NULL
, vs
);
102 vncws_process_handshake(vs
, vs
->ws_input
.buffer
, vs
->ws_input
.offset
);
103 buffer_advance(&vs
->ws_input
, handshake_end
- vs
->ws_input
.buffer
+
104 strlen(WS_HANDSHAKE_END
));
105 } else if (vs
->ws_input
.offset
>= 4096) {
106 VNC_DEBUG("End of headers not found in first 4096 bytes\n");
107 vnc_client_error(vs
);
112 long vnc_client_read_ws(VncState
*vs
)
116 size_t payload_size
, header_size
;
117 VNC_DEBUG("Read websocket %p size %zd offset %zd\n", vs
->ws_input
.buffer
,
118 vs
->ws_input
.capacity
, vs
->ws_input
.offset
);
119 buffer_reserve(&vs
->ws_input
, 4096);
120 ret
= vnc_client_read_buf(vs
, buffer_end(&vs
->ws_input
), 4096);
124 vs
->ws_input
.offset
+= ret
;
127 /* consume as much of ws_input buffer as possible */
129 if (vs
->ws_payload_remain
== 0) {
130 err
= vncws_decode_frame_header(&vs
->ws_input
,
132 &vs
->ws_payload_remain
,
133 &vs
->ws_payload_mask
);
138 buffer_advance(&vs
->ws_input
, header_size
);
140 if (vs
->ws_payload_remain
!= 0) {
141 err
= vncws_decode_frame_payload(&vs
->ws_input
,
142 &vs
->ws_payload_remain
,
143 &vs
->ws_payload_mask
,
154 buffer_reserve(&vs
->input
, payload_size
);
155 buffer_append(&vs
->input
, payload
, payload_size
);
157 buffer_advance(&vs
->ws_input
, payload_size
);
159 } while (vs
->ws_input
.offset
> 0);
164 long vnc_client_write_ws(VncState
*vs
)
167 VNC_DEBUG("Write WS: Pending output %p size %zd offset %zd\n",
168 vs
->output
.buffer
, vs
->output
.capacity
, vs
->output
.offset
);
169 vncws_encode_frame(&vs
->ws_output
, vs
->output
.buffer
, vs
->output
.offset
);
170 buffer_reset(&vs
->output
);
171 ret
= vnc_client_write_buf(vs
, vs
->ws_output
.buffer
, vs
->ws_output
.offset
);
176 buffer_advance(&vs
->ws_output
, ret
);
178 if (vs
->ws_output
.offset
== 0) {
179 qemu_set_fd_handler2(vs
->csock
, NULL
, vnc_client_read
, NULL
, vs
);
185 static char *vncws_extract_handshake_entry(const char *handshake
,
186 size_t handshake_len
, const char *name
)
188 char *begin
, *end
, *ret
= NULL
;
189 char *line
= g_strdup_printf("%s%s: ", WS_HANDSHAKE_DELIM
, name
);
190 begin
= g_strstr_len(handshake
, handshake_len
, line
);
192 begin
+= strlen(line
);
193 end
= g_strstr_len(begin
, handshake_len
- (begin
- handshake
),
196 ret
= g_strndup(begin
, end
- begin
);
203 static void vncws_send_handshake_response(VncState
*vs
, const char* key
)
205 char combined_key
[WS_CLIENT_KEY_LEN
+ WS_GUID_LEN
+ 1];
206 unsigned char hash
[SHA1_DIGEST_LEN
];
207 size_t hash_size
= sizeof(hash
);
208 char *accept
= NULL
, *response
= NULL
;
212 g_strlcpy(combined_key
, key
, WS_CLIENT_KEY_LEN
+ 1);
213 g_strlcat(combined_key
, WS_GUID
, WS_CLIENT_KEY_LEN
+ WS_GUID_LEN
+ 1);
215 /* hash and encode it */
216 in
.data
= (void *)combined_key
;
217 in
.size
= WS_CLIENT_KEY_LEN
+ WS_GUID_LEN
;
218 ret
= gnutls_fingerprint(GNUTLS_DIG_SHA1
, &in
, hash
, &hash_size
);
219 if (ret
== GNUTLS_E_SUCCESS
&& hash_size
<= SHA1_DIGEST_LEN
) {
220 accept
= g_base64_encode(hash
, hash_size
);
222 if (accept
== NULL
) {
223 VNC_DEBUG("Hashing Websocket combined key failed\n");
224 vnc_client_error(vs
);
228 response
= g_strdup_printf(WS_HANDSHAKE
, accept
);
229 vnc_client_write_buf(vs
, (const uint8_t *)response
, strlen(response
));
238 void vncws_process_handshake(VncState
*vs
, uint8_t *line
, size_t size
)
240 char *protocols
= vncws_extract_handshake_entry((const char *)line
, size
,
241 "Sec-WebSocket-Protocol");
242 char *version
= vncws_extract_handshake_entry((const char *)line
, size
,
243 "Sec-WebSocket-Version");
244 char *key
= vncws_extract_handshake_entry((const char *)line
, size
,
245 "Sec-WebSocket-Key");
247 if (protocols
&& version
&& key
248 && g_strrstr(protocols
, "binary")
249 && !strcmp(version
, WS_SUPPORTED_VERSION
)
250 && strlen(key
) == WS_CLIENT_KEY_LEN
) {
251 vncws_send_handshake_response(vs
, key
);
253 VNC_DEBUG("Defective Websockets header or unsupported protocol\n");
254 vnc_client_error(vs
);
262 void vncws_encode_frame(Buffer
*output
, const void *payload
,
263 const size_t payload_size
)
265 size_t header_size
= 0;
266 unsigned char opcode
= WS_OPCODE_BINARY_FRAME
;
268 char buf
[WS_HEAD_MAX_LEN
];
276 header
.ws
.b0
= 0x80 | (opcode
& 0x0f);
277 if (payload_size
<= 125) {
278 header
.ws
.b1
= (uint8_t)payload_size
;
280 } else if (payload_size
< 65536) {
282 header
.ws
.u
.s16
.l16
= cpu_to_be16((uint16_t)payload_size
);
286 header
.ws
.u
.s64
.l64
= cpu_to_be64(payload_size
);
290 buffer_reserve(output
, header_size
+ payload_size
);
291 buffer_append(output
, header
.buf
, header_size
);
292 buffer_append(output
, payload
, payload_size
);
295 int vncws_decode_frame_header(Buffer
*input
,
297 size_t *payload_remain
,
298 WsMask
*payload_mask
)
300 unsigned char opcode
= 0, fin
= 0, has_mask
= 0;
302 WsHeader
*header
= (WsHeader
*)input
->buffer
;
304 if (input
->offset
< WS_HEAD_MIN_LEN
+ 4) {
305 /* header not complete */
309 fin
= (header
->b0
& 0x80) >> 7;
310 opcode
= header
->b0
& 0x0f;
311 has_mask
= (header
->b1
& 0x80) >> 7;
312 payload_len
= header
->b1
& 0x7f;
314 if (opcode
== WS_OPCODE_CLOSE
) {
319 /* Websocket frame sanity check:
320 * * Websocket fragmentation is not supported.
321 * * All websockets frames sent by a client have to be masked.
322 * * Only binary encoding is supported.
324 if (!fin
|| !has_mask
|| opcode
!= WS_OPCODE_BINARY_FRAME
) {
325 VNC_DEBUG("Received faulty/unsupported Websocket frame\n");
329 if (payload_len
< 126) {
330 *payload_remain
= payload_len
;
332 *payload_mask
= header
->u
.m
;
333 } else if (payload_len
== 126 && input
->offset
>= 8) {
334 *payload_remain
= be16_to_cpu(header
->u
.s16
.l16
);
336 *payload_mask
= header
->u
.s16
.m16
;
337 } else if (payload_len
== 127 && input
->offset
>= 14) {
338 *payload_remain
= be64_to_cpu(header
->u
.s64
.l64
);
340 *payload_mask
= header
->u
.s64
.m64
;
342 /* header not complete */
349 int vncws_decode_frame_payload(Buffer
*input
,
350 size_t *payload_remain
, WsMask
*payload_mask
,
351 uint8_t **payload
, size_t *payload_size
)
356 *payload
= input
->buffer
;
357 /* If we aren't at the end of the payload, then drop
358 * off the last bytes, so we're always multiple of 4
359 * for purpose of unmasking, except at end of payload
361 if (input
->offset
< *payload_remain
) {
362 *payload_size
= input
->offset
- (input
->offset
% 4);
364 *payload_size
= *payload_remain
;
366 if (*payload_size
== 0) {
369 *payload_remain
-= *payload_size
;
372 /* process 1 frame (32 bit op) */
373 payload32
= (uint32_t *)(*payload
);
374 for (i
= 0; i
< *payload_size
/ 4; i
++) {
375 payload32
[i
] ^= payload_mask
->u
;
377 /* process the remaining bytes (if any) */
378 for (i
*= 4; i
< *payload_size
; i
++) {
379 (*payload
)[i
] ^= payload_mask
->c
[i
% 4];