From 04b3b2057c2bd2bc1425cadcea4180866c50f49e Mon Sep 17 00:00:00 2001 From: Joel Martin Date: Wed, 1 Feb 2012 19:10:39 -0600 Subject: [PATCH] HyBi/IETF 6455 support to C websockify. It's probably broken and it's definitely still messy in several ways, but basic tests work with Chrome. Several other C websockify cleanups: - Remove most of the non-thread safe global variable usage (still a little bit that could be fixed so that threading would be easier). - Remove wswrapper. It is unmaintained, out of date, and never worked well anyways (since it really needed a way to do asynchronous queued work but it was running in another process context making that hard). - Use md5 routines from openssl. - Remove md5.c and md5.h since no longer needed. Thanks to https://github.com/dew111 for spurring me on to get this done by writing code. I didn't end up using much his forked code, but having something there goaded me enough to just get it working. --- other/Makefile | 12 +- other/md5.c | 466 ------------------ other/md5.h | 148 ------ other/websocket.c | 430 +++++++++++----- other/websocket.h | 60 ++- other/websockify.c | 53 +- other/wswrapper.c | 1156 -------------------------------------------- other/wswrapper.h | 64 --- rebind.c | 2 +- 9 files changed, 403 insertions(+), 1988 deletions(-) delete mode 100644 other/md5.c delete mode 100644 other/md5.h delete mode 100644 other/wswrapper.c delete mode 100644 other/wswrapper.h diff --git a/other/Makefile b/other/Makefile index e39405c..8e4a297 100644 --- a/other/Makefile +++ b/other/Makefile @@ -3,19 +3,11 @@ CFLAGS += -fPIC all: $(TARGETS) -websockify: websockify.o websocket.o md5.o +websockify: websockify.o websocket.o $(CC) $(LDFLAGS) $^ -lssl -lcrypto -lresolv -o $@ -wswrapper.o: wswrapper.h -wswrapper.so: wswrapper.o md5.o - $(CC) $(LDFLAGS) $^ -shared -fPIC -ldl -lresolv -o $@ - -websocket.o: websocket.c websocket.h md5.h +websocket.o: websocket.c websocket.h websockify.o: websockify.c websocket.h -wswrapper.o: wswrapper.c - $(CC) -c $(CFLAGS) -o $@ $*.c -md5.o: md5.c md5.h - $(CC) -c $(CFLAGS) -o $@ $*.c -DHAVE_MEMCPY -DSTDC_HEADERS kumina: kumina.o $(CC) $(LDFLAGS) $^ -lresolv -lssl -o $@ diff --git a/other/md5.c b/other/md5.c deleted file mode 100644 index dcdc368..0000000 --- a/other/md5.c +++ /dev/null @@ -1,466 +0,0 @@ -/* Functions to compute MD5 message digest of files or memory blocks. - according to the definition of MD5 in RFC 1321 from April 1992. - Copyright (C) 1995,1996,1997,1999,2000,2001,2005 - Free Software Foundation, Inc. - This file is part of the GNU C Library. - - The GNU C Library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - The GNU C Library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with the GNU C Library; if not, write to the Free - Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - 02111-1307 USA. */ - -/* Written by Ulrich Drepper , 1995. */ - -#ifdef HAVE_CONFIG_H -# include -#endif - -#include - -#if STDC_HEADERS || defined _LIBC -# include -# include -#else -# ifndef HAVE_MEMCPY -# define memcpy(d, s, n) (bcopy ((s), (d), (n)), (d)) -# endif -#endif - -#ifndef __THROW -#define __THROW -#endif - -#include "md5.h" - -#ifdef _LIBC -# include -# if __BYTE_ORDER == __BIG_ENDIAN -# define WORDS_BIGENDIAN 1 -# endif -/* We need to keep the namespace clean so define the MD5 function - protected using leading __ . */ -# define md5_init_ctx __md5_init_ctx -# define md5_process_block __md5_process_block -# define md5_process_bytes __md5_process_bytes -# define md5_finish_ctx __md5_finish_ctx -# define md5_read_ctx __md5_read_ctx -# define md5_stream __md5_stream -# define md5_buffer __md5_buffer -#else -/* Squelch compiler complaints */ -void md5_process_bytes (const void *buffer, size_t len, struct md5_ctx *ctx); -void md5_process_block (const void *buffer, size_t len, struct md5_ctx *ctx); -#endif - -#ifdef WORDS_BIGENDIAN -# define SWAP(n) \ - (((n) << 24) | (((n) & 0xff00) << 8) | (((n) >> 8) & 0xff00) | ((n) >> 24)) -#else -# define SWAP(n) (n) -#endif - - -/* This array contains the bytes used to pad the buffer to the next - 64-byte boundary. (RFC 1321, 3.1: Step 1) */ -static const unsigned char fillbuf[64] = { 0x80, 0 /* , 0, 0, ... */ }; - - -/* Initialize structure containing state of computation. - (RFC 1321, 3.3: Step 3) */ -void -md5_init_ctx (ctx) - struct md5_ctx *ctx; -{ - ctx->A = 0x67452301; - ctx->B = 0xefcdab89; - ctx->C = 0x98badcfe; - ctx->D = 0x10325476; - - ctx->total[0] = ctx->total[1] = 0; - ctx->buflen = 0; -} - -/* Put result from CTX in first 16 bytes following RESBUF. The result - must be in little endian byte order. - - IMPORTANT: On some systems it is required that RESBUF is correctly - aligned for a 32 bits value. */ -void * -md5_read_ctx (ctx, resbuf) - const struct md5_ctx *ctx; - void *resbuf; -{ - ((md5_uint32 *) resbuf)[0] = SWAP (ctx->A); - ((md5_uint32 *) resbuf)[1] = SWAP (ctx->B); - ((md5_uint32 *) resbuf)[2] = SWAP (ctx->C); - ((md5_uint32 *) resbuf)[3] = SWAP (ctx->D); - - return resbuf; -} - -/* Process the remaining bytes in the internal buffer and the usual - prolog according to the standard and write the result to RESBUF. - - IMPORTANT: On some systems it is required that RESBUF is correctly - aligned for a 32 bits value. */ -void * -md5_finish_ctx (ctx, resbuf) - struct md5_ctx *ctx; - void *resbuf; -{ - /* Take yet unprocessed bytes into account. */ - md5_uint32 bytes = ctx->buflen; - size_t pad; - - /* Now count remaining bytes. */ - ctx->total[0] += bytes; - if (ctx->total[0] < bytes) - ++ctx->total[1]; - - pad = bytes >= 56 ? 64 + 56 - bytes : 56 - bytes; - memcpy (&ctx->buffer[bytes], fillbuf, pad); - - /* Put the 64-bit file length in *bits* at the end of the buffer. */ - *(md5_uint32 *) &ctx->buffer[bytes + pad] = SWAP (ctx->total[0] << 3); - *(md5_uint32 *) &ctx->buffer[bytes + pad + 4] = SWAP ((ctx->total[1] << 3) | - (ctx->total[0] >> 29)); - - /* Process last bytes. */ - md5_process_block (ctx->buffer, bytes + pad + 8, ctx); - - return md5_read_ctx (ctx, resbuf); -} - -/* Compute MD5 message digest for bytes read from STREAM. The - resulting message digest number will be written into the 16 bytes - beginning at RESBLOCK. */ -int -md5_stream (stream, resblock) - FILE *stream; - void *resblock; -{ - /* Important: BLOCKSIZE must be a multiple of 64. */ -#define BLOCKSIZE 4096 - struct md5_ctx ctx; - char buffer[BLOCKSIZE + 72]; - size_t sum; - - /* Initialize the computation context. */ - md5_init_ctx (&ctx); - - /* Iterate over full file contents. */ - while (1) - { - /* We read the file in blocks of BLOCKSIZE bytes. One call of the - computation function processes the whole buffer so that with the - next round of the loop another block can be read. */ - size_t n; - sum = 0; - - /* Read block. Take care for partial reads. */ - do - { - n = fread (buffer + sum, 1, BLOCKSIZE - sum, stream); - - sum += n; - } - while (sum < BLOCKSIZE && n != 0); - if (n == 0 && ferror (stream)) - return 1; - - /* If end of file is reached, end the loop. */ - if (n == 0) - break; - - /* Process buffer with BLOCKSIZE bytes. Note that - BLOCKSIZE % 64 == 0 - */ - md5_process_block (buffer, BLOCKSIZE, &ctx); - } - - /* Add the last bytes if necessary. */ - if (sum > 0) - md5_process_bytes (buffer, sum, &ctx); - - /* Construct result in desired memory. */ - md5_finish_ctx (&ctx, resblock); - return 0; -} - -/* Compute MD5 message digest for LEN bytes beginning at BUFFER. The - result is always in little endian byte order, so that a byte-wise - output yields to the wanted ASCII representation of the message - digest. */ -void * -md5_buffer (buffer, len, resblock) - const char *buffer; - size_t len; - void *resblock; -{ - struct md5_ctx ctx; - - /* Initialize the computation context. */ - md5_init_ctx (&ctx); - - /* Process whole buffer but last len % 64 bytes. */ - md5_process_bytes (buffer, len, &ctx); - - /* Put result in desired memory area. */ - return md5_finish_ctx (&ctx, resblock); -} - - -void -md5_process_bytes (buffer, len, ctx) - const void *buffer; - size_t len; - struct md5_ctx *ctx; -{ - /* When we already have some bits in our internal buffer concatenate - both inputs first. */ - if (ctx->buflen != 0) - { - size_t left_over = ctx->buflen; - size_t add = 128 - left_over > len ? len : 128 - left_over; - - memcpy (&ctx->buffer[left_over], buffer, add); - ctx->buflen += add; - - if (ctx->buflen > 64) - { - md5_process_block (ctx->buffer, ctx->buflen & ~63, ctx); - - ctx->buflen &= 63; - /* The regions in the following copy operation cannot overlap. */ - memcpy (ctx->buffer, &ctx->buffer[(left_over + add) & ~63], - ctx->buflen); - } - - buffer = (const char *) buffer + add; - len -= add; - } - - /* Process available complete blocks. */ - if (len >= 64) - { -#if !_STRING_ARCH_unaligned -/* To check alignment gcc has an appropriate operator. Other - compilers don't. */ -# if __GNUC__ >= 2 -# define UNALIGNED_P(p) (((md5_uintptr) p) % __alignof__ (md5_uint32) != 0) -# else -# define UNALIGNED_P(p) (((md5_uintptr) p) % sizeof (md5_uint32) != 0) -# endif - if (UNALIGNED_P (buffer)) - while (len > 64) - { - md5_process_block (memcpy (ctx->buffer, buffer, 64), 64, ctx); - buffer = (const char *) buffer + 64; - len -= 64; - } - else -#endif - { - md5_process_block (buffer, len & ~63, ctx); - buffer = (const char *) buffer + (len & ~63); - len &= 63; - } - } - - /* Move remaining bytes in internal buffer. */ - if (len > 0) - { - size_t left_over = ctx->buflen; - - memcpy (&ctx->buffer[left_over], buffer, len); - left_over += len; - if (left_over >= 64) - { - md5_process_block (ctx->buffer, 64, ctx); - left_over -= 64; - memcpy (ctx->buffer, &ctx->buffer[64], left_over); - } - ctx->buflen = left_over; - } -} - - -/* These are the four functions used in the four steps of the MD5 algorithm - and defined in the RFC 1321. The first function is a little bit optimized - (as found in Colin Plumbs public domain implementation). */ -/* #define FF(b, c, d) ((b & c) | (~b & d)) */ -#define FF(b, c, d) (d ^ (b & (c ^ d))) -#define FG(b, c, d) FF (d, b, c) -#define FH(b, c, d) (b ^ c ^ d) -#define FI(b, c, d) (c ^ (b | ~d)) - -/* Process LEN bytes of BUFFER, accumulating context into CTX. - It is assumed that LEN % 64 == 0. */ - -void -md5_process_block (buffer, len, ctx) - const void *buffer; - size_t len; - struct md5_ctx *ctx; -{ - md5_uint32 correct_words[16]; - const md5_uint32 *words = buffer; - size_t nwords = len / sizeof (md5_uint32); - const md5_uint32 *endp = words + nwords; - md5_uint32 A = ctx->A; - md5_uint32 B = ctx->B; - md5_uint32 C = ctx->C; - md5_uint32 D = ctx->D; - - /* First increment the byte count. RFC 1321 specifies the possible - length of the file up to 2^64 bits. Here we only compute the - number of bytes. Do a double word increment. */ - ctx->total[0] += len; - if (ctx->total[0] < len) - ++ctx->total[1]; - - /* Process all bytes in the buffer with 64 bytes in each round of - the loop. */ - while (words < endp) - { - md5_uint32 *cwp = correct_words; - md5_uint32 A_save = A; - md5_uint32 B_save = B; - md5_uint32 C_save = C; - md5_uint32 D_save = D; - - /* First round: using the given function, the context and a constant - the next context is computed. Because the algorithms processing - unit is a 32-bit word and it is determined to work on words in - little endian byte order we perhaps have to change the byte order - before the computation. To reduce the work for the next steps - we store the swapped words in the array CORRECT_WORDS. */ - -#define OP(a, b, c, d, s, T) \ - do \ - { \ - a += FF (b, c, d) + (*cwp++ = SWAP (*words)) + T; \ - ++words; \ - CYCLIC (a, s); \ - a += b; \ - } \ - while (0) - - /* It is unfortunate that C does not provide an operator for - cyclic rotation. Hope the C compiler is smart enough. */ -#define CYCLIC(w, s) (w = (w << s) | (w >> (32 - s))) - - /* Before we start, one word to the strange constants. - They are defined in RFC 1321 as - - T[i] = (int) (4294967296.0 * fabs (sin (i))), i=1..64 - */ - - /* Round 1. */ - OP (A, B, C, D, 7, 0xd76aa478); - OP (D, A, B, C, 12, 0xe8c7b756); - OP (C, D, A, B, 17, 0x242070db); - OP (B, C, D, A, 22, 0xc1bdceee); - OP (A, B, C, D, 7, 0xf57c0faf); - OP (D, A, B, C, 12, 0x4787c62a); - OP (C, D, A, B, 17, 0xa8304613); - OP (B, C, D, A, 22, 0xfd469501); - OP (A, B, C, D, 7, 0x698098d8); - OP (D, A, B, C, 12, 0x8b44f7af); - OP (C, D, A, B, 17, 0xffff5bb1); - OP (B, C, D, A, 22, 0x895cd7be); - OP (A, B, C, D, 7, 0x6b901122); - OP (D, A, B, C, 12, 0xfd987193); - OP (C, D, A, B, 17, 0xa679438e); - OP (B, C, D, A, 22, 0x49b40821); - - /* For the second to fourth round we have the possibly swapped words - in CORRECT_WORDS. Redefine the macro to take an additional first - argument specifying the function to use. */ -#undef OP -#define OP(f, a, b, c, d, k, s, T) \ - do \ - { \ - a += f (b, c, d) + correct_words[k] + T; \ - CYCLIC (a, s); \ - a += b; \ - } \ - while (0) - - /* Round 2. */ - OP (FG, A, B, C, D, 1, 5, 0xf61e2562); - OP (FG, D, A, B, C, 6, 9, 0xc040b340); - OP (FG, C, D, A, B, 11, 14, 0x265e5a51); - OP (FG, B, C, D, A, 0, 20, 0xe9b6c7aa); - OP (FG, A, B, C, D, 5, 5, 0xd62f105d); - OP (FG, D, A, B, C, 10, 9, 0x02441453); - OP (FG, C, D, A, B, 15, 14, 0xd8a1e681); - OP (FG, B, C, D, A, 4, 20, 0xe7d3fbc8); - OP (FG, A, B, C, D, 9, 5, 0x21e1cde6); - OP (FG, D, A, B, C, 14, 9, 0xc33707d6); - OP (FG, C, D, A, B, 3, 14, 0xf4d50d87); - OP (FG, B, C, D, A, 8, 20, 0x455a14ed); - OP (FG, A, B, C, D, 13, 5, 0xa9e3e905); - OP (FG, D, A, B, C, 2, 9, 0xfcefa3f8); - OP (FG, C, D, A, B, 7, 14, 0x676f02d9); - OP (FG, B, C, D, A, 12, 20, 0x8d2a4c8a); - - /* Round 3. */ - OP (FH, A, B, C, D, 5, 4, 0xfffa3942); - OP (FH, D, A, B, C, 8, 11, 0x8771f681); - OP (FH, C, D, A, B, 11, 16, 0x6d9d6122); - OP (FH, B, C, D, A, 14, 23, 0xfde5380c); - OP (FH, A, B, C, D, 1, 4, 0xa4beea44); - OP (FH, D, A, B, C, 4, 11, 0x4bdecfa9); - OP (FH, C, D, A, B, 7, 16, 0xf6bb4b60); - OP (FH, B, C, D, A, 10, 23, 0xbebfbc70); - OP (FH, A, B, C, D, 13, 4, 0x289b7ec6); - OP (FH, D, A, B, C, 0, 11, 0xeaa127fa); - OP (FH, C, D, A, B, 3, 16, 0xd4ef3085); - OP (FH, B, C, D, A, 6, 23, 0x04881d05); - OP (FH, A, B, C, D, 9, 4, 0xd9d4d039); - OP (FH, D, A, B, C, 12, 11, 0xe6db99e5); - OP (FH, C, D, A, B, 15, 16, 0x1fa27cf8); - OP (FH, B, C, D, A, 2, 23, 0xc4ac5665); - - /* Round 4. */ - OP (FI, A, B, C, D, 0, 6, 0xf4292244); - OP (FI, D, A, B, C, 7, 10, 0x432aff97); - OP (FI, C, D, A, B, 14, 15, 0xab9423a7); - OP (FI, B, C, D, A, 5, 21, 0xfc93a039); - OP (FI, A, B, C, D, 12, 6, 0x655b59c3); - OP (FI, D, A, B, C, 3, 10, 0x8f0ccc92); - OP (FI, C, D, A, B, 10, 15, 0xffeff47d); - OP (FI, B, C, D, A, 1, 21, 0x85845dd1); - OP (FI, A, B, C, D, 8, 6, 0x6fa87e4f); - OP (FI, D, A, B, C, 15, 10, 0xfe2ce6e0); - OP (FI, C, D, A, B, 6, 15, 0xa3014314); - OP (FI, B, C, D, A, 13, 21, 0x4e0811a1); - OP (FI, A, B, C, D, 4, 6, 0xf7537e82); - OP (FI, D, A, B, C, 11, 10, 0xbd3af235); - OP (FI, C, D, A, B, 2, 15, 0x2ad7d2bb); - OP (FI, B, C, D, A, 9, 21, 0xeb86d391); - - /* Add the starting values of the context. */ - A += A_save; - B += B_save; - C += C_save; - D += D_save; - } - - /* Put checksum in context given as argument. */ - ctx->A = A; - ctx->B = B; - ctx->C = C; - ctx->D = D; -} diff --git a/other/md5.h b/other/md5.h deleted file mode 100644 index b48545b..0000000 --- a/other/md5.h +++ /dev/null @@ -1,148 +0,0 @@ -/* Declaration of functions and data types used for MD5 sum computing - library functions. - Copyright (C) 1995-1997,1999,2000,2001,2004,2005 - Free Software Foundation, Inc. - This file is part of the GNU C Library. - - The GNU C Library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - The GNU C Library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with the GNU C Library; if not, write to the Free - Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - 02111-1307 USA. */ - -#ifndef _MD5_H -#define _MD5_H 1 - -#include - -#if defined HAVE_LIMITS_H || _LIBC -# include -#endif - -#define MD5_DIGEST_SIZE 16 -#define MD5_BLOCK_SIZE 64 - -/* The following contortions are an attempt to use the C preprocessor - to determine an unsigned integral type that is 32 bits wide. An - alternative approach is to use autoconf's AC_CHECK_SIZEOF macro, but - doing that would require that the configure script compile and *run* - the resulting executable. Locally running cross-compiled executables - is usually not possible. */ - -#ifdef _LIBC -# include -typedef uint32_t md5_uint32; -typedef uintptr_t md5_uintptr; -#else -# if defined __STDC__ && __STDC__ -# define UINT_MAX_32_BITS 4294967295U -# else -# define UINT_MAX_32_BITS 0xFFFFFFFF -# endif - -/* If UINT_MAX isn't defined, assume it's a 32-bit type. - This should be valid for all systems GNU cares about because - that doesn't include 16-bit systems, and only modern systems - (that certainly have ) have 64+-bit integral types. */ - -# ifndef UINT_MAX -# define UINT_MAX UINT_MAX_32_BITS -# endif - -# if UINT_MAX == UINT_MAX_32_BITS - typedef unsigned int md5_uint32; -# else -# if USHRT_MAX == UINT_MAX_32_BITS - typedef unsigned short md5_uint32; -# else -# if ULONG_MAX == UINT_MAX_32_BITS - typedef unsigned long md5_uint32; -# else - /* The following line is intended to evoke an error. - Using #error is not portable enough. */ - "Cannot determine unsigned 32-bit data type." -# endif -# endif -# endif -/* We have to make a guess about the integer type equivalent in size - to pointers which should always be correct. */ -typedef unsigned long int md5_uintptr; -#endif - -/* Structure to save state of computation between the single steps. */ -struct md5_ctx -{ - md5_uint32 A; - md5_uint32 B; - md5_uint32 C; - md5_uint32 D; - - md5_uint32 total[2]; - md5_uint32 buflen; - char buffer[128] __attribute__ ((__aligned__ (__alignof__ (md5_uint32)))); -}; - -/* - * The following three functions are build up the low level used in - * the functions `md5_stream' and `md5_buffer'. - */ - -/* Initialize structure containing state of computation. - (RFC 1321, 3.3: Step 3) */ -extern void __md5_init_ctx (struct md5_ctx *ctx) __THROW; - -/* Starting with the result of former calls of this function (or the - initialization function update the context for the next LEN bytes - starting at BUFFER. - It is necessary that LEN is a multiple of 64!!! */ -extern void __md5_process_block (const void *buffer, size_t len, - struct md5_ctx *ctx) __THROW; - -/* Starting with the result of former calls of this function (or the - initialization function update the context for the next LEN bytes - starting at BUFFER. - It is NOT required that LEN is a multiple of 64. */ -extern void __md5_process_bytes (const void *buffer, size_t len, - struct md5_ctx *ctx) __THROW; - -/* Process the remaining bytes in the buffer and put result from CTX - in first 16 bytes following RESBUF. The result is always in little - endian byte order, so that a byte-wise output yields to the wanted - ASCII representation of the message digest. - - IMPORTANT: On some systems it is required that RESBUF is correctly - aligned for a 32 bits value. */ -extern void *__md5_finish_ctx (struct md5_ctx *ctx, void *resbuf) __THROW; - - -/* Put result from CTX in first 16 bytes following RESBUF. The result is - always in little endian byte order, so that a byte-wise output yields - to the wanted ASCII representation of the message digest. - - IMPORTANT: On some systems it is required that RESBUF is correctly - aligned for a 32 bits value. */ -extern void *__md5_read_ctx (const struct md5_ctx *ctx, void *resbuf) __THROW; - - -/* Compute MD5 message digest for bytes read from STREAM. The - resulting message digest number will be written into the 16 bytes - beginning at RESBLOCK. */ -extern int __md5_stream (FILE *stream, void *resblock) __THROW; - -/* Compute MD5 message digest for LEN bytes beginning at BUFFER. The - result is always in little endian byte order, so that a byte-wise - output yields to the wanted ASCII representation of the message - digest. */ -extern void *__md5_buffer (const char *buffer, size_t len, - void *resblock) __THROW; - -#endif /* md5.h */ diff --git a/other/websocket.c b/other/websocket.c index dc2e841..b2902ff 100644 --- a/other/websocket.c +++ b/other/websocket.c @@ -22,18 +22,10 @@ #include #include #include /* base64 encode/decode */ +#include /* md5 hash */ +#include /* sha1 hash */ #include "websocket.h" -const char server_handshake[] = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n\ -Upgrade: WebSocket\r\n\ -Connection: Upgrade\r\n\ -%sWebSocket-Origin: %s\r\n\ -%sWebSocket-Location: %s://%s%s\r\n\ -%sWebSocket-Protocol: sample\r\n\ -\r\n%s"; - -const char policy_response[] = "\n"; - /* * Global state * @@ -41,10 +33,9 @@ const char policy_response[] = "sockfd = socket; + if (! (ctx = malloc(sizeof(ws_ctx_t))) ) + { fatal("malloc()"); } + + if (! (ctx->tbuf = malloc(BUFSIZE)) ) + { fatal("malloc of tbuf"); } + if (! (ctx->cbuf = malloc(BUFSIZE)) ) + { fatal("malloc of cbuf"); } + if (! (ctx->tbuf_tmp = malloc(BUFSIZE)) ) + { fatal("malloc of tbuf_tmp"); } + if (! (ctx->cbuf_tmp = malloc(BUFSIZE)) ) + { fatal("malloc of cbuf_tmp"); } + + ctx->headers = malloc(sizeof(headers_t)); ctx->ssl = NULL; ctx->ssl_ctx = NULL; return ctx; } -ws_ctx_t *ws_socket_ssl(int socket, char * certfile, char * keyfile) { +int free_ws_ctx(ws_ctx_t *ctx) { + free(ctx->tbuf); + free(ctx->cbuf); + free(ctx->tbuf_tmp); + free(ctx->cbuf_tmp); + free(ctx); +} + +ws_ctx_t *ws_socket(ws_ctx_t *ctx, int socket) { + ctx->sockfd = socket; +} + +ws_ctx_t *ws_socket_ssl(ws_ctx_t *ctx, int socket, char * certfile, char * keyfile) { int ret; char msg[1024]; char * use_keyfile; - ws_ctx_t *ctx; - ctx = ws_socket(socket); + ws_socket(ctx, socket); if (keyfile && (keyfile[0] != '\0')) { // Separate key file @@ -192,15 +205,14 @@ int ws_socket_free(ws_ctx_t *ctx) { close(ctx->sockfd); ctx->sockfd = 0; } - free(ctx); } /* ------------------------------------------------------- */ -int encode(u_char const *src, size_t srclength, char *target, size_t targsize) { - int i, sz = 0, len = 0; - unsigned char chr; +int encode_hixie(u_char const *src, size_t srclength, + char *target, size_t targsize) { + int sz = 0, len = 0; target[sz++] = '\x00'; len = b64_ntop(src, srclength, target+sz, targsize-sz); if (len < 0) { @@ -211,7 +223,8 @@ int encode(u_char const *src, size_t srclength, char *target, size_t targsize) { return sz; } -int decode(char *src, size_t srclength, u_char *target, size_t targsize) { +int decode_hixie(char *src, size_t srclength, + u_char *target, size_t targsize, unsigned int *opcode) { char *start, *end, cntstr[4]; int i, len, framecount = 0, retlen = 0; unsigned char chr; @@ -219,10 +232,19 @@ int decode(char *src, size_t srclength, u_char *target, size_t targsize) { handler_emsg("WebSocket framing error\n"); return -1; } + if (srclength == 2 && + (src[0] == '\xff') && + (src[1] == '\x00')) { + // client sent orderly close frame + *opcode = 0x8; // Close frame + return 0; + } + *opcode = 0x1; // Text frame + start = src+1; // Skip '\x00' start do { /* We may have more than one frame */ - end = memchr(start, '\xff', srclength); + end = (char *)memchr(start, '\xff', srclength); *end = '\x00'; len = b64_pton(start, target+retlen, targsize-retlen); if (len < 0) { @@ -239,9 +261,154 @@ int decode(char *src, size_t srclength, u_char *target, size_t targsize) { return retlen; } -int parse_handshake(char *handshake, headers_t *headers) { - char *start, *end; +int encode_hybi(u_char const *src, size_t srclength, + char *target, size_t targsize, unsigned int opcode) +{ + unsigned long long b64_sz, len_offset = 1, payload_offset = 2, len = 0; + + if ((int)srclength <= 0) + { + return 0; + } + b64_sz = ((srclength - 1) / 3) * 4 + 4; + + target[0] = (char)(opcode & 0x0F | 0x80); + + if (b64_sz <= 125) { + target[1] = (char) b64_sz; + payload_offset = 2; + } else if ((b64_sz > 125) && (b64_sz < 65536)) { + target[1] = (char) 126; + *(u_short*)&(target[2]) = htons(b64_sz); + payload_offset = 4; + } else { + handler_emsg("Sending frames larger than 65535 bytes not supported\n"); + return -1; + //target[1] = (char) 127; + //*(u_long*)&(target[2]) = htonl(b64_sz); + //payload_offset = 10; + } + + len = b64_ntop(src, srclength, target+payload_offset, targsize-payload_offset); + + if (len < 0) { + return len; + } + + return len + payload_offset; +} + +int decode_hybi(unsigned char *src, size_t srclength, + u_char *target, size_t targsize, unsigned int *opcode) +{ + unsigned char *frame, *mask, *payload, save_char, cntstr[4];; + int i = 0, len, framecount = 0; + unsigned int target_offset = 0, hdr_length = 0, payload_length = 0; + + if ((unsigned int) srclength <= 0) + { + return 0; + } + + frame = src; + + //printf("Deocde new frame\n"); + while (frame - src < srclength) { + framecount ++; + + *opcode = frame[0] & 0x0f; + //printf("opcode %d, frame[0..3]: 0x%x 0x%x 0x%x 0x%x\n", *opcode, + // (unsigned char) frame[0], + // (unsigned char) frame[1], + // (unsigned char) frame[2], + // (unsigned char) frame[3]); + + payload_length = frame[1] & 0x7f; + if (payload_length == 0) { + // TODO: next frame + handler_msg("empty frame\n"); + frame += 6; + continue; + } else if (payload_length < 126) { + hdr_length = 2; + //frame += 2 * sizeof(char); + } else if (payload_length == 126) { + payload_length = (frame[2] << 8) + frame[3]; + hdr_length = 4; + } else { + handler_emsg("Receiving frames larger than 65535 bytes not supported\n"); + return -1; + } + //printf(" payload_length: %u, raw remaining: %u\n", payload_length, (unsigned int) srclength - target_offset); + payload = frame + hdr_length + 4; + + if (*opcode == 0x8) { + // client sent orderly close frame + break; + } + + if (*opcode != 1 && *opcode != 2) { + handler_msg("Ignoring non-data frame, opcode 0x%x\n", *opcode); + frame = frame + hdr_length + 4 + payload_length; + continue; + } + + + if ((payload_length > 0) && (!(frame[1] & 0x80))) { + handler_emsg("Received unmasked payload from client\n"); + return -1; + } + + if ((hdr_length + 4 + payload_length) > (srclength - target_offset)) { + handler_emsg("Truncated frame received from client\n"); + return -1; + } + + // Terminate with a null for base64 decode + save_char = payload[payload_length]; + payload[payload_length] = '\0'; + + // unmask the data + mask = payload - 4; + for (i = 0; i < payload_length; i++) { + payload[i] ^= mask[i%4]; + } + + // base64 decode the data + len = b64_pton((const char*)payload, target+target_offset, targsize); + + // Restore the first character of the next frame + payload[payload_length] = save_char; + if (len < 0) { + handler_emsg("Base64 decode error code %d", len); + return len; + } + + //printf(" len %d, raw %s\n", len, frame); + + target_offset += len; + frame = frame + hdr_length + 4 + payload_length; + } + + if (framecount > 1) { + snprintf(cntstr, 3, "%d", framecount); + traffic(cntstr); + } + + return target_offset; +} + + + +int parse_handshake(ws_ctx_t *ws_ctx, char *handshake) { + char *start, *end; + headers_t *headers = ws_ctx->headers; + + headers->key1[0] = '\0'; + headers->key2[0] = '\0'; + headers->key3[0] = '\0'; + if ((strlen(handshake) < 92) || (bcmp(handshake, "GET ", 4) != 0)) { return 0; } @@ -258,92 +425,137 @@ int parse_handshake(char *handshake, headers_t *headers) { strncpy(headers->host, start, end-start); headers->host[end-start] = '\0'; + headers->origin[0] = '\0'; start = strstr(handshake, "\r\nOrigin: "); - if (!start) { return 0; } - start += 10; + if (start) { + start += 10; + } else { + start = strstr(handshake, "\r\nSec-WebSocket-Origin: "); + if (!start) { return 0; } + start += 24; + } end = strstr(start, "\r\n"); strncpy(headers->origin, start, end-start); headers->origin[end-start] = '\0'; - start = strstr(handshake, "\r\n\r\n"); - if (!start) { return 0; } - start += 4; - if (strlen(start) == 8) { - strncpy(headers->key3, start, 8); - headers->key3[8] = '\0'; + start = strstr(handshake, "\r\nSec-WebSocket-Version: "); + if (start) { + // HyBi/RFC 6455 + start += 25; + end = strstr(start, "\r\n"); + strncpy(headers->version, start, end-start); + headers->version[end-start] = '\0'; + ws_ctx->hixie = 0; + ws_ctx->hybi = strtol(headers->version, NULL, 10); - start = strstr(handshake, "\r\nSec-WebSocket-Key1: "); + start = strstr(handshake, "\r\nSec-WebSocket-Key: "); if (!start) { return 0; } - start += 22; + start += 21; end = strstr(start, "\r\n"); strncpy(headers->key1, start, end-start); headers->key1[end-start] = '\0'; - - start = strstr(handshake, "\r\nSec-WebSocket-Key2: "); + + start = strstr(handshake, "\r\nConnection: "); if (!start) { return 0; } - start += 22; + start += 14; end = strstr(start, "\r\n"); - strncpy(headers->key2, start, end-start); - headers->key2[end-start] = '\0'; + strncpy(headers->connection, start, end-start); + headers->connection[end-start] = '\0'; + + start = strstr(handshake, "\r\nSec-WebSocket-Protocol: "); + if (!start) { return 0; } + start += 26; + end = strstr(start, "\r\n"); + strncpy(headers->protocols, start, end-start); + headers->protocols[end-start] = '\0'; } else { - headers->key1[0] = '\0'; - headers->key2[0] = '\0'; - headers->key3[0] = '\0'; + // Hixie 75 or 76 + ws_ctx->hybi = 0; + + start = strstr(handshake, "\r\n\r\n"); + if (!start) { return 0; } + start += 4; + if (strlen(start) == 8) { + ws_ctx->hixie = 76; + strncpy(headers->key3, start, 8); + headers->key3[8] = '\0'; + + start = strstr(handshake, "\r\nSec-WebSocket-Key1: "); + if (!start) { return 0; } + start += 22; + end = strstr(start, "\r\n"); + strncpy(headers->key1, start, end-start); + headers->key1[end-start] = '\0'; + + start = strstr(handshake, "\r\nSec-WebSocket-Key2: "); + if (!start) { return 0; } + start += 22; + end = strstr(start, "\r\n"); + strncpy(headers->key2, start, end-start); + headers->key2[end-start] = '\0'; + } else { + ws_ctx->hixie = 75; + } + } return 1; } +int parse_hixie76_key(char * key) { + unsigned long i, spaces = 0, num = 0; + for (i=0; i < strlen(key); i++) { + if (key[i] == ' ') { + spaces += 1; + } + if ((key[i] >= 48) && (key[i] <= 57)) { + num = num * 10 + (key[i] - 48); + } + } + return num / spaces; +} + int gen_md5(headers_t *headers, char *target) { - unsigned int i, spaces1 = 0, spaces2 = 0; - unsigned long num1 = 0, num2 = 0; - unsigned char buf[17]; - for (i=0; i < strlen(headers->key1); i++) { - if (headers->key1[i] == ' ') { - spaces1 += 1; - } - if ((headers->key1[i] >= 48) && (headers->key1[i] <= 57)) { - num1 = num1 * 10 + (headers->key1[i] - 48); - } - } - num1 = num1 / spaces1; + unsigned long key1 = parse_hixie76_key(headers->key1); + unsigned long key2 = parse_hixie76_key(headers->key2); + char *key3 = headers->key3; - for (i=0; i < strlen(headers->key2); i++) { - if (headers->key2[i] == ' ') { - spaces2 += 1; - } - if ((headers->key2[i] >= 48) && (headers->key2[i] <= 57)) { - num2 = num2 * 10 + (headers->key2[i] - 48); - } - } - num2 = num2 / spaces2; + MD5_CTX c; + char in[HIXIE_MD5_DIGEST_LENGTH] = { + key1 >> 24, key1 >> 16, key1 >> 8, key1, + key2 >> 24, key2 >> 16, key2 >> 8, key2, + key3[0], key3[1], key3[2], key3[3], + key3[4], key3[5], key3[6], key3[7] + }; - /* Pack it big-endian */ - buf[0] = (num1 & 0xff000000) >> 24; - buf[1] = (num1 & 0xff0000) >> 16; - buf[2] = (num1 & 0xff00) >> 8; - buf[3] = num1 & 0xff; + MD5_Init(&c); + MD5_Update(&c, (void *)in, sizeof in); + MD5_Final((void *)target, &c); - buf[4] = (num2 & 0xff000000) >> 24; - buf[5] = (num2 & 0xff0000) >> 16; - buf[6] = (num2 & 0xff00) >> 8; - buf[7] = num2 & 0xff; - - strncpy(buf+8, headers->key3, 8); - buf[16] = '\0'; - - md5_buffer(buf, 16, target); - target[16] = '\0'; + target[HIXIE_MD5_DIGEST_LENGTH] = '\0'; return 1; } - +static void gen_sha1(headers_t *headers, char *target) { + SHA_CTX c; + unsigned char hash[SHA_DIGEST_LENGTH]; + int r; + + SHA1_Init(&c); + SHA1_Update(&c, headers->key1, strlen(headers->key1)); + SHA1_Update(&c, HYBI_GUID, 36); + SHA1_Final(hash, &c); + + r = b64_ntop(hash, sizeof hash, target, HYBI10_ACCEPTHDRLEN); + //assert(r == HYBI10_ACCEPTHDRLEN - 1); +} + ws_ctx_t *do_handshake(int sock) { - char handshake[4096], response[4096], trailer[17]; + char handshake[4096], response[4096], sha1[29], trailer[17]; char *scheme, *pre; - headers_t headers; + headers_t *headers; int len, ret; ws_ctx_t * ws_ctx; @@ -357,7 +569,7 @@ ws_ctx_t *do_handshake(int sock) { len = recv(sock, handshake, 1024, 0); handshake[len] = 0; handler_msg("sending flash policy response\n"); - send(sock, policy_response, sizeof(policy_response), 0); + send(sock, POLICY_RESPONSE, sizeof(POLICY_RESPONSE), 0); return NULL; } else if ((bcmp(handshake, "\x16", 1) == 0) || (bcmp(handshake, "\x80", 1) == 0)) { @@ -370,7 +582,8 @@ ws_ctx_t *do_handshake(int sock) { settings.cert); return NULL; } - ws_ctx = ws_socket_ssl(sock, settings.cert, settings.key); + ws_ctx = alloc_ws_ctx(); + ws_socket_ssl(ws_ctx, sock, settings.cert, settings.key); if (! ws_ctx) { return NULL; } scheme = "wss"; handler_msg("using SSL socket\n"); @@ -378,7 +591,8 @@ ws_ctx_t *do_handshake(int sock) { handler_msg("non-SSL connection disallowed\n"); return NULL; } else { - ws_ctx = ws_socket(sock); + ws_ctx = alloc_ws_ctx(); + ws_socket(ws_ctx, sock); if (! ws_ctx) { return NULL; } scheme = "ws"; handler_msg("using plain (not SSL) socket\n"); @@ -390,23 +604,31 @@ ws_ctx_t *do_handshake(int sock) { } handshake[len] = 0; - if (!parse_handshake(handshake, &headers)) { + //handler_msg("handshake: %s\n", handshake); + if (!parse_handshake(ws_ctx, handshake)) { handler_emsg("Invalid WS request\n"); return NULL; } - if (headers.key3[0] != '\0') { - gen_md5(&headers, trailer); - pre = "Sec-"; - handler_msg("using protocol version 76\n"); + headers = ws_ctx->headers; + if (ws_ctx->hybi > 0) { + handler_msg("using protocol HyBi/IETF 6455 %d\n", ws_ctx->hybi); + gen_sha1(headers, sha1); + sprintf(response, SERVER_HANDSHAKE_HYBI, sha1, "base64"); } else { - trailer[0] = '\0'; - pre = ""; - handler_msg("using protocol version 75\n"); + if (ws_ctx->hixie == 76) { + handler_msg("using protocol Hixie 76\n"); + gen_md5(headers, trailer); + pre = "Sec-"; + } else { + handler_msg("using protocol Hixie 75\n"); + trailer[0] = '\0'; + pre = ""; + } + sprintf(response, SERVER_HANDSHAKE_HIXIE, pre, headers->origin, pre, scheme, + headers->host, headers->path, pre, "base64", trailer); } - sprintf(response, server_handshake, pre, headers.origin, pre, scheme, - headers.host, headers.path, pre, trailer); //handler_msg("response: %s\n", response); ws_send(ws_ctx, response, strlen(response)); @@ -461,17 +683,8 @@ void start_server() { struct sockaddr_in serv_addr, cli_addr; ws_ctx_t *ws_ctx; - /* Initialize buffers */ - bufsize = 65536; - if (! (tbuf = malloc(bufsize)) ) - { fatal("malloc()"); } - if (! (cbuf = malloc(bufsize)) ) - { fatal("malloc()"); } - if (! (tbuf_tmp = malloc(bufsize)) ) - { fatal("malloc()"); } - if (! (cbuf_tmp = malloc(bufsize)) ) - { fatal("malloc()"); } + /* Initialize buffers */ lsock = socket(AF_INET, SOCK_STREAM, 0); if (lsock < 0) { error("ERROR creating listener socket"); } bzero((char *) &serv_addr, sizeof(serv_addr)); @@ -499,6 +712,7 @@ void start_server() { daemonize(lsock); } + // Reep zombies signal(SIGCHLD, SIG_IGN); @@ -518,9 +732,6 @@ void start_server() { } handler_msg("got client connection from %s\n", inet_ntoa(cli_addr.sin_addr)); - /* base64 is 4 bytes for every 3 - * 20 for WS '\x00' / '\xff' and good measure */ - dbufsize = (bufsize * 3)/4 - 20; handler_msg("forking handler process\n"); pid = fork(); @@ -544,6 +755,7 @@ void start_server() { if (pid == 0) { if (ws_ctx) { ws_socket_free(ws_ctx); + free_ws_ctx(ws_ctx); } else { shutdown(csock, SHUT_RDWR); close(csock); diff --git a/other/websocket.h b/other/websocket.h index c428fa5..5ac2936 100644 --- a/other/websocket.h +++ b/other/websocket.h @@ -1,9 +1,54 @@ #include +#define BUFSIZE 65536 +#define DBUFSIZE (BUFSIZE * 3) / 4 - 20 + +#define SERVER_HANDSHAKE_HIXIE "HTTP/1.1 101 Web Socket Protocol Handshake\r\n\ +Upgrade: WebSocket\r\n\ +Connection: Upgrade\r\n\ +%sWebSocket-Origin: %s\r\n\ +%sWebSocket-Location: %s://%s%s\r\n\ +%sWebSocket-Protocol: %s\r\n\ +\r\n%s" + +#define SERVER_HANDSHAKE_HYBI "HTTP/1.1 101 Switching Protocols\r\n\ +Upgrade: websocket\r\n\ +Connection: Upgrade\r\n\ +Sec-WebSocket-Accept: %s\r\n\ +Sec-WebSocket-Protocol: %s\r\n\ +\r\n" + +#define HYBI_GUID "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" + +#define HYBI10_ACCEPTHDRLEN 29 + +#define HIXIE_MD5_DIGEST_LENGTH 16 + +#define POLICY_RESPONSE "\n" + typedef struct { - int sockfd; - SSL_CTX *ssl_ctx; - SSL *ssl; + char path[1024+1]; + char host[1024+1]; + char origin[1024+1]; + char version[1024+1]; + char connection[1024+1]; + char protocols[1024+1]; + char key1[1024+1]; + char key2[1024+1]; + char key3[8+1]; +} headers_t; + +typedef struct { + int sockfd; + SSL_CTX *ssl_ctx; + SSL *ssl; + int hixie; + int hybi; + headers_t *headers; + char *tbuf; + char *cbuf; + char *tbuf_tmp; + char *cbuf_tmp; } ws_ctx_t; typedef struct { @@ -18,15 +63,6 @@ typedef struct { int daemon; } settings_t; -typedef struct { - char path[1024+1]; - char host[1024+1]; - char origin[1024+1]; - char key1[1024+1]; - char key2[1024+1]; - char key3[8+1]; -} headers_t; - ssize_t ws_recv(ws_ctx_t *ctx, void *buf, size_t len); diff --git a/other/websockify.c b/other/websockify.c index 42bb45e..b9dd9f8 100644 --- a/other/websockify.c +++ b/other/websockify.c @@ -49,14 +49,12 @@ int target_port; extern pipe_error; extern settings_t settings; -extern char *tbuf, *cbuf, *tbuf_tmp, *cbuf_tmp; -extern unsigned int bufsize, dbufsize; void do_proxy(ws_ctx_t *ws_ctx, int target) { fd_set rlist, wlist, elist; struct timeval tv; int i, maxfd, client = ws_ctx->sockfd; - unsigned int tstart, tend, cstart, cend, ret; + unsigned int opcode, tstart, tend, cstart, cend, ret; ssize_t len, bytes; tstart = tend = cstart = cend = 0; @@ -110,7 +108,7 @@ void do_proxy(ws_ctx_t *ws_ctx, int target) { if (FD_ISSET(target, &wlist)) { len = tend-tstart; - bytes = send(target, tbuf + tstart, len, 0); + bytes = send(target, ws_ctx->tbuf + tstart, len, 0); if (pipe_error) { break; } if (bytes < 0) { handler_emsg("target connection error: %s\n", @@ -128,10 +126,12 @@ void do_proxy(ws_ctx_t *ws_ctx, int target) { if (FD_ISSET(client, &wlist)) { len = cend-cstart; - bytes = ws_send(ws_ctx, cbuf + cstart, len); + bytes = ws_send(ws_ctx, ws_ctx->cbuf + cstart, len); if (pipe_error) { break; } if (len < 3) { - handler_emsg("len: %d, bytes: %d: %d\n", len, bytes, *(cbuf + cstart)); + handler_emsg("len: %d, bytes: %d: %d\n", + (int) len, (int) bytes, + (int) *(ws_ctx->cbuf + cstart)); } cstart += bytes; if (cstart >= cend) { @@ -143,18 +143,24 @@ void do_proxy(ws_ctx_t *ws_ctx, int target) { } if (FD_ISSET(target, &rlist)) { - bytes = recv(target, cbuf_tmp, dbufsize , 0); + bytes = recv(target, ws_ctx->cbuf_tmp, DBUFSIZE , 0); if (pipe_error) { break; } if (bytes <= 0) { handler_emsg("target closed connection\n"); break; } cstart = 0; - cend = encode(cbuf_tmp, bytes, cbuf, bufsize); + if (ws_ctx->hybi) { + cend = encode_hybi(ws_ctx->cbuf_tmp, bytes, + ws_ctx->cbuf, BUFSIZE, 1); + } else { + cend = encode_hixie(ws_ctx->cbuf_tmp, bytes, + ws_ctx->cbuf, BUFSIZE); + } /* printf("encoded: "); for (i=0; i< cend; i++) { - printf("%u,", (unsigned char) *(cbuf+i)); + printf("%u,", (unsigned char) *(ws_ctx->cbuf+i)); } printf("\n"); */ @@ -166,29 +172,36 @@ void do_proxy(ws_ctx_t *ws_ctx, int target) { } if (FD_ISSET(client, &rlist)) { - bytes = ws_recv(ws_ctx, tbuf_tmp, bufsize-1); + bytes = ws_recv(ws_ctx, ws_ctx->tbuf_tmp, BUFSIZE-1); if (pipe_error) { break; } if (bytes <= 0) { handler_emsg("client closed connection\n"); break; - } else if ((bytes == 2) && - (tbuf_tmp[0] == '\xff') && - (tbuf_tmp[1] == '\x00')) { - handler_emsg("client sent orderly close frame\n"); - break; } /* printf("before decode: "); for (i=0; i< bytes; i++) { - printf("%u,", (unsigned char) *(tbuf_tmp+i)); + printf("%u,", (unsigned char) *(ws_ctx->tbuf_tmp+i)); } printf("\n"); */ - len = decode(tbuf_tmp, bytes, tbuf, bufsize-1); + if (ws_ctx->hybi) { + len = decode_hybi(ws_ctx->tbuf_tmp, bytes, + ws_ctx->tbuf, BUFSIZE-1, &opcode); + } else { + len = decode_hixie(ws_ctx->tbuf_tmp, bytes, + ws_ctx->tbuf, BUFSIZE-1, &opcode); + } + + if (opcode == 8) { + handler_emsg("client sent orderly close frame\n"); + break; + } + /* printf("decoded: "); for (i=0; i< len; i++) { - printf("%u,", (unsigned char) *(tbuf+i)); + printf("%u,", (unsigned char) *(ws_ctx->tbuf+i)); } printf("\n"); */ @@ -346,8 +359,4 @@ int main(int argc, char *argv[]) settings.handler = proxy_handler; start_server(); - free(tbuf); - free(cbuf); - free(tbuf_tmp); - free(cbuf_tmp); } diff --git a/other/wswrapper.c b/other/wswrapper.c deleted file mode 100644 index bd4e6f0..0000000 --- a/other/wswrapper.c +++ /dev/null @@ -1,1156 +0,0 @@ -/* - * wswrap/wswrapper: Add WebSockets support to any service. - * Copyright 2010 Joel Martin - * Licensed under LGPL version 3 (see docs/LICENSE.LGPL-3) - * - * wswrapper is an LD_PRELOAD library that converts a TCP listen socket of an - * existing program to a be a WebSockets socket. The `wswrap` script can be - * used to easily launch a program using wswrapper. Here is an example of - * using wswrapper with vncserver. wswrapper will convert the socket listening - * on port 5901 to be a WebSockets port: - * - * cd noVNC/utils - * ./wswrap 5901 vncserver -geometry 640x480 :1 - * - * This is tricky a subtle process so there are some serious limitations: - * - multi-threaded programs may not work - * - programs that fork may behave in strange and mysterious ways (such as - * fork bombing your system) - * - programs using ppoll or epoll will not work correctly - * - doesn't support fopencookie, streams, putc, etc. - * - * ********************************************************************** - * WARNING: - * Due to the above limitations, this code should be considered an experiment - * only. Consider using the program wrap mode of wsproxy.py instead. - * ********************************************************************** - */ - -#define DO_MSG 1 -#define DO_DEBUG 1 -#define DO_TRACE 1 - -#include -#include - -#define __USE_GNU 1 // Pull in RTLD_NEXT -#include - -#include -#include -#include -#include -#include -#include /* base64 encode/decode */ -#include -#include "md5.h" -#include "wswrapper.h" - -/* - * If WSWRAP_PORT environment variable is set then listen to the bind fd that - * matches WSWRAP_PORT - */ -int _WS_listen_fd = -1; -int _WS_nfds = 0; -int _WS_fds[WS_MAX_FDS]; -_WS_connection *_WS_connections[65536]; - - -/* - * Utillity routines - */ - -/* - * Subtract the `struct timeval' values X and Y, storing the - * result in RESULT. If TS is set then RESULT and X are really - * type-cast `struct timespec` so scale them appropriately. - * Return 1 if the difference is negative or 0, otherwise 0. - */ -int _WS_subtract_time (result, x, y, ts) - struct timeval *result, *x, *y; -{ - int scale = ts ? 1000 : 1; - /* Perform the carry for the later subtraction by updating y. */ - if ((x->tv_usec / scale) < y->tv_usec) { - int sec = (y->tv_usec - (x->tv_usec / scale)) / 1000000 + 1; - y->tv_usec -= 1000000 * sec; - y->tv_sec += sec; - } - if ((x->tv_usec / scale) - y->tv_usec > 1000000) { - int sec = ((x->tv_usec / scale) - y->tv_usec) / 1000000; - y->tv_usec += 1000000 * sec; - y->tv_sec -= sec; - } - - /* Compute the time remaining to wait. - * tv_usec is certainly positive. */ - result->tv_sec = x->tv_sec - y->tv_sec; - result->tv_usec = x->tv_usec - (y->tv_usec * scale); - - /* Return 1 if result is negative or 0. */ - return x->tv_sec <= y->tv_sec; -} - -int _WS_alloc(int fd) { - if (_WS_connections[fd]) { - RET_ERROR(ENOMEM, "Memory already allocated for fd %d\n", fd); - } - if (! (_WS_connections[fd] = malloc(sizeof(_WS_connection)))) { - RET_ERROR(ENOMEM, "Could not allocate interposer memory\n"); - } - _WS_connections[fd]->rcarry_cnt = 0; - _WS_connections[fd]->rcarry[0] = '\0'; - _WS_connections[fd]->newframe = 1; - _WS_connections[fd]->refcnt = 1; - - /* Add to search list for select/pselect */ - _WS_fds[_WS_nfds] = fd; - _WS_nfds++; - - return 0; -} - -int _WS_free(int fd) { - int i; - _WS_connection * wsptr; - wsptr = _WS_connections[fd]; - if (wsptr) { - TRACE(">> _WS_free(%d)\n", fd); - - wsptr->refcnt--; - if (wsptr->refcnt <= 0) { - free(wsptr); - DEBUG("freed memory for fd %d\n", fd); - } - _WS_connections[fd] = NULL; - - /* Remove from the search list for select/pselect */ - for (i = 0; i < _WS_nfds; i++) { - if (_WS_fds[i] == fd) { - break; - } - } - if (_WS_nfds - i - 1 > 0) { - memmove(_WS_fds + i, _WS_fds + i + 1, _WS_nfds - i - 1); - } - _WS_nfds--; - - MSG("finished interposing on fd %d\n", fd); - TRACE("<< _WS_free(%d)\n", fd); - } -} - - -/* - * WebSocket handshake routines - */ - -/* For WebSockets v76, use key1, key2 and key3 to generate md5 hash */ -int _WS_gen_md5(char *key1, char *key2, char *key3, char *target) { - unsigned int i, spaces1 = 0, spaces2 = 0; - unsigned long num1 = 0, num2 = 0; - unsigned char buf[17]; - - /* Parse number 1 from key 1 */ - for (i=0; i < strlen(key1); i++) { - if (key1[i] == ' ') { - spaces1 += 1; - } - if ((key1[i] >= 48) && (key1[i] <= 57)) { - num1 = num1 * 10 + (key1[i] - 48); - } - } - num1 = num1 / spaces1; - - /* Parse number 2 from key 2 */ - for (i=0; i < strlen(key2); i++) { - if (key2[i] == ' ') { - spaces2 += 1; - } - if ((key2[i] >= 48) && (key2[i] <= 57)) { - num2 = num2 * 10 + (key2[i] - 48); - } - } - num2 = num2 / spaces2; - - /* Pack it big-endian as the first 8 bytes */ - buf[0] = (num1 & 0xff000000) >> 24; - buf[1] = (num1 & 0xff0000) >> 16; - buf[2] = (num1 & 0xff00) >> 8; - buf[3] = num1 & 0xff; - - buf[4] = (num2 & 0xff000000) >> 24; - buf[5] = (num2 & 0xff0000) >> 16; - buf[6] = (num2 & 0xff00) >> 8; - buf[7] = num2 & 0xff; - - /* Add key 3 as the last 8 bytes */ - strncpy(buf+8, key3, 8); - buf[16] = '\0'; - - /* md5 hash all 16 bytes to generate 16 byte target */ - md5_buffer(buf, 16, target); - target[16] = '\0'; - - return 1; -} - -/* Do v75 and v76 handshake on WebSocket connection */ -int _WS_handshake(int sockfd) -{ - int sz = 0, len, idx; - int ret = -1, save_errno = EPROTO; - char *last, *start, *end; - long flags; - char handshake[4096], response[4096], - path[1024], prefix[5] = "", scheme[10] = "ws", host[1024], - origin[1024], key1[100], key2[100], key3[9], chksum[17]; - - static void * (*rfunc)(), * (*wfunc)(); - if (!rfunc) rfunc = (void *(*)()) dlsym(RTLD_NEXT, "recv"); - if (!wfunc) wfunc = (void *(*)()) dlsym(RTLD_NEXT, "send"); - DEBUG("_WS_handshake starting\n"); - - /* Disable NONBLOCK if set */ - flags = fcntl(sockfd, F_GETFL, 0); - if (flags & O_NONBLOCK) { - fcntl(sockfd, F_SETFL, flags^O_NONBLOCK); - } - - while (1) { - len = (int) rfunc(sockfd, handshake+sz, 4095, 0); - if (len < 1) { - ret = len; - save_errno = errno; - break; - } - sz += len; - handshake[sz] = '\x00'; - if (sz < 4) { - // Not enough yet - continue; - } - if (strstr(handshake, "GET ") != handshake) { - MSG("Got non-WebSockets client connection\n"); - break; - } - last = strstr(handshake, "\r\n\r\n"); - if (! last) { - continue; - } - if (! strstr(handshake, "Upgrade: WebSocket\r\n")) { - MSG("Invalid WebSockets handshake\n"); - break; - } - - /* Now parse out the data elements */ - start = handshake+4; - end = strstr(start, " HTTP/1.1"); - if (!end) { break; } - snprintf(path, end-start+1, "%s", start); - - start = strstr(handshake, "\r\nHost: "); - if (!start) { break; } - start += 8; - end = strstr(start, "\r\n"); - snprintf(host, end-start+1, "%s", start); - - start = strstr(handshake, "\r\nOrigin: "); - if (!start) { break; } - start += 10; - end = strstr(start, "\r\n"); - snprintf(origin, end-start+1, "%s", start); - - start = strstr(handshake, "\r\n\r\n") + 4; - if (strlen(start) == 8) { - sprintf(prefix, "Sec-"); - - snprintf(key3, 8+1, "%s", start); - - start = strstr(handshake, "\r\nSec-WebSocket-Key1: "); - if (!start) { break; } - start += 22; - end = strstr(start, "\r\n"); - snprintf(key1, end-start+1, "%s", start); - - start = strstr(handshake, "\r\nSec-WebSocket-Key2: "); - if (!start) { break; } - start += 22; - end = strstr(start, "\r\n"); - snprintf(key2, end-start+1, "%s", start); - - _WS_gen_md5(key1, key2, key3, chksum); - - //DEBUG("Got handshake (v76): %s\n", handshake); - MSG("New WebSockets client (v76)\n"); - - } else { - sprintf(prefix, ""); - sprintf(key1, ""); - sprintf(key2, ""); - sprintf(key3, ""); - sprintf(chksum, ""); - - //DEBUG("Got handshake (v75): %s\n", handshake); - MSG("New WebSockets client (v75)\n"); - } - sprintf(response, _WS_response, prefix, origin, prefix, scheme, - host, path, prefix, chksum); - //DEBUG("Handshake response: %s\n", response); - wfunc(sockfd, response, strlen(response), 0); - save_errno = 0; - ret = 0; - break; - } - - /* Re-enable NONBLOCK if it was set */ - if (flags & O_NONBLOCK) { - fcntl(sockfd, F_SETFL, flags); - } - errno = save_errno; - return ret; -} - -/* - * Strip empty WebSockets frames and return a positive value if there is - * enough data to base64 decode (a 4 byte chunk). If nonblock is not set then - * it will block until there is enough data (or until an error occurs). - */ -ssize_t _WS_ready(int sockfd, int nonblock) -{ - _WS_connection *ws = _WS_connections[sockfd]; - char buf[6]; - int count, len, flags, i; - static void * (*rfunc)(); - if (!rfunc) rfunc = (void *(*)()) dlsym(RTLD_NEXT, "recv"); - - TRACE(">> _WS_ready(%d, %d)\n", sockfd, nonblock); - - count = 4 + ws->newframe; - flags = MSG_PEEK; - if (nonblock) { - flags |= MSG_DONTWAIT; - } - while (1) { - len = (int) rfunc(sockfd, buf, count, flags); - if (len < 1) { - TRACE("<< _WS_ready(%d, %d) len %d, errno: %d\n", - sockfd, nonblock, len, errno); - return len; - } - if (len >= 2 && buf[0] == '\x00' && buf[1] == '\xff') { - /* Strip emtpy frame */ - DEBUG("_WS_ready(%d, %d), strip empty\n", sockfd, nonblock); - len = (int) rfunc(sockfd, buf, 2, 0); - if (len < 2) { - MSG("Failed to strip empty frame headers\n"); - TRACE("<< _WS_ready: failed to strip empty frame headers\n"); - return len; - } else if (len == 2 && nonblock) { - errno = EAGAIN; - TRACE("<< _WS_ready(%d, %d), len == 2, EAGAIN\n", - sockfd, nonblock); - return -1; - } - continue; - } - if (len < count) { - if (nonblock) { - errno = EAGAIN; - TRACE("<< _WS_ready(%d, %d), len < count, EAGAIN\n", - sockfd, nonblock); - return -1; - } else { - fprintf(stderr, "_WS_ready(%d, %d), loop: len %d, buf:", - sockfd, nonblock, len, (unsigned char) buf[0]); - for (i = 0; i < len; i++) { - fprintf(stderr, "%d", (unsigned char) buf[i]); - } - fprintf(stderr, "\n"); - - continue; - } - } - TRACE("<< _WS_ready(%d, %d) len: %d\n", sockfd, nonblock, len); - return len; - } -} - -/* - * WebSockets recv/read interposer routine - */ -ssize_t _WS_recv(int recvf, int sockfd, const void *buf, - size_t len, int flags) -{ - _WS_connection *ws = _WS_connections[sockfd]; - int rawcount, deccount, left, striplen, decodelen, ready; - ssize_t retlen, rawlen; - int sockflags; - int i; - char *fstart, *fend, *cstart; - - static void * (*rfunc)(), * (*rfunc2)(); - if (!rfunc) rfunc = (void *(*)()) dlsym(RTLD_NEXT, "recv"); - if (!rfunc2) rfunc2 = (void *(*)()) dlsym(RTLD_NEXT, "read"); - - if (! ws) { - // Not our file descriptor, just pass through - if (recvf) { - return (ssize_t) rfunc(sockfd, buf, len, flags); - } else { - return (ssize_t) rfunc2(sockfd, buf, len); - } - } - TRACE(">> _WS_recv(%d)\n", sockfd); - - if (len == 0) { - TRACE("<< _WS_recv(%d) len == 0\n", sockfd); - return 0; - } - - sockflags = fcntl(sockfd, F_GETFL, 0); - if (sockflags & O_NONBLOCK) { - TRACE("_WS_recv(%d, _, %d) with O_NONBLOCK\n", sockfd, len); - } else { - TRACE("_WS_recv(%d, _, %d) without O_NONBLOCK\n", sockfd, len); - } - - left = len; - retlen = 0; - - /* first copy in carry-over bytes from previous recv/read */ - if (ws->rcarry_cnt) { - if (ws->rcarry_cnt == 1) { - DEBUG("Using carry byte: %u (", ws->rcarry[0]); - } else if (ws->rcarry_cnt == 2) { - DEBUG("Using carry bytes: %u,%u (", ws->rcarry[0], - ws->rcarry[1]); - } else { - RET_ERROR(EIO, "Too many carry-over bytes\n"); - } - if (len <= ws->rcarry_cnt) { - DEBUG("final)\n"); - memcpy((char *) buf, ws->rcarry, len); - ws->rcarry_cnt -= len; - return len; - } else { - DEBUG("prepending)\n"); - memcpy((char *) buf, ws->rcarry, ws->rcarry_cnt); - retlen += ws->rcarry_cnt; - left -= ws->rcarry_cnt; - ws->rcarry_cnt = 0; - } - } - - /* Determine the number of base64 encoded bytes needed */ - rawcount = (left * 4) / 3 + 3; - rawcount -= rawcount%4; - - if (rawcount > WS_BUFSIZE - 1) { - RET_ERROR(ENOMEM, "recv of %d bytes is larger than buffer\n", rawcount); - } - - ready = _WS_ready(sockfd, 0); - if (ready < 1) { - if (retlen) { - /* We had some carry over, don't error until next call */ - errno = 0; - } else { - retlen = ready; - } - TRACE("<< _WS_recv(%d, _, %d) retlen %d\n", sockfd, len, retlen); - return retlen; - } - - /* We have enough data to return something */ - - /* Peek at everything available */ - rawlen = (ssize_t) rfunc(sockfd, ws->rbuf, WS_BUFSIZE-1, - flags | MSG_PEEK); - if (rawlen <= 0) { - RET_ERROR(EPROTO, "Socket was ready but then had failure"); - } - fstart = ws->rbuf; - fstart[rawlen] = '\x00'; - - - if (ws->newframe) { - if (fstart[0] != '\x00') { - RET_ERROR(EPROTO, "Missing frame start\n"); - } - fstart++; - rawlen--; - ws->newframe = 0; - } - - fend = memchr(fstart, '\xff', rawlen); - - if (fend) { - ws->newframe = 1; - if ((fend - fstart) % 4) { - RET_ERROR(EPROTO, "Frame length is not multiple of 4\n"); - } - } else { - fend = fstart + rawlen - (rawlen % 4); - if (fend - fstart < 4) { - RET_ERROR(EPROTO, "Frame too short\n"); - } - } - - /* Determine amount to consume */ - if (rawcount < fend - fstart) { - ws->newframe = 0; - deccount = rawcount; - } else { - deccount = fend - fstart; - } - - /* Now consume what was processed (if not MSG_PEEK) */ - if (flags & MSG_PEEK) { - DEBUG("_WS_recv(%d, _, %d) MSG_PEEK, not consuming\n", sockfd, len); - } else { - rfunc(sockfd, ws->rbuf, fstart - ws->rbuf + deccount + ws->newframe, flags); - } - - fstart[deccount] = '\x00'; // base64 terminator - - /* Do base64 decode into the return buffer */ - decodelen = b64_pton(fstart, (char *) buf + retlen, deccount); - if (decodelen <= 0) { - RET_ERROR(EPROTO, "Base64 decode error\n"); - } - - /* Calculate return length and carry-over */ - if (decodelen <= left) { - retlen += decodelen; - } else { - retlen += left; - - if (! (flags & MSG_PEEK)) { - /* Add anything left over to the carry-over */ - ws->rcarry_cnt = decodelen - left; - if (ws->rcarry_cnt > 2) { - RET_ERROR(EPROTO, "Got too much base64 data\n"); - } - memcpy(ws->rcarry, buf + retlen, ws->rcarry_cnt); - if (ws->rcarry_cnt == 1) { - DEBUG("Saving carry byte: %u\n", ws->rcarry[0]); - } else if (ws->rcarry_cnt == 2) { - DEBUG("Saving carry bytes: %u,%u\n", ws->rcarry[0], - ws->rcarry[1]); - } else { - RET_ERRO(EPROTO, "Too many carry bytes!\n"); - } - } - } - ((char *) buf)[retlen] = '\x00'; - - TRACE("<< _WS_recv(%d) retlen %d\n", sockfd, retlen); - return retlen; -} - -/* - * WebSockets send and write interposer routine - */ -ssize_t _WS_send(int sendf, int sockfd, const void *buf, - size_t len, int flags) -{ - _WS_connection *ws = _WS_connections[sockfd]; - int rawlen, enclen, rlen, over, left, clen, retlen, dbufsize; - int sockflags; - char * target; - int i; - static void * (*sfunc)(), * (*sfunc2)(); - if (!sfunc) sfunc = (void *(*)()) dlsym(RTLD_NEXT, "send"); - if (!sfunc2) sfunc2 = (void *(*)()) dlsym(RTLD_NEXT, "write"); - - if (! ws) { - // Not our file descriptor, just pass through - if (sendf) { - return (ssize_t) sfunc(sockfd, buf, len, flags); - } else { - return (ssize_t) sfunc2(sockfd, buf, len); - } - } - TRACE(">> _WS_send(%d, _, %d)\n", sockfd, len); - - sockflags = fcntl(sockfd, F_GETFL, 0); - - dbufsize = (WS_BUFSIZE * 3)/4 - 2; - if (len > dbufsize) { - RET_ERROR(ENOMEM, "send of %d bytes is larger than send buffer\n", len); - } - - /* base64 encode and add frame markers */ - rawlen = 0; - ws->sbuf[rawlen++] = '\x00'; - enclen = b64_ntop(buf, len, ws->sbuf+rawlen, WS_BUFSIZE-rawlen); - if (enclen < 0) { - RET_ERROR(EPROTO, "Base64 encoding error\n"); - } - rawlen += enclen; - ws->sbuf[rawlen++] = '\xff'; - - rlen = (int) sfunc(sockfd, ws->sbuf, rawlen, flags); - - if (rlen <= 0) { - TRACE("<< _WS_send(%d, _, %d) send failed, returning\n", sockfd, len); - return rlen; - } else if (rlen < rawlen) { - /* Spin until we can send a whole base64 chunck and frame end */ - over = (rlen - 1) % 4; - left = (4 - over) % 4 + 1; // left to send - DEBUG("_WS_send: rlen: %d (over: %d, left: %d), rawlen: %d\n", - rlen, over, left, rawlen); - rlen += left; - ws->sbuf[rlen-1] = '\xff'; - i = 0; - do { - i++; - clen = (int) sfunc(sockfd, ws->sbuf + rlen - left, left, flags); - if (clen > 0) { - left -= clen; - } else if (clen == 0) { - MSG("_WS_send: got clen %d\n", clen); - } else if (!(sockflags & O_NONBLOCK)) { - MSG("<< _WS_send: clen %d\n", clen); - return clen; - } - if (i > 1000000) { - RET_ERROR(EIO, "Could not send final part of frame\n"); - } - } while (left > 0); - //DEBUG("_WS_send: spins until finished %d\n", i); - DEBUG("_WS_send: spins until finished %d\n", i); - } - - - /* - * Report back the number of original characters sent, - * not the raw number sent - */ - - /* Adjust for framing */ - retlen = rlen - 2; - - /* Adjust for base64 padding */ - if (ws->sbuf[rlen-1] == '=') { retlen --; } - if (ws->sbuf[rlen-2] == '=') { retlen --; } - - /* Scale return value for base64 encoding size */ - retlen = (retlen*3)/4; - - TRACE(">> _WS_send(%d, _, %d) retlen %d\n", sockfd, len, retlen); - return (ssize_t) retlen; -} - -/* - * Interpose select/pselect/poll. - * - * WebSocket descriptors are not ready until we have received a frame start - * ('\x00') and at least 4 bytes of base64 encoded data. In addition we may - * have carry-over data from the last 4 bytes of base64 data in which case the - * WebSockets socket is ready even though there might not be data in the raw - * socket itself. - */ - -/* Interpose on select (mode==0) and pselect (mode==1) */ -int _WS_select(int mode, int nfds, fd_set *readfds, - fd_set *writefds, fd_set *exceptfds, - void *timeptr, const sigset_t *sigmask) -{ - _WS_connection *ws; - fd_set carryfds, savefds; - /* Assumes timeptr is two longs whether timeval or timespec */ - struct timeval savetv, starttv, nowtv, difftv; - int carrycnt = 0, less = 0; - int ret, i, ready, fd; - static void * (*func0)(), * (*func1)(); - if (!func0) func0 = (void *(*)()) dlsym(RTLD_NEXT, "select"); - if (!func1) func1 = (void *(*)()) dlsym(RTLD_NEXT, "pselect"); - - if ((_WS_listen_fd == -1) || (_WS_nfds == 0)) { - if (mode == 0) { - ret = (int) func0(nfds, readfds, writefds, exceptfds, - timeptr); - } else if (mode == 1) { - ret = (int) func1(nfds, readfds, writefds, exceptfds, - timeptr, sigmask); - } - return ret; - } - -#ifdef DO_TRACE - TRACE(">> _WS_select(%d, %d, _, _, _, _)\n", mode, nfds); - for (i = 0; i < _WS_nfds; i++) { - fd = _WS_fds[i]; - if (readfds && (FD_ISSET(fd, readfds))) { - TRACE(" WS %d is in readfds\n", fd, nfds); - } - if (writefds && (FD_ISSET(fd, writefds))) { - TRACE(" WS %d is in writefds\n", fd, nfds); - } - if (exceptfds && (FD_ISSET(fd, exceptfds))) { - TRACE(" WS %d is in exceptfds\n", fd, nfds); - } - } -#endif - if (timeptr) { - memcpy(&savetv, timeptr, sizeof(savetv)); - gettimeofday(&starttv, NULL); - } - - /* If we have carry-over return it right away */ - FD_ZERO(&carryfds); - if (readfds) { - memcpy(&savefds, readfds, sizeof(savefds)); - for (i = 0; i < _WS_nfds; i++) { - fd = _WS_fds[i]; - ws = _WS_connections[fd]; - if ((ws->rcarry_cnt) && (FD_ISSET(fd, readfds))) { - FD_SET(fd, &carryfds); - carrycnt++; - } - } - } - if (carrycnt) { - if (writefds) { - FD_ZERO(writefds); - } - if (exceptfds) { - FD_ZERO(writefds); - } - memcpy(readfds, &carryfds, sizeof(carryfds)); - TRACE("<< _WS_select(%d, %d, _, _, _, _) carrycnt %d\n", - mode, nfds, carrycnt); - return carrycnt; - } - - do { - if (timeptr) { - TRACE(" _WS_select tv/ts: %ld:%ld\n", - ((struct timeval *) timeptr)->tv_sec, - ((struct timeval *) timeptr)->tv_usec); - } - if (mode == 0) { - ret = (int) func0(nfds, readfds, writefds, exceptfds, - timeptr); - } else if (mode == 1) { - ret = (int) func1(nfds, readfds, writefds, exceptfds, - timeptr, sigmask); - } - if (! readfds) { - break; - } - if (ret <= 0) { - break; - } - - for (i = 0; i < _WS_nfds; i++) { - fd = _WS_fds[i]; - ws = _WS_connections[fd]; - if (FD_ISSET(fd, readfds)) { - ready = _WS_ready(fd, 1); - if (ready == 0) { - /* 0 means EOF which is also a ready condition */ - DEBUG("_WS_select: detected %d is closed\n", fd); - } else if (ready < 0) { - DEBUG("_WS_select: FD_CLR(%d,readfds) - not enough to decode\n", fd); - FD_CLR(fd, readfds); - ret--; - } - } - } - errno = 0; /* errno could be set by _WS_ready */ - - if (ret == 0) { - /* - * If all the ready readfds were WebSockets, but none of - * them were really ready (empty frames) then we select again. But - * first restore original values less passage of time. - */ - if (! timeptr) { - /* No timeout, spin forever */ - continue; - } - memcpy(readfds, &savefds, sizeof(savefds)); - gettimeofday(&nowtv, NULL); - /* Amount of time that has passed */ - _WS_subtract_time(&difftv, &nowtv, &starttv, 0); - /* Subtract from original timout */ - less = _WS_subtract_time((struct timeval *) timeptr, - &savetv, &difftv, mode); - if (less) { - /* Timer has expired */ - TRACE(" _WS_select expired timer\n", mode, nfds); - break; - } - } - } while (ret == 0); - - /* Restore original time value for pselect glibc does */ - if (timeptr && mode == 1) { - memcpy(timeptr, &savetv, sizeof(savetv)); - } - -#ifdef DO_TRACE - TRACE("<< _WS_select(%d, %d, _, _, _, _) ret %d, errno %d\n", - mode, nfds, ret, errno); - for (i = 0; i < _WS_nfds; i++) { - fd = _WS_fds[i]; - if (readfds && (FD_ISSET(fd, readfds))) { - TRACE(" WS %d is set in readfds\n", fd, nfds); - } - if (writefds && (FD_ISSET(fd, writefds))) { - TRACE(" WS %d is set in writefds\n", fd, nfds); - } - if (exceptfds && (FD_ISSET(fd, exceptfds))) { - TRACE(" WS %d is set in exceptfds\n", fd, nfds); - } - } -#endif - return ret; -} - -/* Interpose on poll (mode==0) and ppoll (mode==1) */ -int _WS_poll(int mode, struct pollfd *fds, nfds_t nfds, int timeout, - struct timespec *ptimeout, sigset_t *sigmask) -{ - _WS_connection *ws; - int savetimeout; - struct timespec savets; - struct timeval starttv, nowtv, difftv; - struct pollfd *pfd; - int carrycnt = 0, less = 0; - int ret, i, ready, fd; - static void * (*func0)(), * (*func1)(); - if (!func0) func0 = (void *(*)()) dlsym(RTLD_NEXT, "poll"); - if (!func1) func1 = (void *(*)()) dlsym(RTLD_NEXT, "ppoll"); - - if ((_WS_listen_fd == -1) || (_WS_nfds == 0)) { - if (mode == 0) { - ret = (int) func0(fds, nfds, timeout); - } else if (mode == 1) { - ret = (int) func1(fds, nfds, ptimeout, sigmask); - } - return ret; - } - - TRACE(">> _WS_poll(%d, %ld, _, _, _, _)\n", mode, nfds); - if (mode == 0) { - savetimeout = timeout; - } else if (mode == 1) { - memcpy(&savets, ptimeout, sizeof(savets)); - } - gettimeofday(&starttv, NULL); - - do { - TRACE(" _WS_poll(%d, %ld, _, _, _, _) tv/ts: %ld:%ld\n", mode, nfds, - ptimeout->tv_sec, ptimeout->tv_nsec); - - if (mode == 0) { - ret = (int) func0(fds, nfds, timeout); - } else if (mode == 1) { - ret = (int) func1(fds, nfds, ptimeout, sigmask); - } - if (ret <= 0) { - break; - } - - for (i = 0; i < nfds; i++) { - pfd = &fds[i]; - if (! (pfd->events & POLLIN)) { - continue; - } - ws = _WS_connections[pfd->fd]; - if (! ws) { - continue; - } - if (ws->rcarry_cnt) { - if (! (pfd->revents & POLLIN)) { - pfd->revents |= POLLIN; - ret++; - } - } else if (pfd->revents & POLLIN) { - ready = _WS_ready(pfd->fd, 1); - if (ready == 0) { - /* 0 means EOF which is also a ready condition */ - DEBUG("_WS_poll: detected %d is closed\n", fd); - } else if (ready < 0) { - DEBUG("_WS_poll: not enough to decode\n", fd); - pfd->revents -= POLLIN; - ret--; - } - } - } - errno = 0; /* errno could be set by _WS_ready */ - - if (ret == 0) { - /* - * If all the ready readfds were WebSockets, but none of - * them were really ready (empty frames) then we select again. But - * first restore original values less passage of time. - */ - gettimeofday(&nowtv, NULL); - /* Amount of time that has passed */ - _WS_subtract_time(&difftv, &nowtv, &starttv, 0); - if (mode == 0) { - if (timeout < 0) { - /* Negative timeout means infinite */ - continue; - } - timeout -= difftv.tv_sec * 1000 + difftv.tv_usec / 1000; - if (timeout <= 0) { - less = 1; - } - } else if (mode == 1) { - /* Subtract from original timout */ - less = _WS_subtract_time((struct timeval *) ptimeout, - (struct timeval *) &savets, - &difftv, 1); - } - if (less) { - /* Timer has expired */ - TRACE(" _WS_poll expired timer\n", mode, nfds); - break; - } - } - } while (ret == 0); - - /* Restore original time value for pselect glibc does */ - if (mode == 1) { - memcpy(ptimeout, &savets, sizeof(savets)); - } - - TRACE("<< _WS_poll(%d, %ld, _, _, _, _) ret %d, errno %d\n", - mode, nfds, ret, errno); - return ret; -} - - -/* - * Overload (LD_PRELOAD) standard library network routines - */ - -int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen) -{ - static void * (*func)(); - struct sockaddr_in * addr_in = (struct sockaddr_in *)addr; - char * WSWRAP_PORT, * end; - int ret, envport, bindport = htons(addr_in->sin_port); - if (!func) func = (void *(*)()) dlsym(RTLD_NEXT, "bind"); - TRACE(">> bind(%d, _, %d)\n", sockfd, addrlen); - - ret = (int) func(sockfd, addr, addrlen); - - if (addr_in->sin_family != AF_INET) { - // TODO: handle IPv6 - TRACE("<< bind, ignoring non-IPv4 socket\n"); - return ret; - } - - WSWRAP_PORT = getenv("WSWRAP_PORT"); - if ((! WSWRAP_PORT) || (*WSWRAP_PORT == '\0')) { - // TODO: interpose on all sockets when WSWRAP_PORT not set - TRACE("<< bind, not interposing: WSWRAP_PORT is not set\n"); - return ret; - } - - envport = strtol(WSWRAP_PORT, &end, 10); - if ((envport == 0) || (*end != '\0')) { - TRACE("<< bind, not interposing: WSWRAP_PORT is not a number\n"); - return ret; - } - - if (envport != bindport) { - TRACE("<< bind, not interposing on port: %d (fd %d)\n", bindport, sockfd); - return ret; - } - - _WS_listen_fd = sockfd; - - TRACE("<< bind, listening for WebSockets connections on port: %d (fd %d)\n", envport, sockfd); - return ret; -} - -int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen) -{ - int fd, ret, envfd; - static void * (*func)(); - if (!func) func = (void *(*)()) dlsym(RTLD_NEXT, "accept"); - TRACE("<< accept(%d, _, _)\n", sockfd); - - fd = (int) func(sockfd, addr, addrlen); - - if (_WS_listen_fd == -1) { - TRACE("<< accept: not interposing\n"); - return fd; - } - - if (_WS_listen_fd != sockfd) { - TRACE("<< accept: not interposing on fd %d\n", sockfd); - return fd; - } - - - if (_WS_connections[fd]) { - RET_ERROR(EINVAL, "already interposing on fd %d\n", fd); - } else { - /* It's a port we're interposing on so allocate memory for it */ - if (_WS_nfds >= WS_MAX_FDS) { - RET_ERROR(ENOMEM, "Too many interposer fds\n"); - } - if (_WS_alloc(fd) < 0) { - return -1; - } - - ret = _WS_handshake(fd); - if (ret < 0) { - _WS_free(fd); - errno = EPROTO; - TRACE("<< accept(%d, _, _): ret %d\n", sockfd, ret); - return ret; - } - MSG("interposing on fd %d (allocated memory)\n", fd); - } - - return fd; -} - -int close(int fd) -{ - static void * (*func)(); - if (!func) func = (void *(*)()) dlsym(RTLD_NEXT, "close"); - - TRACE("close(%d) called\n", fd); - - _WS_free(fd); - - return (int) func(fd); -} - - -ssize_t read(int fd, void *buf, size_t count) -{ - TRACE("read(%d, _, %d) called\n", fd, count); - return (ssize_t) _WS_recv(0, fd, buf, count, 0); -} - -ssize_t write(int fd, const void *buf, size_t count) -{ - TRACE("write(%d, _, %d) called\n", fd, count); - return (ssize_t) _WS_send(0, fd, buf, count, 0); -} - -ssize_t recv(int sockfd, void *buf, size_t len, int flags) -{ - TRACE("recv(%d, _, %d, %d) called\n", sockfd, len, flags); - return (ssize_t) _WS_recv(1, sockfd, buf, len, flags); -} - -ssize_t send(int sockfd, const void *buf, size_t len, int flags) -{ - TRACE("send(%d, _, %d, %d) called\n", sockfd, len, flags); - return (ssize_t) _WS_send(1, sockfd, buf, len, flags); -} - -int select(int nfds, fd_set *readfds, fd_set *writefds, - fd_set *exceptfds, struct timeval *timeout) -{ - TRACE("select(%d, _, _, _, _) called\n", nfds); - return _WS_select(0, nfds, readfds, writefds, exceptfds, - (void *) timeout, NULL); -} - -int pselect(int nfds, fd_set *readfds, fd_set *writefds, - fd_set *exceptfds, const struct timespec *timeout, - const sigset_t *sigmask) -{ - TRACE("pselect(%d, _, _, _, _, _) called\n", nfds); - return _WS_select(1, nfds, readfds, writefds, exceptfds, - (void *) timeout, sigmask); -} - -int poll(struct pollfd *fds, nfds_t nfds, int timeout) -{ - TRACE("poll(_, %ld, %d) called\n", nfds, timeout); - return _WS_poll(0, fds, nfds, timeout, NULL, NULL); -} - -int ppoll(struct pollfd *fds, nfds_t nfds, - const struct timespec *timeout, const sigset_t *sigmask) -{ - TRACE("ppoll(_, %ld, _, _) called\n", nfds); - return _WS_poll(0, fds, nfds, 0, (struct timespec *)timeout, - (sigset_t *)sigmask); -} - -int dup(int oldfd) { - int ret; - static void * (*func)(); - if (!func) func = (void *(*)()) dlsym(RTLD_NEXT, "dup"); - - TRACE(">> dup(%d) called\n", oldfd); - - ret = (int) func(oldfd); - - TRACE("<< dup(%d) ret %d\n", oldfd, ret); - return ret; -} - -int dup2(int oldfd, int newfd) { - int ret; - static void * (*func)(); - if (!func) func = (void *(*)()) dlsym(RTLD_NEXT, "dup2"); - - TRACE(">> dup2(%d, %d) called\n", oldfd, newfd); - - ret = (int) func(oldfd, newfd); - if ((! _WS_connections[oldfd]) && (! _WS_connections[newfd])) { - return ret; - } - - if ((ret < 0) || (oldfd == newfd) || - (_WS_connections[oldfd] == _WS_connections[newfd])) { - TRACE("<< dup2(%d, %d) ret %d\n", oldfd, newfd, ret); - return ret; - } - - /* dup2 behavior is to close newfd if it's open */ - if (_WS_connections[newfd]) { - _WS_free(newfd); - } - - if (! _WS_connections[oldfd]) { - TRACE("<< dup2(%d, %d) ret %d\n", oldfd, newfd, ret); - return ret; - } - - MSG("interposing on duplicated fd %d\n", newfd); - /* oldfd and newfd are now descriptors for the same socket, - * re-use the same context memory area */ - _WS_connections[newfd] = _WS_connections[oldfd]; - _WS_connections[newfd]->refcnt++; - - /* Add to search list for select/pselect */ - _WS_fds[_WS_nfds] = newfd; - _WS_nfds++; - - TRACE("<< dup2(%d, %d) ret %d\n", oldfd, newfd, ret); - return ret; - -} - -int dup3(int oldfd, int newfd, int flags) { - int ret; - static void * (*func)(); - if (!func) func = (void *(*)()) dlsym(RTLD_NEXT, "dup3"); - - TRACE(">> dup3(%d, %d, %d) called\n", oldfd, newfd, flags); - - ret = (int) func(oldfd, newfd, flags); - - TRACE("<< dup3(%d, %d, %d) ret %d\n", oldfd, newfd, flags, ret); - return ret; -} - diff --git a/other/wswrapper.h b/other/wswrapper.h deleted file mode 100644 index 412b17a..0000000 --- a/other/wswrapper.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * wswrap/wswrapper: Add WebSockets support to any service. - * Copyright 2010 Joel Martin - * Licensed under LGPL version 3 (see docs/LICENSE.LGPL-3) - */ - -#ifdef DO_MSG -#define MSG(...) \ - fprintf(stderr, "wswrapper: "); \ - fprintf(stderr, __VA_ARGS__); -#else -#define MSG(...) -#endif - -#ifdef DO_DEBUG -#define DEBUG(...) \ - if (DO_DEBUG) { \ - fprintf(stderr, "wswrapper: "); \ - fprintf(stderr, __VA_ARGS__); \ - } -#else -#define DEBUG(...) -#endif - -#ifdef DO_TRACE -#define TRACE(...) \ - if (DO_TRACE) { \ - fprintf(stderr, "wswrapper: "); \ - fprintf(stderr, __VA_ARGS__); \ - } -#else -#define TRACE(...) -#endif - -#define RET_ERROR(eno, ...) \ - fprintf(stderr, "wswrapper error: "); \ - fprintf(stderr, __VA_ARGS__); \ - errno = eno; \ - return -1; - - -const char _WS_response[] = "\ -HTTP/1.1 101 Web Socket Protocol Handshake\r\n\ -Upgrade: WebSocket\r\n\ -Connection: Upgrade\r\n\ -%sWebSocket-Origin: %s\r\n\ -%sWebSocket-Location: %s://%s%s\r\n\ -%sWebSocket-Protocol: sample\r\n\ -\r\n%s"; - -#define WS_BUFSIZE 65536 -#define WS_MAX_FDS 1024 - -/* Buffers and state for each wrapped WebSocket connection */ -typedef struct { - char rbuf[WS_BUFSIZE]; - char sbuf[WS_BUFSIZE]; - int rcarry_cnt; - char rcarry[3]; - int newframe; - int refcnt; -} _WS_connection; - - diff --git a/rebind.c b/rebind.c index c7e83de..f6c5ed0 100644 --- a/rebind.c +++ b/rebind.c @@ -31,7 +31,7 @@ #if defined(DO_DEBUG) #define DEBUG(...) \ - fprintf(stderr, "wswrapper: "); \ + fprintf(stderr, "rebind: "); \ fprintf(stderr, __VA_ARGS__); #else #define DEBUG(...)