root / ui / vnc-ws.c @ a8aec295
History | View | Annotate | Download (10.2 kB)
1 | 7536ee4b | Tim Hardeck | /*
|
---|---|---|---|
2 | 7536ee4b | Tim Hardeck | * QEMU VNC display driver: Websockets support
|
3 | 7536ee4b | Tim Hardeck | *
|
4 | 7536ee4b | Tim Hardeck | * Copyright (C) 2010 Joel Martin
|
5 | 7536ee4b | Tim Hardeck | * Copyright (C) 2012 Tim Hardeck
|
6 | 7536ee4b | Tim Hardeck | *
|
7 | 7536ee4b | Tim Hardeck | * This is free software; you can redistribute it and/or modify
|
8 | 7536ee4b | Tim Hardeck | * it under the terms of the GNU General Public License as published by
|
9 | 7536ee4b | Tim Hardeck | * the Free Software Foundation; either version 2 of the License, or
|
10 | 7536ee4b | Tim Hardeck | * (at your option) any later version.
|
11 | 7536ee4b | Tim Hardeck | *
|
12 | 7536ee4b | Tim Hardeck | * This software is distributed in the hope that it will be useful,
|
13 | 7536ee4b | Tim Hardeck | * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14 | 7536ee4b | Tim Hardeck | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
15 | 7536ee4b | Tim Hardeck | * GNU General Public License for more details.
|
16 | 7536ee4b | Tim Hardeck | *
|
17 | 7536ee4b | Tim Hardeck | * You should have received a copy of the GNU General Public License
|
18 | 7536ee4b | Tim Hardeck | * along with this software; if not, see <http://www.gnu.org/licenses/>.
|
19 | 7536ee4b | Tim Hardeck | */
|
20 | 7536ee4b | Tim Hardeck | |
21 | 7536ee4b | Tim Hardeck | #include "vnc.h" |
22 | 7536ee4b | Tim Hardeck | |
23 | 0057a0d5 | Tim Hardeck | #ifdef CONFIG_VNC_TLS
|
24 | 0057a0d5 | Tim Hardeck | #include "qemu/sockets.h" |
25 | 0057a0d5 | Tim Hardeck | |
26 | 0057a0d5 | Tim Hardeck | static void vncws_tls_handshake_io(void *opaque); |
27 | 0057a0d5 | Tim Hardeck | |
28 | 0057a0d5 | Tim Hardeck | static int vncws_start_tls_handshake(struct VncState *vs) |
29 | 0057a0d5 | Tim Hardeck | { |
30 | 0057a0d5 | Tim Hardeck | int ret = gnutls_handshake(vs->ws_tls.session);
|
31 | 0057a0d5 | Tim Hardeck | |
32 | 0057a0d5 | Tim Hardeck | if (ret < 0) { |
33 | 0057a0d5 | Tim Hardeck | if (!gnutls_error_is_fatal(ret)) {
|
34 | 0057a0d5 | Tim Hardeck | VNC_DEBUG("Handshake interrupted (blocking)\n");
|
35 | 0057a0d5 | Tim Hardeck | if (!gnutls_record_get_direction(vs->ws_tls.session)) {
|
36 | 0057a0d5 | Tim Hardeck | qemu_set_fd_handler(vs->csock, vncws_tls_handshake_io, |
37 | 0057a0d5 | Tim Hardeck | NULL, vs);
|
38 | 0057a0d5 | Tim Hardeck | } else {
|
39 | 0057a0d5 | Tim Hardeck | qemu_set_fd_handler(vs->csock, NULL, vncws_tls_handshake_io,
|
40 | 0057a0d5 | Tim Hardeck | vs); |
41 | 0057a0d5 | Tim Hardeck | } |
42 | 0057a0d5 | Tim Hardeck | return 0; |
43 | 0057a0d5 | Tim Hardeck | } |
44 | 0057a0d5 | Tim Hardeck | VNC_DEBUG("Handshake failed %s\n", gnutls_strerror(ret));
|
45 | 0057a0d5 | Tim Hardeck | vnc_client_error(vs); |
46 | 0057a0d5 | Tim Hardeck | return -1; |
47 | 0057a0d5 | Tim Hardeck | } |
48 | 0057a0d5 | Tim Hardeck | |
49 | 0057a0d5 | Tim Hardeck | VNC_DEBUG("Handshake done, switching to TLS data mode\n");
|
50 | 0057a0d5 | Tim Hardeck | vs->ws_tls.wiremode = VNC_WIREMODE_TLS; |
51 | 0057a0d5 | Tim Hardeck | qemu_set_fd_handler2(vs->csock, NULL, vncws_handshake_read, NULL, vs); |
52 | 0057a0d5 | Tim Hardeck | |
53 | 0057a0d5 | Tim Hardeck | return 0; |
54 | 0057a0d5 | Tim Hardeck | } |
55 | 0057a0d5 | Tim Hardeck | |
56 | 0057a0d5 | Tim Hardeck | static void vncws_tls_handshake_io(void *opaque) |
57 | 0057a0d5 | Tim Hardeck | { |
58 | 0057a0d5 | Tim Hardeck | struct VncState *vs = (struct VncState *)opaque; |
59 | 0057a0d5 | Tim Hardeck | |
60 | 0057a0d5 | Tim Hardeck | VNC_DEBUG("Handshake IO continue\n");
|
61 | 0057a0d5 | Tim Hardeck | vncws_start_tls_handshake(vs); |
62 | 0057a0d5 | Tim Hardeck | } |
63 | 0057a0d5 | Tim Hardeck | |
64 | 0057a0d5 | Tim Hardeck | void vncws_tls_handshake_peek(void *opaque) |
65 | 0057a0d5 | Tim Hardeck | { |
66 | 0057a0d5 | Tim Hardeck | VncState *vs = opaque; |
67 | 0057a0d5 | Tim Hardeck | long ret;
|
68 | 0057a0d5 | Tim Hardeck | |
69 | 0057a0d5 | Tim Hardeck | if (!vs->ws_tls.session) {
|
70 | 0057a0d5 | Tim Hardeck | char peek[4]; |
71 | 0057a0d5 | Tim Hardeck | ret = qemu_recv(vs->csock, peek, sizeof(peek), MSG_PEEK);
|
72 | 0057a0d5 | Tim Hardeck | if (ret && (strncmp(peek, "\x16", 1) == 0 |
73 | 0057a0d5 | Tim Hardeck | || strncmp(peek, "\x80", 1) == 0)) { |
74 | 0057a0d5 | Tim Hardeck | VNC_DEBUG("TLS Websocket connection recognized");
|
75 | 0057a0d5 | Tim Hardeck | vnc_tls_client_setup(vs, 1);
|
76 | 0057a0d5 | Tim Hardeck | vncws_start_tls_handshake(vs); |
77 | 0057a0d5 | Tim Hardeck | } else {
|
78 | 0057a0d5 | Tim Hardeck | vncws_handshake_read(vs); |
79 | 0057a0d5 | Tim Hardeck | } |
80 | 0057a0d5 | Tim Hardeck | } else {
|
81 | 0057a0d5 | Tim Hardeck | qemu_set_fd_handler2(vs->csock, NULL, vncws_handshake_read, NULL, vs); |
82 | 0057a0d5 | Tim Hardeck | } |
83 | 0057a0d5 | Tim Hardeck | } |
84 | 0057a0d5 | Tim Hardeck | #endif /* CONFIG_VNC_TLS */ |
85 | 0057a0d5 | Tim Hardeck | |
86 | 7536ee4b | Tim Hardeck | void vncws_handshake_read(void *opaque) |
87 | 7536ee4b | Tim Hardeck | { |
88 | 7536ee4b | Tim Hardeck | VncState *vs = opaque; |
89 | 7536ee4b | Tim Hardeck | uint8_t *handshake_end; |
90 | 7536ee4b | Tim Hardeck | long ret;
|
91 | 7536ee4b | Tim Hardeck | buffer_reserve(&vs->ws_input, 4096);
|
92 | 7536ee4b | Tim Hardeck | ret = vnc_client_read_buf(vs, buffer_end(&vs->ws_input), 4096);
|
93 | 7536ee4b | Tim Hardeck | |
94 | 7536ee4b | Tim Hardeck | if (!ret) {
|
95 | 7536ee4b | Tim Hardeck | if (vs->csock == -1) { |
96 | 7536ee4b | Tim Hardeck | vnc_disconnect_finish(vs); |
97 | 7536ee4b | Tim Hardeck | } |
98 | 7536ee4b | Tim Hardeck | return;
|
99 | 7536ee4b | Tim Hardeck | } |
100 | 7536ee4b | Tim Hardeck | vs->ws_input.offset += ret; |
101 | 7536ee4b | Tim Hardeck | |
102 | 7536ee4b | Tim Hardeck | handshake_end = (uint8_t *)g_strstr_len((char *)vs->ws_input.buffer,
|
103 | 7536ee4b | Tim Hardeck | vs->ws_input.offset, WS_HANDSHAKE_END); |
104 | 7536ee4b | Tim Hardeck | if (handshake_end) {
|
105 | 7536ee4b | Tim Hardeck | qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, vs); |
106 | 7536ee4b | Tim Hardeck | vncws_process_handshake(vs, vs->ws_input.buffer, vs->ws_input.offset); |
107 | 7536ee4b | Tim Hardeck | buffer_advance(&vs->ws_input, handshake_end - vs->ws_input.buffer + |
108 | 7536ee4b | Tim Hardeck | strlen(WS_HANDSHAKE_END)); |
109 | 7536ee4b | Tim Hardeck | } |
110 | 7536ee4b | Tim Hardeck | } |
111 | 7536ee4b | Tim Hardeck | |
112 | 7536ee4b | Tim Hardeck | |
113 | 7536ee4b | Tim Hardeck | long vnc_client_read_ws(VncState *vs)
|
114 | 7536ee4b | Tim Hardeck | { |
115 | 7536ee4b | Tim Hardeck | int ret, err;
|
116 | 7536ee4b | Tim Hardeck | uint8_t *payload; |
117 | 7536ee4b | Tim Hardeck | size_t payload_size, frame_size; |
118 | 7536ee4b | Tim Hardeck | VNC_DEBUG("Read websocket %p size %zd offset %zd\n", vs->ws_input.buffer,
|
119 | 7536ee4b | Tim Hardeck | vs->ws_input.capacity, vs->ws_input.offset); |
120 | 7536ee4b | Tim Hardeck | buffer_reserve(&vs->ws_input, 4096);
|
121 | 7536ee4b | Tim Hardeck | ret = vnc_client_read_buf(vs, buffer_end(&vs->ws_input), 4096);
|
122 | 7536ee4b | Tim Hardeck | if (!ret) {
|
123 | 7536ee4b | Tim Hardeck | return 0; |
124 | 7536ee4b | Tim Hardeck | } |
125 | 7536ee4b | Tim Hardeck | vs->ws_input.offset += ret; |
126 | 7536ee4b | Tim Hardeck | |
127 | 7536ee4b | Tim Hardeck | /* make sure that nothing is left in the ws_input buffer */
|
128 | 7536ee4b | Tim Hardeck | do {
|
129 | 7536ee4b | Tim Hardeck | err = vncws_decode_frame(&vs->ws_input, &payload, |
130 | 7536ee4b | Tim Hardeck | &payload_size, &frame_size); |
131 | 7536ee4b | Tim Hardeck | if (err <= 0) { |
132 | 7536ee4b | Tim Hardeck | return err;
|
133 | 7536ee4b | Tim Hardeck | } |
134 | 7536ee4b | Tim Hardeck | |
135 | 7536ee4b | Tim Hardeck | buffer_reserve(&vs->input, payload_size); |
136 | 7536ee4b | Tim Hardeck | buffer_append(&vs->input, payload, payload_size); |
137 | 7536ee4b | Tim Hardeck | |
138 | 7536ee4b | Tim Hardeck | buffer_advance(&vs->ws_input, frame_size); |
139 | 7536ee4b | Tim Hardeck | } while (vs->ws_input.offset > 0); |
140 | 7536ee4b | Tim Hardeck | |
141 | 7536ee4b | Tim Hardeck | return ret;
|
142 | 7536ee4b | Tim Hardeck | } |
143 | 7536ee4b | Tim Hardeck | |
144 | 7536ee4b | Tim Hardeck | long vnc_client_write_ws(VncState *vs)
|
145 | 7536ee4b | Tim Hardeck | { |
146 | 7536ee4b | Tim Hardeck | long ret;
|
147 | 7536ee4b | Tim Hardeck | VNC_DEBUG("Write WS: Pending output %p size %zd offset %zd\n",
|
148 | 7536ee4b | Tim Hardeck | vs->output.buffer, vs->output.capacity, vs->output.offset); |
149 | 7536ee4b | Tim Hardeck | vncws_encode_frame(&vs->ws_output, vs->output.buffer, vs->output.offset); |
150 | 7536ee4b | Tim Hardeck | buffer_reset(&vs->output); |
151 | 7536ee4b | Tim Hardeck | ret = vnc_client_write_buf(vs, vs->ws_output.buffer, vs->ws_output.offset); |
152 | 7536ee4b | Tim Hardeck | if (!ret) {
|
153 | 7536ee4b | Tim Hardeck | return 0; |
154 | 7536ee4b | Tim Hardeck | } |
155 | 7536ee4b | Tim Hardeck | |
156 | 7536ee4b | Tim Hardeck | buffer_advance(&vs->ws_output, ret); |
157 | 7536ee4b | Tim Hardeck | |
158 | 7536ee4b | Tim Hardeck | if (vs->ws_output.offset == 0) { |
159 | 7536ee4b | Tim Hardeck | qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, vs); |
160 | 7536ee4b | Tim Hardeck | } |
161 | 7536ee4b | Tim Hardeck | |
162 | 7536ee4b | Tim Hardeck | return ret;
|
163 | 7536ee4b | Tim Hardeck | } |
164 | 7536ee4b | Tim Hardeck | |
165 | 7536ee4b | Tim Hardeck | static char *vncws_extract_handshake_entry(const char *handshake, |
166 | 7536ee4b | Tim Hardeck | size_t handshake_len, const char *name) |
167 | 7536ee4b | Tim Hardeck | { |
168 | 7536ee4b | Tim Hardeck | char *begin, *end, *ret = NULL; |
169 | 7536ee4b | Tim Hardeck | char *line = g_strdup_printf("%s%s: ", WS_HANDSHAKE_DELIM, name); |
170 | 7536ee4b | Tim Hardeck | begin = g_strstr_len(handshake, handshake_len, line); |
171 | 7536ee4b | Tim Hardeck | if (begin != NULL) { |
172 | 7536ee4b | Tim Hardeck | begin += strlen(line); |
173 | 7536ee4b | Tim Hardeck | end = g_strstr_len(begin, handshake_len - (begin - handshake), |
174 | 7536ee4b | Tim Hardeck | WS_HANDSHAKE_DELIM); |
175 | 7536ee4b | Tim Hardeck | if (end != NULL) { |
176 | 7536ee4b | Tim Hardeck | ret = g_strndup(begin, end - begin); |
177 | 7536ee4b | Tim Hardeck | } |
178 | 7536ee4b | Tim Hardeck | } |
179 | 7536ee4b | Tim Hardeck | g_free(line); |
180 | 7536ee4b | Tim Hardeck | return ret;
|
181 | 7536ee4b | Tim Hardeck | } |
182 | 7536ee4b | Tim Hardeck | |
183 | 7536ee4b | Tim Hardeck | static void vncws_send_handshake_response(VncState *vs, const char* key) |
184 | 7536ee4b | Tim Hardeck | { |
185 | 7536ee4b | Tim Hardeck | char combined_key[WS_CLIENT_KEY_LEN + WS_GUID_LEN + 1]; |
186 | cfba8e6f | Markus Armbruster | unsigned char hash[SHA1_DIGEST_LEN]; |
187 | cfba8e6f | Markus Armbruster | size_t hash_size = sizeof(hash);
|
188 | 7536ee4b | Tim Hardeck | char *accept = NULL, *response = NULL; |
189 | 7536ee4b | Tim Hardeck | gnutls_datum_t in; |
190 | cfba8e6f | Markus Armbruster | int ret;
|
191 | 7536ee4b | Tim Hardeck | |
192 | 7536ee4b | Tim Hardeck | g_strlcpy(combined_key, key, WS_CLIENT_KEY_LEN + 1);
|
193 | 7536ee4b | Tim Hardeck | g_strlcat(combined_key, WS_GUID, WS_CLIENT_KEY_LEN + WS_GUID_LEN + 1);
|
194 | 7536ee4b | Tim Hardeck | |
195 | 7536ee4b | Tim Hardeck | /* hash and encode it */
|
196 | 7536ee4b | Tim Hardeck | in.data = (void *)combined_key;
|
197 | 7536ee4b | Tim Hardeck | in.size = WS_CLIENT_KEY_LEN + WS_GUID_LEN; |
198 | cfba8e6f | Markus Armbruster | ret = gnutls_fingerprint(GNUTLS_DIG_SHA1, &in, hash, &hash_size); |
199 | cfba8e6f | Markus Armbruster | if (ret == GNUTLS_E_SUCCESS && hash_size <= SHA1_DIGEST_LEN) {
|
200 | cfba8e6f | Markus Armbruster | accept = g_base64_encode(hash, hash_size); |
201 | 7536ee4b | Tim Hardeck | } |
202 | 7536ee4b | Tim Hardeck | if (accept == NULL) { |
203 | 7536ee4b | Tim Hardeck | VNC_DEBUG("Hashing Websocket combined key failed\n");
|
204 | 7536ee4b | Tim Hardeck | vnc_client_error(vs); |
205 | 7536ee4b | Tim Hardeck | return;
|
206 | 7536ee4b | Tim Hardeck | } |
207 | 7536ee4b | Tim Hardeck | |
208 | 7536ee4b | Tim Hardeck | response = g_strdup_printf(WS_HANDSHAKE, accept); |
209 | 7536ee4b | Tim Hardeck | vnc_write(vs, response, strlen(response)); |
210 | 7536ee4b | Tim Hardeck | vnc_flush(vs); |
211 | 7536ee4b | Tim Hardeck | |
212 | 7536ee4b | Tim Hardeck | g_free(accept); |
213 | 7536ee4b | Tim Hardeck | g_free(response); |
214 | 7536ee4b | Tim Hardeck | |
215 | 7536ee4b | Tim Hardeck | vs->encode_ws = 1;
|
216 | 7536ee4b | Tim Hardeck | vnc_init_state(vs); |
217 | 7536ee4b | Tim Hardeck | } |
218 | 7536ee4b | Tim Hardeck | |
219 | 7536ee4b | Tim Hardeck | void vncws_process_handshake(VncState *vs, uint8_t *line, size_t size)
|
220 | 7536ee4b | Tim Hardeck | { |
221 | 7536ee4b | Tim Hardeck | char *protocols = vncws_extract_handshake_entry((const char *)line, size, |
222 | 7536ee4b | Tim Hardeck | "Sec-WebSocket-Protocol");
|
223 | 7536ee4b | Tim Hardeck | char *version = vncws_extract_handshake_entry((const char *)line, size, |
224 | 7536ee4b | Tim Hardeck | "Sec-WebSocket-Version");
|
225 | 7536ee4b | Tim Hardeck | char *key = vncws_extract_handshake_entry((const char *)line, size, |
226 | 7536ee4b | Tim Hardeck | "Sec-WebSocket-Key");
|
227 | 7536ee4b | Tim Hardeck | |
228 | 7536ee4b | Tim Hardeck | if (protocols && version && key
|
229 | 7536ee4b | Tim Hardeck | && g_strrstr(protocols, "binary")
|
230 | 7536ee4b | Tim Hardeck | && !strcmp(version, WS_SUPPORTED_VERSION) |
231 | 7536ee4b | Tim Hardeck | && strlen(key) == WS_CLIENT_KEY_LEN) { |
232 | 7536ee4b | Tim Hardeck | vncws_send_handshake_response(vs, key); |
233 | 7536ee4b | Tim Hardeck | } else {
|
234 | 7536ee4b | Tim Hardeck | VNC_DEBUG("Defective Websockets header or unsupported protocol\n");
|
235 | 7536ee4b | Tim Hardeck | vnc_client_error(vs); |
236 | 7536ee4b | Tim Hardeck | } |
237 | 7536ee4b | Tim Hardeck | |
238 | 7536ee4b | Tim Hardeck | g_free(protocols); |
239 | 7536ee4b | Tim Hardeck | g_free(version); |
240 | 7536ee4b | Tim Hardeck | g_free(key); |
241 | 7536ee4b | Tim Hardeck | } |
242 | 7536ee4b | Tim Hardeck | |
243 | 7536ee4b | Tim Hardeck | void vncws_encode_frame(Buffer *output, const void *payload, |
244 | 7536ee4b | Tim Hardeck | const size_t payload_size)
|
245 | 7536ee4b | Tim Hardeck | { |
246 | 7536ee4b | Tim Hardeck | size_t header_size = 0;
|
247 | 7536ee4b | Tim Hardeck | unsigned char opcode = WS_OPCODE_BINARY_FRAME; |
248 | 7536ee4b | Tim Hardeck | union {
|
249 | 7536ee4b | Tim Hardeck | char buf[WS_HEAD_MAX_LEN];
|
250 | 7536ee4b | Tim Hardeck | WsHeader ws; |
251 | 7536ee4b | Tim Hardeck | } header; |
252 | 7536ee4b | Tim Hardeck | |
253 | 7536ee4b | Tim Hardeck | if (!payload_size) {
|
254 | 7536ee4b | Tim Hardeck | return;
|
255 | 7536ee4b | Tim Hardeck | } |
256 | 7536ee4b | Tim Hardeck | |
257 | 7536ee4b | Tim Hardeck | header.ws.b0 = 0x80 | (opcode & 0x0f); |
258 | 7536ee4b | Tim Hardeck | if (payload_size <= 125) { |
259 | 7536ee4b | Tim Hardeck | header.ws.b1 = (uint8_t)payload_size; |
260 | 7536ee4b | Tim Hardeck | header_size = 2;
|
261 | 7536ee4b | Tim Hardeck | } else if (payload_size < 65536) { |
262 | 7536ee4b | Tim Hardeck | header.ws.b1 = 0x7e;
|
263 | 7536ee4b | Tim Hardeck | header.ws.u.s16.l16 = cpu_to_be16((uint16_t)payload_size); |
264 | 7536ee4b | Tim Hardeck | header_size = 4;
|
265 | 7536ee4b | Tim Hardeck | } else {
|
266 | 7536ee4b | Tim Hardeck | header.ws.b1 = 0x7f;
|
267 | 7536ee4b | Tim Hardeck | header.ws.u.s64.l64 = cpu_to_be64(payload_size); |
268 | 7536ee4b | Tim Hardeck | header_size = 10;
|
269 | 7536ee4b | Tim Hardeck | } |
270 | 7536ee4b | Tim Hardeck | |
271 | 7536ee4b | Tim Hardeck | buffer_reserve(output, header_size + payload_size); |
272 | 7536ee4b | Tim Hardeck | buffer_append(output, header.buf, header_size); |
273 | 7536ee4b | Tim Hardeck | buffer_append(output, payload, payload_size); |
274 | 7536ee4b | Tim Hardeck | } |
275 | 7536ee4b | Tim Hardeck | |
276 | 7536ee4b | Tim Hardeck | int vncws_decode_frame(Buffer *input, uint8_t **payload,
|
277 | 7536ee4b | Tim Hardeck | size_t *payload_size, size_t *frame_size) |
278 | 7536ee4b | Tim Hardeck | { |
279 | 7536ee4b | Tim Hardeck | unsigned char opcode = 0, fin = 0, has_mask = 0; |
280 | 7536ee4b | Tim Hardeck | size_t header_size = 0;
|
281 | 7536ee4b | Tim Hardeck | uint32_t *payload32; |
282 | 7536ee4b | Tim Hardeck | WsHeader *header = (WsHeader *)input->buffer; |
283 | 7536ee4b | Tim Hardeck | WsMask mask; |
284 | 7536ee4b | Tim Hardeck | int i;
|
285 | 7536ee4b | Tim Hardeck | |
286 | 7536ee4b | Tim Hardeck | if (input->offset < WS_HEAD_MIN_LEN + 4) { |
287 | 7536ee4b | Tim Hardeck | /* header not complete */
|
288 | 7536ee4b | Tim Hardeck | return 0; |
289 | 7536ee4b | Tim Hardeck | } |
290 | 7536ee4b | Tim Hardeck | |
291 | 7536ee4b | Tim Hardeck | fin = (header->b0 & 0x80) >> 7; |
292 | 7536ee4b | Tim Hardeck | opcode = header->b0 & 0x0f;
|
293 | 7536ee4b | Tim Hardeck | has_mask = (header->b1 & 0x80) >> 7; |
294 | 7536ee4b | Tim Hardeck | *payload_size = header->b1 & 0x7f;
|
295 | 7536ee4b | Tim Hardeck | |
296 | 7536ee4b | Tim Hardeck | if (opcode == WS_OPCODE_CLOSE) {
|
297 | 7536ee4b | Tim Hardeck | /* disconnect */
|
298 | 7536ee4b | Tim Hardeck | return -1; |
299 | 7536ee4b | Tim Hardeck | } |
300 | 7536ee4b | Tim Hardeck | |
301 | 7536ee4b | Tim Hardeck | /* Websocket frame sanity check:
|
302 | 7536ee4b | Tim Hardeck | * * Websocket fragmentation is not supported.
|
303 | 7536ee4b | Tim Hardeck | * * All websockets frames sent by a client have to be masked.
|
304 | 7536ee4b | Tim Hardeck | * * Only binary encoding is supported.
|
305 | 7536ee4b | Tim Hardeck | */
|
306 | 7536ee4b | Tim Hardeck | if (!fin || !has_mask || opcode != WS_OPCODE_BINARY_FRAME) {
|
307 | 7536ee4b | Tim Hardeck | VNC_DEBUG("Received faulty/unsupported Websocket frame\n");
|
308 | 7536ee4b | Tim Hardeck | return -2; |
309 | 7536ee4b | Tim Hardeck | } |
310 | 7536ee4b | Tim Hardeck | |
311 | 7536ee4b | Tim Hardeck | if (*payload_size < 126) { |
312 | 7536ee4b | Tim Hardeck | header_size = 6;
|
313 | 7536ee4b | Tim Hardeck | mask = header->u.m; |
314 | 7536ee4b | Tim Hardeck | } else if (*payload_size == 126 && input->offset >= 8) { |
315 | 7536ee4b | Tim Hardeck | *payload_size = be16_to_cpu(header->u.s16.l16); |
316 | 7536ee4b | Tim Hardeck | header_size = 8;
|
317 | 7536ee4b | Tim Hardeck | mask = header->u.s16.m16; |
318 | 7536ee4b | Tim Hardeck | } else if (*payload_size == 127 && input->offset >= 14) { |
319 | 7536ee4b | Tim Hardeck | *payload_size = be64_to_cpu(header->u.s64.l64); |
320 | 7536ee4b | Tim Hardeck | header_size = 14;
|
321 | 7536ee4b | Tim Hardeck | mask = header->u.s64.m64; |
322 | 7536ee4b | Tim Hardeck | } else {
|
323 | 7536ee4b | Tim Hardeck | /* header not complete */
|
324 | 7536ee4b | Tim Hardeck | return 0; |
325 | 7536ee4b | Tim Hardeck | } |
326 | 7536ee4b | Tim Hardeck | |
327 | 7536ee4b | Tim Hardeck | *frame_size = header_size + *payload_size; |
328 | 7536ee4b | Tim Hardeck | |
329 | 7536ee4b | Tim Hardeck | if (input->offset < *frame_size) {
|
330 | 7536ee4b | Tim Hardeck | /* frame not complete */
|
331 | 7536ee4b | Tim Hardeck | return 0; |
332 | 7536ee4b | Tim Hardeck | } |
333 | 7536ee4b | Tim Hardeck | |
334 | 7536ee4b | Tim Hardeck | *payload = input->buffer + header_size; |
335 | 7536ee4b | Tim Hardeck | |
336 | 7536ee4b | Tim Hardeck | /* unmask frame */
|
337 | 7536ee4b | Tim Hardeck | /* process 1 frame (32 bit op) */
|
338 | 7536ee4b | Tim Hardeck | payload32 = (uint32_t *)(*payload); |
339 | 7536ee4b | Tim Hardeck | for (i = 0; i < *payload_size / 4; i++) { |
340 | 7536ee4b | Tim Hardeck | payload32[i] ^= mask.u; |
341 | 7536ee4b | Tim Hardeck | } |
342 | 7536ee4b | Tim Hardeck | /* process the remaining bytes (if any) */
|
343 | 7536ee4b | Tim Hardeck | for (i *= 4; i < *payload_size; i++) { |
344 | 7536ee4b | Tim Hardeck | (*payload)[i] ^= mask.c[i % 4];
|
345 | 7536ee4b | Tim Hardeck | } |
346 | 7536ee4b | Tim Hardeck | |
347 | 7536ee4b | Tim Hardeck | return 1; |
348 | 7536ee4b | Tim Hardeck | } |