diff options
Diffstat (limited to 'libvncserver/websockets.c')
| -rwxr-xr-x | libvncserver/websockets.c | 300 | 
1 files changed, 125 insertions, 175 deletions
| diff --git a/libvncserver/websockets.c b/libvncserver/websockets.c index da00522..0cce3c9 100755 --- a/libvncserver/websockets.c +++ b/libvncserver/websockets.c @@ -31,8 +31,10 @@  /* errno */  #include <errno.h> -#include <md5.h>  #include <byteswap.h> +#include <string.h> +#include "md5.h" +#include "sha1.h"  #include "rfbconfig.h"  #include "rfbssl.h" @@ -58,10 +60,12 @@ enum {    WEBSOCKETS_VERSION_HYBI  }; +#if 0  #include <sys/syscall.h>  static int gettid() {      return (int)syscall(SYS_gettid);  } +#endif  typedef int (*wsEncodeFunc)(rfbClientPtr cl, const char *src, int len, char **dst);  typedef int (*wsDecodeFunc)(rfbClientPtr cl, char *dst, int len); @@ -162,23 +166,36 @@ min (int a, int b) {      return a < b ? a : b;  } -#ifdef LIBVNCSERVER_WITH_CLIENT_GCRYPT -#else -#include <openssl/sha.h> -static void webSocketsGenSha1Key(char *target, int size, char *key) +void +webSocketsGenSha1Key(char * target, int size, char *key)  { -    SHA_CTX c; -    unsigned char tmp[SHA_DIGEST_LENGTH]; - -    SHA1_Init(&c); -    SHA1_Update(&c, key, strlen(key)); -    SHA1_Update(&c, GUID, sizeof(GUID) - 1); -    SHA1_Final(tmp, &c); -    if (-1 == __b64_ntop(tmp, SHA_DIGEST_LENGTH, target, size)) -	rfbErr("b64_ntop failed\n"); +    int len; +    SHA1Context sha; +    uint8_t digest[SHA1HashSize]; +     +    if (size < B64LEN(SHA1HashSize) + 1) { +        rfbErr("webSocketsGenSha1Key: not enough space in target\n"); +        target[0] = '\0'; +        return; +    } + +    SHA1Reset(&sha); +    SHA1Input(&sha, (unsigned char *)key, strlen(key)); +    SHA1Input(&sha, (unsigned char *)GUID, strlen(GUID)); +    SHA1Result(&sha, digest); + +    len = __b64_ntop((unsigned char *)digest, SHA1HashSize, target, size); +    if (len < size - 1) { +        rfbErr("webSocketsGenSha1Key: b64_ntop failed\n"); +        target[0] = '\0'; +        return; +    } + +    target[len] = '\0'; +    return;  } -#endif +  /*   * rfbWebSocketsHandshake is called to handle new WebSockets connections @@ -237,7 +254,7 @@ static rfbBool  webSocketsHandshake(rfbClientPtr cl, char *scheme)  {      char *buf, *response, *line; -    int n, linestart = 0, len = 0, llen, base64 = 0; +    int n, linestart = 0, len = 0, llen, base64 = TRUE;      char prefix[5], trailer[17];      char *path = NULL, *host = NULL, *origin = NULL, *protocol = NULL;      char *key1 = NULL, *key2 = NULL, *key3 = NULL; @@ -268,6 +285,8 @@ webSocketsHandshake(rfbClientPtr cl, char *scheme)                  rfbLog("webSocketsHandshake: client gone\n");              else                  rfbLogPerror("webSocketsHandshake: read"); +            free(response); +            free(buf);              return FALSE;          } @@ -285,6 +304,8 @@ webSocketsHandshake(rfbClientPtr cl, char *scheme)                              rfbLog("webSocketsHandshake: client gone\n");                          else                              rfbLogPerror("webSocketsHandshake: read"); +                        free(response); +                        free(buf);                          return FALSE;                      }                      rfbLog("Got key3\n"); @@ -298,7 +319,6 @@ webSocketsHandshake(rfbClientPtr cl, char *scheme)                  /* 16 = 4 ("GET ") + 1 ("/.*") + 11 (" HTTP/1.1\r\n") */                  path = line+4;                  buf[len-11] = '\0'; /* Trim trailing " HTTP/1.1\r\n" */ -                base64 = TRUE;                  cl->wspath = strdup(path);                  /* rfbLog("Got path: %s\n", path); */              } else if ((strncasecmp("host: ", line, min(llen,6))) == 0) { @@ -345,14 +365,25 @@ webSocketsHandshake(rfbClientPtr cl, char *scheme)          return FALSE;      } -    /* -    if ((!protocol) || (!strcasestr(protocol, "base64"))) { -        rfbErr("webSocketsHandshake: base64 subprotocol not supported by client\n"); -        free(response); -        free(buf); -        return FALSE; +    if ((protocol) && (strstr(protocol, "binary"))) { +        if (! sec_ws_version) { +            rfbErr("webSocketsHandshake: 'binary' protocol not supported with Hixie\n"); +            free(response); +            free(buf); +            return FALSE; +        } +        rfbLog("  - webSocketsHandshake: using binary/raw encoding\n"); +        base64 = FALSE; +        protocol = "binary"; +    } else { +        rfbLog("  - webSocketsHandshake: using base64 encoding\n"); +        base64 = TRUE; +        if ((protocol) && (strstr(protocol, "base64"))) { +            protocol = "base64"; +        } else { +            protocol = ""; +        }      } -    */      /*       * Generate the WebSockets server response based on the the headers sent @@ -360,7 +391,7 @@ webSocketsHandshake(rfbClientPtr cl, char *scheme)       */      if (sec_ws_version) { -	char accept[SHA_DIGEST_LENGTH * 3]; +	char accept[B64LEN(SHA1HashSize) + 1];  	rfbLog("  - WebSockets client version hybi-%02d\n", sec_ws_version);  	webSocketsGenSha1Key(accept, sizeof(accept), sec_ws_key);  	len = snprintf(response, WEBSOCKETS_MAX_HANDSHAKE_LEN, @@ -457,38 +488,16 @@ webSocketsGenMd5(char * target, char *key1, char *key2, char *key3)  static int  webSocketsEncodeHixie(rfbClientPtr cl, const char *src, int len, char **dst)  { -    int i, sz = 0; -    unsigned char chr; +    int sz = 0;      ws_ctx_t *wsctx = (ws_ctx_t *)cl->wsctx;      wsctx->encodeBuf[sz++] = '\x00'; -    if (wsctx->base64) { -        len = __b64_ntop((unsigned char *)src, len, wsctx->encodeBuf+sz, sizeof(wsctx->encodeBuf) - (sz + 1)); -        if (len < 0) { -            return len; -        } -        sz += len; -    } else { -        for (i=0; i < len; i++) { -            chr = src[i]; -            if (chr < 128) { -                if (chr == 0x00) { -                    wsctx->encodeBuf[sz++] = '\xc4'; -                    wsctx->encodeBuf[sz++] = '\x80'; -                } else { -                    wsctx->encodeBuf[sz++] = chr; -                } -            } else { -                if (chr < 192) { -                    wsctx->encodeBuf[sz++] = '\xc2'; -                    wsctx->encodeBuf[sz++] = chr; -                } else { -                    wsctx->encodeBuf[sz++] = '\xc3'; -                    wsctx->encodeBuf[sz++] = chr - 64; -                } -            } -        } +    len = __b64_ntop((unsigned char *)src, len, wsctx->encodeBuf+sz, sizeof(wsctx->encodeBuf) - (sz + 1)); +    if (len < 0) { +        return len;      } +    sz += len; +      wsctx->encodeBuf[sz++] = '\xff';      *dst = wsctx->encodeBuf;      return sz; @@ -524,9 +533,8 @@ ws_peek(rfbClientPtr cl, char *buf, int len)  static int  webSocketsDecodeHixie(rfbClientPtr cl, char *dst, int len)  { -    int retlen = 0, n, i, avail, modlen, needlen, actual; +    int retlen = 0, n, i, avail, modlen, needlen;      char *buf, *end = NULL; -    unsigned char chr, chr2;      ws_ctx_t *wsctx = (ws_ctx_t *)cl->wsctx;      buf = wsctx->decodeBuf; @@ -539,133 +547,75 @@ webSocketsDecodeHixie(rfbClientPtr cl, char *dst, int len)      } -    if (wsctx->base64) { -        /* Base64 encoded WebSockets stream */ +    /* Base64 encoded WebSockets stream */ -        if (buf[0] == '\xff') { -            i = ws_read(cl, buf, 1); /* Consume marker */ -            buf++; -            n--; -        } -        if (n == 0) { -            errno = EAGAIN; -            return -1; -        } -        if (buf[0] == '\x00') { -            i = ws_read(cl, buf, 1); /* Consume marker */ -            buf++; -            n--; -        } -        if (n == 0) { -            errno = EAGAIN; -            return -1; -        } - -        /* end = memchr(buf, '\xff', len*2+2); */ -        end = memchr(buf, '\xff', n); -        if (!end) { -            end = buf + n; -        } -        avail = end - buf; - -        len -= wsctx->carrylen; - -        /* Determine how much base64 data we need */ -        modlen = len + (len+2)/3; -        needlen = modlen; -        if (needlen % 4) { -            needlen += 4 - (needlen % 4); -        } +    if (buf[0] == '\xff') { +        i = ws_read(cl, buf, 1); /* Consume marker */ +        buf++; +        n--; +    } +    if (n == 0) { +        errno = EAGAIN; +        return -1; +    } +    if (buf[0] == '\x00') { +        i = ws_read(cl, buf, 1); /* Consume marker */ +        buf++; +        n--; +    } +    if (n == 0) { +        errno = EAGAIN; +        return -1; +    } -        if (needlen > avail) { -            /* rfbLog("Waiting for more base64 data\n"); */ -            errno = EAGAIN; -            return -1; -        } +    /* end = memchr(buf, '\xff', len*2+2); */ +    end = memchr(buf, '\xff', n); +    if (!end) { +        end = buf + n; +    } +    avail = end - buf; -        /* Any carryover from previous decode */ -        for (i=0; i < wsctx->carrylen; i++) { -	    /* rfbLog("Adding carryover %d\n", wsctx->carryBuf[i]); */ -            dst[i] = wsctx->carryBuf[i]; -            retlen += 1; -        } +    len -= wsctx->carrylen; -        /* Decode the rest of what we need */ -        buf[needlen] = '\x00';  /* Replace end marker with end of string */ -        /* rfbLog("buf: %s\n", buf); */ -        n = __b64_pton(buf, (unsigned char *)dst+retlen, 2+len); -        if (n < len) { -            rfbErr("Base64 decode error\n"); -            errno = EIO; -            return -1; -        } -        retlen += n; +    /* Determine how much base64 data we need */ +    modlen = len + (len+2)/3; +    needlen = modlen; +    if (needlen % 4) { +        needlen += 4 - (needlen % 4); +    } -        /* Consume the data from socket */ -        i = ws_read(cl, buf, needlen); +    if (needlen > avail) { +        /* rfbLog("Waiting for more base64 data\n"); */ +        errno = EAGAIN; +        return -1; +    } -        wsctx->carrylen = n - len; -        retlen -= wsctx->carrylen; -        for (i=0; i < wsctx->carrylen; i++) { -            /* rfbLog("Saving carryover %d\n", dst[retlen + i]); */ -            wsctx->carryBuf[i] = dst[retlen + i]; -        } -    } else { -        /* UTF-8 encoded WebSockets stream */ - -        actual = 0; -        for (needlen = 0; needlen < n && actual < len; needlen++) { -            chr = buf[needlen]; -            if ((chr > 0) && (chr < 128)) { -                actual++; -            } else if ((chr > 127) && (chr < 255)) { -                if (needlen + 1 >= n) { -                    break; -                } -                needlen++; -                actual++; -            } -        } +    /* Any carryover from previous decode */ +    for (i=0; i < wsctx->carrylen; i++) { +        /* rfbLog("Adding carryover %d\n", wsctx->carryBuf[i]); */ +        dst[i] = wsctx->carryBuf[i]; +        retlen += 1; +    } -        if (actual < len) { -            errno = EAGAIN; -            return -1; -        } +    /* Decode the rest of what we need */ +    buf[needlen] = '\x00';  /* Replace end marker with end of string */ +    /* rfbLog("buf: %s\n", buf); */ +    n = __b64_pton(buf, (unsigned char *)dst+retlen, 2+len); +    if (n < len) { +        rfbErr("Base64 decode error\n"); +        errno = EIO; +        return -1; +    } +    retlen += n; -        /* Consume what we need */ -        if ((n = ws_read(cl, buf, needlen)) < needlen) { -            return n; -        } +    /* Consume the data from socket */ +    i = ws_read(cl, buf, needlen); -        while (retlen < len) { -            chr = buf[0]; -            buf += 1; -            if (chr == 0) { -                /* Begin frame marker, just skip it */ -            } else if (chr == 255) { -                /* Begin frame marker, just skip it */ -	    } else if (chr < 128) { -                dst[retlen++] = chr; -            } else { -                chr2 = buf[0]; -                buf += 1; -                switch (chr) { -                case (unsigned char) '\xc2': -                    dst[retlen++] = chr2; -                    break; -                case (unsigned char) '\xc3': -                    dst[retlen++] = chr2 + 64; -                    break; -                case (unsigned char) '\xc4': -                    dst[retlen++] = 0; -                    break; -                default: -                    rfbErr("Invalid UTF-8 encoding\n"); -                    errno = EIO; -                    return -1; -                } -            } -        } +    wsctx->carrylen = n - len; +    retlen -= wsctx->carrylen; +    for (i=0; i < wsctx->carrylen; i++) { +        /* rfbLog("Saving carryover %d\n", dst[retlen + i]); */ +        wsctx->carryBuf[i] = dst[retlen + i];      }      /* rfbLog("<< webSocketsDecode, retlen: %d\n", retlen); */ @@ -675,7 +625,7 @@ webSocketsDecodeHixie(rfbClientPtr cl, char *dst, int len)  static int  webSocketsDecodeHybi(rfbClientPtr cl, char *dst, int len)  { -    char *buf, *payload, *rbuf; +    char *buf, *payload;      int ret = -1, result = -1;      int total = 0;      ws_mask_t mask; @@ -683,7 +633,7 @@ webSocketsDecodeHybi(rfbClientPtr cl, char *dst, int len)      int i, j;      unsigned char opcode;      ws_ctx_t *wsctx = (ws_ctx_t *)cl->wsctx; -    int flength, fin, fhlen, blen; +    int flength, fin, fhlen;      // 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)); | 
