1/* 2 * websockets.c - deal with WebSockets clients. 3 * 4 * This code should be independent of any changes in the RFB protocol. It is 5 * an additional handshake and framing of normal sockets: 6 * http://www.whatwg.org/specs/web-socket-protocol/ 7 * 8 */ 9 10/* 11 * Copyright (C) 2010 Joel Martin 12 * 13 * This is free software; you can redistribute it and/or modify 14 * it under the terms of the GNU General Public License as published by 15 * the Free Software Foundation; either version 2 of the License, or 16 * (at your option) any later version. 17 * 18 * This software is distributed in the hope that it will be useful, 19 * but WITHOUT ANY WARRANTY; without even the implied warranty of 20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 * GNU General Public License for more details. 22 * 23 * You should have received a copy of the GNU General Public License 24 * along with this software; if not, write to the Free Software 25 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 26 * USA. 27 */ 28 29#ifdef __STRICT_ANSI__ 30#define _BSD_SOURCE 31#endif 32 33#include <rfb/rfb.h> 34/* errno */ 35#include <errno.h> 36 37#ifndef _MSC_VER 38#include <resolv.h> /* __b64_ntop */ 39#endif 40 41#ifdef LIBVNCSERVER_HAVE_ENDIAN_H 42#include <endian.h> 43#elif LIBVNCSERVER_HAVE_SYS_ENDIAN_H 44#include <sys/endian.h> 45#endif 46 47#ifdef LIBVNCSERVER_HAVE_SYS_TYPES_H 48#include <sys/types.h> 49#endif 50 51#include <string.h> 52#include <unistd.h> 53#include "rfb/rfbconfig.h" 54#include "rfbssl.h" 55#include "rfbcrypto.h" 56 57#define WS_NTOH64(n) htobe64(n) 58#define WS_NTOH32(n) htobe32(n) 59#define WS_NTOH16(n) htobe16(n) 60#define WS_HTON64(n) htobe64(n) 61#define WS_HTON16(n) htobe16(n) 62 63#define B64LEN(__x) (((__x + 2) / 3) * 12 / 3) 64#define WSHLENMAX 14 /* 2 + sizeof(uint64_t) + sizeof(uint32_t) */ 65 66enum { 67 WEBSOCKETS_VERSION_HIXIE, 68 WEBSOCKETS_VERSION_HYBI 69}; 70 71#if 0 72#include <sys/syscall.h> 73static int gettid() { 74 return (int)syscall(SYS_gettid); 75} 76#endif 77 78typedef int (*wsEncodeFunc)(rfbClientPtr cl, const char *src, int len, char **dst); 79typedef int (*wsDecodeFunc)(rfbClientPtr cl, char *dst, int len); 80 81typedef struct ws_ctx_s { 82 char codeBuf[B64LEN(UPDATE_BUF_SIZE) + WSHLENMAX]; /* base64 + maximum frame header length */ 83 char readbuf[8192]; 84 int readbufstart; 85 int readbuflen; 86 int dblen; 87 char carryBuf[3]; /* For base64 carry-over */ 88 int carrylen; 89 int version; 90 int base64; 91 wsEncodeFunc encode; 92 wsDecodeFunc decode; 93} ws_ctx_t; 94 95typedef union ws_mask_s { 96 char c[4]; 97 uint32_t u; 98} ws_mask_t; 99 100/* XXX: The union and the structs do not need to be named. 101 * We are working around a bug present in GCC < 4.6 which prevented 102 * it from recognizing anonymous structs and unions. 103 * See http://gcc.gnu.org/bugzilla/show_bug.cgi?id=4784 104 */ 105typedef struct __attribute__ ((__packed__)) ws_header_s { 106 unsigned char b0; 107 unsigned char b1; 108 union { 109 struct __attribute__ ((__packed__)) { 110 uint16_t l16; 111 ws_mask_t m16; 112 } s16; 113 struct __attribute__ ((__packed__)) { 114 uint64_t l64; 115 ws_mask_t m64; 116 } s64; 117 ws_mask_t m; 118 } u; 119} ws_header_t; 120 121enum 122{ 123 WS_OPCODE_CONTINUATION = 0x0, 124 WS_OPCODE_TEXT_FRAME, 125 WS_OPCODE_BINARY_FRAME, 126 WS_OPCODE_CLOSE = 0x8, 127 WS_OPCODE_PING, 128 WS_OPCODE_PONG 129}; 130 131#define FLASH_POLICY_RESPONSE "<cross-domain-policy><allow-access-from domain=\"*\" to-ports=\"*\" /></cross-domain-policy>\n" 132#define SZ_FLASH_POLICY_RESPONSE 93 133 134/* 135 * draft-ietf-hybi-thewebsocketprotocol-10 136 * 5.2.2. Sending the Server's Opening Handshake 137 */ 138#define GUID "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" 139 140#define SERVER_HANDSHAKE_HIXIE "HTTP/1.1 101 Web Socket Protocol Handshake\r\n\ 141Upgrade: WebSocket\r\n\ 142Connection: Upgrade\r\n\ 143%sWebSocket-Origin: %s\r\n\ 144%sWebSocket-Location: %s://%s%s\r\n\ 145%sWebSocket-Protocol: %s\r\n\ 146\r\n%s" 147 148#define SERVER_HANDSHAKE_HYBI "HTTP/1.1 101 Switching Protocols\r\n\ 149Upgrade: websocket\r\n\ 150Connection: Upgrade\r\n\ 151Sec-WebSocket-Accept: %s\r\n\ 152Sec-WebSocket-Protocol: %s\r\n\ 153\r\n" 154 155 156#define WEBSOCKETS_CLIENT_CONNECT_WAIT_MS 100 157#define WEBSOCKETS_CLIENT_SEND_WAIT_MS 100 158#define WEBSOCKETS_MAX_HANDSHAKE_LEN 4096 159 160#if defined(__linux__) && defined(NEED_TIMEVAL) 161struct timeval 162{ 163 long int tv_sec,tv_usec; 164} 165; 166#endif 167 168static rfbBool webSocketsHandshake(rfbClientPtr cl, char *scheme); 169void webSocketsGenMd5(char * target, char *key1, char *key2, char *key3); 170 171static int webSocketsEncodeHybi(rfbClientPtr cl, const char *src, int len, char **dst); 172static int webSocketsEncodeHixie(rfbClientPtr cl, const char *src, int len, char **dst); 173static int webSocketsDecodeHybi(rfbClientPtr cl, char *dst, int len); 174static int webSocketsDecodeHixie(rfbClientPtr cl, char *dst, int len); 175 176static int 177min (int a, int b) { 178 return a < b ? a : b; 179} 180 181static void webSocketsGenSha1Key(char *target, int size, char *key) 182{ 183 struct iovec iov[2]; 184 unsigned char hash[20]; 185 186 iov[0].iov_base = key; 187 iov[0].iov_len = strlen(key); 188 iov[1].iov_base = GUID; 189 iov[1].iov_len = sizeof(GUID) - 1; 190 digestsha1(iov, 2, hash); 191 if (-1 == __b64_ntop(hash, sizeof(hash), target, size)) 192 rfbErr("b64_ntop failed\n"); 193} 194 195/* 196 * rfbWebSocketsHandshake is called to handle new WebSockets connections 197 */ 198 199rfbBool 200webSocketsCheck (rfbClientPtr cl) 201{ 202 char bbuf[4], *scheme; 203 int ret; 204 205 ret = rfbPeekExactTimeout(cl, bbuf, 4, 206 WEBSOCKETS_CLIENT_CONNECT_WAIT_MS); 207 if ((ret < 0) && (errno == ETIMEDOUT)) { 208 rfbLog("Normal socket connection\n"); 209 return TRUE; 210 } else if (ret <= 0) { 211 rfbErr("webSocketsHandshake: unknown connection error\n"); 212 return FALSE; 213 } 214 215 if (strncmp(bbuf, "<", 1) == 0) { 216 rfbLog("Got Flash policy request, sending response\n"); 217 if (rfbWriteExact(cl, FLASH_POLICY_RESPONSE, 218 SZ_FLASH_POLICY_RESPONSE) < 0) { 219 rfbErr("webSocketsHandshake: failed sending Flash policy response"); 220 } 221 return FALSE; 222 } else if (strncmp(bbuf, "\x16", 1) == 0 || strncmp(bbuf, "\x80", 1) == 0) { 223 rfbLog("Got TLS/SSL WebSockets connection\n"); 224 if (-1 == rfbssl_init(cl)) { 225 rfbErr("webSocketsHandshake: rfbssl_init failed\n"); 226 return FALSE; 227 } 228 ret = rfbPeekExactTimeout(cl, bbuf, 4, WEBSOCKETS_CLIENT_CONNECT_WAIT_MS); 229 scheme = "wss"; 230 } else { 231 scheme = "ws"; 232 } 233 234 if (strncmp(bbuf, "GET ", 4) != 0) { 235 rfbErr("webSocketsHandshake: invalid client header\n"); 236 return FALSE; 237 } 238 239 rfbLog("Got '%s' WebSockets handshake\n", scheme); 240 241 if (!webSocketsHandshake(cl, scheme)) { 242 return FALSE; 243 } 244 /* Start WebSockets framing */ 245 return TRUE; 246} 247 248static rfbBool 249webSocketsHandshake(rfbClientPtr cl, char *scheme) 250{ 251 char *buf, *response, *line; 252 int n, linestart = 0, len = 0, llen, base64 = TRUE; 253 char prefix[5], trailer[17]; 254 char *path = NULL, *host = NULL, *origin = NULL, *protocol = NULL; 255 char *key1 = NULL, *key2 = NULL, *key3 = NULL; 256 char *sec_ws_origin = NULL; 257 char *sec_ws_key = NULL; 258 char sec_ws_version = 0; 259 ws_ctx_t *wsctx = NULL; 260 261 buf = (char *) malloc(WEBSOCKETS_MAX_HANDSHAKE_LEN); 262 if (!buf) { 263 rfbLogPerror("webSocketsHandshake: malloc"); 264 return FALSE; 265 } 266 response = (char *) malloc(WEBSOCKETS_MAX_HANDSHAKE_LEN); 267 if (!response) { 268 free(buf); 269 rfbLogPerror("webSocketsHandshake: malloc"); 270 return FALSE; 271 } 272 273 while (len < WEBSOCKETS_MAX_HANDSHAKE_LEN-1) { 274 if ((n = rfbReadExactTimeout(cl, buf+len, 1, 275 WEBSOCKETS_CLIENT_SEND_WAIT_MS)) <= 0) { 276 if ((n < 0) && (errno == ETIMEDOUT)) { 277 break; 278 } 279 if (n == 0) 280 rfbLog("webSocketsHandshake: client gone\n"); 281 else 282 rfbLogPerror("webSocketsHandshake: read"); 283 free(response); 284 free(buf); 285 return FALSE; 286 } 287 288 len += 1; 289 llen = len - linestart; 290 if (((llen >= 2)) && (buf[len-1] == '\n')) { 291 line = buf+linestart; 292 if ((llen == 2) && (strncmp("\r\n", line, 2) == 0)) { 293 if (key1 && key2) { 294 if ((n = rfbReadExact(cl, buf+len, 8)) <= 0) { 295 if ((n < 0) && (errno == ETIMEDOUT)) { 296 break; 297 } 298 if (n == 0) 299 rfbLog("webSocketsHandshake: client gone\n"); 300 else 301 rfbLogPerror("webSocketsHandshake: read"); 302 free(response); 303 free(buf); 304 return FALSE; 305 } 306 rfbLog("Got key3\n"); 307 key3 = buf+len; 308 len += 8; 309 } else { 310 buf[len] = '\0'; 311 } 312 break; 313 } else if ((llen >= 16) && ((strncmp("GET ", line, min(llen,4))) == 0)) { 314 /* 16 = 4 ("GET ") + 1 ("/.*") + 11 (" HTTP/1.1\r\n") */ 315 path = line+4; 316 buf[len-11] = '\0'; /* Trim trailing " HTTP/1.1\r\n" */ 317 cl->wspath = strdup(path); 318 /* rfbLog("Got path: %s\n", path); */ 319 } else if ((strncasecmp("host: ", line, min(llen,6))) == 0) { 320 host = line+6; 321 buf[len-2] = '\0'; 322 /* rfbLog("Got host: %s\n", host); */ 323 } else if ((strncasecmp("origin: ", line, min(llen,8))) == 0) { 324 origin = line+8; 325 buf[len-2] = '\0'; 326 /* rfbLog("Got origin: %s\n", origin); */ 327 } else if ((strncasecmp("sec-websocket-key1: ", line, min(llen,20))) == 0) { 328 key1 = line+20; 329 buf[len-2] = '\0'; 330 /* rfbLog("Got key1: %s\n", key1); */ 331 } else if ((strncasecmp("sec-websocket-key2: ", line, min(llen,20))) == 0) { 332 key2 = line+20; 333 buf[len-2] = '\0'; 334 /* rfbLog("Got key2: %s\n", key2); */ 335 /* HyBI */ 336 337 } else if ((strncasecmp("sec-websocket-protocol: ", line, min(llen,24))) == 0) { 338 protocol = line+24; 339 buf[len-2] = '\0'; 340 rfbLog("Got protocol: %s\n", protocol); 341 } else if ((strncasecmp("sec-websocket-origin: ", line, min(llen,22))) == 0) { 342 sec_ws_origin = line+22; 343 buf[len-2] = '\0'; 344 } else if ((strncasecmp("sec-websocket-key: ", line, min(llen,19))) == 0) { 345 sec_ws_key = line+19; 346 buf[len-2] = '\0'; 347 } else if ((strncasecmp("sec-websocket-version: ", line, min(llen,23))) == 0) { 348 sec_ws_version = strtol(line+23, NULL, 10); 349 buf[len-2] = '\0'; 350 } 351 352 linestart = len; 353 } 354 } 355 356 if (!(path && host && (origin || sec_ws_origin))) { 357 rfbErr("webSocketsHandshake: incomplete client handshake\n"); 358 free(response); 359 free(buf); 360 return FALSE; 361 } 362 363 if ((protocol) && (strstr(protocol, "binary"))) { 364 if (! sec_ws_version) { 365 rfbErr("webSocketsHandshake: 'binary' protocol not supported with Hixie\n"); 366 free(response); 367 free(buf); 368 return FALSE; 369 } 370 rfbLog(" - webSocketsHandshake: using binary/raw encoding\n"); 371 base64 = FALSE; 372 protocol = "binary"; 373 } else { 374 rfbLog(" - webSocketsHandshake: using base64 encoding\n"); 375 base64 = TRUE; 376 if ((protocol) && (strstr(protocol, "base64"))) { 377 protocol = "base64"; 378 } else { 379 protocol = ""; 380 } 381 } 382 383 /* 384 * Generate the WebSockets server response based on the the headers sent 385 * by the client. 386 */ 387 388 if (sec_ws_version) { 389 char accept[B64LEN(SHA1_HASH_SIZE) + 1]; 390 rfbLog(" - WebSockets client version hybi-%02d\n", sec_ws_version); 391 webSocketsGenSha1Key(accept, sizeof(accept), sec_ws_key); 392 len = snprintf(response, WEBSOCKETS_MAX_HANDSHAKE_LEN, 393 SERVER_HANDSHAKE_HYBI, accept, protocol); 394 } else { 395 /* older hixie handshake, this could be removed if 396 * a final standard is established */ 397 if (!(key1 && key2 && key3)) { 398 rfbLog(" - WebSockets client version hixie-75\n"); 399 prefix[0] = '\0'; 400 trailer[0] = '\0'; 401 } else { 402 rfbLog(" - WebSockets client version hixie-76\n"); 403 snprintf(prefix, 5, "Sec-"); 404 webSocketsGenMd5(trailer, key1, key2, key3); 405 } 406 len = snprintf(response, WEBSOCKETS_MAX_HANDSHAKE_LEN, 407 SERVER_HANDSHAKE_HIXIE, prefix, origin, prefix, scheme, 408 host, path, prefix, protocol, trailer); 409 } 410 411 if (rfbWriteExact(cl, response, len) < 0) { 412 rfbErr("webSocketsHandshake: failed sending WebSockets response\n"); 413 free(response); 414 free(buf); 415 return FALSE; 416 } 417 /* rfbLog("webSocketsHandshake: %s\n", response); */ 418 free(response); 419 free(buf); 420 421 422 wsctx = calloc(1, sizeof(ws_ctx_t)); 423 if (sec_ws_version) { 424 wsctx->version = WEBSOCKETS_VERSION_HYBI; 425 wsctx->encode = webSocketsEncodeHybi; 426 wsctx->decode = webSocketsDecodeHybi; 427 } else { 428 wsctx->version = WEBSOCKETS_VERSION_HIXIE; 429 wsctx->encode = webSocketsEncodeHixie; 430 wsctx->decode = webSocketsDecodeHixie; 431 } 432 wsctx->base64 = base64; 433 cl->wsctx = (wsCtx *)wsctx; 434 return TRUE; 435} 436 437void 438webSocketsGenMd5(char * target, char *key1, char *key2, char *key3) 439{ 440 unsigned int i, spaces1 = 0, spaces2 = 0; 441 unsigned long num1 = 0, num2 = 0; 442 unsigned char buf[17]; 443 struct iovec iov[1]; 444 445 for (i=0; i < strlen(key1); i++) { 446 if (key1[i] == ' ') { 447 spaces1 += 1; 448 } 449 if ((key1[i] >= 48) && (key1[i] <= 57)) { 450 num1 = num1 * 10 + (key1[i] - 48); 451 } 452 } 453 num1 = num1 / spaces1; 454 455 for (i=0; i < strlen(key2); i++) { 456 if (key2[i] == ' ') { 457 spaces2 += 1; 458 } 459 if ((key2[i] >= 48) && (key2[i] <= 57)) { 460 num2 = num2 * 10 + (key2[i] - 48); 461 } 462 } 463 num2 = num2 / spaces2; 464 465 /* Pack it big-endian */ 466 buf[0] = (num1 & 0xff000000) >> 24; 467 buf[1] = (num1 & 0xff0000) >> 16; 468 buf[2] = (num1 & 0xff00) >> 8; 469 buf[3] = num1 & 0xff; 470 471 buf[4] = (num2 & 0xff000000) >> 24; 472 buf[5] = (num2 & 0xff0000) >> 16; 473 buf[6] = (num2 & 0xff00) >> 8; 474 buf[7] = num2 & 0xff; 475 476 strncpy((char *)buf+8, key3, 8); 477 buf[16] = '\0'; 478 479 iov[0].iov_base = buf; 480 iov[0].iov_len = 16; 481 digestmd5(iov, 1, target); 482 target[16] = '\0'; 483 484 return; 485} 486 487static int 488webSocketsEncodeHixie(rfbClientPtr cl, const char *src, int len, char **dst) 489{ 490 int sz = 0; 491 ws_ctx_t *wsctx = (ws_ctx_t *)cl->wsctx; 492 493 wsctx->codeBuf[sz++] = '\x00'; 494 len = __b64_ntop((unsigned char *)src, len, wsctx->codeBuf+sz, sizeof(wsctx->codeBuf) - (sz + 1)); 495 if (len < 0) { 496 return len; 497 } 498 sz += len; 499 500 wsctx->codeBuf[sz++] = '\xff'; 501 *dst = wsctx->codeBuf; 502 return sz; 503} 504 505static int 506ws_read(rfbClientPtr cl, char *buf, int len) 507{ 508 int n; 509 if (cl->sslctx) { 510 n = rfbssl_read(cl, buf, len); 511 } else { 512 n = read(cl->sock, buf, len); 513 } 514 return n; 515} 516 517static int 518ws_peek(rfbClientPtr cl, char *buf, int len) 519{ 520 int n; 521 if (cl->sslctx) { 522 n = rfbssl_peek(cl, buf, len); 523 } else { 524 while (-1 == (n = recv(cl->sock, buf, len, MSG_PEEK))) { 525 if (errno != EAGAIN) 526 break; 527 } 528 } 529 return n; 530} 531 532static int 533webSocketsDecodeHixie(rfbClientPtr cl, char *dst, int len) 534{ 535 int retlen = 0, n, i, avail, modlen, needlen; 536 char *buf, *end = NULL; 537 ws_ctx_t *wsctx = (ws_ctx_t *)cl->wsctx; 538 539 buf = wsctx->codeBuf; 540 541 n = ws_peek(cl, buf, len*2+2); 542 543 if (n <= 0) { 544 /* save errno because rfbErr() will tamper it */ 545 int olderrno = errno; 546 rfbErr("%s: peek (%d) %m\n", __func__, errno); 547 errno = olderrno; 548 return n; 549 } 550 551 552 /* Base64 encoded WebSockets stream */ 553 554 if (buf[0] == '\xff') { 555 i = ws_read(cl, buf, 1); /* Consume marker */ 556 buf++; 557 n--; 558 } 559 if (n == 0) { 560 errno = EAGAIN; 561 return -1; 562 } 563 if (buf[0] == '\x00') { 564 i = ws_read(cl, buf, 1); /* Consume marker */ 565 buf++; 566 n--; 567 } 568 if (n == 0) { 569 errno = EAGAIN; 570 return -1; 571 } 572 573 /* end = memchr(buf, '\xff', len*2+2); */ 574 end = memchr(buf, '\xff', n); 575 if (!end) { 576 end = buf + n; 577 } 578 avail = end - buf; 579 580 len -= wsctx->carrylen; 581 582 /* Determine how much base64 data we need */ 583 modlen = len + (len+2)/3; 584 needlen = modlen; 585 if (needlen % 4) { 586 needlen += 4 - (needlen % 4); 587 } 588 589 if (needlen > avail) { 590 /* rfbLog("Waiting for more base64 data\n"); */ 591 errno = EAGAIN; 592 return -1; 593 } 594 595 /* Any carryover from previous decode */ 596 for (i=0; i < wsctx->carrylen; i++) { 597 /* rfbLog("Adding carryover %d\n", wsctx->carryBuf[i]); */ 598 dst[i] = wsctx->carryBuf[i]; 599 retlen += 1; 600 } 601 602 /* Decode the rest of what we need */ 603 buf[needlen] = '\x00'; /* Replace end marker with end of string */ 604 /* rfbLog("buf: %s\n", buf); */ 605 n = __b64_pton(buf, (unsigned char *)dst+retlen, 2+len); 606 if (n < len) { 607 rfbErr("Base64 decode error\n"); 608 errno = EIO; 609 return -1; 610 } 611 retlen += n; 612 613 /* Consume the data from socket */ 614 i = ws_read(cl, buf, needlen); 615 616 wsctx->carrylen = n - len; 617 retlen -= wsctx->carrylen; 618 for (i=0; i < wsctx->carrylen; i++) { 619 /* rfbLog("Saving carryover %d\n", dst[retlen + i]); */ 620 wsctx->carryBuf[i] = dst[retlen + i]; 621 } 622 623 /* rfbLog("<< webSocketsDecode, retlen: %d\n", retlen); */ 624 return retlen; 625} 626 627static int 628webSocketsDecodeHybi(rfbClientPtr cl, char *dst, int len) 629{ 630 char *buf, *payload; 631 uint32_t *payload32; 632 int ret = -1, result = -1; 633 int total = 0; 634 ws_mask_t mask; 635 ws_header_t *header; 636 int i; 637 unsigned char opcode; 638 ws_ctx_t *wsctx = (ws_ctx_t *)cl->wsctx; 639 int flength, fhlen; 640 /* int fin; */ /* not used atm */ 641 642 /* rfbLog(" <== %s[%d]: %d cl: %p, wsctx: %p-%p (%d)\n", __func__, gettid(), len, cl, wsctx, (char *)wsctx + sizeof(ws_ctx_t), sizeof(ws_ctx_t)); */ 643 644 if (wsctx->readbuflen) { 645 /* simply return what we have */ 646 if (wsctx->readbuflen > len) { 647 memcpy(dst, wsctx->readbuf + wsctx->readbufstart, len); 648 result = len; 649 wsctx->readbuflen -= len; 650 wsctx->readbufstart += len; 651 } else { 652 memcpy(dst, wsctx->readbuf + wsctx->readbufstart, wsctx->readbuflen); 653 result = wsctx->readbuflen; 654 wsctx->readbuflen = 0; 655 wsctx->readbufstart = 0; 656 } 657 goto spor; 658 } 659 660 buf = wsctx->codeBuf; 661 header = (ws_header_t *)wsctx->codeBuf; 662 663 ret = ws_peek(cl, buf, B64LEN(len) + WSHLENMAX); 664 665 if (ret < 2) { 666 /* save errno because rfbErr() will tamper it */ 667 if (-1 == ret) { 668 int olderrno = errno; 669 rfbErr("%s: peek; %m\n", __func__); 670 errno = olderrno; 671 } else if (0 == ret) { 672 result = 0; 673 } else { 674 errno = EAGAIN; 675 } 676 goto spor; 677 } 678 679 opcode = header->b0 & 0x0f; 680 /* fin = (header->b0 & 0x80) >> 7; */ /* not used atm */ 681 flength = header->b1 & 0x7f; 682 683 /* 684 * 4.3. Client-to-Server Masking 685 * 686 * The client MUST mask all frames sent to the server. A server MUST 687 * close the connection upon receiving a frame with the MASK bit set to 0. 688 **/ 689 if (!(header->b1 & 0x80)) { 690 rfbErr("%s: got frame without mask\n", __func__, ret); 691 errno = EIO; 692 goto spor; 693 } 694 695 if (flength < 126) { 696 fhlen = 2; 697 mask = header->u.m; 698 } else if (flength == 126 && 4 <= ret) { 699 flength = WS_NTOH16(header->u.s16.l16); 700 fhlen = 4; 701 mask = header->u.s16.m16; 702 } else if (flength == 127 && 10 <= ret) { 703 flength = WS_NTOH64(header->u.s64.l64); 704 fhlen = 10; 705 mask = header->u.s64.m64; 706 } else { 707 /* Incomplete frame header */ 708 rfbErr("%s: incomplete frame header\n", __func__, ret); 709 errno = EIO; 710 goto spor; 711 } 712 713 /* absolute length of frame */ 714 total = fhlen + flength + 4; 715 payload = buf + fhlen + 4; /* header length + mask */ 716 717 if (-1 == (ret = ws_read(cl, buf, total))) { 718 int olderrno = errno; 719 rfbErr("%s: read; %m", __func__); 720 errno = olderrno; 721 return ret; 722 } else if (ret < total) { 723 /* GT TODO: hmm? */ 724 rfbLog("%s: read; got partial data\n", __func__); 725 } else { 726 buf[ret] = '\0'; 727 } 728 729 /* process 1 frame (32 bit op) */ 730 payload32 = (uint32_t *)payload; 731 for (i = 0; i < flength / 4; i++) { 732 payload32[i] ^= mask.u; 733 } 734 /* process the remaining bytes (if any) */ 735 for (i*=4; i < flength; i++) { 736 payload[i] ^= mask.c[i % 4]; 737 } 738 739 switch (opcode) { 740 case WS_OPCODE_CLOSE: 741 rfbLog("got closure, reason %d\n", WS_NTOH16(((uint16_t *)payload)[0])); 742 errno = ECONNRESET; 743 break; 744 case WS_OPCODE_TEXT_FRAME: 745 if (-1 == (flength = __b64_pton(payload, (unsigned char *)wsctx->codeBuf, sizeof(wsctx->codeBuf)))) { 746 rfbErr("%s: Base64 decode error; %m\n", __func__); 747 break; 748 } 749 payload = wsctx->codeBuf; 750 /* fall through */ 751 case WS_OPCODE_BINARY_FRAME: 752 if (flength > len) { 753 memcpy(wsctx->readbuf, payload + len, flength - len); 754 wsctx->readbufstart = 0; 755 wsctx->readbuflen = flength - len; 756 flength = len; 757 } 758 memcpy(dst, payload, flength); 759 result = flength; 760 break; 761 default: 762 rfbErr("%s: unhandled opcode %d, b0: %02x, b1: %02x\n", __func__, (int)opcode, header->b0, header->b1); 763 } 764 765 /* single point of return, if someone has questions :-) */ 766spor: 767 /* rfbLog("%s: ret: %d/%d\n", __func__, result, len); */ 768 return result; 769} 770 771static int 772webSocketsEncodeHybi(rfbClientPtr cl, const char *src, int len, char **dst) 773{ 774 int blen, ret = -1, sz = 0; 775 unsigned char opcode = '\0'; /* TODO: option! */ 776 ws_header_t *header; 777 ws_ctx_t *wsctx = (ws_ctx_t *)cl->wsctx; 778 779 780 /* Optional opcode: 781 * 0x0 - continuation 782 * 0x1 - text frame (base64 encode buf) 783 * 0x2 - binary frame (use raw buf) 784 * 0x8 - connection close 785 * 0x9 - ping 786 * 0xA - pong 787 **/ 788 if (!len) { 789 /* nothing to encode */ 790 return 0; 791 } 792 793 header = (ws_header_t *)wsctx->codeBuf; 794 795 if (wsctx->base64) { 796 opcode = WS_OPCODE_TEXT_FRAME; 797 /* calculate the resulting size */ 798 blen = B64LEN(len); 799 } else { 800 opcode = WS_OPCODE_BINARY_FRAME; 801 blen = len; 802 } 803 804 header->b0 = 0x80 | (opcode & 0x0f); 805 if (blen <= 125) { 806 header->b1 = (uint8_t)blen; 807 sz = 2; 808 } else if (blen <= 65536) { 809 header->b1 = 0x7e; 810 header->u.s16.l16 = WS_HTON16((uint16_t)blen); 811 sz = 4; 812 } else { 813 header->b1 = 0x7f; 814 header->u.s64.l64 = WS_HTON64(blen); 815 sz = 10; 816 } 817 818 if (wsctx->base64) { 819 if (-1 == (ret = __b64_ntop((unsigned char *)src, len, wsctx->codeBuf + sz, sizeof(wsctx->codeBuf) - sz))) { 820 rfbErr("%s: Base 64 encode failed\n", __func__); 821 } else { 822 if (ret != blen) 823 rfbErr("%s: Base 64 encode; something weird happened\n", __func__); 824 ret += sz; 825 } 826 } else { 827 memcpy(wsctx->codeBuf + sz, src, len); 828 ret = sz + len; 829 } 830 831 *dst = wsctx->codeBuf; 832 return ret; 833} 834 835int 836webSocketsEncode(rfbClientPtr cl, const char *src, int len, char **dst) 837{ 838 return ((ws_ctx_t *)cl->wsctx)->encode(cl, src, len, dst); 839} 840 841int 842webSocketsDecode(rfbClientPtr cl, char *dst, int len) 843{ 844 return ((ws_ctx_t *)cl->wsctx)->decode(cl, dst, len); 845} 846 847 848/* returns TRUE if client sent a close frame or a single 'end of frame' 849 * marker was received, FALSE otherwise 850 * 851 * Note: This is a Hixie-only hack! 852 **/ 853rfbBool 854webSocketCheckDisconnect(rfbClientPtr cl) 855{ 856 ws_ctx_t *wsctx = (ws_ctx_t *)cl->wsctx; 857 /* With Base64 encoding we need at least 4 bytes */ 858 char peekbuf[4]; 859 int n; 860 861 if (wsctx->version == WEBSOCKETS_VERSION_HYBI) 862 return FALSE; 863 864 if (cl->sslctx) 865 n = rfbssl_peek(cl, peekbuf, 4); 866 else 867 n = recv(cl->sock, peekbuf, 4, MSG_PEEK); 868 869 if (n <= 0) { 870 if (n != 0) 871 rfbErr("%s: peek; %m", __func__); 872 rfbCloseClient(cl); 873 return TRUE; 874 } 875 876 if (peekbuf[0] == '\xff') { 877 int doclose = 0; 878 /* Make sure we don't miss a client disconnect on an end frame 879 * marker. Because we use a peek buffer in some cases it is not 880 * applicable to wait for more data per select(). */ 881 switch (n) { 882 case 3: 883 if (peekbuf[1] == '\xff' && peekbuf[2] == '\x00') 884 doclose = 1; 885 break; 886 case 2: 887 if (peekbuf[1] == '\x00') 888 doclose = 1; 889 break; 890 default: 891 return FALSE; 892 } 893 894 if (cl->sslctx) 895 n = rfbssl_read(cl, peekbuf, n); 896 else 897 n = read(cl->sock, peekbuf, n); 898 899 if (doclose) { 900 rfbErr("%s: websocket close frame received\n", __func__); 901 rfbCloseClient(cl); 902 } 903 return TRUE; 904 } 905 return FALSE; 906} 907 908