/src/libvncserver/src/libvncserver/ws_decode.c
Line | Count | Source |
1 | | #include "ws_decode.h" |
2 | | #include "base64.h" |
3 | | |
4 | | #include <string.h> |
5 | | #include <errno.h> |
6 | | |
7 | 0 | #define WS_HYBI_MASK_LEN 4 |
8 | 0 | #define WS_HYBI_HEADER_LEN_SHORT 2 + WS_HYBI_MASK_LEN |
9 | 0 | #define WS_HYBI_HEADER_LEN_EXTENDED 4 + WS_HYBI_MASK_LEN |
10 | 0 | #define WS_HYBI_HEADER_LEN_LONG 10 + WS_HYBI_MASK_LEN |
11 | | |
12 | | #undef WS_DECODE_DEBUG |
13 | | /* set to 1 to produce very fine debugging output */ |
14 | | #define WS_DECODE_DEBUG 0 |
15 | | |
16 | | #if WS_DECODE_DEBUG == 1 |
17 | | #define ws_dbg(fmt, ...) rfbLog((fmt), ##__VA_ARGS) |
18 | | #else |
19 | | #define ws_dbg(fmt, ...) |
20 | | #endif |
21 | | |
22 | | |
23 | | static inline int |
24 | | isControlFrame(ws_ctx_t *wsctx) |
25 | 0 | { |
26 | 0 | return 0 != (wsctx->header.opcode & 0x08); |
27 | 0 | } |
28 | | |
29 | | static uint64_t |
30 | | hybiRemaining(ws_ctx_t *wsctx) |
31 | 0 | { |
32 | 0 | return wsctx->header.payloadLen - wsctx->nReadPayload; |
33 | 0 | } |
34 | | |
35 | | static void |
36 | | hybiDecodeCleanupBasics(ws_ctx_t *wsctx) |
37 | 0 | { |
38 | | /* keep opcode, cleanup rest */ |
39 | 0 | wsctx->header.opcode = WS_OPCODE_INVALID; |
40 | 0 | wsctx->header.payloadLen = 0; |
41 | 0 | wsctx->header.mask.u = 0; |
42 | 0 | wsctx->header.headerLen = 0; |
43 | 0 | wsctx->header.data = NULL; |
44 | 0 | wsctx->header.nRead = 0; |
45 | 0 | wsctx->nReadPayload = 0; |
46 | 0 | wsctx->carrylen = 0; |
47 | 0 | wsctx->readPos = (unsigned char *)wsctx->codeBufDecode; |
48 | 0 | wsctx->readlen = 0; |
49 | 0 | wsctx->hybiDecodeState = WS_HYBI_STATE_HEADER_PENDING; |
50 | 0 | wsctx->writePos = NULL; |
51 | 0 | } |
52 | | |
53 | | static void |
54 | | hybiDecodeCleanupForContinuation(ws_ctx_t *wsctx) |
55 | 0 | { |
56 | 0 | hybiDecodeCleanupBasics(wsctx); |
57 | 0 | ws_dbg("clean up frame, but expect continuation with opcode %d\n", wsctx->continuation_opcode); |
58 | 0 | } |
59 | | |
60 | | void |
61 | | hybiDecodeCleanupComplete(ws_ctx_t *wsctx) |
62 | 0 | { |
63 | 0 | hybiDecodeCleanupBasics(wsctx); |
64 | 0 | wsctx->continuation_opcode = WS_OPCODE_INVALID; |
65 | 0 | ws_dbg("cleaned up wsctx completely\n"); |
66 | 0 | } |
67 | | |
68 | | |
69 | | /** |
70 | | * Return payload data that has been decoded/unmasked from |
71 | | * a websocket frame. |
72 | | * |
73 | | * @param[out] dst destination buffer |
74 | | * @param[in] len bytes to copy to destination buffer |
75 | | * @param[in,out] wsctx internal state of decoding procedure |
76 | | * @param[out] number of bytes actually written to dst buffer |
77 | | * @return next hybi decoding state |
78 | | */ |
79 | | static int |
80 | | hybiReturnData(char *dst, int len, ws_ctx_t *wsctx, int *nWritten) |
81 | 0 | { |
82 | 0 | int nextState = WS_HYBI_STATE_ERR; |
83 | | |
84 | | /* if we have something already decoded copy and return */ |
85 | 0 | if (wsctx->readlen > 0) { |
86 | | /* simply return what we have */ |
87 | 0 | if (wsctx->readlen > len) { |
88 | 0 | ws_dbg("copy to %d bytes to dst buffer; readPos=%p, readLen=%d\n", len, wsctx->readPos, wsctx->readlen); |
89 | 0 | memcpy(dst, wsctx->readPos, len); |
90 | 0 | *nWritten = len; |
91 | 0 | wsctx->readlen -= len; |
92 | 0 | wsctx->readPos += len; |
93 | 0 | nextState = WS_HYBI_STATE_DATA_AVAILABLE; |
94 | 0 | } else { |
95 | 0 | ws_dbg("copy to %d bytes to dst buffer; readPos=%p, readLen=%d\n", wsctx->readlen, wsctx->readPos, wsctx->readlen); |
96 | 0 | memcpy(dst, wsctx->readPos, wsctx->readlen); |
97 | 0 | *nWritten = wsctx->readlen; |
98 | 0 | wsctx->readlen = 0; |
99 | 0 | wsctx->readPos = NULL; |
100 | 0 | if (hybiRemaining(wsctx) == 0) { |
101 | 0 | nextState = WS_HYBI_STATE_FRAME_COMPLETE; |
102 | 0 | } else { |
103 | 0 | nextState = WS_HYBI_STATE_DATA_NEEDED; |
104 | 0 | } |
105 | 0 | } |
106 | 0 | ws_dbg("after copy: readPos=%p, readLen=%d\n", wsctx->readPos, wsctx->readlen); |
107 | 0 | } else { |
108 | | /* it may happen that we read some bytes but could not decode them, |
109 | | * in that case, set errno to EAGAIN and return -1 */ |
110 | 0 | nextState = wsctx->hybiDecodeState; |
111 | 0 | errno = EAGAIN; |
112 | 0 | *nWritten = -1; |
113 | 0 | } |
114 | 0 | return nextState; |
115 | 0 | } |
116 | | |
117 | | /** |
118 | | * Read an RFC 6455 websocket frame (IETF hybi working group). |
119 | | * |
120 | | * Internal state is updated according to bytes received and the |
121 | | * decoding of header information. |
122 | | * |
123 | | * @param[in] cl client ptr with ptr to raw socket and ws_ctx_t ptr |
124 | | * @param[out] sockRet emulated recv return value |
125 | | * @param[out] nPayload number of payload bytes already read |
126 | | * @return next hybi decoding state; WS_HYBI_STATE_HEADER_PENDING indicates |
127 | | * that the header was not received completely. |
128 | | */ |
129 | | static int |
130 | | hybiReadHeader(ws_ctx_t *wsctx, int *sockRet, int *nPayload) |
131 | 0 | { |
132 | 0 | int ret; |
133 | 0 | char *headerDst = wsctx->codeBufDecode + wsctx->header.nRead; |
134 | 0 | int n = ((uint64_t)WS_HYBI_HEADER_LEN_SHORT) - wsctx->header.nRead; |
135 | | |
136 | |
|
137 | 0 | ws_dbg("header_read to %p with len=%d\n", headerDst, n); |
138 | 0 | ret = wsctx->ctxInfo.readFunc(wsctx->ctxInfo.ctxPtr, headerDst, n); |
139 | 0 | ws_dbg("read %d bytes from socket\n", ret); |
140 | 0 | if (ret <= 0) { |
141 | 0 | if (-1 == ret) { |
142 | | /* save errno because rfbErr() will tamper it */ |
143 | 0 | int olderrno = errno; |
144 | 0 | rfbErr("%s: read; %s\n", __func__, strerror(errno)); |
145 | 0 | errno = olderrno; |
146 | 0 | goto err_cleanup_state; |
147 | 0 | } else { |
148 | 0 | *sockRet = 0; |
149 | 0 | goto err_cleanup_state_sock_closed; |
150 | 0 | } |
151 | 0 | } |
152 | | |
153 | 0 | wsctx->header.nRead += ret; |
154 | 0 | if (wsctx->header.nRead < 2) { |
155 | | /* cannot decode header with less than two bytes */ |
156 | 0 | goto ret_header_pending; |
157 | 0 | } |
158 | | |
159 | | /* first two header bytes received; interpret header data and get rest */ |
160 | 0 | wsctx->header.data = (ws_header_t *)wsctx->codeBufDecode; |
161 | |
|
162 | 0 | wsctx->header.opcode = wsctx->header.data->b0 & 0x0f; |
163 | 0 | wsctx->header.fin = (wsctx->header.data->b0 & 0x80) >> 7; |
164 | 0 | if (isControlFrame(wsctx)) { |
165 | 0 | ws_dbg("is control frame\n"); |
166 | | /* is a control frame, leave remembered continuation opcode unchanged; |
167 | | * just check if there is a wrong fragmentation */ |
168 | 0 | if (wsctx->header.fin == 0) { |
169 | | |
170 | | /* we only accept text/binary continuation frames; RFC6455: |
171 | | * Control frames (see Section 5.5) MAY be injected in the middle of |
172 | | * a fragmented message. Control frames themselves MUST NOT be |
173 | | * fragmented. */ |
174 | 0 | rfbErr("control frame with FIN bit cleared received, aborting\n"); |
175 | 0 | errno = EPROTO; |
176 | 0 | goto err_cleanup_state; |
177 | 0 | } |
178 | 0 | } else { |
179 | 0 | ws_dbg("not a control frame\n"); |
180 | | /* not a control frame, check for continuation opcode */ |
181 | 0 | if (wsctx->header.opcode == WS_OPCODE_CONTINUATION) { |
182 | 0 | ws_dbg("cont_frame\n"); |
183 | | /* do we have state (i.e., opcode) for continuation frame? */ |
184 | 0 | if (wsctx->continuation_opcode == WS_OPCODE_INVALID) { |
185 | 0 | rfbErr("no continuation state\n"); |
186 | 0 | errno = EPROTO; |
187 | 0 | goto err_cleanup_state; |
188 | 0 | } |
189 | | |
190 | | /* otherwise, set opcode = continuation_opcode */ |
191 | 0 | wsctx->header.opcode = wsctx->continuation_opcode; |
192 | 0 | ws_dbg("set opcode to continuation_opcode: %d\n", wsctx->header.opcode); |
193 | 0 | } else { |
194 | 0 | if (wsctx->header.fin == 0) { |
195 | 0 | wsctx->continuation_opcode = wsctx->header.opcode; |
196 | 0 | } else { |
197 | 0 | wsctx->continuation_opcode = WS_OPCODE_INVALID; |
198 | 0 | } |
199 | 0 | ws_dbg("set continuation_opcode to %d\n", wsctx->continuation_opcode); |
200 | 0 | } |
201 | 0 | } |
202 | | |
203 | 0 | wsctx->header.payloadLen = (uint64_t)(wsctx->header.data->b1 & 0x7f); |
204 | 0 | ws_dbg("first header bytes received; opcode=%d lenbyte=%d fin=%d\n", wsctx->header.opcode, wsctx->header.payloadLen, wsctx->header.fin); |
205 | | |
206 | | /* |
207 | | * 4.3. Client-to-Server Masking |
208 | | * |
209 | | * The client MUST mask all frames sent to the server. A server MUST |
210 | | * close the connection upon receiving a frame with the MASK bit set to 0. |
211 | | **/ |
212 | 0 | if (!(wsctx->header.data->b1 & 0x80)) { |
213 | 0 | rfbErr("%s: got frame without mask; ret=%d\n", __func__, ret); |
214 | 0 | errno = EPROTO; |
215 | 0 | goto err_cleanup_state; |
216 | 0 | } |
217 | | |
218 | | /* Read now the rest of the frame header, if it is longer as the minimum */ |
219 | 0 | if ((wsctx->header.payloadLen == 126) || (wsctx->header.payloadLen == 127)) { |
220 | 0 | headerDst = wsctx->codeBufDecode + wsctx->header.nRead; |
221 | 0 | if (wsctx->header.payloadLen == 126) { |
222 | 0 | n = ((uint64_t)WS_HYBI_HEADER_LEN_EXTENDED) - wsctx->header.nRead; |
223 | 0 | } else if (wsctx->header.payloadLen == 127) { |
224 | 0 | n = ((uint64_t)WS_HYBI_HEADER_LEN_LONG) - wsctx->header.nRead; |
225 | 0 | } |
226 | 0 | ret = wsctx->ctxInfo.readFunc(wsctx->ctxInfo.ctxPtr, headerDst, n); |
227 | 0 | if (ret <= 0) { |
228 | 0 | if (-1 == ret) { |
229 | | /* save errno because rfbErr() will tamper it */ |
230 | 0 | int olderrno = errno; |
231 | 0 | rfbErr("%s: read; %s\n", __func__, strerror(errno)); |
232 | 0 | errno = olderrno; |
233 | 0 | goto err_cleanup_state; |
234 | 0 | } else { |
235 | 0 | *sockRet = 0; |
236 | 0 | goto err_cleanup_state_sock_closed; |
237 | 0 | } |
238 | 0 | } |
239 | | |
240 | | /* if more header data was read, account for it */ |
241 | 0 | wsctx->header.nRead += ret; |
242 | 0 | } |
243 | | |
244 | 0 | if (wsctx->header.payloadLen < 126 && wsctx->header.nRead >= 6) { |
245 | 0 | wsctx->header.headerLen = WS_HYBI_HEADER_LEN_SHORT; |
246 | 0 | wsctx->header.mask = wsctx->header.data->u.m; |
247 | 0 | } else if (wsctx->header.payloadLen == 126 && 8 <= wsctx->header.nRead) { |
248 | 0 | wsctx->header.headerLen = WS_HYBI_HEADER_LEN_EXTENDED; |
249 | 0 | wsctx->header.payloadLen = WS_NTOH16(wsctx->header.data->u.s16.l16); |
250 | 0 | wsctx->header.mask = wsctx->header.data->u.s16.m16; |
251 | 0 | } else if (wsctx->header.payloadLen == 127 && 14 <= wsctx->header.nRead) { |
252 | 0 | wsctx->header.headerLen = WS_HYBI_HEADER_LEN_LONG; |
253 | 0 | wsctx->header.payloadLen = WS_NTOH64(wsctx->header.data->u.s64.l64); |
254 | 0 | wsctx->header.mask = wsctx->header.data->u.s64.m64; |
255 | 0 | } else { |
256 | | /* Incomplete frame header, try again */ |
257 | 0 | rfbErr("%s: incomplete frame header; ret=%d\n", __func__, ret); |
258 | 0 | goto ret_header_pending; |
259 | 0 | } |
260 | | |
261 | 0 | int i; |
262 | 0 | ws_dbg("Header:\n"); |
263 | 0 | for (i=0; i <10; i++) { |
264 | 0 | ws_dbg("0x%02X\n", (unsigned char)wsctx->codeBufDecode[i]); |
265 | 0 | } |
266 | 0 | ws_dbg("\n"); |
267 | | |
268 | | /* while RFC 6455 mandates that lengths MUST be encoded with the minimum |
269 | | * number of bytes, it does not specify for the server how to react on |
270 | | * 'wrongly' encoded frames --- this implementation rejects them*/ |
271 | 0 | if ((wsctx->header.headerLen > WS_HYBI_HEADER_LEN_SHORT |
272 | 0 | && wsctx->header.payloadLen < (uint64_t)126) |
273 | 0 | || (wsctx->header.headerLen > WS_HYBI_HEADER_LEN_EXTENDED |
274 | 0 | && wsctx->header.payloadLen < (uint64_t)65536)) { |
275 | 0 | rfbErr("%s: invalid length field; headerLen=%d payloadLen=%llu\n", __func__, wsctx->header.headerLen, wsctx->header.payloadLen); |
276 | 0 | errno = EPROTO; |
277 | 0 | goto err_cleanup_state; |
278 | 0 | } |
279 | | |
280 | | /* update write position for next bytes */ |
281 | 0 | wsctx->writePos = wsctx->codeBufDecode + wsctx->header.nRead; |
282 | | |
283 | | /* set payload pointer just after header */ |
284 | 0 | wsctx->readPos = (unsigned char *)(wsctx->codeBufDecode + wsctx->header.headerLen); |
285 | |
|
286 | 0 | *nPayload = wsctx->header.nRead - wsctx->header.headerLen; |
287 | 0 | wsctx->nReadPayload = *nPayload; |
288 | |
|
289 | 0 | ws_dbg("header complete: state=%d headerlen=%d payloadlen=%llu writeTo=%p nPayload=%d\n", wsctx->hybiDecodeState, wsctx->header.headerLen, wsctx->header.payloadLen, wsctx->writePos, *nPayload); |
290 | |
|
291 | 0 | return WS_HYBI_STATE_DATA_NEEDED; |
292 | | |
293 | 0 | ret_header_pending: |
294 | 0 | errno = EAGAIN; |
295 | 0 | *sockRet = -1; |
296 | 0 | return WS_HYBI_STATE_HEADER_PENDING; |
297 | | |
298 | 0 | err_cleanup_state: |
299 | 0 | *sockRet = -1; |
300 | 0 | err_cleanup_state_sock_closed: |
301 | 0 | hybiDecodeCleanupComplete(wsctx); |
302 | 0 | return WS_HYBI_STATE_ERR; |
303 | 0 | } |
304 | | |
305 | | static int |
306 | | hybiWsFrameComplete(ws_ctx_t *wsctx) |
307 | 0 | { |
308 | 0 | return wsctx != NULL && hybiRemaining(wsctx) == 0; |
309 | 0 | } |
310 | | |
311 | | static char * |
312 | | hybiPayloadStart(ws_ctx_t *wsctx) |
313 | 0 | { |
314 | 0 | return wsctx->codeBufDecode + wsctx->header.headerLen; |
315 | 0 | } |
316 | | |
317 | | |
318 | | /** |
319 | | * Read the remaining payload bytes from associated raw socket. |
320 | | * |
321 | | * - try to read remaining bytes from socket |
322 | | * - unmask all multiples of 4 |
323 | | * - if frame incomplete but some bytes are left, these are copied to |
324 | | * the carry buffer |
325 | | * - if opcode is TEXT: Base64-decode all unmasked received bytes |
326 | | * - set state for reading decoded data |
327 | | * - reset write position to begin of buffer (+ header) |
328 | | * --> before we retrieve more data we let the caller clear all bytes |
329 | | * from the reception buffer |
330 | | * - execute return data routine |
331 | | * |
332 | | * Sets errno corresponding to what it gets from the underlying |
333 | | * socket or EPROTO if some invalid data is in the received frame |
334 | | * or ECONNRESET if a close reason + message is received. EIO is used if |
335 | | * an internal sanity check fails. |
336 | | * |
337 | | * @param[in] cl client ptr with raw socket reference |
338 | | * @param[out] dst destination buffer |
339 | | * @param[in] len size of destination buffer |
340 | | * @param[out] sockRet emulated recv return value |
341 | | * @param[in] nInBuf number of undecoded bytes before writePos from header read |
342 | | * @return next hybi decode state |
343 | | */ |
344 | | static int |
345 | | hybiReadAndDecode(ws_ctx_t *wsctx, char *dst, int len, int *sockRet, int nInBuf) |
346 | 0 | { |
347 | 0 | int n; |
348 | 0 | int i; |
349 | 0 | int toReturn; /* number of data bytes to return */ |
350 | 0 | int toDecode; /* number of bytes to decode starting at wsctx->writePos */ |
351 | 0 | int bufsize; |
352 | 0 | int nextRead; |
353 | 0 | unsigned char *data; |
354 | | |
355 | | /* if data was carried over, copy to start of buffer */ |
356 | 0 | memcpy(wsctx->writePos, wsctx->carryBuf, wsctx->carrylen); |
357 | 0 | wsctx->writePos += wsctx->carrylen; |
358 | | |
359 | | /* -1 accounts for potential '\0' terminator for base64 decoding */ |
360 | 0 | bufsize = wsctx->codeBufDecode + ARRAYSIZE(wsctx->codeBufDecode) - wsctx->writePos - 1; |
361 | 0 | ws_dbg("bufsize=%d\n", bufsize); |
362 | 0 | if (hybiRemaining(wsctx) > bufsize) { |
363 | 0 | nextRead = bufsize; |
364 | 0 | } else { |
365 | 0 | nextRead = hybiRemaining(wsctx); |
366 | 0 | } |
367 | |
|
368 | 0 | ws_dbg("calling read with buf=%p and len=%d (decodebuf=%p headerLen=%d)\n", wsctx->writePos, nextRead, wsctx->codeBufDecode, wsctx->header.headerLen); |
369 | |
|
370 | 0 | if (nextRead > 0) { |
371 | | /* decode more data */ |
372 | 0 | if (-1 == (n = wsctx->ctxInfo.readFunc(wsctx->ctxInfo.ctxPtr, wsctx->writePos, nextRead))) { |
373 | 0 | int olderrno = errno; |
374 | 0 | rfbErr("%s: read; %s", __func__, strerror(errno)); |
375 | 0 | errno = olderrno; |
376 | 0 | *sockRet = -1; |
377 | 0 | return WS_HYBI_STATE_ERR; |
378 | 0 | } else if (n == 0) { |
379 | 0 | *sockRet = 0; |
380 | 0 | return WS_HYBI_STATE_ERR; |
381 | 0 | } else { |
382 | 0 | ws_dbg("read %d bytes from socket; nRead=%d\n", n, wsctx->nReadPayload); |
383 | 0 | } |
384 | 0 | } else { |
385 | 0 | n = 0; |
386 | 0 | } |
387 | | |
388 | 0 | wsctx->nReadPayload += n; |
389 | 0 | wsctx->writePos += n; |
390 | |
|
391 | 0 | if (hybiRemaining(wsctx) == 0) { |
392 | 0 | wsctx->hybiDecodeState = WS_HYBI_STATE_FRAME_COMPLETE; |
393 | 0 | } |
394 | | |
395 | | /* number of not yet unmasked payload bytes: what we read here + what was |
396 | | * carried over + what was read with the header */ |
397 | 0 | toDecode = n + wsctx->carrylen + nInBuf; |
398 | 0 | ws_dbg("toDecode=%d from n=%d carrylen=%d headerLen=%d\n", toDecode, n, wsctx->carrylen, wsctx->header.headerLen); |
399 | 0 | if (toDecode < 0) { |
400 | 0 | rfbErr("%s: internal error; negative number of bytes to decode: %d", __func__, toDecode); |
401 | 0 | errno=EIO; |
402 | 0 | *sockRet = -1; |
403 | 0 | return WS_HYBI_STATE_ERR; |
404 | 0 | } |
405 | | |
406 | | /* for a possible base64 decoding, we decode multiples of 4 bytes until |
407 | | * the whole frame is received and carry over any remaining bytes in the carry buf*/ |
408 | 0 | data = (unsigned char *)(wsctx->writePos - toDecode); |
409 | |
|
410 | 0 | for (i = 0; i < (toDecode >> 2); i++) { |
411 | 0 | uint32_t tmp; |
412 | 0 | memcpy(&tmp, data + i * sizeof(tmp), sizeof(tmp)); |
413 | 0 | tmp ^= wsctx->header.mask.u; |
414 | 0 | memcpy(data + i * sizeof(tmp), &tmp, sizeof(tmp)); |
415 | 0 | } |
416 | 0 | ws_dbg("mask decoding; i=%d toDecode=%d\n", i, toDecode); |
417 | |
|
418 | 0 | if (wsctx->hybiDecodeState == WS_HYBI_STATE_FRAME_COMPLETE) { |
419 | | /* process the remaining bytes (if any) */ |
420 | 0 | for (i*=4; i < toDecode; i++) { |
421 | 0 | data[i] ^= wsctx->header.mask.c[i % 4]; |
422 | 0 | } |
423 | | |
424 | | /* all data is here, no carrying */ |
425 | 0 | wsctx->carrylen = 0; |
426 | 0 | } else { |
427 | | /* carry over remaining, non-multiple-of-four bytes */ |
428 | 0 | wsctx->carrylen = toDecode - (i * 4); |
429 | 0 | if (wsctx->carrylen < 0 || wsctx->carrylen > ARRAYSIZE(wsctx->carryBuf)) { |
430 | 0 | rfbErr("%s: internal error, invalid carry over size: carrylen=%d, toDecode=%d, i=%d", __func__, wsctx->carrylen, toDecode, i); |
431 | 0 | *sockRet = -1; |
432 | 0 | errno = EIO; |
433 | 0 | return WS_HYBI_STATE_ERR; |
434 | 0 | } |
435 | 0 | ws_dbg("carrying over %d bytes from %p to %p\n", wsctx->carrylen, wsctx->writePos + (i * 4), wsctx->carryBuf); |
436 | 0 | memcpy(wsctx->carryBuf, data + (i * 4), wsctx->carrylen); |
437 | 0 | wsctx->writePos -= wsctx->carrylen; |
438 | 0 | } |
439 | | |
440 | 0 | toReturn = toDecode - wsctx->carrylen; |
441 | |
|
442 | 0 | switch (wsctx->header.opcode) { |
443 | 0 | case WS_OPCODE_CLOSE: |
444 | | /* this data is not returned as payload data */ |
445 | 0 | if (hybiWsFrameComplete(wsctx)) { |
446 | 0 | *(wsctx->writePos) = '\0'; |
447 | 0 | ws_dbg("got close cmd %d, reason %d: %s\n", (int)(wsctx->writePos - hybiPayloadStart(wsctx)), WS_NTOH16(((uint16_t *)hybiPayloadStart(wsctx))[0]), &hybiPayloadStart(wsctx)[2]); |
448 | 0 | errno = ECONNRESET; |
449 | 0 | *sockRet = -1; |
450 | 0 | return WS_HYBI_STATE_FRAME_COMPLETE; |
451 | 0 | } else { |
452 | 0 | ws_dbg("got close cmd; waiting for %d more bytes to arrive\n", hybiRemaining(wsctx)); |
453 | 0 | *sockRet = -1; |
454 | 0 | errno = EAGAIN; |
455 | 0 | return WS_HYBI_STATE_CLOSE_REASON_PENDING; |
456 | 0 | } |
457 | 0 | break; |
458 | 0 | case WS_OPCODE_TEXT_FRAME: |
459 | 0 | data[toReturn] = '\0'; |
460 | 0 | ws_dbg("Initiate Base64 decoding in %p with max size %d and '\\0' at %p\n", data, bufsize, data + toReturn); |
461 | 0 | if (-1 == (wsctx->readlen = rfbBase64PtoN((char *)data, data, bufsize))) { |
462 | 0 | rfbErr("%s: Base64 decode error; %s\n", __func__, strerror(errno)); |
463 | 0 | } |
464 | 0 | wsctx->writePos = hybiPayloadStart(wsctx); |
465 | 0 | break; |
466 | 0 | case WS_OPCODE_BINARY_FRAME: |
467 | 0 | wsctx->readlen = toReturn; |
468 | 0 | wsctx->writePos = hybiPayloadStart(wsctx); |
469 | 0 | ws_dbg("set readlen=%d writePos=%p\n", wsctx->readlen, wsctx->writePos); |
470 | 0 | break; |
471 | 0 | default: |
472 | 0 | rfbErr("%s: unhandled opcode %d, b0: %02x, b1: %02x\n", __func__, (int)wsctx->header.opcode, wsctx->header.data->b0, wsctx->header.data->b1); |
473 | 0 | } |
474 | 0 | wsctx->readPos = data; |
475 | |
|
476 | 0 | return hybiReturnData(dst, len, wsctx, sockRet); |
477 | 0 | } |
478 | | |
479 | | /** |
480 | | * Read function for websocket-socket emulation. |
481 | | * |
482 | | * 0 1 2 3 |
483 | | * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 |
484 | | * +-+-+-+-+-------+-+-------------+-------------------------------+ |
485 | | * |F|R|R|R| opcode|M| Payload len | Extended payload length | |
486 | | * |I|S|S|S| (4) |A| (7) | (16/64) | |
487 | | * |N|V|V|V| |S| | (if payload len==126/127) | |
488 | | * | |1|2|3| |K| | | |
489 | | * +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - + |
490 | | * | Extended payload length continued, if payload len == 127 | |
491 | | * + - - - - - - - - - - - - - - - +-------------------------------+ |
492 | | * | |Masking-key, if MASK set to 1 | |
493 | | * +-------------------------------+-------------------------------+ |
494 | | * | Masking-key (continued) | Payload Data | |
495 | | * +-------------------------------- - - - - - - - - - - - - - - - + |
496 | | * : Payload Data continued ... : |
497 | | * + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + |
498 | | * | Payload Data continued ... | |
499 | | * +---------------------------------------------------------------+ |
500 | | * |
501 | | * Using the decode buffer, this function: |
502 | | * - reads the complete header from the underlying socket |
503 | | * - reads any remaining data bytes |
504 | | * - unmasks the payload data using the provided mask |
505 | | * - decodes Base64 encoded text data |
506 | | * - copies len bytes of decoded payload data into dst |
507 | | * |
508 | | * Emulates a read call on a socket. |
509 | | */ |
510 | | int |
511 | | webSocketsDecodeHybi(ws_ctx_t *wsctx, char *dst, int len) |
512 | 0 | { |
513 | 0 | int result = -1; |
514 | | /* int fin; */ /* not used atm */ |
515 | |
|
516 | 0 | ws_dbg("%s_enter: len=%d; " |
517 | 0 | "CTX: readlen=%d readPos=%p " |
518 | 0 | "writeTo=%p " |
519 | 0 | "state=%d payloadtoRead=%d payloadRemaining=%llu " |
520 | 0 | " nReadPayload=%d carrylen=%d carryBuf=%p\n", |
521 | 0 | __func__, len, |
522 | 0 | wsctx->readlen, wsctx->readPos, |
523 | 0 | wsctx->writePos, |
524 | 0 | wsctx->hybiDecodeState, wsctx->header.payloadLen, hybiRemaining(wsctx), |
525 | 0 | wsctx->nReadPayload, wsctx->carrylen, wsctx->carryBuf); |
526 | |
|
527 | 0 | switch (wsctx->hybiDecodeState){ |
528 | 0 | int nInBuf; |
529 | 0 | case WS_HYBI_STATE_HEADER_PENDING: |
530 | 0 | wsctx->hybiDecodeState = hybiReadHeader(wsctx, &result, &nInBuf); |
531 | 0 | if (wsctx->hybiDecodeState == WS_HYBI_STATE_ERR) { |
532 | 0 | goto spor; |
533 | 0 | } |
534 | 0 | if (wsctx->hybiDecodeState != WS_HYBI_STATE_HEADER_PENDING) { |
535 | | |
536 | | /* when header is complete, try to read some more data */ |
537 | 0 | wsctx->hybiDecodeState = hybiReadAndDecode(wsctx, dst, len, &result, nInBuf); |
538 | 0 | } |
539 | 0 | break; |
540 | 0 | case WS_HYBI_STATE_DATA_AVAILABLE: |
541 | 0 | wsctx->hybiDecodeState = hybiReturnData(dst, len, wsctx, &result); |
542 | 0 | break; |
543 | 0 | case WS_HYBI_STATE_DATA_NEEDED: |
544 | 0 | case WS_HYBI_STATE_CLOSE_REASON_PENDING: |
545 | 0 | wsctx->hybiDecodeState = hybiReadAndDecode(wsctx, dst, len, &result, 0); |
546 | 0 | break; |
547 | 0 | default: |
548 | | /* invalid state */ |
549 | 0 | rfbErr("%s: called with invalid state %d\n", wsctx->hybiDecodeState); |
550 | 0 | result = -1; |
551 | 0 | errno = EIO; |
552 | 0 | wsctx->hybiDecodeState = WS_HYBI_STATE_ERR; |
553 | 0 | } |
554 | | |
555 | | /* single point of return, if someone has questions :-) */ |
556 | 0 | spor: |
557 | 0 | if (wsctx->hybiDecodeState == WS_HYBI_STATE_FRAME_COMPLETE) { |
558 | 0 | ws_dbg("frame received successfully, cleaning up: read=%d hlen=%d plen=%d\n", wsctx->header.nRead, wsctx->header.headerLen, wsctx->header.payloadLen); |
559 | 0 | if (wsctx->header.fin && !isControlFrame(wsctx)) { |
560 | | /* frame finished, cleanup state */ |
561 | 0 | hybiDecodeCleanupComplete(wsctx); |
562 | 0 | } else { |
563 | | /* always retain continuation opcode for unfinished data frames |
564 | | * or control frames, which may interleave with data frames */ |
565 | 0 | hybiDecodeCleanupForContinuation(wsctx); |
566 | 0 | } |
567 | 0 | } else if (wsctx->hybiDecodeState == WS_HYBI_STATE_ERR) { |
568 | 0 | hybiDecodeCleanupComplete(wsctx); |
569 | 0 | } |
570 | |
|
571 | 0 | ws_dbg("%s_exit: len=%d; " |
572 | 0 | "CTX: readlen=%d readPos=%p " |
573 | 0 | "writePos=%p " |
574 | 0 | "state=%d payloadtoRead=%d payloadRemaining=%d " |
575 | 0 | "nRead=%d carrylen=%d carryBuf=%p " |
576 | 0 | "result=%d " |
577 | 0 | "errno=%d\n", |
578 | 0 | __func__, len, |
579 | 0 | wsctx->readlen, wsctx->readPos, |
580 | 0 | wsctx->writePos, |
581 | 0 | wsctx->hybiDecodeState, wsctx->header.payloadLen, hybiRemaining(wsctx), |
582 | 0 | wsctx->nReadPayload, wsctx->carrylen, wsctx->carryBuf, |
583 | 0 | result, |
584 | 0 | errno); |
585 | 0 | return result; |
586 | 0 | } |