Jelajahi Sumber

Update C proxy to WS protocol version 76.

Pull in LGPL md5.c and md5.h files (written by Ulrich Drepper).

Now both python and C version of the proxy support both protocol 75
and protocol 76 (hybi 00).

Reorganize websocket.py slightly to match websocket.c.
Joel Martin 15 tahun lalu
induk
melakukan
1eba7b4279
8 mengubah file dengan 800 tambahan dan 68 penghapusan
  1. 1 1
      docs/TODO
  2. 5 2
      utils/Makefile
  3. 458 0
      utils/md5.c
  4. 148 0
      utils/md5.h
  5. 3 0
      utils/md5_test.c
  6. 130 25
      utils/websocket.c
  7. 9 0
      utils/websocket.h
  8. 46 40
      utils/websocket.py

+ 1 - 1
docs/TODO

@@ -4,7 +4,7 @@ Short Term:
 
 - Support Opera 10.60 (WebSocket frames dropped).
 
-- Possibly support IE <= 8.0 using excanvas of fxcanvas:
+- Possibly support IE <= 8.0 using excanvas or fxcanvas:
   http://excanvas.sourceforge.net/
   http://code.google.com/p/fxcanvas/
 

+ 5 - 2
utils/Makefile

@@ -1,7 +1,10 @@
-wsproxy: wsproxy.o websocket.o
+wsproxy: wsproxy.o websocket.o md5.o
 	$(CC) $^ -l ssl -l resolv -o $@
 
-websocket.o wsproxy.o: websocket.h
+websocket.o: websocket.c websocket.h md5.h
+wsproxy.o: wsproxy.c websocket.h
+md5.o: md5.c md5.h
+	$(CC) -c -o $@ $*.c -DHAVE_MEMCPY -DSTDC_HEADERS
 
 clean:
 	rm -f wsproxy wsproxy.o websocket.o

+ 458 - 0
utils/md5.c

@@ -0,0 +1,458 @@
+/* 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 <drepper@gnu.ai.mit.edu>, 1995.  */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <sys/types.h>
+
+#if STDC_HEADERS || defined _LIBC
+# include <stdlib.h>
+# include <string.h>
+#else
+# ifndef HAVE_MEMCPY
+#  define memcpy(d, s, n) (bcopy ((s), (d), (n)), (d))
+# endif
+#endif
+
+#include "md5.h"
+
+#ifdef _LIBC
+# include <endian.h>
+# 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
+#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;
+}

+ 148 - 0
utils/md5.h

@@ -0,0 +1,148 @@
+/* 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 <stdio.h>
+
+#if defined HAVE_LIMITS_H || _LIBC
+# include <limits.h>
+#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 <stdint.h>
+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 <limits.h>) 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 */

+ 3 - 0
utils/md5_test.c

@@ -0,0 +1,3 @@
+int main () {
+    printf("hello world\n");
+}

+ 130 - 25
utils/websocket.c

@@ -24,10 +24,10 @@
 const char server_handshake[] = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n\
 Upgrade: WebSocket\r\n\
 Connection: Upgrade\r\n\
-WebSocket-Origin: %s\r\n\
-WebSocket-Location: %s://%s%s\r\n\
-WebSocket-Protocol: sample\r\n\
-\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[] = "<cross-domain-policy><allow-access-from domain=\"*\" to-ports=\"*\" /></cross-domain-policy>\n";
 
@@ -272,11 +272,113 @@ 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;
+
+    if ((strlen(handshake) < 92) || (bcmp(handshake, "GET ", 4) != 0)) {
+        return 0;
+    }
+    start = handshake+4;
+    end = strstr(start, " HTTP/1.1");
+    if (!end) { return 0; }
+    strncpy(headers->path, start, end-start);
+    headers->path[end-start] = '\0';
+
+    start = strstr(handshake, "\r\nHost: ");
+    if (!start) { return 0; }
+    start += 8;
+    end = strstr(start, "\r\n");
+    strncpy(headers->host, start, end-start);
+    headers->host[end-start] = '\0';
+
+    start = strstr(handshake, "\r\nOrigin: ");
+    if (!start) { return 0; }
+    start += 10;
+    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-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 {
+        headers->key1[0] = '\0';
+        headers->key2[0] = '\0';
+        headers->key3[0] = '\0';
+    }
+
+    return 1;
+}
+
+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;
+
+    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;
+
+    /* Pack it big-endian */
+    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;
+
+    strncpy(buf+8, headers->key3, 8);
+    buf[16] = '\0';
+
+    md5_buffer(buf, 16, target);
+    target[16] = '\0';
+
+    return 1;
+}
+
+    
+
 ws_ctx_t *do_handshake(int sock) {
-    char handshake[4096], response[4096];
-    char *scheme, *line, *path, *host, *origin;
+    char handshake[4096], response[4096], trailer[17];
+    char *scheme, *pre;
+    headers_t headers;
     char *args_start, *args_end, *arg_idx;
-    int len;
+    int len, ret;
     ws_ctx_t * ws_ctx;
 
     // Reset settings
@@ -287,7 +389,11 @@ ws_ctx_t *do_handshake(int sock) {
     // Peek, but don't read the data
     len = recv(sock, handshake, 1024, MSG_PEEK);
     handshake[len] = 0;
-    if (bcmp(handshake, "<policy-file-request/>", 22) == 0) {
+    if (len == 0) {
+        printf("Ignoring empty handshake\n");
+        close(sock);
+        return NULL;
+    } else if (bcmp(handshake, "<policy-file-request/>", 22) == 0) {
         len = recv(sock, handshake, 1024, 0);
         handshake[len] = 0;
         printf("Sending flash policy response\n");
@@ -313,27 +419,25 @@ ws_ctx_t *do_handshake(int sock) {
     }
     len = ws_recv(ws_ctx, handshake, 4096);
     handshake[len] = 0;
-    //printf("handshake: %s\n", handshake);
-    if ((len < 92) || (bcmp(handshake, "GET ", 4) != 0)) {
+
+    if (!parse_handshake(handshake, &headers)) {
         fprintf(stderr, "Invalid WS request\n");
+        close(sock);
         return NULL;
     }
-    strtok(handshake, " ");      // Skip "GET "
-    path = strtok(NULL, " ");    // Extract path
-    strtok(NULL, "\n");          // Skip to Upgrade line
-    strtok(NULL, "\n");          // Skip to Connection line
-    strtok(NULL, "\n");          // Skip to Host line
-    strtok(NULL, " ");           // Skip "Host: "
-    host = strtok(NULL, "\r");   // Extract host
-    strtok(NULL, " ");           // Skip "Origin: "
-    origin = strtok(NULL, "\r"); // Extract origin
-
-    //printf("path: %s\n", path);
-    //printf("host: %s\n", host);
-    //printf("origin: %s\n", origin);
+
+    if (headers.key3[0] != '\0') {
+        gen_md5(&headers, trailer);
+        pre = "Sec-";
+        printf("  using protocol version 76\n");
+    } else {
+        trailer[0] = '\0';
+        pre = "";
+        printf("  using protocol version 75\n");
+    }
     
     // Parse client settings from the GET path
-    args_start = strstr(path, "?");
+    args_start = strstr(headers.path, "?");
     if (args_start) {
         if (strstr(args_start, "#")) {
             args_end = strstr(args_start, "#");
@@ -352,7 +456,8 @@ ws_ctx_t *do_handshake(int sock) {
         }
     }
 
-    sprintf(response, server_handshake, origin, scheme, host, path);
+    sprintf(response, server_handshake, pre, headers.origin, pre, scheme,
+            headers.host, headers.path, pre, trailer);
     //printf("response: %s\n", response);
     ws_send(ws_ctx, response, strlen(response));
 

+ 9 - 0
utils/websocket.h

@@ -22,6 +22,15 @@ typedef struct {
     int seq_num;
 } client_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);
 

+ 46 - 40
utils/websocket.py

@@ -43,6 +43,20 @@ def traffic(token="."):
     sys.stdout.write(token)
     sys.stdout.flush()
 
+def encode(buf):
+    global send_seq
+    if client_settings['b64encode']:
+        buf = b64encode(buf)
+    else:
+        # Modified UTF-8 encode
+        buf = buf.decode('latin-1').encode('utf-8').replace("\x00", "\xc4\x80")
+
+    if client_settings['seq_num']:
+        send_seq += 1
+        return "\x00%d:%s\xff" % (send_seq-1, buf)
+    else:
+        return "\x00%s\xff" % buf
+
 def decode(buf):
     """ Parse out WebSocket packets. """
     if buf.count('\xff') > 1:
@@ -57,19 +71,32 @@ def decode(buf):
         else:
             return [buf[1:-1].replace("\xc4\x80", "\x00").decode('utf-8').encode('latin-1')]
 
-def encode(buf):
-    global send_seq
-    if client_settings['b64encode']:
-        buf = b64encode(buf)
-    else:
-        # Modified UTF-8 encode
-        buf = buf.decode('latin-1').encode('utf-8').replace("\x00", "\xc4\x80")
+def parse_handshake(handshake):
+    ret = {}
+    req_lines = handshake.split("\r\n")
+    if not req_lines[0].startswith("GET "):
+        raise Exception("Invalid handshake: no GET request line")
+    ret['path'] = req_lines[0].split(" ")[1]
+    for line in req_lines[1:]:
+        if line == "": break
+        var, delim, val = line.partition(": ")
+        ret[var] = val
 
-    if client_settings['seq_num']:
-        send_seq += 1
-        return "\x00%d:%s\xff" % (send_seq-1, buf)
-    else:
-        return "\x00%s\xff" % buf
+    if req_lines[-2] == "":
+        ret['key3'] = req_lines[-1]
+
+    return ret
+
+def gen_md5(keys):
+    key1 = keys['Sec-WebSocket-Key1']
+    key2 = keys['Sec-WebSocket-Key2']
+    key3 = keys['key3']
+    spaces1 = key1.count(" ")
+    spaces2 = key2.count(" ")
+    num1 = int("".join([c for c in key1 if c.isdigit()])) / spaces1
+    num2 = int("".join([c for c in key2 if c.isdigit()])) / spaces2
+
+    return md5(struct.pack('>II8s', num1, num2, key3)).digest()
 
 
 def do_handshake(sock):
@@ -82,7 +109,11 @@ def do_handshake(sock):
     # Peek, but don't read the data
     handshake = sock.recv(1024, socket.MSG_PEEK)
     #print "Handshake [%s]" % repr(handshake)
-    if handshake.startswith("<policy-file-request/>"):
+    if handshake == "":
+        print "Ignoring empty handshake"
+        sock.close()
+        return False
+    elif handshake.startswith("<policy-file-request/>"):
         handshake = sock.recv(1024)
         print "Sending flash policy response"
         sock.send(policy_response)
@@ -120,9 +151,11 @@ def do_handshake(sock):
     if h.get('key3'):
         trailer = gen_md5(h)
         pre = "Sec-"
+        print "  using protocol version 76"
     else:
         trailer = ""
         pre = ""
+        print "  using protocol version 75"
 
     response = server_handshake % (pre, h['Origin'], pre, scheme,
             h['Host'], h['path'], pre, trailer)
@@ -131,33 +164,6 @@ def do_handshake(sock):
     retsock.send(response)
     return retsock
 
-def parse_handshake(handshake):
-    ret = {}
-    req_lines = handshake.split("\r\n")
-    if not req_lines[0].startswith("GET "):
-        raise "Invalid handshake: no GET request line"
-    ret['path'] = req_lines[0].split(" ")[1]
-    for line in req_lines[1:]:
-        if line == "": break
-        var, delim, val = line.partition(": ")
-        ret[var] = val
-
-    if req_lines[-2] == "":
-        ret['key3'] = req_lines[-1]
-
-    return ret
-
-def gen_md5(keys):
-    key1 = keys['Sec-WebSocket-Key1']
-    key2 = keys['Sec-WebSocket-Key2']
-    key3 = keys['key3']
-    spaces1 = key1.count(" ")
-    spaces2 = key2.count(" ")
-    num1 = int("".join([c for c in key1 if c.isdigit()])) / spaces1
-    num2 = int("".join([c for c in key2 if c.isdigit()])) / spaces2
-
-    return md5(struct.pack('>II8s', num1, num2, key3)).digest()
-
 def daemonize():
     os.umask(0)
     os.chdir('/')