[tor-commits] [stegotorus/master] first version of stegotorus... binary name still obfsproxy

zwol at torproject.org zwol at torproject.org
Fri Jul 20 23:17:05 UTC 2012


commit 2e5825fee2123ad6fcac1fd5aff7b85ea65b5b06
Author: Vinod Yegneswaran <vinod at csl.sri.com>
Date:   Mon Oct 31 22:33:46 2011 +0000

    first version of stegotorus...  binary name still obfsproxy
    js + flash steg enabled.
    
    git-svn-id: svn+ssh://spartan.csl.sri.com/svn/private/DEFIANCE@107 a58ff0ac-194c-e011-a152-003048836090
---
 Makefile.am            |   10 +-
 NOTES                  |   20 +
 run-autogen.csh        |    3 +
 src/steg/cookies.c     |  230 ++++++++
 src/steg/cookies.h     |   17 +
 src/steg/crc32.c       |   82 +++
 src/steg/crc32.h       |   18 +
 src/steg/jsSteg.c      | 1200 ++++++++++++++++++++++++++++++++++++++
 src/steg/jsSteg.h      |   65 +++
 src/steg/payloads.c    | 1486 ++++++++++++++++++++++++++++++++++++++++++++++++
 src/steg/payloads.h    |  159 ++++++
 src/steg/pdfSteg.c     |  618 ++++++++++++++++++++
 src/steg/pdfSteg.h     |   29 +
 src/steg/swfSteg.c     |  282 +++++++++
 src/steg/swfSteg.c.old |  264 +++++++++
 src/steg/swfSteg.h     |   42 ++
 src/steg/x_http.c.old  |  337 +++++++++++
 src/steg/x_http2.c     |  700 +++++++++++++++++++++++
 src/steg/zpack.c       |  408 +++++++++++++
 src/steg/zpack.h       |   14 +
 start-client.csh       |    8 +
 start-server.csh       |    6 +
 torrc                  |   12 +
 23 files changed, 6009 insertions(+), 1 deletions(-)

diff --git a/Makefile.am b/Makefile.am
index bcb6e88..e8b68f5 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -22,7 +22,15 @@ PROTOCOLS = \
 	src/protocols/x_rr.c
 
 STEGANOGRAPHERS = \
-	src/steg/x_http.c
+	src/steg/x_http.c \
+	src/steg/x_http2.c \
+	src/steg/payloads.c \
+	src/steg/cookies.c \
+	src/steg/jsSteg.c \
+	src/steg/swfSteg.c \
+	src/steg/zpack.c \
+	src/steg/crc32.c \
+	src/steg/pdfSteg.c
 
 libobfsproxy_a_SOURCES = \
 	src/connections.c \
diff --git a/NOTES b/NOTES
new file mode 100644
index 0000000..5b5f23c
--- /dev/null
+++ b/NOTES
@@ -0,0 +1,20 @@
+To Test ObfsProxy:
+
+start server (start-server.csh)
+start client (start-client.csh)
+start Tor (copy torrc.sample to etc/tor)
+
+
+Now test tor: 
+curl --socks4a 127.0.0.1:9060 -o - http://check.torproject.org
+
+
+To add new steg modules:
+
+1. Add a file below src/steg which implements a steg module; note that
+the STEG_DEFINE_MODULE boilerplate macro is mandatory.  The name of
+the file should be the same as the name of the module (as set by
+STEG_DEFINE_MODULE) plus the .c extension.  
+2. Add the file to the STEGANOGRAPHERS list in Makefile.am.  
+
+That should be all that is necessary.
diff --git a/run-autogen.csh b/run-autogen.csh
new file mode 100644
index 0000000..9fe8c44
--- /dev/null
+++ b/run-autogen.csh
@@ -0,0 +1,3 @@
+#!/bin/csh
+setenv PATH /usr/local/bin:`echo $PATH`
+./autogen.sh
diff --git a/src/steg/cookies.c b/src/steg/cookies.c
new file mode 100644
index 0000000..e8d43b9
--- /dev/null
+++ b/src/steg/cookies.c
@@ -0,0 +1,230 @@
+
+#include "cookies.h"
+
+int unwrap_cookie(unsigned char* inbuf, unsigned char* outbuf, int buflen) {
+  int i,j;
+  j = 0;
+
+  for (i=0; i < buflen; i++) {
+    char c  = inbuf[i];
+
+    if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f'))
+      outbuf[j++] = c; 
+  }
+
+  return j;
+
+}
+
+
+
+
+
+
+/* valid cookie characters: anything between between 33 and 126, with the exception of "=" and ';'*/
+/* writes a line of the form XXXX=YYYYY of length cookielen to outbuf, outbuf length >= cookielen */
+/* returns data consumed if success.  datalen assumed to be greater than cookielen*/
+
+
+int gen_one_cookie(unsigned char* outbuf, int cookielen, unsigned char* data, int datalen) {
+  int sofar = 0;
+  unsigned char c;
+  int namelen, vlen;
+  int data_consumed = 0;
+
+  if (cookielen < 4)
+    return -1;
+
+
+
+  if (cookielen > 13)
+    namelen = rand() % 10 + 1;
+  else 
+    namelen = rand() % (cookielen - 3) + 1;
+
+  vlen = cookielen - namelen;
+
+
+
+  while (sofar < namelen) {
+    c = rand() % (127 - 33) + 33;
+    if (c == '=' || c == ';' || c == '`' || c == '\'' || c == '%')
+      continue;
+
+    if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F') || (rand () % 4 != 0)) {
+      if (data_consumed < datalen) 
+	outbuf[sofar++] = data[data_consumed++];
+    }
+    else
+      outbuf[sofar++] = c;
+  }
+
+
+  outbuf[sofar++] = '=';
+
+
+  while (sofar < cookielen) {
+    c = rand() % (127 - 33) + 33;
+    if (c == '=' || c == ';' || c == '`' || c == '\'' || c == '%')
+      continue;
+
+    if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F') || (rand() % 4 != 0)) {
+      if (data_consumed < datalen) 
+	outbuf[sofar++] = data[data_consumed++];
+    }
+    else
+      outbuf[sofar++] = c;
+  }
+
+
+  
+  return data_consumed;
+
+}
+
+
+
+
+/* dummy version for testing */
+int gen_one_cookie2(unsigned char* outbuf, int cookielen, unsigned char* data, int datalen) {
+  int i;
+
+  if (cookielen < 4)
+    return -1;
+
+  if (datalen >= cookielen) {
+    memcpy(outbuf, data, cookielen);
+    return cookielen;
+  }
+
+  memcpy(outbuf, data, datalen);
+  for (i=datalen; i < cookielen; i++)
+    outbuf[i] = '+';
+
+  return datalen;
+
+}
+
+/* returns data consumed */
+int gen_cookie_field(unsigned char* outbuf, int total_cookie_len, unsigned char* data, int datalen) {
+  int rem_cookie_len = total_cookie_len;
+  int consumed = 0;
+
+
+  if (total_cookie_len < 4) {
+    fprintf(stderr, "error: cookie length too small\n");
+    return -1;
+  }
+
+  while (rem_cookie_len > 4) {
+    int cookielen = 4 + rand() % (rem_cookie_len - 3);
+
+    int cnt =  gen_one_cookie(outbuf, cookielen, data + consumed, datalen - consumed);
+
+    if (cnt < 0) {
+      fprintf(stderr, "error: couldn't create cookie %d\n", cnt);
+      return cnt;
+    }
+
+
+
+    consumed += cnt;
+    //    fprintf(stderr, "cnt = %d %d %d; consumed = %d\n", cnt, rem_cookie_len, cookielen, consumed);
+    rem_cookie_len = rem_cookie_len - cookielen;
+    outbuf += cookielen;
+
+
+ 
+   if (rem_cookie_len == 0) {
+      break;
+    }
+    else if (rem_cookie_len <= 5) {
+      int i = 0;
+      if ((consumed < datalen) && (consumed % 2 == 1)) {
+	outbuf[0] = data[consumed];
+	consumed++;
+	outbuf++;
+	rem_cookie_len--;
+      }
+
+      for (i=0; i < rem_cookie_len; i++) 
+	outbuf[i] = "ghijklmnopqrstuvwxyzGHIJKLMNOPQRSTUVWXYZ"[rand() % 40];
+      
+      return consumed;
+    }
+    
+    
+    outbuf[0] = ';';
+    outbuf++;
+    rem_cookie_len--;
+  }
+
+
+  if (consumed % 2 == 1) {
+    if ((outbuf[-1] >= '0' && outbuf[-1] <= '9') || (outbuf[-1] >= 'a' && outbuf[-1] <= 'f')) {
+      outbuf[-1] = '*';
+      consumed--;
+    }
+    else {
+      outbuf[-1] = data[consumed];
+      consumed++;
+    }	  
+  }
+
+
+  return consumed;
+}
+
+
+/* dummy version for testing */
+int gen_cookie_field2(unsigned char* outbuf, int total_cookie_len, unsigned char* data, int datalen) {
+  int i;
+  if (datalen >= total_cookie_len) {
+    memcpy(outbuf, data, total_cookie_len);
+    return total_cookie_len;
+  }
+
+  memcpy(outbuf, data, datalen);
+  for (i=datalen; i < total_cookie_len; i++)
+    outbuf[i] = '*';
+
+  return datalen;
+}
+
+
+
+
+/*
+
+int main () {
+  char outbuf[200];
+  char data[52] = "1a239023820389023802380389abc2322132321932847203aedf";
+  char data2[200];
+  //  srand(time(NULL));
+  srand (20);
+
+
+
+  int i=0;
+
+  for (i=0; i < 1000000; i++) {
+    int cookielen = rand()%50 + 5;
+    bzero(outbuf, sizeof(outbuf));
+    int len = gen_cookie_field(outbuf, cookielen, data, sizeof(data));
+    //    printf("len = %d cookie = %s %d\n", len, outbuf, cookielen);
+    bzero(data2, sizeof(data2));
+    int len2 = unwrap_cookie(outbuf, data2, cookielen);
+    //    printf("unwrapped datalen = %d data = %s\n", len, data2);
+
+    if (len != len2)
+      printf("hello %d\n", i);
+  }
+  
+
+
+
+}
+
+*/
+
+
diff --git a/src/steg/cookies.h b/src/steg/cookies.h
new file mode 100644
index 0000000..4970776
--- /dev/null
+++ b/src/steg/cookies.h
@@ -0,0 +1,17 @@
+#ifndef _COOKIES_H
+#define _COOKIES_H
+
+
+
+#include <stdio.h>
+#include <strings.h>
+#include <stdlib.h>
+
+int unwrap_cookie(unsigned char* inbuf, unsigned char* outbuf, int buflen);
+int gen_cookie_field(unsigned char* outbuf, int total_cookie_len, unsigned char* data, int datalen);
+int gen_one_cookie(unsigned char* outbuf, int cookielen, unsigned char* data, int datalen);
+int gen_one_cookie2(unsigned char* outbuf, int cookielen, unsigned char* data, int datalen);
+int gen_cookie_field2(unsigned char* outbuf, int total_cookie_len, unsigned char* data, int datalen);
+
+
+#endif
diff --git a/src/steg/crc32.c b/src/steg/crc32.c
new file mode 100644
index 0000000..7fdc847
--- /dev/null
+++ b/src/steg/crc32.c
@@ -0,0 +1,82 @@
+#include "crc32.h"
+
+#define CRC32C(c,d) (c=(c>>8)^crc_c[(c^(d))&0xFF])
+
+static const unsigned int crc_c[256] = {
+	0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
+	0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
+	0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
+	0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
+	0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
+	0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
+	0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,
+	0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
+	0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
+	0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
+	0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940,
+	0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
+	0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116,
+	0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
+	0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
+	0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
+	0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a,
+	0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
+	0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818,
+	0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
+	0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
+	0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
+	0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c,
+	0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
+	0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
+	0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
+	0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
+	0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
+	0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086,
+	0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
+	0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4,
+	0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
+	0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
+	0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
+	0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
+	0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
+	0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe,
+	0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
+	0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
+	0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
+	0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252,
+	0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
+	0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60,
+	0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
+	0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
+	0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
+	0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04,
+	0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
+	0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a,
+	0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
+	0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
+	0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
+	0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e,
+	0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
+	0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
+	0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
+	0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
+	0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
+	0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0,
+	0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
+	0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6,
+	0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
+	0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
+	0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,
+};
+
+
+unsigned int generate_crc32c(char *buffer, size_t length) {
+  size_t i;
+  unsigned int crc32 = ~0L;
+
+  for (i = 0; i < length; i++){
+      CRC32C(crc32, (unsigned char)buffer[i]);
+  }
+  return ~crc32;
+}
+
diff --git a/src/steg/crc32.h b/src/steg/crc32.h
new file mode 100644
index 0000000..780e7bd
--- /dev/null
+++ b/src/steg/crc32.h
@@ -0,0 +1,18 @@
+#ifndef __crc32cr_table_h__
+#define __crc32cr_table_h__
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <sys/types.h>
+
+#if defined HAVE_STDINT_H
+# include <stdint.h>
+#elif defined HAVE_INTTYPES_H
+# include <inttypes.h>
+#endif
+
+unsigned int generate_crc32c(char *string, size_t length);
+
+#endif
diff --git a/src/steg/jsSteg.c b/src/steg/jsSteg.c
new file mode 100644
index 0000000..5946062
--- /dev/null
+++ b/src/steg/jsSteg.c
@@ -0,0 +1,1200 @@
+#include "payloads.h"
+#include "jsSteg.h"
+#include "cookies.h"
+
+
+/*
+ * jsSteg: A Javascript-based steganography module
+ *
+ */
+
+
+/*
+ * int isxString(char *str)
+ *
+ * description:
+ *   return 1 if all char in str are hexadecimal
+ *   return 0 otherwise
+ *
+ */
+int isxString(char *str) {
+  unsigned int i;
+  char *dp = str;
+  for (i=0; i<strlen(str); i++) {
+    if (! isxdigit(*dp) ) {
+      return 0;
+    }
+  }
+  return 1;
+}
+
+
+/*
+ * int encode(char *data, char *jTemplate, char *jData,
+ *            unsigned int dlen, unsigned int jtlen, unsigned int jdlen)
+ *
+ * description:
+ *   embed hex-encoded data (data) in the input Javascript (jTemplate)
+ *   and put the result in jData
+ *   function returns the number of characters in data successfully
+ *   embedded in jData, or returns one of the error codes
+ *
+ * approach: 
+ *   replaces characters in jTemplate that are hexadecimal (i.e., {0-9,a-f,A-F})
+ *   with those in data, and leave the non-hex char in place
+ *
+ * input:
+ *   - data[] : hex data to hide
+ *   - dlen   : size of data
+ *   - jTemplate[] : Javascript
+ *   - jlen   : size of jTemplate
+ *   - jdlen  : size of jData, output buffer
+ *
+ * output:
+ *   - jData  : result of encoding data in jTemplate
+ *
+ * assumptions:
+ *   - data is hex-encoded
+ *
+ * exceptions:
+ *   - if (jdlen < jtlen) return INVALID_BUF_SIZE
+ *   - if (data contains non-hex char) return INVALID_DATA_CHAR
+ *
+ * example:
+ *   data      = "0123456789ABCDEF"
+ *   jTemplate = "dfp_ord=Math.random()*10000000000000000; dfp_tile = 1;"
+ *   encode() returns 16
+ *   jData     = "01p_or2=M3th.r4n5om()*6789ABCDEF0000000; dfp_tile = 1;"
+ *
+ */
+int encode(char *data, char *jTemplate, char *jData,
+	   unsigned int dlen, unsigned int jtlen, unsigned int jdlen )
+{
+  unsigned int encCnt = 0;  /* num of data encoded in jData */
+  char *dp, *jtp, *jdp; /* current pointers for data, jTemplate, and jData */
+  
+  unsigned int j;
+
+  /* 
+   *  insanity checks
+   */
+  if (jdlen < jtlen) { return INVALID_BUF_SIZE; }
+
+  dp = data; jtp = jTemplate; jdp = jData;
+
+  if (! isxString(dp) ) { return INVALID_DATA_CHAR; }
+
+  /* handling boundary case: dlen == 0 */
+  if (dlen < 1) { return 0; }
+
+
+  for (j=0; j<jtlen; j++) {
+    /* found a hex char in jTemplate that can be used for encoding data */
+    if ( isxdigit(*jtp) ) {
+      *jdp = *dp;
+      dp++;
+      encCnt++;
+      if (encCnt == dlen) { 
+	jtp++; jdp++;
+	break; 
+      }
+    } else {
+      *jdp = *jtp;
+    }
+    jtp++; jdp++;
+  }
+
+
+  /* copying the rest of jTemplate to jdata */
+  while (jtp < (jTemplate+jtlen)) {
+    *jdp++ = *jtp++;
+  }
+
+  return encCnt;
+}
+
+
+#define startScriptTypeJS "<script type=\"text/javascript\">"
+#define endScriptTypeJS "</script>"
+// #define JS_DELIMITER "?"
+// #define JS_DELIMITER_REPLACEMENT "."
+
+
+/*
+ * similar to encode(), but uses offset2Hex to look for usable hex char
+ * in JS for encoding. See offset2Hex for what hex char are considered
+ * usable. encode() also converts JS_DELIMITER that appears in the
+ * the JS to JS_DELIMITER_REPLACEMENT, before all the data is encoded.
+ *
+ * Output:
+ * fin - signal the caller whether all data has been encoded and 
+ *       a JS_DELIMITER has been added
+ */
+int  encode2(char *data, char *jTemplate, char *jData,
+	     unsigned int dlen, unsigned int jtlen,
+	     unsigned int jdlen, int *fin)
+{
+  unsigned int encCnt = 0;  /* num of data encoded in jData */
+  char *dp, *jtp, *jdp; /* current pointers for data, jTemplate, and jData */
+  int i,j;
+
+  /*
+   *  insanity checks
+   */
+  if (jdlen < jtlen) { return INVALID_BUF_SIZE; }
+
+  dp = data; jtp = jTemplate; jdp = jData;
+
+  if (! isxString(dp) ) { return INVALID_DATA_CHAR; }
+
+  /* handling boundary case: dlen == 0 */
+  if (dlen < 1) { return 0; }
+
+
+  i = offset2Hex(jtp, (jTemplate+jtlen)-jtp, 0);
+  while (encCnt < dlen && i != -1) {
+    // copy next i char from jtp to jdp,
+    // except that if *jtp==JS_DELIMITER, copy
+    // JS_DELIMITER_REPLACEMENT to jdp instead
+    j = 0;
+    while (j < i) {
+      if (*jtp == JS_DELIMITER) {
+        *jdp = JS_DELIMITER_REPLACEMENT;
+      } else {
+        *jdp = *jtp;
+      }
+      jtp = jtp + 1; jdp = jdp + 1; j++;
+    }
+
+    *jdp = *dp;
+    encCnt++;
+    dp = dp + 1; jtp = jtp + 1; jdp = jdp + 1;
+
+    i = offset2Hex(jtp, (jTemplate+jtlen)-jtp, 1);
+  }
+
+
+
+  // copy the rest of jTemplate to jdata
+  // if we've encoded all data, replace the first
+  // char in jTemplate by JS_DELIMITER, if needed,
+  // to signal the end of data encoding
+
+#ifdef DEBUG2
+  printf("encode2: encCnt = %d; dlen = %d\n", encCnt, dlen);
+#endif
+
+  *fin = 0;
+  if (encCnt == dlen) {
+    // replace the next char in jTemplate by JS_DELIMITER
+    if (jtp < (jTemplate+jtlen)) {
+      *jdp = JS_DELIMITER;
+    }
+    jdp = jdp+1; jtp = jtp+1;
+    *fin = 1;
+  }
+
+  while (jtp < (jTemplate+jtlen)) {
+    if (*jtp == JS_DELIMITER) {
+      if (encCnt < dlen) {
+        *jdp = JS_DELIMITER_REPLACEMENT;
+      } else {
+        *jdp = *jtp;
+      }
+      // else if (isxdigit(*jtp)) {
+      //   if (encCnt < dlen && *fin == 0) {
+      //     *jdp = JS_DELIMITER;
+      //     *fin = 1;
+      //   } else {
+      //     *jdp = *jtp;
+      //   }
+      // }
+    } else {
+      *jdp = *jtp;
+    }
+    jdp = jdp+1; jtp = jtp+1;
+  }
+
+#ifdef DEBUG2
+  printf("encode2: encCnt = %d; dlen = %d\n", encCnt, dlen);
+  printf("encode2: fin= %d\n", *fin);
+#endif
+
+  return encCnt;
+
+}
+
+
+
+int encodeHTTPBody(char *data, char *jTemplate, char *jData,
+		   unsigned int dlen, unsigned int jtlen,
+		   unsigned int jdlen, int mode)
+{
+  char *dp, *jtp, *jdp; // current pointers for data, jTemplate, and jData
+  unsigned int encCnt = 0;  // num of data encoded in jData
+  int n; // tmp for updating encCnt
+  char *jsStart, *jsEnd;
+  int skip;
+  int scriptLen;
+  int fin;
+  unsigned int dlen2 = dlen;
+  dp = data; 
+  jtp = jTemplate; 
+  jdp = jData;
+
+
+  if (mode == CONTENT_JAVASCRIPT) {
+    // assumption: the javascript pertaining to jTemplate has enough capacity
+    // to encode jData. thus, we only invoke encode() once here.
+    encCnt = encode2(dp, jtp, jdp, dlen, jtlen, jdlen, &fin);
+    // ensure that all dlen char from data have been encoded in jData
+#ifdef DEBUG
+    if (encCnt != dlen || fin == 0) {
+      printf("Problem encoding all data to the JS\n");
+    }
+#endif
+    return encCnt;
+
+  } 
+
+  else if (mode == CONTENT_HTML_JAVASCRIPT) {
+    while (encCnt < dlen2) {
+      jsStart = strstr(jtp, startScriptTypeJS);
+      if (jsStart == NULL) { 
+#ifdef DEBUG
+	printf("lack of usable JS; can't find startScriptType\n");
+#endif
+	return encCnt; 
+      }
+      skip = strlen(startScriptTypeJS)+jsStart-jtp;
+#ifdef DEBUG2
+      printf("copying %d (skip) char from jtp to jdp\n", skip);
+#endif
+      memcpy(jdp, jtp, skip);
+      jtp = jtp+skip; jdp = jdp+skip;
+      jsEnd = strstr(jtp, endScriptTypeJS);
+      if (jsEnd == NULL) { 
+#ifdef DEBUG
+	printf("lack of usable JS; can't find endScriptType\n");
+#endif
+	return encCnt; 
+      }
+
+      // the JS for encoding data is between jsStart and jsEnd
+      scriptLen = jsEnd - jtp;
+      // n = encode2(dp, jtp, jdp, dlen, jtlen, jdlen, &fin);
+      n = encode2(dp, jtp, jdp, dlen, scriptLen, jdlen, &fin);
+      // update encCnt, dp, and dlen based on n
+      if (n > 0) {
+	encCnt = encCnt+n; dp = dp+n; dlen = dlen-n;
+      }
+      // update jtp, jdp, jdlen
+      skip = jsEnd-jtp;
+      jtp = jtp+skip; jdp = jdp+skip; jdlen = jdlen-skip;
+      skip = strlen(endScriptTypeJS);
+      memcpy(jdp, jtp, skip);
+      jtp = jtp+skip; jdp = jdp+skip; jdlen = jdlen-skip;
+    }
+
+    // copy the rest of jTemplate to jdp
+    skip = jTemplate+jtlen-jtp;
+
+    // handling the boundary case in which JS_DELIMITER hasn't been
+    // added by encode()
+    if (fin == 0 && dlen == 0) {
+      if (skip > 0) {
+	*jtp = JS_DELIMITER;
+	jtp = jtp+1; jdp = jdp+1;
+	skip--;
+      }
+    }
+    memcpy(jdp, jtp, skip);
+    return encCnt;
+
+  } else {
+    log_warn("Unknown mode (%d) for encode2()", mode);
+    return 0;
+  }
+
+
+}
+
+/*
+ * int decode(char *jData, char *dataBuf,
+ *            unsigned int jdlen, unsigned int dlen, unsigned int dataBufSize)
+ *
+ * description:
+ *   extract hex char from Javascript embedded with data (jData)
+ *   and put the result in dataBuf
+ *   function returns the number of hex char extracted from jData
+ *   to dataBuf, or returns one of the error codes
+ *
+ * input:
+ *   - jData[]: Javascript embedded with hex-encoded data
+ *   - jdlen  : size of jData
+ *   - dlen   : size of data to recover
+ *   - dataBufSize : size of output data buffer (dataBuf)
+ *
+ * output:
+ *   - dataBuf[] : output buffer for recovered data
+ *
+ * assumptions:
+ *   - data is hex-encoded
+ *
+ * exceptions:
+ *   - if (dlen > dataBufSize) return INVALID_BUF_SIZE
+ *
+ * example:
+ *   jData  = "01p_or2=M3th.r4n5om()*6789ABCDEF0000000; dfp_tile = 1;"
+ *   jdlen  = 54
+ *   dlen   = 16
+ *   dataBufSize = 1000 
+ *   decode() returns 16
+ *   dataBuf= "0123456789ABCDEF"
+ *
+ */
+int decode (char *jData, char *dataBuf, unsigned int jdlen,
+	    unsigned int dlen, unsigned int dataBufSize )
+{
+  unsigned int decCnt = 0;  /* num of data decoded */
+  char *dp, *jdp; /* current pointers for dataBuf and jData */
+  unsigned int j;
+
+  if (dlen > dataBufSize) { return INVALID_BUF_SIZE; }
+
+  dp = dataBuf; jdp = jData;
+  for (j=0; j<jdlen; j++) {
+    if ( isxdigit(*jdp) ) {
+      if (decCnt < dlen) {
+	decCnt++;
+	*dp++ = *jdp++;
+      } else {
+	break;
+      }
+    } else {
+      jdp++;
+    }
+  }
+  return decCnt;
+}
+
+
+/*
+ * decode2() is similar to decode(), but uses offset2Hex to look for
+ * applicable hex char in JS for decoding. Also, the decoding process
+ * stops when JS_DELIMITER is encountered.
+ */
+int decode2 (char *jData, char *dataBuf, unsigned int jdlen,
+	     unsigned int dataBufSize, int *fin )
+{
+  unsigned int decCnt = 0;  /* num of data decoded */
+  char *dp, *jdp; /* current pointers for dataBuf and jData */
+  int i,j;
+  int cjdlen = jdlen;
+  
+  *fin = 0;
+  dp = dataBuf; jdp = jData;
+  
+  i = offset2Hex(jdp, cjdlen, 0);
+  while (i != -1) {
+    // return if JS_DELIMITER exists between jdp and jdp+i
+    for (j=0; j<i; j++) {
+      if (*jdp == JS_DELIMITER) {
+        *fin = 1;
+        return decCnt;
+      }
+      jdp = jdp+1; cjdlen--;
+    }
+    // copy hex data from jdp to dp
+    if (dataBufSize <= 0) {
+      return decCnt;
+    }
+    *dp = *jdp;
+    jdp = jdp+1; cjdlen--;
+    dp = dp+1; dataBufSize--;
+    decCnt++;
+   
+    // find the next hex char
+    i = offset2Hex(jdp, cjdlen, 1); 
+  }
+
+  // look for JS_DELIMITER between jdp to jData+jdlen
+  while (jdp < jData+jdlen) {
+    if (*jdp == JS_DELIMITER) {
+      *fin = 1;
+      break;
+    }
+    jdp = jdp+1; 
+  }
+
+  return decCnt;
+}
+
+
+int decodeHTTPBody (char *jData, char *dataBuf, unsigned int jdlen,
+		    unsigned int dataBufSize, int *fin, int mode )
+{
+  char *jsStart, *jsEnd;
+  char *dp, *jdp; // current pointers for data and jData
+  int scriptLen;
+  int decCnt = 0;
+  int n;
+  int dlen = dataBufSize;
+  dp = dataBuf; jdp = jData; 
+
+  if (mode == CONTENT_JAVASCRIPT) {
+    decCnt = decode2(jData, dataBuf, jdlen, dataBufSize, fin);
+    if (*fin == 0) {
+      log_warn("Unable to find JS_DELIMITER");
+    }
+  } 
+  else if (mode == CONTENT_HTML_JAVASCRIPT) {
+    *fin = 0;
+    while (*fin == 0) {
+      jsStart = strstr(jdp, startScriptTypeJS);
+      if (jsStart == NULL) {
+#ifdef DEBUG
+	printf("Can't find startScriptType for decoding data inside script type JS\n");
+#endif
+        return decCnt; 
+      }
+      jdp = jsStart+strlen(startScriptTypeJS);
+      jsEnd = strstr(jdp, endScriptTypeJS);
+      if (jsEnd == NULL) { 
+#ifdef DEBUG
+	printf("Can't find endScriptType for decoding data inside script type JS\n");
+#endif
+        return decCnt; 
+      }
+
+      // the JS for decoding data is between jsStart and jsEnd
+      scriptLen = jsEnd - jdp;
+      n = decode2(jdp, dp, scriptLen, dlen, fin);
+      if (n > 0) {
+        decCnt = decCnt+n; dlen=dlen-n; dp=dp+n;
+      }
+      jdp = jsEnd+strlen(endScriptTypeJS);
+    } // while (*fin==0)
+  } else {
+    log_warn("Unknown mode (%d) for encode2()", mode);
+    return 0;
+  }
+
+  return decCnt;
+}
+
+
+
+
+
+void printerr(int errno) {
+  if (errno == INVALID_BUF_SIZE) {
+    printf ("Error: Output buffer too small\n");
+  } 
+  else if (errno == INVALID_DATA_CHAR) {
+    printf ("Error: Non-hex char in data\n");
+  } 
+  else {
+    printf ("Unknown error: %i\n", errno);
+  }
+}
+
+
+int testEncode(char *data, char *js, char *outBuf, unsigned int dlen, unsigned int jslen, 
+	       unsigned int outBufLen, int testNum) {
+  int r;
+
+  printf ("***** Start of testEncode (%i) *****\n", testNum);
+  printf ("Input:\n");
+  printf ("data         = %s\n", data);
+  printf ("data len     = %i\n", dlen);
+  printf ("js           = %s\n", js);
+  printf ("js len       = %i\n", jslen);
+  r = encode (data, js, outBuf, dlen, jslen, outBufLen);
+  if (r < 0) {
+    printerr(r);
+  } else {
+    printf ("\nOutput:\n");
+    printf ("%i char of data embedded in outBuf\n", r);
+    outBuf[jslen]    = '\0';
+    printf ("outBuf       = %s\n", outBuf);
+  }
+  printf ("***** End of testEncode (%i) *****\n", testNum);
+  return r;
+}
+
+int testDecode(char *inBuf, char *outBuf, unsigned int inBufSize, unsigned int dlen, 
+	       unsigned int outBufSize, int testNum) {
+
+  int r;
+
+  printf ("***** Start of testDecode (%i) *****\n", testNum);
+  printf ("Input:\n");
+  printf ("inBuf       = %s\n", inBuf);
+  printf ("inBuf size  = %i\n", inBufSize);
+  printf ("data len    = %i\n", dlen);
+  printf ("outBuf size = %i\n", outBufSize);
+  r = decode(inBuf, outBuf, inBufSize, dlen, outBufSize);
+  if (r < 0) {
+    printerr(r);
+  } else {
+    printf ("\nOutput:\n");
+    printf ("%i char of data recovered from inBuf (to outBuf)\n", r);
+    outBuf[r] = '\0';
+    printf ("outBuf   = %s\n", outBuf);
+  }
+  printf ("***** End of testDecode (%i) *****\n", testNum);
+  return r;
+}
+
+
+int testEncode2(char *data, char *js, char *outBuf, 
+		unsigned int dlen, unsigned int jslen, unsigned int outBufLen,
+		int mode, int testNum) {
+  int r;
+  // int fin;
+
+  printf ("***** Start of testEncode2 (%i) *****\n", testNum);
+  printf ("Input:\n");
+  printf ("data         = %s\n", data);
+  printf ("data len     = %i\n", dlen);
+  printf ("js           = %s\n", js);
+  printf ("js len       = %i\n", jslen);
+  // r = encode2(data, js, outBuf, dlen, jslen, outBufLen, &fin);
+  r = encodeHTTPBody(data, js, outBuf, dlen, jslen, outBufLen, mode);
+
+  if (r < 0) {
+    printerr(r);
+  } 
+  else {
+    printf ("\nOutput:\n");
+    printf ("%i char of data embedded in outBuf\n", r);
+    //    printf ("fin          = %d\n", fin);
+    outBuf[jslen]    = '\0';
+    printf ("outBuf       = %s\n", outBuf);
+    
+    if ((unsigned int) r < dlen) {
+      printf ("Incomplete data encoding\n");
+    }
+  }
+  printf ("***** End of testEncode (%i) *****\n", testNum);
+  return r;
+}
+
+
+
+
+int testDecode2(char *inBuf, char *outBuf, 
+	     unsigned int inBufSize, unsigned int outBufSize,
+	     int mode, int testNum) {
+  int r;
+  int fin;
+
+  printf ("***** Start of testDecode2 (%i) *****\n", testNum);
+  printf ("Input:\n");
+  printf ("inBuf       = %s\n", inBuf);
+  printf ("inBuf size  = %i\n", inBufSize);
+  printf ("outBuf size = %i\n", outBufSize);
+  r = decodeHTTPBody(inBuf, outBuf, inBufSize, outBufSize, &fin, mode);
+  if (r < 0) {
+    printerr(r);
+  } else {
+    printf ("\nOutput:\n");
+    printf ("%i char of data recovered from inBuf (to outBuf)\n", r);
+    outBuf[r] = '\0';
+    printf ("outBuf   = %s\n", outBuf);
+  }
+  printf ("***** End of testDecode2 (%i) *****\n", testNum);
+  return r;
+}
+
+
+int 
+x_http2_server_JS_transmit (steg_t* s, struct evbuffer *source, conn_t *conn) {
+
+  struct evbuffer_iovec *iv;
+  int nv;
+  struct evbuffer *dest = conn_get_outbound(conn);
+  size_t sbuflen = evbuffer_get_length(source);
+  char outbuf[HTTP_MSG_BUF_SIZE];
+  char data[(int) sbuflen*2];
+  unsigned int datalen;
+
+  size_t sofar = 0;
+  unsigned int cnt = 0;
+  int r;
+
+
+
+  // by dynamically configuring the size returned by x_http2_transmit_room(),
+  // we can ensure that the size of data (source) is less than the max
+  // data length the JavaScript (jsTemplate) can encode
+
+
+
+  //  do {
+    int i;
+    unsigned int jsLen;
+    int mode;
+    char *hend;
+    unsigned int hLen;
+    unsigned int mjs;
+
+    char *jsTemplate = NULL;
+    int jsTemplateSize = 0;
+
+      
+
+
+    /*    int hdrLen;
+      int content_len;
+      int fin2;
+
+      char* tmp;
+      char data2[20000];
+      int decCnt;
+    */
+
+    datalen = 0;
+
+
+    log_debug("sbuflen = %d sofar = %d\n", (int) sbuflen, (int) sofar);
+
+    // log_debug("SERVER: dumping data with length %d:", (int) sbuflen);
+    // evbuffer_dump(source, stderr);
+    // Convert data in 'source' to hexadecimal and write it to data
+
+    nv = evbuffer_peek(source, sbuflen - sofar, NULL, NULL, 0);
+    iv = xzalloc(sizeof(struct evbuffer_iovec) * nv);
+
+    if (evbuffer_peek(source, sbuflen - sofar, NULL, iv, nv) != nv) {
+      free(iv);
+      return -1;
+    }
+
+    // jsTemplate should be init already, by x_http2_new or the previous invocation
+    // of this function
+
+    mjs = get_max_JS_capacity();
+
+    if (mjs <= 0) {
+      log_debug("SERVER ERROR: (server_transmit) No JavaScript found in jsTemplate\n");
+      return -1;
+    } 
+
+    if (sbuflen > (size_t) mjs) {
+      log_debug("SERVER ERROR: (server_transmit) jsTemplate cannot accommodate data %d %dn",
+		(int) sbuflen, (int) mjs);
+      return -1;
+    }
+    
+
+    cnt = 0;
+
+    for (i = 0; i < nv; i++) {
+      const unsigned char *p = iv[i].iov_base;
+      const unsigned char *limit = p + iv[i].iov_len;
+      char c;
+
+      while (p < limit && cnt < sbuflen-sofar) {
+        c = *p++;
+        data[datalen] = "0123456789abcdef"[(c & 0xF0) >> 4];
+        data[datalen+1] = "0123456789abcdef"[(c & 0x0F) >> 0];
+        datalen += 2;
+        cnt++;
+      }
+    }
+
+    log_debug("SERVER encoded data in hex string (len %d):", datalen);
+    //    buf_dump((unsigned char*)data, datalen, stderr);
+
+    free(iv);
+
+
+
+    if (get_payload(HTTP_CONTENT_JAVASCRIPT, datalen, &jsTemplate, &jsTemplateSize) == 1) {
+      log_debug("SERVER found the next HTTP response template with size %d", jsTemplateSize);
+    } else {
+      log_debug("SERVER couldn't find the next HTTP response template; reusing the previous one");
+    }
+
+    log_debug("MJS %d %d", datalen, mjs);
+
+
+
+    if (jsTemplate == NULL) {
+      fprintf(stderr, "NO suitable payload found %d %d\n", datalen, mjs);
+      exit(-1);
+    }
+
+
+    // use encodeHTTPBody to embed data in the JS jsTemplate
+    // assumption: jsTemplate is null-terminated
+
+    log_debug("strlen(jsTemplate) = %d", (int)strlen(jsTemplate));
+    log_debug("jsTemplateSize = %d", jsTemplateSize);
+    // unsigned int jsLen = strlen(jsTemplate);
+    jsLen = jsTemplateSize;
+
+
+    mode = has_eligible_HTTP_content (jsTemplate, jsLen, HTTP_CONTENT_JAVASCRIPT);
+    hend = strstr(jsTemplate, "\r\n\r\n");
+    if (hend == NULL) {
+      log_warn("Unable to find end of header in the HTTP template");
+      return -1;
+    }
+
+    log_debug("SERVER: using HTTP resp template of length = %d\n", jsLen);
+    // log_debug("HTTP resp tempmlate:");
+    // buf_dump((unsigned char*)jsTemplate, jsLen, stderr);
+    // fprintf(stderr, "==========================\n");
+
+    hLen = hend+4-jsTemplate;
+    r = encodeHTTPBody(data, hend+4, outbuf, datalen, jsLen-hLen, HTTP_MSG_BUF_SIZE, mode);
+
+
+
+
+    /// NEW STUFF
+
+    
+/*     hdrLen  = strstr(jsTemplate, "\r\n\r\n") - jsTemplate + 4;
+     tmp = strstr(jsTemplate, "Content-Length: ") + strlen("Content-Length: ");
+
+     content_len = atoi(tmp);
+      
+     
+     decCnt = decodeHTTPBody(jsTemplate + hdrLen, data2, content_len, HTTP_MSG_BUF_SIZE, &fin2, mode);
+     
+     
+     if (decCnt == (int) datalen)
+	fprintf(stderr, "cnts match\n");
+      else
+	fprintf(stderr, "cnts don't match %d %d\n", decCnt, datalen);
+
+*/   
+
+
+    if (r < 0 || ((unsigned int) r < datalen)) {
+      fprintf(stderr, "incomplete data encoding\n");
+      exit(-1);
+      log_debug("SERVER ERROR: Incomplete data encoding");
+      return -1;
+    }
+
+    // note: the transformation is length-preserving for now
+    log_debug("SERVER: HTTP body with encoded data:");
+    //     buf_dump((unsigned char*)outbuf, jsLen-hLen, stderr);
+    //    fprintf(stderr, "==========================\n");
+
+    if (evbuffer_add(dest, jsTemplate, hLen)) {
+      log_debug("SERVER ERROR: x_http2_server_transmit: evbuffer_add() fails for jsTemplate");
+      return -1;
+    }
+
+    //    fprintf(stderr, "HELLO ==========================\n");
+    
+    if (evbuffer_add(dest, outbuf, jsLen-hLen)) {
+      log_debug("SERVER ERROR: x_http2_server_transmit: evbuffer_add() fails for outbuf");
+      return -1;
+    }
+
+    sofar += datalen/2;
+    evbuffer_drain(source, datalen/2);
+    //  } while (sbuflen > sofar);
+
+
+
+    //    fprintf(stderr, "SERVER TRANSMITTED payload of size %d\n", (int) sbuflen);
+
+  // obtain a usable HTTP response template for the next data, and
+  // modify jsTemplateCapacity
+
+
+  log_debug("SERVER finding the next HTTP response template");
+
+
+
+  
+
+  // conn_cease_transmission(conn);
+  conn_close_after_transmit(conn);
+  //  downcast_steg(s)->have_transmitted = 1;
+  return 0;
+}
+
+
+
+
+
+
+int
+x_http2_handle_client_JS_receive(steg_t *s, conn_t *conn, struct evbuffer *dest, struct evbuffer* source) {
+  struct evbuffer_ptr s2;
+  unsigned int response_len = 0;
+  unsigned int content_len = 0;
+  unsigned int hdrLen;
+  char buf[10];
+  char respMsg[HTTP_MSG_BUF_SIZE];
+  char data[HTTP_MSG_BUF_SIZE];
+
+  unsigned char *field; 
+  unsigned char *fieldStart;
+  unsigned char* fieldEnd;
+  unsigned char *fieldValStart;
+  char *httpBody;
+ 
+  ev_ssize_t r;
+  int mode, decCnt, fin;
+  struct evbuffer * scratch;
+  int i,j,k;
+  char c;
+  
+  
+  s2 = evbuffer_search(source, "\r\n\r\n", sizeof ("\r\n\r\n") -1 , NULL);
+  if (s2.pos == -1) {
+    log_debug("CLIENT Did not find end of HTTP header %d", (int) evbuffer_get_length(source));
+    //      evbuffer_dump(source, stderr);
+    return RECV_INCOMPLETE;
+    }
+  
+  log_debug("CLIENT received response header with len %d", (int)s2.pos);
+  
+  response_len = 0;
+  hdrLen = s2.pos + strlen("\r\n\r\n"); 
+  response_len += hdrLen;
+  
+  // get content length, e.g., Content-Length: 22417
+  field = evbuffer_pullup(source, s2.pos);
+  if (field == NULL) {
+    log_debug("CLIENT unable to pullup the complete HTTP header");
+    return RECV_BAD;
+  }
+  
+  fieldStart = (unsigned char*) strstr((char*) field, "Content-Length: ");
+  if (fieldStart == NULL) {
+    log_debug("CLIENT unable to find Content-Length in the header");
+    return RECV_BAD;
+  }
+  
+  fieldEnd = (unsigned char*) strstr((char *)fieldStart, "\r\n");
+  if (fieldEnd == NULL) {
+    log_debug("CLIENT unable to find end of line for Content-Length");
+    return RECV_BAD;
+  }
+
+  fieldValStart = fieldStart+strlen("Content-Length: ");
+  if ((unsigned int) (fieldEnd-fieldValStart) > (sizeof(buf)-1)) {
+    log_debug("CLIENT: Value of Content-Length too large");
+    return RECV_BAD;
+  }
+  memcpy(buf, fieldValStart, fieldEnd-fieldValStart);
+  buf[fieldEnd-fieldValStart] = 0;
+  
+  content_len = atoi(buf);
+  log_debug("CLIENT received Content-Length = %d\n", content_len);
+  
+  response_len += content_len;
+
+  if (response_len > evbuffer_get_length(source))
+    return RECV_INCOMPLETE;
+  
+  
+  
+  // read the entire HTTP resp
+  if (response_len < HTTP_MSG_BUF_SIZE) {
+    r = evbuffer_copyout(source, respMsg, response_len);
+    log_debug("CLIENT %d char copied from source to respMsg (expected %d)", (int)r, response_len);
+    if (r < 0) {
+      log_debug("CLIENT ERROR: evbuffer_copyout fails");
+      return RECV_INCOMPLETE;
+    }
+    if (r < response_len) {
+      log_debug("CLIENT: evbuffer_copyout incomplete; got %d instead of %d", (int)r, response_len);
+      return RECV_INCOMPLETE;
+    }
+    respMsg[response_len] = 0;
+  } else {
+    log_debug("CLIENT: HTTP response too large to handle");
+    return RECV_BAD;
+  }
+  
+  log_debug("CLIENT received HTTP response with length %d\n", response_len);
+  log_debug("HTTP response:");
+  //    buf_dump((unsigned char*)respMsg, response_len, stderr);
+  //    fprintf(stderr, "==========================\n");
+  
+  httpBody = respMsg + hdrLen;
+  
+  log_debug("CLIENT Before has_eligible HTTP content\n");
+  // find out if the resp is a JavaScript or JS embedded in HTML doc
+  mode = has_eligible_HTTP_content (respMsg, response_len, HTTP_CONTENT_JAVASCRIPT);
+  if (mode != 1 && mode != 2) {
+    log_debug("CLIENT ERROR: HTTP response not useful for jsSteg (mode %d)", mode);
+    return RECV_BAD;
+  }
+
+  log_debug("CLIENT Before decodeHTTPBody; mode: %d\n", mode);
+  
+  // call decodeHTTPBody
+  decCnt = decodeHTTPBody(httpBody, data, response_len-hdrLen, HTTP_MSG_BUF_SIZE, &fin, mode);
+  data[decCnt] = 0;
+  
+  log_debug("After decodeHTTPBody; decCnt: %d\n", decCnt);
+
+  // decCnt is an odd number or data is not a hex string
+  if (decCnt % 2) {
+    log_debug("CLIENT ERROR: An odd number of hex characters received");
+    //      buf_dump((unsigned char*)data, decCnt, stderr);
+    return RECV_BAD;
+  }
+  
+  if (! isxString(data)) {
+    log_debug("CLIENT ERROR: Data received not hex");
+    //      buf_dump((unsigned char*)data, decCnt, stderr);
+    return RECV_BAD;
+  }
+  
+  log_debug("Hex data received:");
+  //    buf_dump ((unsigned char*)data, decCnt, stderr);
+  
+  // get a scratch buffer
+  scratch = evbuffer_new();
+  if (!scratch) return RECV_BAD;
+  
+  if (evbuffer_expand(scratch, decCnt/2)) {
+    log_debug("CLIENT ERROR: Evbuffer expand failed \n");
+    evbuffer_free(scratch);
+    return RECV_BAD;
+  }
+  
+  // convert hex data back to binary
+  for (i=0, j=0; i< decCnt; i=i+2, ++j) {
+    sscanf(&data[i], "%2x", (unsigned int*) &k);
+    c = (char)k;
+    evbuffer_add(scratch, &c, 1);
+  }
+  
+  log_debug("CLIENT Done converting hex data to binary:\n");
+  // evbuffer_dump(scratch, stderr);
+  
+ 
+  //  fprintf(stderr, "CLIENT RECEIVED payload of size %d\n", (int) evbuffer_get_length(scratch));
+ // add the scratch buffer (which contains the data) to dest
+  
+  if (evbuffer_add_buffer(dest, scratch)) {
+    evbuffer_free(scratch);
+    log_debug("CLIENT ERROR: Failed to transfer buffer");
+    return RECV_BAD;
+  }
+  log_debug("Added scratch (buffer) to dest\n");
+  
+  evbuffer_free(scratch);
+  
+  
+  if (response_len <= evbuffer_get_length(source)) {
+    if (evbuffer_drain(source, response_len) == -1) {
+      log_debug("CLIENT ERROR: Added scratch (buffer) to dest\n");
+      return RECV_BAD;
+    }
+  }
+  else {
+    log_warn("response_len > buffer size... can't drain");
+    exit(-1);
+  }
+  
+    
+  log_debug("Drained source for %d char\n", response_len);
+   
+  //  downcast_steg(s)->have_received = 1;
+  conn_expect_close(conn);
+  
+  return RECV_GOOD;
+}
+
+
+
+//int 
+//x_http2_handle_server_JS_receive(steg_t *s, conn_t *conn, struct evbuffer *dest, struct evbuffer* source) {
+//
+//  int cnt = 0;
+//
+//  do {
+//    struct evbuffer_ptr s2 = evbuffer_search(source, "\r\n\r\n", sizeof ("\r\n\r\n") -1 , NULL);
+//    unsigned char* data;
+//    unsigned char* limit;
+//    unsigned char *p;
+//    int unwrapped_cookie_len;
+//    struct evbuffer *scratch;
+//    unsigned char c, h, secondhalf;
+//    unsigned char buf[evbuffer_get_length(source)];
+//
+//
+//    if (s2.pos == -1) {
+//      log_debug("Did not find end of request %d", (int) evbuffer_get_length(source));
+//      //      evbuffer_dump(source, stderr);
+//      return RECV_INCOMPLETE;
+//    }
+//
+//    log_debug("SERVER received request header of length %d", (int)s2.pos);
+//
+//    data = evbuffer_pullup(source, s2.pos);
+//    if (data == NULL) {
+//      log_debug("SERVER evbuffer_pullup fails");
+//      return RECV_BAD;
+//    }
+//
+//    limit = data + s2.pos;
+//
+//    data = (unsigned char*) strstr((char*) data, "Cookie:");
+//
+//    if (data == NULL || memcmp(data, "Cookie:", sizeof "Cookie:"-1)) {
+//      log_debug("Unexpected HTTP verb: %.*s", 5, data);
+//      return RECV_BAD;
+//    }
+//
+//    p = data + sizeof "Cookie: "-1;
+//    unwrapped_cookie_len = unwrap_cookie(p, buf, (int) (limit - p));
+//
+//    log_debug("SERVER: received cookie of length = %d %d\n", unwrapped_cookie_len, (int) (limit-p));
+//    //    buf_dump(buf, unwrapped_cookie_len, stderr);
+//    //    fprintf(stderr, "==========================\n");
+//    //    buf_dump(p, (int) (limit-p), stderr);
+//
+//    
+//    //    log_debug("hello SERVER received %d cnt = %d\n", (int) (limit - p), cnt);
+//    //     buf_dump(p, (int) (limit-p), stderr);
+//
+//    /* We need a scratch buffer here because the contract is that if
+//       we hit a decode error we *don't* write anything to 'dest'. */
+//    scratch = evbuffer_new();
+//
+//    if (!scratch) return RECV_BAD;
+//
+//
+//    if (evbuffer_expand(scratch, unwrapped_cookie_len/2)) {
+//      log_debug("Evbuffer expand failed \n");
+//      evbuffer_free(scratch);
+//      return RECV_BAD;
+//    }
+//    p = buf;
+//
+//
+//    secondhalf = 0;
+//    while ((int) (p - buf) < unwrapped_cookie_len) {
+//      if (!secondhalf) c = 0;
+//      if ('0' <= *p && *p <= '9') h = *p - '0';
+//      else if ('a' <= *p && *p <= 'f') h = *p - 'a' + 10;
+//      else if ('A' <= *p && *p <= 'F') h = *p - 'A' + 10;
+//      else if (*p == '=' && !secondhalf) {
+//	p++;
+//	continue;
+//      } else {
+//	evbuffer_free(scratch);
+//	log_debug("Decode error: unexpected URI characterasdfaf %d", *p);
+//	return RECV_BAD;
+//      }
+//
+//      c = (c << 4) + h;
+//      if (secondhalf) {
+//	evbuffer_add(scratch, &c, 1);
+//	//	log_debug("adding to scratch");
+//	cnt++;
+//      }
+//      secondhalf = !secondhalf;
+//      p++;
+//    }
+//
+//
+//
+//    if (evbuffer_add_buffer(dest, scratch)) {
+//      evbuffer_free(scratch);
+//      log_debug("Failed to transfer buffer");
+//      return RECV_BAD;
+//    } 
+//    evbuffer_drain(source, s2.pos + sizeof("\r\n\r\n") - 1);
+//    evbuffer_free(scratch);
+//  } while (evbuffer_get_length(source));
+//  
+//
+//  log_debug("SERVER RECEIVED payload %d\n", cnt);
+//  //  downcast_steg(s)->have_received = 1;
+//  conn_transmit_soon(conn, 100);
+//  return RECV_GOOD;
+//}
+
+
+
+
+/*****
+      int
+      main() {
+      int jDataSize = 1000; 
+      char jData[jDataSize];
+      int outDataBufSize = 1000; 
+      char outDataBuf[outDataBufSize];
+
+      int r;
+      // test case 1: data embedded in javascript
+      r = testEncode2(data1, js1, jData, strlen(data1), strlen(js1), jDataSize,
+      CONTENT_JAVASCRIPT, 1); 
+      if (r > 0) { testDecode2(jData, outDataBuf, strlen(js1), outDataBufSize, CONTENT_JAVASCRIPT, 1); }
+    
+      // test case 4: data embedded in one script type javascript
+      r = testEncode2(data1, js4, jData, strlen(data1), strlen(js4), jDataSize,
+      CONTENT_HTML_JAVASCRIPT, 4); 
+      if (r > 0) { testDecode2(jData, outDataBuf, strlen(js4), outDataBufSize, CONTENT_HTML_JAVASCRIPT, 4); }
+
+      // test case 5: data embedded in one script type javascript
+      r = testEncode2(data1, js5, jData, strlen(data1), strlen(js5), jDataSize,
+      CONTENT_HTML_JAVASCRIPT, 5); 
+      if (r > 0) { testDecode2(jData, outDataBuf, strlen(js5), outDataBufSize, CONTENT_HTML_JAVASCRIPT, 5); }
+
+
+      return 0;
+      }
+*****/
+
+/*****
+      int
+      main() {
+      int jDataSize = 1000; 
+      char jData[jDataSize];
+      int jDataSmallSize = 5; 
+      char jDataSmall[jDataSmallSize];
+
+      int outDataBufSize = 1000; 
+      char outDataBuf[outDataBufSize];
+      int outDataSmallSize = 5; 
+      char outDataSmall[outDataSmallSize];
+
+      int r;
+
+      // test case 1: data embedded in javascript
+      r = testEncode(data1, js1, jData, strlen(data1), strlen(js1), jDataSize, 1); 
+      if (r > 0) { testDecode(jData, outDataBuf, strlen(js1), r, outDataBufSize, 1); }
+
+      // test case 2: data embedded in javascript
+      r = testEncode(data1, js2, jData, strlen(data1), strlen(js2), jDataSize, 2); 
+      if (r > 0) { testDecode(jData, outDataBuf, strlen(js2), r, outDataBufSize, 2); }
+
+      // test case 3: data partially embedded in javascript; num of hex char in js < data len
+      r = testEncode(data1, js3, jData, strlen(data1), strlen(js3), jDataSize, 3); 
+      if (r > 0) { testDecode(jData, outDataBuf, strlen(js3), r, outDataBufSize, 3); }
+
+      // test case 4: data embedded in javascript; larger data
+      r = testEncode(data2, js1, jData, strlen(data2), strlen(js1), jDataSize, 4); 
+      if (r > 0) { testDecode(jData, outDataBuf, strlen(js1), r, outDataBufSize, 4); }
+
+      // test case 5 (for encode): err for non-hex data
+      testEncode(nonhexstr, js1, jData, strlen(nonhexstr), strlen(js1), jDataSize, 5); 
+ 
+      // test case 6 (for encode): err for small output buf
+      testEncode(data1, js1, jDataSmall, strlen(data1), strlen(js1), jDataSmallSize, 6); 
+
+      // test case 7 (for decode): err for small output buf
+      r = testEncode(data1, js1, jData, strlen(data1), strlen(js1), jDataSize, 7); 
+      if (r > 0) { testDecode(jData, outDataSmall, strlen(js1), r, outDataSmallSize, 7); }
+      }
+*****/
+
diff --git a/src/steg/jsSteg.h b/src/steg/jsSteg.h
new file mode 100644
index 0000000..3c5f6ae
--- /dev/null
+++ b/src/steg/jsSteg.h
@@ -0,0 +1,65 @@
+#ifndef _JSSTEG_H
+#define _JSSTEG_H
+
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include "util.h"
+#include "connections.h"
+#include "steg.h"
+#include <event2/buffer.h>
+
+
+/* error codes */
+#define INVALID_BUF_SIZE	-1
+#define INVALID_DATA_CHAR	-2
+
+
+int encodeHTTPBody(char *data, char *jTemplate,  char *jData,unsigned int dlen, 
+		   unsigned int jtlen, unsigned int jdlen, int mode);
+int isxString(char *str);
+int decodeHTTPBody (char *jData, char *dataBuf, unsigned int jdlen,
+		    unsigned int dataBufSize, int *fin, int mode);
+
+int encode(char *data, char *jTemplate, char *jData,
+	   unsigned int dlen, unsigned int jtlen, unsigned int jdlen );
+
+int  encode2(char *data, char *jTemplate, char *jData,
+	     unsigned int dlen, unsigned int jtlen,
+	     unsigned int jdlen, int *fin);
+
+int decode (char *jData, char *dataBuf, unsigned int jdlen,
+	    unsigned int dlen, unsigned int dataBufSize );
+
+int decode2 (char *jData, char *dataBuf, unsigned int jdlen,
+	     unsigned int dataBufSize, int *fin );
+
+void printerr(int errno);
+
+int testEncode(char *data, char *js, char *outBuf, unsigned int dlen, unsigned int jslen, 
+	       unsigned int outBufLen, int testNum);
+
+int testDecode(char *inBuf, char *outBuf, unsigned int inBufSize, unsigned int dlen, 
+	       unsigned int outBufSize, int testNum);
+
+int testEncode2(char *data, char *js, char *outBuf, 
+		unsigned int dlen, unsigned int jslen, unsigned int outBufLen,
+		int mode, int testNum);
+
+int testDecode2(char *inBuf, char *outBuf, 
+	     unsigned int inBufSize, unsigned int outBufSize,
+		int mode, int testNum);
+
+
+int 
+x_http2_server_JS_transmit (steg_t* s, struct evbuffer *source, conn_t *conn);
+
+int
+x_http2_handle_client_JS_receive(steg_t *s, conn_t *conn, struct evbuffer *dest, struct evbuffer* source);
+
+
+
+#endif
+
+
diff --git a/src/steg/payloads.c b/src/steg/payloads.c
new file mode 100644
index 0000000..2782aa6
--- /dev/null
+++ b/src/steg/payloads.c
@@ -0,0 +1,1486 @@
+#include "util.h"
+#include "payloads.h"
+#include "swfSteg.h"
+
+
+/* These variables below are write-once, hence they should be race-safe */
+
+static int initTypePayload[MAX_CONTENT_TYPE];
+static int typePayloadCount[MAX_CONTENT_TYPE];
+static int typePayload[MAX_CONTENT_TYPE][MAX_PAYLOADS];
+static int typePayloadCap[MAX_CONTENT_TYPE][MAX_PAYLOADS];
+
+
+static unsigned int max_JS_capacity = 0;
+static unsigned int max_PDF_capacity = 0;
+
+
+
+pentry_header payload_hdrs[MAX_PAYLOADS];
+char* payloads[MAX_PAYLOADS];
+int payload_count = 0;
+
+
+unsigned int get_max_JS_capacity() {
+  return max_JS_capacity;
+}
+
+unsigned int get_max_PDF_capacity() {
+  return max_PDF_capacity;
+}
+
+
+
+/*
+ * fixContentLen corrects the Content-Length for an HTTP msg that
+ * has been ungzipped, and removes the "Content-Encoding: gzip"
+ * field from the header.
+ *
+ * The function returns -1 if no change to the HTTP msg has been made,
+ * when the msg wasn't gzipped or an error has been encountered
+ * If fixContentLen changes the msg header, it will put the new HTTP
+ * msg in buf and returns the length of the new msg
+ *
+ * Input:
+ * payload - pointer to the (input) HTTP msg
+ * payloadLen - length of the (input) HTTP msg
+ *
+ * Ouptut:
+ * buf - pointer to the buffer containing the new HTTP msg
+ * bufLen - length of buf
+ * 
+ */
+int fixContentLen (char* payload, int payloadLen, char *buf, int bufLen) {
+
+  int gzipFlag=0, clFlag=0, clZeroFlag=0;
+  char* ptr = payload;
+  char* clPtr = payload;
+  char* gzipPtr = payload;
+  char* end;
+
+
+  char *cp, *clEndPtr;
+  int hdrLen, bodyLen, r, len;
+
+
+
+
+
+  // note that the ordering between the Content-Length and the Content-Encoding
+  // in an HTTP msg may be different for different msg 
+
+  // if payloadLen is larger than the size of our buffer,
+  // stop and return -1
+  if (payloadLen > bufLen) { return -1; }
+
+  while (1) {
+    end = strstr(ptr, "\r\n");
+    if (end == NULL) {
+      // log_debug("invalid header %d %d %s \n", payloadLen, (int) (ptr - payload), payload);
+      return -1;
+    }
+
+    if (!strncmp(ptr, "Content-Encoding: gzip\r\n", 24)) {
+        gzipFlag = 1;
+        gzipPtr = ptr;     
+    } else if (!strncmp(ptr, "Content-Length: 0", 17)) {
+        clZeroFlag = 1;
+    } else if (!strncmp(ptr, "Content-Length:", 15)) {
+        clFlag = 1;
+        clPtr = ptr;
+    }
+
+    if (!strncmp(end, "\r\n\r\n", 4)){
+      break;
+    }
+    ptr = end+2;
+  }
+
+  // stop if zero Content-Length or Content-Length not found
+  if (clZeroFlag || ! clFlag) return -1;
+  
+  // end now points to the end of the header, before "\r\n\r\n"
+  cp=buf;
+  bodyLen = (int)(payloadLen - (end+4-payload));
+
+  clEndPtr = strstr(clPtr, "\r\n");
+  if (clEndPtr == NULL) {
+    log_debug("unable to find end of line for Content-Length");
+    return -1;
+  }
+  if (gzipFlag && clFlag) {
+    if (gzipPtr < clPtr) { // Content-Encoding appears before Content-Length
+
+      // copy the part of the header before Content-Encoding
+      len = (int)(gzipPtr-payload);
+      memcpy(cp, payload, len);
+      cp = cp+len;
+
+      // copy the part of the header between Content-Encoding and Content-Length
+      // skip 24 char, the len of "Content-Encoding: gzip\r\n"
+      // *** this is temporary; we'll remove this after the obfsproxy can perform gzip
+      len = (int)(clPtr-(gzipPtr+24));  
+      memcpy(cp, gzipPtr+24, len);
+      cp = cp+len;
+
+      // put the new Content-Length
+      memcpy(cp, "Content-Length: ", 16);
+      cp = cp+16;
+      r = sprintf(cp, "%d\r\n", bodyLen);
+      if (r < 0) {
+        log_debug("sprintf fails");
+        return -1;
+      }
+      cp = cp+r;
+
+      // copy the part of the header after Content-Length, if any
+      if (clEndPtr != end) { // there is header info after Content-Length
+        len = (int)(end-(clEndPtr+2));
+        memcpy(cp, clEndPtr+2, len);
+        cp = cp+len;
+        memcpy(cp, "\r\n\r\n", 4);
+        cp = cp+4;
+      } else { // Content-Length is the last hdr field
+        memcpy(cp, "\r\n", 2);
+        cp = cp+2;
+      }
+
+      hdrLen = cp-buf;
+
+/****
+log_debug("orig: hdrLen = %d, bodyLen = %d, payloadLen = %d", (int)(end+4-payload), bodyLen, payloadLen);
+log_debug("new: hdrLen = %d, bodyLen = %d, payloadLen = %d", hdrLen, bodyLen, hdrLen+bodyLen);
+ ****/
+
+      // copy the HTTP body
+      memcpy(cp, end+4, bodyLen);
+      return (hdrLen+bodyLen);
+
+    } else { // Content-Length before Content-Encoding
+      // copy the part of the header before Content-Length
+      len = (int)(clPtr-payload);
+      memcpy(cp, payload, len);
+      cp = cp+len;
+
+      // put the new Content-Length
+      memcpy(cp, "Content-Length: ", 16);
+      cp = cp+16;
+      r = sprintf(cp, "%d\r\n", bodyLen);
+      if (r < 0) {
+        log_debug("sprintf fails");
+        return -1;
+      }
+      cp = cp+r;
+
+      // copy the part of the header between Content-Length and Content-Encoding
+      len = (int)(gzipPtr-(clEndPtr+2));
+      memcpy(cp, clEndPtr+2, len);
+      cp = cp+len;
+      
+      // copy the part of the header after Content-Encoding
+      // skip 24 char, the len of "Content-Encoding: gzip\r\n"
+      // *** this is temporary; we'll remove this after the obfsproxy can perform gzip
+      if (end > (gzipPtr+24)) { // there is header info after Content-Encoding
+        len = (int)(end-(gzipPtr+24));
+        memcpy(cp, gzipPtr+24, len);
+        cp = cp+len;
+        memcpy(cp, "\r\n\r\n", 4);
+        cp = cp+4;
+      } else { // Content-Encoding is the last field in the hdr
+        memcpy(cp, "\r\n", 2);
+        cp = cp+2;
+      }
+      hdrLen = cp-buf;
+
+/****
+log_debug("orig: hdrLen = %d, bodyLen = %d, payloadLen = %d", (int)(end+4-payload), bodyLen, payloadLen);
+log_debug("new: hdrLen = %d, bodyLen = %d, payloadLen = %d", hdrLen, bodyLen, hdrLen+bodyLen);
+ ****/
+
+      // copy the HTTP body
+      memcpy(cp, end+4, bodyLen);
+      return (hdrLen+bodyLen);
+    }
+  }
+  return -1;
+}
+
+void load_payloads(const char* fname) {
+  FILE* f;
+  char buf[HTTP_MSG_BUF_SIZE];
+  char buf2[HTTP_MSG_BUF_SIZE];
+  pentry_header pentry;
+  int pentryLen;
+  int r;
+
+  if (payload_count != 0)
+    return;
+
+  srand(time(NULL));
+  f = fopen(fname, "r");
+  if (f == NULL) {
+    fprintf(stderr, "Cannot open trace file %s. Exiting\n", fname);
+    exit(1);
+  }
+
+  bzero(payload_hdrs, sizeof(payload_hdrs));
+
+  while (payload_count < MAX_PAYLOADS) {
+
+    if (fread(&pentry, 1, sizeof(pentry_header), f) < sizeof(pentry_header)) {
+      break;
+    }
+   
+    pentryLen = ntohl(pentry.length);
+    if((unsigned int) pentryLen > sizeof(buf)) {
+#ifdef DEBUG
+      // fprintf(stderr, "pentry too big %d %d\n", pentry.length, ntohl(pentry.length));
+      fprintf(stderr, "pentry too big %d\n", pentryLen);
+#endif
+      // skip to the next pentry
+      if (fseek(f, pentryLen, SEEK_CUR)) {
+        fprintf(stderr, "skipping to next pentry fails\n");
+      }
+      continue;
+      // exit(0);
+    }
+
+    pentry.length = pentryLen;
+    pentry.ptype = ntohs(pentry.ptype);
+
+    if (fread(buf, 1, pentry.length, f) < (unsigned int) pentry.length)
+      break;
+
+    // todo:
+    // fixed content length for gzip'd HTTP msg
+    // fixContentLen returns -1, if no change to the msg
+    // otherwise, it put the new HTTP msg (with hdr changed) in buf2
+    // and returns the size of the new msg
+
+    r = -1;
+    if (pentry.ptype == TYPE_HTTP_RESPONSE) {
+      r = fixContentLen (buf, pentry.length, buf2, HTTP_MSG_BUF_SIZE);
+      // log_debug("for payload_count %d, fixContentLen returns %d", payload_count, r);
+    }
+    // else {
+    // log_debug("for payload_count %d, pentry.ptype = %d", payload_count, pentry.ptype);
+    // }
+
+    if (r < 0) {
+      payloads[payload_count] = malloc(pentry.length + 1);
+      memcpy(payloads[payload_count], buf, pentry.length);
+    } else {
+      pentry.length = r;
+      payloads[payload_count] = malloc(pentry.length + 1);
+      memcpy(payloads[payload_count], buf2, pentry.length);
+    }
+    payload_hdrs[payload_count] = pentry;
+    payloads[payload_count][pentry.length] = 0;
+    payload_count++;
+  } // while
+
+#ifdef DEBUG
+  printf("loading payload count = %d\n", payload_count);
+#endif
+  
+  fclose(f);
+}
+
+
+
+
+
+void gen_rfc_1123_date(char* buf, int buf_size) {
+  time_t t = time(NULL);
+  struct tm *my_tm = gmtime(&t);
+  strftime(buf, buf_size, "Date: %a, %d %b %Y %H:%M:%S GMT\r\n", my_tm);
+}
+
+
+
+/* sample response header */
+/*
+
+HTTP/1.1 200 OK
+Date: Fri, 21 Oct 2011 19:09:49 GMT
+Server: Apache
+Content-Length: 1441
+* Content-Encoding: gzip
+Content-Type: text/html;charset=UTF-8
+* Keep-Alive: timeout=15, max=93
+Connection: close
+
+*/
+
+
+int gen_response_header(char* content_type, int gzip, int length, char* buf, int buflen) {
+  char* ptr;
+
+  // conservative assumption here.... 
+  if (buflen < 256) {
+    fprintf(stderr, "gen_response_header: buflen too small\n");
+    return -1;
+  }
+
+  sprintf(buf, "HTTP/1.1 200 OK\r\n");
+  ptr = buf + strlen("HTTP/1.1 200 OK\r\n");
+  gen_rfc_1123_date(ptr, buflen - (ptr - buf));
+  ptr = ptr + strlen(ptr);
+
+  if (gzip) 
+    sprintf(ptr, "Server: Apache\r\nContent-Length: %d\r\nContent-Encoding: gzip\r\nContent-Type: %s\r\nConnection: close\r\n\r\n", length, content_type);
+  else
+    sprintf(ptr, "Server: Apache\r\nContent-Length: %d\r\nContent-Type: %s\r\nConnection: close\r\n\r\n", length, content_type);
+    
+  ptr += strlen(ptr);
+
+  return ptr - buf;
+}
+
+
+
+
+
+
+int parse_client_headers(char* inbuf, char* outbuf, int len) {
+  // client-side
+  // remove Host: field
+  // remove referrer fields?
+
+  char* ptr = inbuf;
+  int outlen = 0;
+
+  while (1) {
+    // char* end = strstr(ptr, "\r\n", len - (ptr - inbuf));
+    char* end = strstr(ptr, "\r\n");
+    if (end == NULL) {
+      fprintf(stderr, "invalid client header %d %d %s \n PTR = %s\n", len, (int) (len - (ptr - inbuf)), inbuf, ptr);
+      // fprintf(stderr, "HERE %s\n", ptr);
+      break;
+    }
+
+    if (!strncmp(ptr, "Host:", 5) ||
+	!strncmp(ptr, "Referer:", 8) ||
+	!strncmp(ptr, "Cookie:", 7)) {
+      goto next;
+    }
+
+    memcpy(outbuf + outlen, ptr, end - ptr + 2);
+    outlen += end - ptr + 2;
+
+  next:
+    if (!strncmp(end, "\r\n\r\n", 4)){
+      break;
+    }
+    ptr = end+2;
+  }
+  
+  return outlen;
+
+  // server-side
+  // fix date fields
+  // fix content-length
+
+
+
+}
+
+
+
+
+/* first line is of the form....
+   GET /XX/XXXX.swf[?YYYY] HTTP/1.1\r\n
+*/
+
+
+int 
+find_uri_type(char* buf) {
+
+  char* uri;
+  int uri_len;
+  char* ext;
+
+  if (strncmp(buf, "GET", 3) != 0 && strncmp(buf, "POST", 4) != 0) 
+    return -1;
+
+  buf = strchr(buf, ' ') + 1;
+  uri_len = strchr(buf, ' ') - buf;
+  uri = malloc(uri_len + 1);
+
+  strncpy(uri, buf, uri_len);
+  uri[uri_len] = 0;
+
+  if (strchr(uri, '?'))
+    ext = strchr(uri, '?') - 4;
+  else
+    ext = uri + uri_len - 4;
+
+
+  if (!strncmp(ext, ".pdf", 4) || !strncmp(ext, ".PDF", 4))
+    return HTTP_CONTENT_PDF;
+
+  if (!strncmp(ext, ".swf", 4) || !strncmp(ext, ".SWF", 4))
+    return HTTP_CONTENT_SWF;
+
+  //  if (!strncmp(ext, ".js", 3) || !strncmp(ext, ".JS", 3))
+  return HTTP_CONTENT_JAVASCRIPT;
+
+  if (!strncmp(ext-1, "html", 4) || !strncmp(ext, "htm", 3) || strchr(ext-1, '.') == NULL)
+    return HTTP_CONTENT_HTML;
+
+  return -1;
+  
+}
+
+
+
+
+
+
+
+
+
+
+
+unsigned int find_client_payload(char* buf, int len, int type) {
+  int r = rand() % payload_count;
+  int cnt = 0;
+  char* inbuf;
+
+#ifdef DEBUG
+  fprintf(stderr, "TRYING payload %d \n", r);
+#endif
+  while (1) {
+    pentry_header* p = &payload_hdrs[r];
+    if (p->ptype == type) {
+      inbuf = payloads[r];
+      if (find_uri_type(inbuf) != HTTP_CONTENT_SWF   &&
+	  find_uri_type(inbuf) != HTTP_CONTENT_JAVASCRIPT)
+
+	goto next;
+	
+      if (p->length > len) {
+	fprintf(stderr, "BUFFER TOO SMALL... \n");
+	goto next;
+      }
+      else
+	len = p->length;
+      break;
+    }
+  next:
+    r = (r+1) % payload_count;
+    
+
+    // no matching payloads...
+    if (cnt++ == payload_count) {
+      fprintf(stderr, "NO MATCHING PAYLOADS... \n");
+      return 0;
+    }
+  }
+
+  inbuf[len] = 0;
+
+  // clean up the buffer...
+  return parse_client_headers(inbuf, buf, len);
+  
+}
+
+
+/*
+ * skipJSPattern returns the number of characters to skip when
+ * the input pointer matches the start of a common JavaScript
+ * keyword 
+ *
+ * todo: 
+ * Use a more efficient algo (e.g., Aho-Corasick) in the next iteration
+ */
+int skipJSPattern (char *cp, int len) {
+
+  // log_debug("Turning off skipJSPattern for debugging");
+  return 0;
+
+  if (len < 1) return 0;
+
+  if (len > 8) {
+    // "function " and "function("
+    if (cp[0] == 'f' &&
+        !strncmp(cp+1, "un", 2) &&
+        isxdigit(cp[3]) &&
+        !strncmp(cp+4, "tion", 4) &&
+        (cp[8] == ' ' || cp[8] == '('))
+    return 9;
+  }
+
+  if (len > 6) {
+    // "return "
+    if (cp[0] == 'r' &&
+        isxdigit(cp[1]) &&
+        !strncmp(cp+2, "turn ", 5)) 
+    return 7;
+    // "switch "
+    if (cp[0] == 's' &&
+        !strncmp(cp+1, "wit", 3) &&
+        isxdigit(cp[4]) &&
+        !strncmp(cp+5, "h ", 2)) 
+    return 7;
+  }
+
+  if (len > 5) {
+    // "while " and "while("
+    if (cp[0] == 'w' &&
+        !strncmp(cp+1, "hil", 3) &&
+        isxdigit(cp[4]) &&
+        (cp[5] == ' ' || cp[5] == '('))
+    return 6;
+  }
+
+  if (len > 4) {
+    // "else " and "else{"
+    if (cp[0] == 'e' &&
+        !strncmp(cp, "ls", 2) &&
+        isxdigit(cp[3]) &&
+        (cp[4] == ' ' || cp[4] == '{'))
+    return 5;
+  }
+
+  if (len > 3) {
+    // "var "
+    if (cp[0] == 'v' &&
+        isxdigit(cp[1]) &&
+        cp[2] == 'r' &&
+        cp[3] == ' ')
+    return 4;
+  }
+
+  return 0;
+}
+
+
+int isalnum_ (char c) {
+  if (isalnum(c) || c == '_') return 1;
+  else return 0;
+}
+
+int offset2Alnum_ (char *p, int range) {
+  char *cp = p;
+
+  while ((cp < (p+range)) && !isalnum_(*cp)) {
+    cp++;
+  }
+
+  if (cp < (p+range)) {
+    return (cp-p);
+  } else {
+    return -1;
+  }
+}
+
+/*
+ * offset2Hex returns the offset to the next usable hex char.
+ * usable here refer to char that our steg module can use to encode
+ * data. in particular, words that correspond to common JavaScript keywords
+ * are not used for data encoding (see skipJSPattern). Also, because
+ * JS var name must start with an underscore or a letter (but not a digit)
+ * we don't use the first char of a word for encoding data
+ *
+ * e.g., the JS statement "var a;" won't be used for encoding data
+ * because "var" is a common JS keyword and "a" is the first char of a word
+ *
+ * Input:
+ * p - ptr to the starting pos 
+ * range - max number of char to look
+ * isLastCharHex - is the char pointed to by (p-1) a hex char 
+ *
+ * Output:
+ * offset2Hex returns the offset to the next usable hex char
+ * between p and (p+range), if it exists;
+ * otherwise, it returns -1
+ *
+ */
+int offset2Hex (char *p, int range, int isLastCharHex) {
+  char *cp = p;
+  int i,j;
+  int isFirstWordChar = 1;
+
+  if (range < 1) return -1;
+
+  // case 1: last char is hexadecimal
+  if (isLastCharHex) {
+    if (isxdigit(*cp)) return 0; // base case
+    else {
+      cp++;
+      i = offset2Alnum_(cp, p+range-cp);
+      while (i == 0) {
+        if (isxdigit(*cp)) return (cp-p);
+        cp++;
+        i = offset2Alnum_(cp, p+range-cp);
+      }
+      if (i == -1) return -1;
+      // if (i > 0), fallthru and handle case 2
+    }
+  }
+ 
+  // case 2:
+  // find the next word that starts with alnum or underscore,
+  // which could be a variable, keyword, or literal inside a string
+
+  i = offset2Alnum_(cp, p+range-cp);
+  if (i == -1) return -1;
+  // isFirstWordChar = 1; 
+
+  while (cp < (p+range) && i != -1) {
+  
+    if (i == 0) {
+      j = skipJSPattern(cp, p+range-cp); 
+      if (j > 0) {
+      // cp points a word that should not be used (e.g., JS keyword)
+        cp = cp+j; isFirstWordChar = 1;
+      } else { // j == 0
+        if (isFirstWordChar) { 
+          cp++; isFirstWordChar = 0; // skip the 1st char of a word
+        } 
+	else { // we are in the middle of a word; no need to invoke skipJSPattern
+          if (isxdigit(*cp)) return (cp-p);
+          else cp++;
+        }
+      }
+    } else { // i > 0
+      cp += i; isFirstWordChar = 1;
+    }
+    i = offset2Alnum_(cp, p+range-cp);
+     
+  } // while
+
+  // cannot find next usable hex char 
+  return -1;
+ 
+}
+
+/*
+ * capacityJS returns the number of usable hex char 
+ * in the input HTTP message for data encoding
+ *
+ * Input:
+ * buf - pointer to HTTP message
+ * len - sizeof buf
+ * mode - 
+ *   CONTENT_JAVASCRIPT (case 1); HTTP message body is a JavaScript 
+ *     (e.g., Content-Type: {text/javascript, application/x-javascript,
+ *     application/javascript})
+ *   CONTENT_HTML_JAVASCRIPT (case 2)
+ *     Content-Type: text/html and HTTP message body contains
+ *     <script type="text/javascript"> ... </script>
+ *
+ * Output:
+ * capacityJS returns the number of usable hex char that can be embedded
+ * in the HTTP message
+ *
+ * Note:
+ * This is a prototype for the simple version (all hex char are usable)
+ * will refine this to skip JavaScript keywords in the next iteration
+ *
+ */
+
+
+
+unsigned int capacityJS (char* buf, int len, int mode) {
+  char *hEnd, *bp, *jsStart, *jsEnd;
+  int cnt=0;
+
+  // jump to the beginning of the body of the HTTP message
+  hEnd = strstr(buf, "\r\n\r\n");
+  if (hEnd == NULL) {
+    // cannot find the separator between HTTP header and HTTP body
+    return 0;
+  }
+  bp = hEnd + 4;
+
+  if (mode == CONTENT_JAVASCRIPT) {
+     while (bp < (buf+len)) {
+       if (isxdigit(*bp)) {
+         cnt++;
+#ifdef DEBUG
+         printf("%c", *bp);
+#endif
+       }
+       bp++;
+     }
+#ifdef DEBUG
+     printf("\n");
+#endif
+     return cnt;
+  } else if (mode == CONTENT_HTML_JAVASCRIPT) {
+     while (bp < (buf+len)) {
+       jsStart = strstr(bp, "<script type=\"text/javascript\">");
+       if (jsStart == NULL) break;
+       bp = jsStart+31;
+       jsEnd = strstr(bp, "</script>");
+       if (jsEnd == NULL) break;
+       // count the number of usable hex char between jsStart+31 and jsEnd
+       while (bp < jsEnd) {
+         if (isxdigit(*bp)) {
+           cnt++;
+#ifdef DEBUG
+           printf("%c", *bp);
+#endif
+         }
+         bp++;
+       }
+       bp += 9;
+     }
+#ifdef DEBUG
+     printf("\n");
+#endif
+     return cnt;
+  } else {
+    fprintf(stderr, "Unknown mode (%d) for capacityJS() ... \n", mode);
+    return 0;
+  }
+
+}
+
+/*
+ * capacityJS3 is the next iteration for capacityJS
+ */
+unsigned int capacityJS3 (char* buf, int len, int mode) {
+  char *hEnd, *bp, *jsStart, *jsEnd;
+  int cnt=0;
+  int j;
+
+  // jump to the beginning of the body of the HTTP message
+  hEnd = strstr(buf, "\r\n\r\n");
+  if (hEnd == NULL) {
+    // cannot find the separator between HTTP header and HTTP body
+    return 0;
+  }
+  bp = hEnd + 4;
+
+
+  if (mode == CONTENT_JAVASCRIPT) {
+    j = offset2Hex(bp, (buf+len)-bp, 0);
+    while (j != -1) {
+      cnt++;
+      if (j == 0) {
+        bp = bp+1;
+      } else {
+        bp = bp+j+1;
+      }
+// #ifdef DEBUG
+// printf("got |%c|\n", *(bp-1));
+// #endif
+      j = offset2Hex(bp, (buf+len)-bp, 1);
+    } // while
+    return cnt;
+  } else if (mode == CONTENT_HTML_JAVASCRIPT) {
+     while (bp < (buf+len)) {
+       jsStart = strstr(bp, "<script type=\"text/javascript\">");
+       if (jsStart == NULL) break;
+       bp = jsStart+31;
+       jsEnd = strstr(bp, "</script>");
+       if (jsEnd == NULL) break;
+       // count the number of usable hex char between jsStart+31 and jsEnd
+
+       j = offset2Hex(bp, jsEnd-bp, 0);
+       while (j != -1) {
+         cnt++;
+         if (j == 0) {
+           bp = bp+1;
+         } else {
+           bp = bp+j+1;
+         }
+#ifdef DEBUG
+printf("got |%c|\n", *(bp-1));
+#endif
+         j = offset2Hex(bp, jsEnd-bp, 1);
+       } // while (j != -1)
+
+       bp += 9;
+     } // while (bp < (buf+len))
+     return cnt;
+  } else {
+    fprintf(stderr, "Unknown mode (%d) for capacityJS() ... \n", mode);
+    return 0;
+  }
+}
+
+
+/*
+ * strInBinary looks for char array pattern of length patternLen in a char array
+ * blob of length blobLen
+ *
+ * return a pointer for the first occurrence of pattern in blob, if found
+ * otherwise, return NULL
+ * 
+ */
+char *
+strInBinary (const char *pattern, unsigned int patternLen, 
+             const char *blob, unsigned int blobLen) {
+  int found = 0;
+  char *cp = (char *)blob;
+
+  while (1) {
+    if (blob+blobLen-cp < patternLen) break;
+    if (*cp == pattern[0]) {
+      if (memcmp(cp, pattern, patternLen) == 0) {
+        found = 1;
+        break;
+      }
+    }
+    cp++; 
+  }
+  if (found) return cp;
+  else return NULL;
+}
+
+
+/*
+ * has_eligible_HTTP_content() identifies if the input HTTP message 
+ * contains a specified type of content, used by a steg module to
+ * select candidate HTTP message as cover traffic
+ */
+
+// for JavaScript, there are two cases:
+// 1) If Content-Type: has one of the following values
+//       text/javascript 
+//       application/x-javascript
+//       application/javascript
+// 2) Content-Type: text/html and 
+//    HTTP body contains <script type="text/javascript"> ... </script>
+// #define CONTENT_JAVASCRIPT		1 (for case 1)
+// #define CONTENT_HTML_JAVASCRIPT	2 (for case 2)
+//
+// for pdf, we look for the msgs whose Content-Type: has one of the
+// following values
+// 1) application/pdf
+// 2) application/x-pdf
+// 
+
+int has_eligible_HTTP_content (char* buf, int len, int type) {
+  char* ptr = buf;
+  char* matchptr;
+  int tjFlag=0, thFlag=0, ceFlag=0, teFlag=0, http304Flag=0, clZeroFlag=0, pdfFlag=0, swfFlag=0;
+  char* end, *cp;
+
+#ifdef DEBUG
+  fprintf(stderr, "TESTING availabilty of js in payload ... \n");
+#endif
+
+  if (type != HTTP_CONTENT_JAVASCRIPT &&
+      type != HTTP_CONTENT_PDF && type != HTTP_CONTENT_SWF)
+    return 0;
+
+
+  // assumption: buf is null-terminated
+  if (!strstr(buf, "\r\n\r\n"))
+    return 0;
+
+
+  while (1) {
+    end = strstr(ptr, "\r\n");
+    if (end == NULL) {
+      break;
+    }
+
+    if (!strncmp(ptr, "Content-Type:", 13)) {
+	
+      if (!strncmp(ptr+14, "text/javascript", 15) || 
+	  !strncmp(ptr+14, "application/javascript", 22) || 
+	  !strncmp(ptr+14, "application/x-javascript", 24)) {
+	tjFlag = 1;
+      }
+      if (!strncmp(ptr+14, "text/html", 9)) {
+	thFlag = 1;
+      }
+      if (!strncmp(ptr+14, "application/pdf", 15) || 
+	  !strncmp(ptr+14, "application/x-pdf", 17)) {
+	pdfFlag = 1;
+      }
+      if (!strncmp(ptr+14, "application/x-shockwave-flash", strlen("application/x-shockwave-flash"))) {
+	swfFlag = 1;
+      }
+
+    } else if (!strncmp(ptr, "Content-Encoding:", 17)) {
+      ceFlag = 1;
+    } else if (!strncmp(ptr, "Transfer-Encoding:", 18)) {
+      teFlag = 1;
+    } else if (!strncmp(ptr, "HTTP/1.1 304 ", 13)) {
+      http304Flag = 1;
+    } else if (!strncmp(ptr, "Content-Length: 0", 17)) {
+      clZeroFlag = 1;
+    }
+    
+    if (!strncmp(end, "\r\n\r\n", 4)){
+      break;
+    }
+    ptr = end+2;
+  }
+
+#ifdef DEBUG
+  printf("tjFlag=%d; thFlag=%d; ceFlag=%d; teFlag=%d; http304Flag=%d; clZeroFlag=%d\n", 
+    tjFlag, thFlag, ceFlag, teFlag, http304Flag, clZeroFlag);
+#endif
+
+  if (type == HTTP_CONTENT_JAVASCRIPT) {
+    // empty body if it's HTTP not modified (304) or zero Content-Length
+    if (http304Flag || clZeroFlag) return 0; 
+
+    // for now, we're not dealing with Transfer-Encoding (e.g., chunked)
+    // if (teFlag) return 0;
+    // if (ceFlag) return 0; // for now, we remove "Content-Encoding: gzip" in fixContentLen
+    if (teFlag || ceFlag) return 0;
+/*****
+    if (teFlag) return 0;
+ *****/
+
+    if (tjFlag && ceFlag && end != NULL) {
+      log_debug("(JS) gzip flag detected with hdr len %d", (int)(end-buf+4));
+    } else if (thFlag && ceFlag && end != NULL) {
+      log_debug("(HTML) gzip flag detected with hdr len %d", (int)(end-buf+4));
+    }
+
+    // case 1
+    if (tjFlag) return 1; 
+
+    // case 2: check if HTTP body contains <script type="text/javascript">
+    if (thFlag) {
+      matchptr = strstr(ptr, "<script type=\"text/javascript\">");
+      if (matchptr != NULL) {
+        return 2;
+      }
+    }
+  }
+
+  if (type == HTTP_CONTENT_PDF && pdfFlag) {
+    // reject msg with empty body: HTTP not modified (304) or zero Content-Length
+    if (http304Flag || clZeroFlag) return 0; 
+
+    // for now, we're not dealing with Transfer-Encoding (e.g., chunked)
+    // or Content-Encoding (e.g., gzip)
+    // if (teFlag) return 0;
+    // if (ceFlag) return 0;
+    if (teFlag || ceFlag) return 0;
+
+    // check if HTTP body contains "endstream";
+    // strlen("endstream") == 9
+    
+    cp = strInBinary("endstream", 9, ptr, buf+len-ptr);
+    if (cp != NULL) {
+      // log_debug("Matched endstream!");
+      return 1;
+    }
+  }
+  
+  if (type == HTTP_CONTENT_SWF && swfFlag == 1 && 
+      ((len + buf - end) > SWF_SAVE_FOOTER_LEN + SWF_SAVE_HEADER_LEN + 8))
+    return 1;
+
+  return 0;
+}
+
+
+
+unsigned int capacityPDF (char* buf, int len) {
+  char *hEnd, *bp, *streamStart, *streamEnd;
+  int cnt=0;
+  int size;
+
+  // jump to the beginning of the body of the HTTP message
+  hEnd = strstr(buf, "\r\n\r\n");
+  if (hEnd == NULL) {
+    // cannot find the separator between HTTP header and HTTP body
+    return 0;
+  }
+  bp = hEnd + 4;
+
+  while (bp < (buf+len)) {
+     streamStart = strInBinary("stream", 6, bp, (buf+len)-bp);
+     // streamStart = strstr(bp, "stream");
+     if (streamStart == NULL) break;
+     bp = streamStart+6;
+     streamEnd = strInBinary("endstream", 9, bp, (buf+len)-bp);
+     // streamEnd = strstr(bp, "endstream");
+     if (streamEnd == NULL) break;
+     // count the number of char between streamStart+6 and streamEnd
+     size = streamEnd - (streamStart+6) - 2; // 2 for \r\n before streamEnd
+     if (size > 0) {
+       cnt = cnt + size;
+       log_debug("capacity of pdf increase by %d", size);
+     }
+     bp += 9;
+  }
+  return cnt;
+}
+
+
+
+
+
+
+
+
+
+/*
+ * init_payload_pool initializes the arrays pertaining to 
+ * message payloads for the specified content type
+ *
+ * Specifically, it populates the following arrays
+ * static int initTypePayload[MAX_CONTENT_TYPE];
+ * static int typePayloadCount[MAX_CONTENT_TYPE];
+ * static int typePayload[MAX_CONTENT_TYPE][MAX_PAYLOADS];
+ * static int typePayloadCap[MAX_CONTENT_TYPE][MAX_PAYLOADS];
+ *
+ * Input:
+ * len - max length of payload
+ * type - ptype field value in pentry_header
+ * contentType - (e.g, HTTP_CONTENT_JAVASCRIPT for JavaScript content)
+ */
+
+
+
+
+int  init_JS_payload_pool(int len, int type, int minCapacity) {
+
+  // stat for usable payload
+  int minPayloadSize = 0, maxPayloadSize = 0; 
+  int sumPayloadSize = 0;
+  int minPayloadCap = 0, maxPayloadCap = 0;
+  int sumPayloadCap = 0;
+
+  unsigned int contentType = HTTP_CONTENT_JAVASCRIPT;
+
+  int cnt = 0;
+  int r;
+  pentry_header* p;
+  char* msgbuf;
+  int cap;
+  int mode;
+
+
+
+  if (payload_count == 0) {
+    log_debug("payload_count == 0; forgot to run load_payloads()?\n");
+    return 0;
+  }
+  
+  if (initTypePayload[contentType] != 0) return 1; // init is done already
+
+
+  for (r = 0; r < payload_count; r++) {
+    p = &payload_hdrs[r];
+    if (p->ptype != type || p->length > len) {
+      continue;
+    }
+
+    msgbuf = payloads[r];
+
+    mode = has_eligible_HTTP_content(msgbuf, p->length, HTTP_CONTENT_JAVASCRIPT);
+    if (mode > 0) {
+      
+      cap = capacityJS3(msgbuf, p->length, mode);
+      if (cap <  JS_DELIMITER_SIZE) 
+	continue;
+
+      cap = (cap - JS_DELIMITER_SIZE)/2;
+
+      if (cap > minCapacity) {
+	typePayloadCap[contentType][cnt] = cap; // (cap-JS_DELIMITER_SIZE)/2;
+	// because we use 2 hex char to encode every data byte, the available
+	// capacity for encoding data is divided by 2
+	typePayload[contentType][cnt] = r;
+	cnt++;
+	
+	// update stat
+	if (cnt == 1) {
+	  minPayloadSize = p->length; maxPayloadSize = p->length;
+	  minPayloadCap = cap; maxPayloadCap = cap;
+	} 
+	else {
+	  if (minPayloadSize > p->length) minPayloadSize = p->length; 
+	  if (maxPayloadSize < p->length) maxPayloadSize = p->length; 
+	  if (minPayloadCap > cap) minPayloadCap = cap;
+	  if (maxPayloadCap < cap) {
+	    maxPayloadCap = cap;
+	  }
+	  
+	}
+	sumPayloadSize += p->length; sumPayloadCap += cap;
+      }
+    }
+  }
+
+  
+  max_JS_capacity = maxPayloadCap;
+
+
+  initTypePayload[contentType] = 1;
+  typePayloadCount[contentType] = cnt;
+  log_debug("init_payload_pool: typePayloadCount for contentType %d = %d",
+     contentType, typePayloadCount[contentType]); 
+  log_debug("minPayloadSize = %d", minPayloadSize); 
+  log_debug("maxPayloadSize = %d", maxPayloadSize); 
+  log_debug("avgPayloadSize = %f", (float)sumPayloadSize/(float)cnt); 
+  log_debug("minPayloadCap  = %d", minPayloadCap); 
+  log_debug("maxPayloadCap  = %d", maxPayloadCap); 
+  log_debug("avgPayloadCap  = %f", (float)sumPayloadCap/(float)cnt); 
+  return 1;
+}
+
+
+
+
+
+
+
+
+
+int  init_PDF_payload_pool(int len, int type, int minCapacity) {
+
+  // stat for usable payload
+  int minPayloadSize = 0, maxPayloadSize = 0; 
+  int sumPayloadSize = 0;
+  int minPayloadCap = 0, maxPayloadCap = 0;
+  int sumPayloadCap = 0;
+
+  int cnt = 0;
+  int r;
+  pentry_header* p;
+  char* msgbuf;
+  int cap;
+  int mode;
+  unsigned int contentType = HTTP_CONTENT_PDF;
+  
+
+  if (payload_count == 0) {
+     fprintf(stderr, "payload_count == 0; forgot to run load_payloads()?\n");
+     return 0;
+  }
+  
+  if (initTypePayload[contentType] != 0) return 1; // init is done already
+
+
+  for (r = 0; r < payload_count; r++) {
+    p = &payload_hdrs[r];
+    if (p->ptype != type || p->length > len) {
+      continue;
+    }
+
+    msgbuf = payloads[r];
+
+    mode = has_eligible_HTTP_content(msgbuf, p->length, HTTP_CONTENT_PDF);
+    if (mode > 0) {
+      // use capacityPDF() to find out the amount of data that we
+      // can encode in the pdf doc 
+      // cap = minCapacity+1;
+      cap = capacityPDF(msgbuf, p->length);
+      log_debug("got pdf (index %d) with capacity %d", r, cap);
+      if (cap > minCapacity) {
+	log_debug("pdf (index %d) greater than mincapacity %d", cnt, minCapacity);
+	typePayloadCap[contentType][cnt] = (cap-PDF_DELIMITER_SIZE)/2;
+	typePayload[contentType][cnt] = r;
+	cnt++;
+	
+	// update stat
+	if (cnt == 1) {
+	  minPayloadSize = p->length; maxPayloadSize = p->length;
+	  minPayloadCap = cap; maxPayloadCap = cap;
+	} 
+	else {
+	  if (minPayloadSize > p->length) minPayloadSize = p->length; 
+	  if (maxPayloadSize < p->length) maxPayloadSize = p->length; 
+	  if (minPayloadCap > cap) minPayloadCap = cap;
+	  if (maxPayloadCap < cap) maxPayloadCap = cap;
+	}
+	sumPayloadSize += p->length; sumPayloadCap += cap;
+      }
+    }
+  }
+
+  max_PDF_capacity = maxPayloadCap;
+  initTypePayload[contentType] = 1;
+  typePayloadCount[contentType] = cnt;
+  log_debug("init_payload_pool: typePayloadCount for contentType %d = %d",
+     contentType, typePayloadCount[contentType]); 
+  log_debug("minPayloadSize = %d", minPayloadSize); 
+  log_debug("maxPayloadSize = %d", maxPayloadSize); 
+  log_debug("avgPayloadSize = %f", (float)sumPayloadSize/(float)cnt); 
+  log_debug("minPayloadCap  = %d", minPayloadCap); 
+  log_debug("maxPayloadCap  = %d", maxPayloadCap); 
+  log_debug("avgPayloadCap  = %f", (float)sumPayloadCap/(float)cnt); 
+  return 1;
+}
+
+
+
+
+
+int  init_SWF_payload_pool(int len, int type, int minCapacity) {
+
+  // stat for usable payload
+  int minPayloadSize = 0, maxPayloadSize = 0; 
+  int sumPayloadSize = 0;
+
+  int cnt = 0;
+  int r;
+  pentry_header* p;
+  char* msgbuf;
+  int mode;
+  unsigned int contentType = HTTP_CONTENT_SWF;
+
+
+  if (payload_count == 0) {
+     fprintf(stderr, "payload_count == 0; forgot to run load_payloads()?\n");
+     return 0;
+  }
+  
+  if (initTypePayload[contentType] != 0) return 1; // init is done already
+
+
+  for (r = 0; r < payload_count; r++) {
+    p = &payload_hdrs[r];
+    if (p->ptype != type || p->length > len) {
+      continue;
+    }
+
+    msgbuf = payloads[r];
+    // found a payload corr to the specified contentType
+
+    mode = has_eligible_HTTP_content(msgbuf, p->length, HTTP_CONTENT_SWF);
+    if (mode > 0) {
+      typePayload[contentType][cnt] = r;
+      cnt++;
+      // update stat
+      if (cnt == 1) {
+	minPayloadSize = p->length; 
+	maxPayloadSize = p->length;
+      } 
+      else {
+	if (minPayloadSize > p->length) 
+	  minPayloadSize = p->length; 
+	if (maxPayloadSize < p->length) 
+	  maxPayloadSize = p->length; 
+      }
+      sumPayloadSize += p->length;
+    }
+  }
+    
+  initTypePayload[contentType] = 1;
+  typePayloadCount[contentType] = cnt;
+  log_debug("init_payload_pool: typePayloadCount for contentType %d = %d",
+     contentType, typePayloadCount[contentType]); 
+  log_debug("minPayloadSize = %d", minPayloadSize); 
+  log_debug("maxPayloadSize = %d", maxPayloadSize); 
+  log_debug("avgPayloadSize = %f", (float)sumPayloadSize/(float)cnt); 
+  return 1;
+}
+
+
+
+
+
+
+
+
+
+int get_next_payload (int contentType, char** buf, int* size, int* cap) {
+  int r;
+
+  log_debug("get_next_payload: contentType = %d, initTypePayload = %d, typePayloadCount = %d",
+      contentType, initTypePayload[contentType], typePayloadCount[contentType]);
+
+
+  if (contentType <= 0 ||
+      contentType >= MAX_CONTENT_TYPE ||
+      initTypePayload[contentType] == 0 ||
+      typePayloadCount[contentType] == 0)
+    return 0;
+
+  r = rand() % typePayloadCount[contentType];
+//  int r = 1;
+//  log_debug("SERVER: *** always choose the same payload ***");
+
+  log_debug("SERVER: picked payload with index %d", r);
+  *buf = payloads[typePayload[contentType][r]];
+  *size = payload_hdrs[typePayload[contentType][r]].length;
+  *cap = typePayloadCap[contentType][r];
+  return 1;
+}
+
+
+
+
+
+
+
+
+int get_payload (int contentType, int cap, char** buf, int* size) {
+  int r;
+  unsigned int i = 0;
+  unsigned int cnt = 0;
+
+  log_debug("get_payload: contentType = %d, initTypePayload = %d, typePayloadCount = %d",
+      contentType, initTypePayload[contentType], typePayloadCount[contentType]);
+
+
+  if (contentType <= 0 ||
+      contentType >= MAX_CONTENT_TYPE ||
+      initTypePayload[contentType] == 0 ||
+      typePayloadCount[contentType] == 0)
+    return 0;
+
+
+  cnt = typePayloadCount[contentType];
+   r = rand() % cnt;
+
+  for (i=0; i < cnt; i++) {
+
+    if (typePayloadCap[contentType][(r+i) % cnt] <= cap)
+      continue;
+
+    *buf = payloads[typePayload[contentType][(r+i)%cnt]];
+    *size = payload_hdrs[typePayload[contentType][(r+i)%cnt]].length;
+    return 1;
+  }
+
+
+
+  return 0;
+
+}
+
+
+
+
+int
+find_content_length (char *hdr, int hlen) {
+  char *clStart;
+  char* clEnd;
+  char *clValStart;
+  int valLen;
+  int contentLen;
+  char buf[10];
+
+  clStart = strstr(hdr, "Content-Length: ");
+  if (clStart == NULL) {
+    log_debug("Unable to find Content-Length in the header");
+    return -1;
+  }
+
+  clEnd = strstr((char *)clStart, "\r\n");
+  if (clEnd == NULL) {
+    log_debug("Unable to find end of line for Content-Length");
+    return -1;
+  }
+
+  // clValStart = clStart+strlen("Content-Length: ");
+  clValStart = clStart+16;
+
+  valLen = clEnd-clValStart;
+  if (valLen > 9) return -1;
+  memcpy(buf, clValStart, valLen);
+  buf[valLen] = 0;
+  contentLen = atoi(buf);
+  return contentLen;
+}
+
+
+
+
+
+
+/*
+
+void testOffset2Alnum_skipJSPattern () {
+  char s1[] = "for (i=0; i<10; i++) { print i; }";
+
+  char s2[] = "***abcde*****";
+  int d, i;
+
+  printf("s1 = %s\n", s1);
+  printf("s2 = %s\n", s2);
+
+
+  d = offset2Alnum_(s1, strlen(s1));
+  printf ("offset2Alnum_ for s1 = %d\n", d);
+  d = offset2Alnum_(s2, strlen(s2));
+  printf ("offset2Alnum_ for s2 = %d\n", d);
+
+  i = skipJSPattern (s1, strlen(s1));
+  printf ("skipJSPattern for s1 = %d\n", i);
+  i = skipJSPattern (s2, strlen(s2));
+  printf ("skipJSPattern for s2 = %d\n", i);
+}
+
+
+
+
+void testOffset2Hex () {
+  int d;
+  char s3[] = "for (bc=0; bc<10; bc++) { ad=2*bc+ad; }";
+  printf("len(s3)=%d; s3 = |%s|\n", (int)strlen(s3), s3);
+
+  d = offset2Alnum_(s3, strlen(s3));
+  printf ("offset2Alnum_ for s3 = %d\n", d);
+  d = offset2Hex(s3, strlen(s3), 0);
+  printf ("offset2Hex for s3 = %d\n", d);
+}
+
+
+void testCapacityJS () {
+  int d;
+  char s4[] = "\r\n\r\n abc = abc + 1;";
+  char s6[] = "\r\n\r\n <script type=\"text/javascript\">abc = abc + 1;</script>";
+
+  printf("\nTest for CONTENT_JAVASCRIPT:\n");
+  printf("len(s4)=%d; s4 = |%s|\n", (int)strlen(s4), s4);
+
+  d = offset2Alnum_(s4, strlen(s4));
+  printf ("offset2Alnum_ for s4 = %d\n", d);
+  d = offset2Hex(s4, strlen(s4), 0);
+  printf ("offset2Hex for s4 = %d\n", d);
+
+  printf("capacityJS  (JS) returns %d\n", capacityJS(s4, strlen(s4), CONTENT_JAVASCRIPT));
+  printf("capacityJS3 (JS) returns %d\n", capacityJS3(s4, strlen(s4), CONTENT_JAVASCRIPT));
+
+  printf("\nTest for CONTENT_HTML_JAVASCRIPT:\n");
+  printf("len(s6)=%d; s6 = |%s|\n", (int)strlen(s6), s6);
+
+  d = offset2Alnum_(s6, strlen(s6));
+  printf ("offset2Alnum_ for s6 = %d\n", d);
+  d = offset2Hex(s6, strlen(s6), 0);
+  printf ("offset2Hex for s6 = %d\n", d);
+
+  printf("capacityJS  (HTML) returns %d\n", capacityJS(s6, strlen(s6), CONTENT_HTML_JAVASCRIPT));
+  printf("capacityJS3 (HTML) returns %d\n", capacityJS3(s6, strlen(s6), CONTENT_HTML_JAVASCRIPT));
+}
+*/
+
+
+/*****
+int main() {
+  char buf[HTTP_MSG_BUF_SIZE];
+  bzero(buf, sizeof(buf));
+  // test for TYPE_HTTP_REQUEST
+  // load_payloads("../../traces/client.out");
+  // int len = find_client_payload(buf, 10000, TYPE_HTTP_REQUEST);
+  // printf("%s\n", buf);
+
+  // test for TYPE_HTTP_RESPONSE
+  // load_payloads("../../traces/server-cnn-nogzip.out");
+  // load_payloads("../../traces/server-portals.out"); // ptype==1?
+
+  // testOffset2Alnum_skipJSPattern();
+  // testOffset2Hex();
+  // testCapacityJS();
+  
+  load_payloads("../../traces/server.out");
+  // int r;
+  // r = find_server_payload(&buf, sizeof(buf), TYPE_HTTP_RESPONSE, HTTP_CONTENT_JAVASCRIPT);
+  // if (r > 0) {
+  //   printf("Available payload capablity %d\n", r);
+  // }
+  // return r;
+
+  return 0;
+}
+ *****/
+
diff --git a/src/steg/payloads.h b/src/steg/payloads.h
new file mode 100644
index 0000000..b3fcc9d
--- /dev/null
+++ b/src/steg/payloads.h
@@ -0,0 +1,159 @@
+#ifndef _PAYLOADS_H
+#define _PAYLOADS_H
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <string.h>
+#include <sys/types.h>
+#include <arpa/inet.h>
+#include <ctype.h>
+
+
+/* three files:
+   server_data, client data, protocol data
+*/
+
+
+
+#define RECV_GOOD 0
+#define RECV_INCOMPLETE 0
+#define RECV_BAD -1
+
+
+
+#define CONN_DATA_REQUEST 1  /* payload packet sent by client */
+#define CONN_DATA_REPLY 2    /* payload packet sent by server */
+
+#define NO_NEXT_STATE -1
+
+#define MAX_PAYLOADS 10000
+// #define HTTP_MSG_BUF_SIZE 100000
+
+// jsSteg-specific defines
+#define JS_DELIMITER '?'
+// a JavaScript delimiter is used to signal the end of encoding
+// to facilitate the decoding process
+#define JS_DELIMITER_REPLACEMENT '.'
+// JS_DELIMITER that exists in the JavaScript before the end of
+// data encoding will be replaced by JS_DELIMITER_REPLACEMENT
+#define JS_DELIMITER_SIZE 1
+
+#define JS_MIN_AVAIL_SIZE 2050
+// JS_MIN_AVAIL_SIZE should reflect the min number of data bytes
+// a JavaScript may encapsulate
+// Using hex-based encoding, it takes 2 hex char in JS
+// to encode 1 data byte. Thus the size of data that can be encoded
+// is about half this value
+
+
+#define PDF_DELIMITER_SIZE 2
+#define PDF_MIN_AVAIL_SIZE 10240
+// PDF_MIN_AVAIL_SIZE should reflect the min number of data bytes
+// a pdf doc can encode
+
+// specifying the type of contents as an input argument
+// for has_eligible_HTTP_content()
+#define HTTP_CONTENT_JAVASCRIPT         1
+#define HTTP_CONTENT_PDF                2
+#define HTTP_CONTENT_SWF                3
+#define HTTP_CONTENT_ENCRYPTEDZIP       4
+#define HTTP_CONTENT_HTML               5
+
+// used by the JavaScript steg module to distinguish two cases in which
+// JS may appear in the HTTP msg
+// 1) CONTENT-TYPE in HTTP header specifies that the HTTP body is a JS
+// 2) CONTENT-TYPE corresponds to HTML, and the HTTP body contains JS
+//    denoted by script type for JS
+#define CONTENT_JAVASCRIPT              1
+#define CONTENT_HTML_JAVASCRIPT         2
+
+
+// payloads for specific content type
+//
+// MAX_CONTENT_TYPE specifies the maximum number of supported content types
+// (e.g. HTTP_CONTENT_JAVASCRIPT is a content type)
+//
+// initTypePayload[x] specifies whether the arrays typePayloadCount and
+// typePayloads for content type x
+//
+// typePayloadCount[x] specifies the number of available payloads for
+// content type x
+//
+// typePayload[x][] contains references to the corresponding entries in
+// payload_hdrs[] and payloads[]
+//
+// typePayloadCap[x][] specifies the capacity for typePayload[x][]
+
+#define MAX_CONTENT_TYPE		11
+
+
+
+typedef int SID;
+typedef short PacketType;
+typedef short StateFlag;
+
+#define TYPE_SERVICE_DATA 0x1
+#define TYPE_HTTP_REQUEST 0x2
+#define TYPE_HTTP_RESPONSE 0x4
+#define BEGIN_STATE_FLG 0x1
+#define END_STATE_FLG 0x2
+
+
+/* struct for reading in the payload_gen dump file */
+typedef struct {
+  PacketType ptype;
+  int length;
+  ushort port; /* network format */
+}pentry_header;
+
+
+
+
+typedef struct service_state {
+  SID id;
+  PacketType data_type;
+  SID next_state;
+  //  double* probabilities;
+  StateFlag flg;
+  int dir;
+}state;
+
+
+#define HTTP_MSG_BUF_SIZE 100000
+void load_payloads(const char* fname);
+unsigned int find_client_payload(char* buf, int len, int type);
+unsigned int find_server_payload(char** buf, int len, int type, int contentType);
+
+int init_JS_payload_pool(int len, int type, int minCapacity);
+int init_SWF_payload_pool(int len, int type, int minCapacity);
+int init_PDF_payload_pool(int len, int type,int minCapacity);
+
+
+int get_next_payload (int contentType, char** buf, int* size, int* cap);
+
+int get_payload (int contentType, int cap, char** buf, int* size);
+
+int has_eligible_HTTP_content (char* buf, int len, int type);
+int fixContentLen (char* payload, int payloadLen, char *buf, int bufLen);
+void gen_rfc_1123_date(char* buf, int buf_size);
+int parse_client_headers(char* inbuf, char* outbuf, int len);
+int skipJSPattern (char *cp, int len);
+int isalnum_ (char c);
+int offset2Alnum_ (char *p, int range);
+int offset2Hex (char *p, int range, int isLastCharHex);
+unsigned int capacityJS (char* buf, int len, int mode);
+unsigned int capacityJS3 (char* buf, int len, int mode);
+unsigned int get_max_JS_capacity(void);
+
+char * strInBinary (const char *pattern, unsigned int patternLen, const char *blob, unsigned int blobLen);
+
+
+unsigned int capacityPDF (char* buf, int len);
+unsigned int get_max_PDF_capacity(void);
+int find_content_length (char *hdr, int hlen);
+int find_uri_type(char* buf);
+
+int gen_response_header(char* content_type, int gzip, int length, char* buf, int buflen);
+
+#endif
diff --git a/src/steg/pdfSteg.c b/src/steg/pdfSteg.c
new file mode 100644
index 0000000..58147fa
--- /dev/null
+++ b/src/steg/pdfSteg.c
@@ -0,0 +1,618 @@
+#include "payloads.h"
+#include "pdfSteg.h"
+
+void buf_dump(unsigned char* buf, int len, FILE *out);
+
+#define STREAM_BEGIN       ">>stream"
+#define STREAM_BEGIN_SIZE  8
+#define STREAM_END         "endstream"
+#define STREAM_END_SIZE    9
+
+#define DEBUG
+
+
+/*
+ * pdfSteg: A PDF-based steganography module
+ *
+ */
+
+
+/*
+ * addDelimiter processes the input buffer (inbuf) of length inbuflen,
+ * copies it to output buffer (outbuf) of size outbufsize,
+ * and adds a two-char-long, end-of-data pattern at the end of outbuf
+ * based on delimiter1 and delimiter2.
+ *
+ * The end-of-data pattern consists of delimiter1 followed by a char
+ * that is not delimiter1. Thus, delimiter1 and delimiter2 must be
+ * different.
+ * 
+ * If delimiter1 appears in the input buffer, addDelimiter puts two
+ * delimiter1 char in output buffer (to enable removeDelimiter to perform
+ * the back transformation)
+ *
+ * addDelimiter returns the length of the data written to outbuf, including
+ * the end-of-data pattern, if the transformation succeeds;
+ * otherwise, it returns -1
+ *
+ */
+int
+addDelimiter(char *inbuf, int inbuflen, char *outbuf, int outbuflen, 
+             const char delimiter1, const char delimiter2)
+{
+  int cnt;
+  char *ibp, ic, rc;
+
+  if (delimiter1 == delimiter2) return -1;  
+
+  cnt = 0;
+  ibp = inbuf;
+  while ((ibp-inbuf)<inbuflen && cnt<(outbuflen-2)) {
+    ic = *(ibp++);
+    if (ic != delimiter1) {
+      outbuf[cnt++] = ic;
+    } else {
+      outbuf[cnt++] = delimiter1;
+      outbuf[cnt++] = delimiter1;
+    }
+  }
+
+  // error if outbuf is no large enough for storing the resulting data
+  if (cnt >= (outbuflen-2)) return -1;
+
+  // put delimiter1 and a char that is not a delimiter1
+  // as the end-of-data pattern at the end of outbuf
+  outbuf[cnt++] = delimiter1;
+  // try to get a random char (that is not delimiter1)
+  rc = (char) (rand() % 256);
+  if (rc != delimiter1) {
+    outbuf[cnt++] = rc;
+  } else { // unable to get a rand char != delimiter1, use delimiter2
+    outbuf[cnt++] = delimiter2;
+  }
+  return cnt;
+}
+
+
+/*
+ * removeDelimiter performs the reverse transformation of addDelimiter.
+ * 
+ * returns the length of data written to outbuf, if succeed;
+ * otherwise, it returns -1
+ * endFlag indicates whether the end-of-encoding byte pattern (i.e.,
+ * delimiter1 followed by non-delimiter1) is detected
+ */
+int
+removeDelimiter(char *inbuf, int inbuflen, char *outbuf, int outbuflen, 
+                const char delimiter1, int *endFlag, int *escape)
+{
+  int cnt;
+  char *ibp, ic1, ic2;
+
+  cnt = 0;
+  *endFlag = 0;
+  ibp = inbuf;
+
+  if (inbuflen <= 0) return -1;
+
+  // special case: 2-char, end-of-data pattern could be in two buffers
+  // if *escape == true, we need to see if
+  // 1) (*ibp == delimiter1) -> put delimiter1 in outbuf
+  // 2) (*ibp != delimiter1) -> end-of-data detected
+  if (*escape) {
+    ic1 = *ibp;
+    if (ic1 == delimiter1) {
+      outbuf[cnt++] = ic1; ibp++;
+    } else {
+      *endFlag = 1;
+      return 0;
+    }
+  }
+
+  *escape = 0;
+  while ((ibp-inbuf+1)<inbuflen && cnt<outbuflen) {
+    ic1 = *(ibp++);
+    if (ic1 != delimiter1) {
+      // *escape = 0;
+      outbuf[cnt++] = ic1;
+    } else {
+      // *escape = 1;
+      // lookahead 1 char
+      ic2 = *ibp;
+      // if the next char is delimiter1
+      if (ic2 == delimiter1) {
+        outbuf[cnt++] = delimiter1; ibp++;
+        // *escape = 0;
+      } else { // end-of-data pattern detected
+        *endFlag = 1;
+        break;
+      }
+    }
+  }
+
+  // if (*escape) {
+  //   *escape = 0;
+  //   return cnt;
+  // }
+  if (ibp-inbuf == inbuflen) return cnt;
+
+  // handling the last char in inbuf, if needed
+  ic1 = *ibp;
+  if (ic1 != delimiter1) {
+    outbuf[cnt++] = ic1;
+  } else {
+    // look at the next stream obj to handle the special cases
+    *escape = 1;
+  }
+
+  return cnt;
+}
+
+
+
+/*
+ * pdfWrap embeds data of length dlen inside the stream objects of the PDF
+ * document (length plen) that appears in the body of a HTTP msg, and
+ * stores the result in the output buffer of size outsize
+ *
+ * pdfWrap returns the length of the pdf document with the data embedded
+ * inside, if succeed; otherwise, it returns -1 to indicate an error
+ *  
+ */ 
+int 
+pdfWrap (char *data, unsigned int dlen,
+         char *pdfTemplate, unsigned int plen,
+         char *outbuf, unsigned int outbufsize)
+{
+  char data2[dlen*2+2];
+  char *tp, *dp, *op, *streamStart, *streamEnd, *plimit;
+  int data2len, cnt, size, size2;
+
+  // assumption: pdfWrap is length-preserving
+  if (outbufsize < plen) return -1;
+
+  data2len = addDelimiter(data, dlen, data2, HTTP_MSG_BUF_SIZE, PDF_DELIMITER, PDF_DELIMITER2);
+  if (data2len < 1) return -1;
+
+
+  op = outbuf;       // current pointer for output buffer 
+  tp = pdfTemplate;  // current pointer for http msg template
+  dp = data2;        // current pointer for data2
+  cnt = 0;           // number of data char encoded
+  plimit = pdfTemplate+plen;
+
+  while (tp < plimit) {
+    // find the next stream obj
+    streamStart = strInBinary(STREAM_BEGIN, STREAM_BEGIN_SIZE, tp, plimit-tp);
+    if (streamStart == NULL) {
+      log_warn("Cannot find stream in pdf");
+      return -1;
+    }
+ 
+    // copy everything between tp and "stream" (inclusive) to outbuf
+    size = streamStart - tp + STREAM_BEGIN_SIZE;
+    memcpy(op, tp, size);
+    op += size;
+    tp = streamStart + STREAM_BEGIN_SIZE;
+
+    streamEnd = strInBinary(STREAM_END, STREAM_END_SIZE, tp, plimit-tp);
+    if (streamEnd == NULL) {
+      log_warn("Cannot find endstream in pdf");
+      return -1;
+    }
+
+    // count the number of usable char between tp and streamEnd
+    size = streamEnd-tp;
+
+    // encoding data in the stream obj
+    if (size > 0) {
+        size2 = data2len - cnt;
+        if (size < size2) {
+          memcpy(op, dp, size);
+          op += size; tp += size; dp += size; 
+          memcpy(op, tp, STREAM_END_SIZE);
+          op += STREAM_END_SIZE; tp += STREAM_END_SIZE;
+          cnt += size;
+        } else { // done encoding data
+          memcpy(op, dp, size2);
+          op += size2; tp += size2; dp += size2; 
+          cnt += size2;
+          printf("Encoded %d char in pdf. Done encoding\n", size2);
+          break;
+        }
+        log_debug("Encoded %d char in pdf", size);
+    } else { // empty stream
+      memcpy(op, tp, STREAM_END_SIZE);
+      op += STREAM_END_SIZE; tp += STREAM_END_SIZE;
+    }
+
+    if (cnt >= data2len) break; // this shouldn't happen ...
+  }
+
+  // copy the rest of pdfTemplate to outbuf
+  size = plimit-tp;
+  log_debug("copying the rest of pdfTemplate to outbuf (size %d)", size); 
+  memcpy(op, tp, size);
+  op += size;
+  return (op-outbuf);
+}
+
+
+
+
+/*
+ * pdfUnwrap is the inverse operation of pdfWrap
+ */
+int 
+pdfUnwrap (char *data, unsigned int dlen,
+           char *outbuf, unsigned int outbufsize)
+{
+  char *dp, *op, *streamStart, *streamEnd, *dlimit, *olimit;
+  int cnt, size, size2, endFlag;
+  int escape = 0;
+
+  dp = data;   // current pointer for data
+  op = outbuf; // current pointer for outbuf
+  cnt = 0;     // number of char decoded
+  dlimit = data+dlen;
+  olimit = outbuf+outbufsize;
+
+  while (dp < dlimit) {
+    // find the next stream obj
+    streamStart = strInBinary(STREAM_BEGIN, STREAM_BEGIN_SIZE, dp, dlimit-dp);
+    if (streamStart == NULL) {
+      log_warn("Cannot find stream in pdf");
+      return -1;
+    }
+
+    dp = streamStart + STREAM_BEGIN_SIZE;
+    streamEnd = strInBinary(STREAM_END, STREAM_END_SIZE, dp, dlimit-dp);
+    if (streamEnd == NULL) {
+      log_warn("Cannot find endstream in pdf");
+      return -1;
+    }
+
+    // count the number of usable char between tp and streamEnd
+    size = streamEnd-dp;
+
+    if (size > 0) { 
+      size2 = removeDelimiter(dp, size, op, olimit-op, PDF_DELIMITER, &endFlag, &escape);
+      if (size2 < 0) {
+        return -1;
+      }
+      cnt += size2;
+      if (endFlag) { // Done decoding
+        break;
+      } else { // Continue decoding
+        op += size2;
+        dp = streamEnd + STREAM_END_SIZE;
+      }
+    } else { // empty stream obj
+      dp = streamEnd + STREAM_END_SIZE;
+    }
+  }
+
+  return cnt;
+}
+
+
+
+
+
+int x_http2_server_PDF_transmit (steg_t* s, struct evbuffer *source, conn_t *conn) {
+
+  struct evbuffer *dest = conn_get_outbound(conn);
+  size_t sbuflen = evbuffer_get_length(source);
+  unsigned int mpdf;
+  char *pdfTemplate = NULL, *hend;
+  int pdfTemplateSize = 0;
+  // char data1[HTTP_MSG_BUF_SIZE];
+  char data1[(int) sbuflen];
+  char outbuf[HTTP_MSG_BUF_SIZE];
+  int cnt, hLen, outbuflen, i;
+
+  struct evbuffer_iovec *iv;
+  int nv;
+
+  // for debugging pdfWrap and pdfUnwrap
+  // char data2[(int) sbuflen];
+  // int data2len;
+
+  log_debug("Entering SERVER PDF transmit with sbuflen %d", (int)sbuflen);
+
+  nv = evbuffer_peek(source, sbuflen, NULL, NULL, 0);
+  iv = xzalloc(sizeof(struct evbuffer_iovec) * nv);
+
+  if (evbuffer_peek(source, sbuflen, NULL, iv, nv) != nv) {
+    free(iv);
+    return -1;
+  }
+
+  cnt = 0;
+  for (i = 0; i < nv; i++) {
+    const unsigned char *p = iv[i].iov_base;
+    const unsigned char *limit = p + iv[i].iov_len;
+    while (p < limit && cnt < (int)sbuflen) {
+      data1[cnt++] = *p++;
+    }
+  }
+
+  free(iv);
+
+  log_debug("SERVER sbuflen = %d; cnt = %d", (int)sbuflen, cnt);
+
+  mpdf = get_max_PDF_capacity();
+
+  if (mpdf <= 0) {
+    log_warn("SERVER ERROR: No pdfTemplate found\n");
+    return -1;
+  }
+
+  if (sbuflen > (size_t) mpdf) {
+    log_warn("SERVER ERROR: pdfTemplate cannot accommodate data %d %dn",
+                (int) sbuflen, (int) mpdf);
+    return -1;
+  }
+
+  if (get_payload(HTTP_CONTENT_PDF, sbuflen, &pdfTemplate, &pdfTemplateSize) == 1) {
+    log_debug("SERVER found the next HTTP response template with size %d", pdfTemplateSize);
+  } else {
+    log_warn("SERVER couldn't find the next HTTP response template");
+    return -1;
+  }
+
+  hend = strstr(pdfTemplate, "\r\n\r\n");
+  if (hend == NULL) {
+    log_warn("SERVER unable to find end of header in the HTTP template");
+    return -1;
+  }
+
+  hLen = hend+4-pdfTemplate;
+  
+  log_debug("SERVER calling pdfWrap for data1 with length %d", cnt);
+  outbuflen = pdfWrap(data1, cnt, hend+4, pdfTemplateSize-hLen, outbuf, HTTP_MSG_BUF_SIZE);
+  if (outbuflen < 0) {
+    log_warn("SERVER pdfWrap fails");
+    return -1;
+  }
+  log_debug("SERVER pdfSteg sends resp with hdr len %d body len %d", hLen, outbuflen);
+
+
+  // debugging
+  // buf_dump((unsigned char *)data1, cnt, stderr);
+
+  // data2len = pdfUnwrap(outbuf, outbuflen, data2, sbuflen);
+  // if ((int)sbuflen == data2len) {
+  //   log_warn("sbuflen == data2len == %d", (int)sbuflen);
+  //   if (memcmp(data1, data2, sbuflen) == 0) {
+  //     log_warn("data1 and data2 match");
+  //   } else {
+  //     log_warn("data1 and data2 DO NOT match!! Dumping data1 ...");
+  //     buf_dump((unsigned char *)data1, cnt, stderr);
+  //     log_warn("data1 and data2 DO NOT match!! Dumping data2...");
+  //     buf_dump((unsigned char *)data2, data2len, stderr);
+  //   }
+  // } else {
+  //   log_warn("*** sbuflen = %d, data2len = %d *** Dumping data1 ...", (int)sbuflen, data2len);
+  //   buf_dump((unsigned char *)data1, cnt, stderr);
+  //   log_warn("*** sbuflen = %d, data2len = %d *** Dumping data2 ...", (int)sbuflen, data2len);
+  //   buf_dump((unsigned char *)data2, data2len, stderr);
+  // }
+
+
+  if (evbuffer_add(dest, pdfTemplate, hLen)) {
+    log_warn("SERVER ERROR: evbuffer_add() fails for pdfTemplate");
+    return -1;
+  }
+  if (evbuffer_add(dest, outbuf, outbuflen)) {
+    log_warn("SERVER ERROR: evbuffer_add() fails for outbuf");
+    return -1;
+  }
+
+  evbuffer_drain(source, sbuflen);
+
+  conn_close_after_transmit(conn);
+  //  downcast_steg(s)->have_transmitted = 1;
+  return 0;
+}
+
+
+
+int
+x_http2_handle_client_PDF_receive(steg_t *s, conn_t *conn, struct evbuffer *dest, struct evbuffer* source) {
+  struct evbuffer_ptr s2;
+  unsigned int response_len = 0, hdrLen;
+  char outbuf[HTTP_MSG_BUF_SIZE];
+  int content_len = 0, outbuflen;
+  char *httpHdr, *httpBody;
+
+  log_debug("Entering CLIENT PDF receive");
+
+  s2 = evbuffer_search(source, "\r\n\r\n", sizeof ("\r\n\r\n") -1 , NULL);
+  if (s2.pos == -1) {
+    log_warn("CLIENT Did not find end of HTTP header %d", (int) evbuffer_get_length(source));
+    //    evbuffer_dump(source, stderr);
+    return RECV_INCOMPLETE;
+  }
+
+  log_debug("CLIENT received response header with len %d", (int)s2.pos);
+
+  response_len = 0;
+  hdrLen = s2.pos + strlen("\r\n\r\n");
+  response_len += hdrLen;
+
+  httpHdr = (char *) evbuffer_pullup(source, s2.pos);
+  if (httpHdr == NULL) {
+    log_warn("CLIENT unable to pullup the complete HTTP header");
+    return RECV_BAD;
+  }
+
+  content_len = find_content_length(httpHdr, hdrLen);
+  if (content_len < 0) {
+    log_warn("CLIENT unable to find content length");
+    return RECV_BAD;
+  }
+  log_debug("CLIENT received Content-Length = %d\n", content_len);
+
+  response_len += content_len;
+
+  if (response_len > evbuffer_get_length(source))
+    return RECV_INCOMPLETE;
+
+  httpHdr = (char *) evbuffer_pullup(source, response_len);
+  httpBody = httpHdr + hdrLen;
+
+
+  outbuflen = pdfUnwrap(httpBody, content_len, outbuf, HTTP_MSG_BUF_SIZE);
+  if (outbuflen < 0) {
+    log_warn("CLIENT ERROR: pdfUnwrap fails\n");
+    return RECV_BAD;
+  }
+
+  log_debug("CLIENT unwrapped data of length %d:", outbuflen);
+
+
+  // debugging
+  // buf_dump((unsigned char *)outbuf, outbuflen, stderr);
+  // ***** not sure why there is an extra char at the end of outbuf
+  outbuflen--;
+
+
+  if (evbuffer_add(dest, outbuf, outbuflen)) {
+    log_warn("CLIENT ERROR: evbuffer_add to dest fails\n");
+    return RECV_BAD;
+  }
+
+  // log_debug("Drained source for %d char\n", response_len);
+  if (evbuffer_drain(source, response_len) == -1) {
+    log_warn("CLIENT ERROR: failed to drain source\n");
+    return RECV_BAD;
+  }
+
+  //  downcast_steg(s)->have_received = 1;
+  conn_expect_close(conn);
+  return RECV_GOOD;
+}
+
+
+
+
+/*****
+int main() {
+  char data1[] = "this is a test?? yes!";
+  char data2[100];
+  char data3[100];
+  int dlen1, dlen2, dlen3, end;
+  char last = ' ';
+  printf("hello world\n");
+ 
+  dlen2 = addDelimiter(data1, strlen(data1), data2, 100, '?', '.');
+  printf("dlen2 = %d\n", dlen2);
+  dlen3 = removeDelimiter(data2, dlen2, data3, 100, '?', &end, &last);
+  printf("endflag = %d", end);
+  printf("dlen3 = %d\n", dlen3);
+  if (memcmp(data1, data3, dlen3) == 0) {
+    data1[dlen3] = 0;
+    printf("removeDelimiter(addDelimiter(x)) == x for |%s|\n", data1);
+  } else {
+    printf("removeDelimiter(addDelimiter(x)) != x for |%s|\n", data1);
+  }
+  return 1;
+}
+ *****/
+
+/*****
+int main() {
+  char data1[] = "12345";
+  char data2[] = "123456789012";
+  char data3[] = "12345678901";
+  char data4[] = "1234567890?";
+  char pdf1[] = "[PDFHDR][STUFFS1]>>streamABCDEFGHIJYYendstream[STUFFS2]>>streamABCDEFGHIJYYendstream[STUFF3][PDFTRAILER]";
+  char out[200];
+  char orig[200];
+  int r1, r2;
+
+  printf("********************\n");
+  printf("pdfwrap for %s\n", data1);
+  printf("strlen(pdf1) = %d\n", (int)strlen(pdf1));
+  r1 = pdfWrap(data1, strlen(data1), pdf1, strlen(pdf1), out, (int)sizeof(out));
+  if (r1 > 0) {
+    printf("pdfWrap returns %d\n", r1);
+    out[r1] = 0;
+    printf("out[] contains |%s|\n", out);
+  } else {
+    printf("pdfWrap returns %d\n", r1);
+  }
+
+  r2 = pdfUnwrap(out, r1, orig, (int)sizeof(orig));
+  if (r2 > 0) {
+    printf("pdfUnwrap returns %d\n", r2);
+    orig[r2] = 0;
+    printf("orig[] contains |%s|\n", orig);
+  } else {
+    printf("pdfUnwrap returns %d\n", r2);
+  }
+
+  printf("********************\n");
+  printf("pdfwrap for %s\n", data2);
+  r1 = pdfWrap(data2, strlen(data2), pdf1, strlen(pdf1), out, (int)sizeof(out));
+  if (r1 > 0) {
+    printf("pdfWrap returns %d\n", r1);
+    out[r1] = 0;
+    printf("out[] contains |%s|\n", out);
+  } else {
+    printf("pdfWrap returns %d\n", r1);
+  }
+
+  r2 = pdfUnwrap(out, r1, orig, (int)sizeof(orig));
+  if (r2 > 0) {
+    printf("pdfUnwrap returns %d\n", r2);
+    orig[r2] = 0;
+    printf("orig[] contains |%s|\n", orig);
+  } else {
+    printf("pdfUnwrap returns %d\n", r2);
+  }
+
+  printf("********************\n");
+  printf("pdfwrap for %s\n", data3);
+  r1 = pdfWrap(data3, strlen(data3), pdf1, strlen(pdf1), out, (int)sizeof(out));
+  if (r1 > 0) {
+    printf("pdfWrap returns %d\n", r1);
+    out[r1] = 0;
+    printf("out[] contains |%s|\n", out);
+  } else {
+    printf("pdfWrap returns %d\n", r1);
+  }
+
+  r2 = pdfUnwrap(out, r1, orig, (int)sizeof(orig));
+  if (r2 > 0) {
+    printf("pdfUnwrap returns %d\n", r2);
+    orig[r2] = 0;
+    printf("orig[] contains |%s|\n", orig);
+  } else {
+    printf("pdfUnwrap returns %d\n", r2);
+  }
+
+  printf("********************\n");
+  printf("pdfwrap for %s\n", data4);
+  r1 = pdfWrap(data4, strlen(data4), pdf1, strlen(pdf1), out, (int)sizeof(out));
+  if (r1 > 0) {
+    printf("pdfWrap returns %d\n", r1);
+    out[r1] = 0;
+    printf("out[] contains |%s|\n", out);
+  } else {
+    printf("pdfWrap returns %d\n", r1);
+  }
+
+  r2 = pdfUnwrap(out, r1, orig, (int)sizeof(orig));
+  if (r2 > 0) {
+    printf("pdfUnwrap returns %d\n", r2);
+    orig[r2] = 0;
+    printf("orig[] contains |%s|\n", orig);
+  } else {
+    printf("pdfUnwrap returns %d\n", r2);
+  }
+
+  return 0;
+}
+ *****/
diff --git a/src/steg/pdfSteg.h b/src/steg/pdfSteg.h
new file mode 100644
index 0000000..7e48449
--- /dev/null
+++ b/src/steg/pdfSteg.h
@@ -0,0 +1,29 @@
+#ifndef _PDFSTEG_H
+#define _PDFSTEG_H
+
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include "util.h"
+#include "connections.h"
+#include "steg.h"
+#include <event2/buffer.h>
+
+
+
+#define PDF_DELIMITER    '?'
+#define PDF_DELIMITER2   '.'
+
+int pdfWrap (char *data, unsigned int dlen, char *pdfTemplate, unsigned int plen, char *outbuf, unsigned int outbufsize);
+int pdfUnwrap (char *data, unsigned int dlen, char *outbuf, unsigned int outbufsize);
+
+int addDelimiter(char *inbuf, int inbuflen, char *outbuf, int outbuflen, const char delimiter1, const char delimiter2);
+int removeDelimiter(char *inbuf, int inbuflen, char *outbuf, int outbuflen, const char delimiter1, int* endFlag, int* escape);
+
+int x_http2_server_PDF_transmit (steg_t* s, struct evbuffer *source, conn_t *conn);
+int
+x_http2_handle_client_PDF_receive(steg_t *s, conn_t *conn, struct evbuffer *dest, struct evbuffer* source);
+
+#endif
+
diff --git a/src/steg/swfSteg.c b/src/steg/swfSteg.c
new file mode 100644
index 0000000..ad3d5c8
--- /dev/null
+++ b/src/steg/swfSteg.c
@@ -0,0 +1,282 @@
+#include "swfSteg.h"
+
+
+static const char http_response_1[] =
+  "HTTP/1.1 200 OK\r\n"
+  "Expires: Thu, 01 Jan 1970 00:00:00 GMT\r\n"
+  "Cache-Control: no-store\r\n"
+  "Connection: close\r\n"
+  "Content-Type: application/x-shockwave-flash\r\n"
+  "Content-Length: ";
+
+
+
+
+
+
+
+
+
+
+
+unsigned int 
+swf_wrap(char* inbuf, int in_len, char* outbuf, int out_sz) {
+
+  char* swf;
+  int in_swf_len;
+
+  char* tmp_buf;
+  int out_swf_len;
+
+  char* resp;
+  int resp_len;
+
+  char hdr[512];
+  unsigned int hdr_len;
+
+  char* tmp_buf2;
+  
+
+
+  if (!get_payload(HTTP_CONTENT_SWF, -1, &resp, &resp_len)) {
+    log_warn("swfsteg: no suitable payload found\n");
+    return -1;
+  }
+
+  swf = strstr(resp, "\r\n\r\n") + 4;
+  in_swf_len = resp_len - (swf - resp);
+
+
+
+  
+  if (out_sz - in_len < (SWF_SAVE_FOOTER_LEN + SWF_SAVE_HEADER_LEN + 8 + 512)) {
+    fprintf(stderr, "swfsteg: outbuf too small %d \n", out_sz - in_len);
+
+    log_warn("swfsteg: outbuf too small\n");
+    return -1;
+  }
+
+
+  tmp_buf = malloc(in_len + SWF_SAVE_HEADER_LEN + SWF_SAVE_FOOTER_LEN);
+
+  if (tmp_buf == NULL) {
+    log_warn("swfsteg: malloc failed\n");
+    return -1;
+  }
+
+
+  tmp_buf2 = malloc(in_len + SWF_SAVE_HEADER_LEN + SWF_SAVE_FOOTER_LEN + 512);
+
+  if (tmp_buf2 == NULL) {
+    free(tmp_buf);
+    log_warn("swfsteg: malloc failed\n");
+    return -1;
+  }
+
+
+  memcpy(tmp_buf, swf+8, SWF_SAVE_HEADER_LEN);
+  memcpy(tmp_buf+SWF_SAVE_HEADER_LEN, inbuf, in_len);
+  memcpy(tmp_buf+SWF_SAVE_HEADER_LEN+in_len, swf + in_swf_len - SWF_SAVE_FOOTER_LEN, SWF_SAVE_FOOTER_LEN);
+  out_swf_len = def(tmp_buf, SWF_SAVE_HEADER_LEN + in_len + SWF_SAVE_FOOTER_LEN, tmp_buf2+8, 
+		    in_len + SWF_SAVE_HEADER_LEN + SWF_SAVE_FOOTER_LEN + 512-8, 
+		    Z_DEFAULT_COMPRESSION);
+
+  //  sprintf(hdr, "%s%d\r\n\r\n", http_response_1, out_swf_len + 8);
+
+
+
+  //  fprintf(stderr, "out_swf_len = %d\n", out_swf_len);
+
+
+  hdr_len =   gen_response_header((char*) "application/x-shockwave-flash", 0, out_swf_len + 8, hdr, sizeof(hdr));
+
+  //  fprintf(stderr, "hdr = %s\n", hdr);
+				       
+  memcpy(tmp_buf2, swf, 4);
+  ((int*) (tmp_buf2))[1] = out_swf_len;
+  
+  memcpy(outbuf, hdr, hdr_len);
+  memcpy(outbuf+hdr_len, tmp_buf2, out_swf_len + 8);
+
+  free(tmp_buf);
+  free(tmp_buf2);
+  return out_swf_len + 8 + hdr_len;
+}
+
+
+
+
+unsigned int 
+swf_unwrap(char* inbuf, int in_len, char* outbuf, int out_sz) {
+  char* tmp_buf;
+  int inf_len;
+
+  tmp_buf = malloc(in_len * 8);
+
+  inf_len = inf(inbuf + 8, in_len - 8, tmp_buf, in_len * 8); 
+
+  //  fprintf(stderr, "in_swf_len = %d\n", in_len -8 );
+
+
+  if (inf_len < 0 || out_sz < inf_len - SWF_SAVE_HEADER_LEN - SWF_SAVE_FOOTER_LEN) {
+    fprintf(stderr, "inf_len = %d\n", inf_len);
+    free(tmp_buf);
+    //    buf_dump((unsigned char*) (inbuf+8), in_len -8, stderr);
+    
+
+
+    return -1;
+  }
+
+  memcpy(outbuf, tmp_buf + SWF_SAVE_HEADER_LEN, inf_len - SWF_SAVE_HEADER_LEN - SWF_SAVE_FOOTER_LEN);
+  return inf_len - SWF_SAVE_HEADER_LEN - SWF_SAVE_FOOTER_LEN;
+}
+
+
+
+
+
+int 
+x_http2_server_SWF_transmit (steg_t* s, struct evbuffer *source, conn_t *conn) {
+
+  struct evbuffer *dest = conn_get_outbound(conn);
+  size_t sbuflen = evbuffer_get_length(source);
+  char* inbuf;
+  char* outbuf;
+  int outlen;
+
+
+
+  inbuf = malloc(sbuflen);
+
+  if (inbuf == NULL) {
+    log_warn("malloc inbuf failed\n");
+    return -1;
+  }
+
+  
+  if (evbuffer_remove(source, inbuf, sbuflen) == -1) {
+    log_debug("evbuffer_remove failed in x_http2_server_SWF_transmit");
+    return -1;
+  }
+
+  outbuf = malloc(4*sbuflen + SWF_SAVE_FOOTER_LEN + SWF_SAVE_HEADER_LEN + 512);
+
+  if (outbuf == NULL) {
+    free(inbuf);
+    log_warn("malloc outbuf failed\n");
+    return -1;
+  }
+  
+  //  fprintf(stderr, "server wrapping swf len %d\n", (int) sbuflen);
+  outlen = swf_wrap(inbuf, sbuflen, outbuf, 4*sbuflen + SWF_SAVE_FOOTER_LEN + SWF_SAVE_HEADER_LEN + 512);
+
+  if (outlen < 0) {
+    log_warn("swf_wrap failed\n");
+    //    fprintf(stderr, "swf_wrap failed\n");
+    free(inbuf);
+    free(outbuf);
+    return -1;
+  }
+
+  
+  if (evbuffer_add(dest, outbuf, outlen)) {
+    log_debug("SERVER ERROR: x_http2_server_transmit: evbuffer_add() fails for jsTemplate");
+    free(inbuf);
+    free(outbuf);
+    return -1;
+  }
+
+    
+  // conn_cease_transmission(conn);
+  conn_close_after_transmit(conn);
+
+
+  free(inbuf);
+  free(outbuf);
+  return 0;
+}
+
+
+
+
+int
+x_http2_handle_client_SWF_receive(steg_t *s, conn_t *conn, struct evbuffer *dest, struct evbuffer* source) {
+  struct evbuffer_ptr s2;
+  unsigned int response_len = 0, hdrLen;
+  char outbuf[HTTP_MSG_BUF_SIZE];
+  int content_len = 0, outbuflen;
+  char *httpHdr, *httpBody;
+
+
+
+  s2 = evbuffer_search(source, "\r\n\r\n", sizeof ("\r\n\r\n") -1 , NULL);
+  if (s2.pos == -1) {
+    log_warn("CLIENT Did not find end of HTTP header %d", (int) evbuffer_get_length(source));
+    fprintf(stderr, "client did not find end of HTTP header\n");
+
+    //    evbuffer_dump(source, stderr);
+    return RECV_INCOMPLETE;
+  }
+
+  log_debug("CLIENT received response header with len %d", (int)s2.pos);
+
+  response_len = 0;
+  hdrLen = s2.pos + strlen("\r\n\r\n");
+  response_len += hdrLen;
+
+  httpHdr = (char *) evbuffer_pullup(source, s2.pos);
+  if (httpHdr == NULL) {
+    log_warn("CLIENT unable to pullup the complete HTTP header");
+    return RECV_BAD;
+  }
+
+  content_len = find_content_length(httpHdr, hdrLen);
+
+
+
+  if (content_len < 0) {
+    log_warn("CLIENT unable to find content length");
+    return RECV_BAD;
+  }
+  log_debug("CLIENT received Content-Length = %d\n", content_len);
+
+
+
+  response_len += content_len;
+
+  if (response_len > evbuffer_get_length(source))
+    return RECV_INCOMPLETE;
+
+
+
+  httpHdr = (char *) evbuffer_pullup(source, response_len);
+  httpBody = httpHdr + hdrLen;
+
+
+  outbuflen = swf_unwrap(httpBody, content_len, outbuf, HTTP_MSG_BUF_SIZE);
+
+  if (outbuflen < 0) {
+    fprintf(stderr, "swf_unwrap failed\n");
+    log_debug("CLIENT ERROR: swf_unwrap failed\n");
+    return RECV_BAD;
+  }
+
+  //  fprintf(stderr, "CLIENT unwrapped data of length %d:", outbuflen);
+  // buf_dump(outbuf, outbuflen, stderr);
+
+  if (evbuffer_add(dest, outbuf, outbuflen)) {
+    log_debug("CLIENT ERROR: evbuffer_add to dest fails\n");
+    return RECV_BAD;
+  }
+
+  // log_debug("Drained source for %d char\n", response_len);
+  if (evbuffer_drain(source, response_len) == -1) {
+    log_debug("CLIENT ERROR: failed to drain source\n");
+    return RECV_BAD;
+  }
+
+  //  downcast_steg(s)->have_received = 1;
+  conn_expect_close(conn);
+  return RECV_GOOD;
+}
diff --git a/src/steg/swfSteg.c.old b/src/steg/swfSteg.c.old
new file mode 100644
index 0000000..833ea9f
--- /dev/null
+++ b/src/steg/swfSteg.c.old
@@ -0,0 +1,264 @@
+#include "swfSteg.h"
+
+
+static const char http_response_1[] =
+  "HTTP/1.1 200 OK\r\n"
+  "Expires: Thu, 01 Jan 1970 00:00:00 GMT\r\n"
+  "Cache-Control: no-store\r\n"
+  "Connection: close\r\n"
+  "Content-Type: application/x-shockwave-flash\r\n"
+  "Content-Length: ";
+
+
+
+
+
+
+
+
+
+
+
+unsigned int 
+swf_wrap(char* inbuf, int in_len, char* outbuf, int out_sz) {
+
+  char* swf;
+  int in_swf_len;
+
+  char* tmp_buf;
+  int out_swf_len;
+
+  char* resp;
+  int resp_len;
+
+  char hdr[512];
+  unsigned int hdr_len;
+
+  char* tmp_buf2;
+  
+
+
+  if (!get_payload(HTTP_CONTENT_SWF, -1, &resp, &resp_len)) {
+    log_warn("swfsteg: no suitable payload found\n");
+    return -1;
+  }
+
+  swf = strstr(resp, "\r\n\r\n") + 4;
+  in_swf_len = resp_len - (swf - resp);
+
+
+
+  
+  if (out_sz - in_len < (SWF_SAVE_FOOTER_LEN + SWF_SAVE_HEADER_LEN + 8 + 512)) {
+    fprintf(stderr, "swfsteg: outbuf too small %d \n", out_sz - in_len);
+
+    log_warn("swfsteg: outbuf too small\n");
+    return -1;
+  }
+
+
+  tmp_buf = malloc(in_len + SWF_SAVE_HEADER_LEN + SWF_SAVE_FOOTER_LEN);
+
+  if (tmp_buf == NULL) {
+    log_warn("swfsteg: malloc failed\n");
+    return -1;
+  }
+
+
+  tmp_buf2 = malloc(in_len + SWF_SAVE_HEADER_LEN + SWF_SAVE_FOOTER_LEN + 512);
+
+  if (tmp_buf2 == NULL) {
+    free(tmp_buf);
+    log_warn("swfsteg: malloc failed\n");
+    return -1;
+  }
+
+
+  memcpy(tmp_buf, swf+8, SWF_SAVE_HEADER_LEN);
+  memcpy(tmp_buf+SWF_SAVE_HEADER_LEN, inbuf, in_len);
+  memcpy(tmp_buf+SWF_SAVE_HEADER_LEN+in_len, swf + in_swf_len - SWF_SAVE_FOOTER_LEN, SWF_SAVE_FOOTER_LEN);
+  out_swf_len = def(tmp_buf, SWF_SAVE_HEADER_LEN + in_len + SWF_SAVE_FOOTER_LEN, tmp_buf2+8, 
+		    in_len + SWF_SAVE_HEADER_LEN + SWF_SAVE_FOOTER_LEN + 512-8, 
+		    Z_DEFAULT_COMPRESSION);
+
+  //  sprintf(hdr, "%s%d\r\n\r\n", http_response_1, out_swf_len + 8);
+
+
+  hdr_len =   gen_response_header((char*) "application/x-shockwave-flash", 0, out_swf_len + 8, hdr, sizeof(hdr));
+
+  //  fprintf(stderr, "hdr = %s\n", hdr);
+				       
+  memcpy(tmp_buf2, swf, 4);
+  ((int*) (tmp_buf2))[1] = out_swf_len;
+  
+  memcpy(outbuf, hdr, hdr_len);
+  memcpy(outbuf+hdr_len, tmp_buf2, out_swf_len + 8);
+
+  free(tmp_buf);
+  free(tmp_buf2);
+  return out_swf_len + 8 + hdr_len;
+}
+
+
+
+unsigned int 
+swf_unwrap(char* inbuf, int in_len, char* outbuf, int out_sz) {
+  char* tmp_buf;
+  int inf_len;
+
+  tmp_buf = malloc(in_len * 5);
+  inf_len = inf(inbuf + 8, in_len - 8, tmp_buf, in_len * 5); 
+
+  if (inf_len < 0 || out_sz < inf_len - SWF_SAVE_HEADER_LEN - SWF_SAVE_FOOTER_LEN) {
+    fprintf(stderr, "swf_unwrap failed \n");
+    free(tmp_buf);
+    return -1;
+  }
+
+  memcpy(outbuf, tmp_buf + SWF_SAVE_HEADER_LEN, inf_len - SWF_SAVE_HEADER_LEN - SWF_SAVE_FOOTER_LEN);
+  return inf_len - SWF_SAVE_HEADER_LEN - SWF_SAVE_FOOTER_LEN;
+}
+
+
+
+
+
+int 
+x_http2_server_SWF_transmit (steg_t* s, struct evbuffer *source, conn_t *conn) {
+
+  struct evbuffer *dest = conn_get_outbound(conn);
+  size_t sbuflen = evbuffer_get_length(source);
+  char* inbuf;
+  char* outbuf;
+  int outlen;
+
+
+
+  inbuf = malloc(sbuflen);
+
+  if (inbuf == NULL) {
+    log_warn("malloc inbuf failed\n");
+    return -1;
+  }
+
+  
+  if (evbuffer_remove(source, inbuf, sbuflen) == -1) {
+    log_debug("evbuffer_remove failed in x_http2_server_SWF_transmit");
+    return -1;
+  }
+
+  outbuf = malloc(4*sbuflen + SWF_SAVE_FOOTER_LEN + SWF_SAVE_HEADER_LEN + 512);
+
+  if (outbuf == NULL) {
+    free(inbuf);
+    log_warn("malloc outbuf failed\n");
+    return -1;
+  }
+  
+  //  fprintf(stderr, "server wrapping swf len %d\n", (int) sbuflen);
+  outlen = swf_wrap(inbuf, sbuflen, outbuf, 4*sbuflen + SWF_SAVE_FOOTER_LEN + SWF_SAVE_HEADER_LEN + 512);
+
+  if (outlen < 0) {
+    log_warn("swf_wrap failed\n");
+    fprintf(stderr, "swf_wrap failed\n");
+    free(inbuf);
+    free(outbuf);
+    return -1;
+  }
+
+  
+  if (evbuffer_add(dest, outbuf, outlen)) {
+    log_debug("SERVER ERROR: x_http2_server_transmit: evbuffer_add() fails for jsTemplate");
+    free(inbuf);
+    free(outbuf);
+    return -1;
+  }
+
+    
+  // conn_cease_transmission(conn);
+  conn_close_after_transmit(conn);
+
+
+  free(inbuf);
+  free(outbuf);
+  return 0;
+}
+
+
+
+
+int
+x_http2_handle_client_SWF_receive(steg_t *s, conn_t *conn, struct evbuffer *dest, struct evbuffer* source) {
+  struct evbuffer_ptr s2;
+  unsigned int response_len = 0, hdrLen;
+  char outbuf[HTTP_MSG_BUF_SIZE];
+  int content_len = 0, outbuflen;
+  char *httpHdr, *httpBody;
+
+
+
+  s2 = evbuffer_search(source, "\r\n\r\n", sizeof ("\r\n\r\n") -1 , NULL);
+  if (s2.pos == -1) {
+    log_warn("CLIENT Did not find end of HTTP header %d", (int) evbuffer_get_length(source));
+    fprintf(stderr, "client did not find end of HTTP header\n");
+
+    //    evbuffer_dump(source, stderr);
+    return RECV_INCOMPLETE;
+  }
+
+  log_debug("CLIENT received response header with len %d", (int)s2.pos);
+
+  response_len = 0;
+  hdrLen = s2.pos + strlen("\r\n\r\n");
+  response_len += hdrLen;
+
+  httpHdr = (char *) evbuffer_pullup(source, s2.pos);
+  if (httpHdr == NULL) {
+    log_warn("CLIENT unable to pullup the complete HTTP header");
+    return RECV_BAD;
+  }
+
+  content_len = find_content_length(httpHdr, hdrLen);
+
+
+
+  if (content_len < 0) {
+    log_warn("CLIENT unable to find content length");
+    return RECV_BAD;
+  }
+  log_debug("CLIENT received Content-Length = %d\n", content_len);
+
+
+
+  response_len += content_len;
+
+  if (response_len > evbuffer_get_length(source))
+    return RECV_INCOMPLETE;
+
+  httpBody = httpHdr + hdrLen;
+  outbuflen = swf_unwrap(httpBody, content_len, outbuf, HTTP_MSG_BUF_SIZE);
+
+  if (outbuflen < 0) {
+    fprintf(stderr, "swf_unwrap failed\n");
+    log_debug("CLIENT ERROR: swf_unwrap failed\n");
+    return RECV_BAD;
+  }
+
+  //  fprintf(stderr, "CLIENT unwrapped data of length %d:", outbuflen);
+  // buf_dump(outbuf, outbuflen, stderr);
+
+  if (evbuffer_add(dest, outbuf, outbuflen)) {
+    log_debug("CLIENT ERROR: evbuffer_add to dest fails\n");
+    return RECV_BAD;
+  }
+
+  // log_debug("Drained source for %d char\n", response_len);
+  if (evbuffer_drain(source, response_len) == -1) {
+    log_debug("CLIENT ERROR: failed to drain source\n");
+    return RECV_BAD;
+  }
+
+  //  downcast_steg(s)->have_received = 1;
+  conn_expect_close(conn);
+  return RECV_GOOD;
+}
diff --git a/src/steg/swfSteg.h b/src/steg/swfSteg.h
new file mode 100644
index 0000000..dc6bc04
--- /dev/null
+++ b/src/steg/swfSteg.h
@@ -0,0 +1,42 @@
+#ifndef _SWFSTEG_H
+#define _SWFSTEG_H
+
+
+#include "util.h"
+#include "connections.h"
+#include "steg.h"
+#include "payloads.h"
+#include "cookies.h"
+#include "pdfSteg.h"
+#include "zpack.h"
+
+
+#include <event2/buffer.h>
+#include <stdio.h>
+
+
+
+
+
+
+
+#define SWF_SAVE_HEADER_LEN 1500
+#define SWF_SAVE_FOOTER_LEN 1500
+
+
+unsigned int 
+swf_wrap(char* inbuf, int in_len, char* outbuf, int out_sz);
+
+unsigned int 
+swf_unwrap(char* inbuf, int in_len, char* outbuf, int out_sz);
+
+int 
+x_http2_server_SWF_transmit (steg_t* s, struct evbuffer *source, conn_t *conn);
+
+
+int
+x_http2_handle_client_SWF_receive(steg_t *s, conn_t *conn, struct evbuffer *dest, struct evbuffer* source);
+
+#endif
+
+
diff --git a/src/steg/x_http.c.old b/src/steg/x_http.c.old
new file mode 100644
index 0000000..2799078
--- /dev/null
+++ b/src/steg/x_http.c.old
@@ -0,0 +1,337 @@
+/* Copyright 2011 Zack Weinberg
+   See LICENSE for other credits and copying information
+*/
+
+#include "util.h"
+#include "connections.h"
+#include "steg.h"
+
+#include <event2/buffer.h>
+
+
+
+/* This is an example steganography module.  Don't use it to disguise real
+   traffic!  It packages client->server traffic as HTTP GET requests and
+   server->client traffic as HTTP responses, but makes no actual attempt
+   to obscure the data proper. */
+
+struct x_http_steg_t
+{
+  steg_t super;
+  /* no extra stuff is presently necessary */
+};
+
+STEG_DEFINE_MODULE(x_http,
+                   1024,  /* client-server max data rate - made up */
+                   10240, /* server-client max data rate - ditto */
+                   1,     /* max concurrent connections per IP */
+                   1);     /* max concurrent IPs */
+
+/* Canned HTTP query and response headers. */
+static const char http_query_1[] =
+  "GET /";
+static const char http_query_2[] =
+  " HTTP/1.1\r\n"
+  "Host: ";
+static const char http_query_3[] =
+  "\r\n"
+  "Connection: close\r\n\r\n";
+
+static const char http_response_1[] =
+  "HTTP/1.1 200 OK\r\n"
+  "Expires: Thu, 01 Jan 1970 00:00:00 GMT\r\n"
+  "Cache-Control: no-store\r\n"
+  "Connection: close\r\n"
+  "Content-Type: application/octet-stream\r\n"
+  "Content-Length: ";
+static const char http_response_2[] =
+  "%lu\r\n"
+  "\r\n";
+
+
+steg_t *
+x_http_new(rng_t *rng, unsigned int is_clientside)
+{
+  STEG_NEW(x_http, state, rng, is_clientside);
+  /* if there were extra stuff to fill in, you would do it here */
+  return upcast_steg(state);
+}
+
+void
+x_http_del(steg_t *s)
+{
+  x_http_steg_t *state = downcast_steg(s);
+  STEG_DEL(s);
+  /* if there were extra stuff to deallocate, you would do it here */
+  free(state);
+}
+
+unsigned int
+x_http_detect(conn_t *conn)
+{
+  struct evbuffer *buf = conn_get_inbound(conn);
+  unsigned char *data;
+
+  return 0;
+
+  /* Look for the text of http_response_1. */
+  if (evbuffer_get_length(buf) >= sizeof http_response_1 - 1) {
+    data = evbuffer_pullup(buf, sizeof http_response_1 - 1);
+    if (!memcmp(data, http_response_1, sizeof http_response_1 - 1))
+      return 1;
+  }
+
+  /* The client always transmits "GET /" followed by at least four
+     characters that are either lowercase hex digits or equals
+     signs, so we need nine bytes of incoming data. */
+  if (evbuffer_get_length(buf) >= 9) {
+    data = evbuffer_pullup(buf, 9);
+    if (!memcmp(data, "GET /", 5) &&
+        (ascii_isxdigit(data[5]) || data[5] == '=') &&
+        (ascii_isxdigit(data[6]) || data[6] == '=') &&
+        (ascii_isxdigit(data[7]) || data[7] == '=') &&
+        (ascii_isxdigit(data[8]) || data[8] == '='))
+      return 1;
+  }
+
+  /* Didn't find either the client or the server pattern. */
+  return 0;
+}
+
+size_t
+x_http_transmit_room(steg_t *s, conn_t *conn)
+{
+  if (s->is_clientside)
+    /* per http://www.boutell.com/newfaq/misc/urllength.html,
+       IE<9 can handle no more than 2048 characters in the path
+       component of a URL; we're not talking to IE, but this limit
+       means longer paths look fishy; we hex-encode the path, so
+       we have to cut the number in half. */
+    return 1024;
+  else
+    /* no practical limit applies */
+    return SIZE_MAX;
+}
+
+int
+x_http_transmit(steg_t *s, struct evbuffer *source, conn_t *conn)
+{
+  struct evbuffer *dest = conn_get_outbound(conn);
+
+  if (s->is_clientside) {
+    /* On the client side, we have to embed the data in a GET query somehow;
+       the only plausible places to put it are the URL and cookies.  This
+       presently uses the URL. And it can't be binary. */
+    struct evbuffer *scratch;
+    struct evbuffer_iovec *iv;
+    int i, nv;
+
+    /* Convert all the data in 'source' to hexadecimal and write it to
+       'scratch'. Data is padded to a multiple of four characters with
+       equals signs. */
+    size_t slen = evbuffer_get_length(source);
+    size_t dlen = slen * 2;
+
+    dlen = dlen + 3 - (dlen-1)%4;
+    if (dlen == 0) dlen = 4;
+
+    scratch = evbuffer_new();
+    if (!scratch) return -1;
+    if (evbuffer_expand(scratch, dlen)) {
+      evbuffer_free(scratch);
+      return -1;
+    }
+
+    nv = evbuffer_peek(source, slen, NULL, NULL, 0);
+    iv = xzalloc(sizeof(struct evbuffer_iovec) * nv);
+    if (evbuffer_peek(source, slen, NULL, iv, nv) != nv) {
+      evbuffer_free(scratch);
+      free(iv);
+      return -1;
+    }
+
+    for (i = 0; i < nv; i++) {
+      const unsigned char *p = iv[i].iov_base;
+      const unsigned char *limit = p + iv[i].iov_len;
+      char hex[2], c;
+      while (p < limit) {
+        c = *p++;
+        hex[0] = "0123456789abcdef"[(c & 0xF0) >> 4];
+        hex[1] = "0123456789abcdef"[(c & 0x0F) >> 0];
+        evbuffer_add(scratch, hex, 2);
+      }
+    }
+    free(iv);
+    while (evbuffer_get_length(scratch) == 0 ||
+           evbuffer_get_length(scratch) % 4 != 0)
+      evbuffer_add(scratch, "=", 1);
+
+    if (evbuffer_add(dest, http_query_1, sizeof http_query_1-1) ||
+        evbuffer_add_buffer(dest, scratch) ||
+        evbuffer_add(dest, http_query_2, sizeof http_query_2-1) ||
+        evbuffer_add(dest, conn->peername, strlen(conn->peername)) ||
+        evbuffer_add(dest, http_query_3, sizeof http_query_3-1)) {
+      evbuffer_free(scratch);
+      return -1;
+    }
+
+    evbuffer_free(scratch);
+    evbuffer_drain(source, slen);
+    conn_cease_transmission(conn);
+    return 0;
+
+  } else {
+    /* On the server side, we just fake up some HTTP response headers and
+       then splat the data we were given. Binary is OK. */
+
+    if (evbuffer_add(dest, http_response_1, sizeof http_response_1-1))
+        return -1;
+    if (evbuffer_add_printf(dest, http_response_2,
+                            (unsigned long)evbuffer_get_length(source)) == -1)
+      return -1;
+    if (evbuffer_add_buffer(dest, source))
+      return -1;
+
+    conn_close_after_transmit(conn);
+    return 0;
+  }
+}
+
+// enum recv_ret
+static int
+x_http_receive(steg_t *s, conn_t *conn, struct evbuffer *dest)
+{
+  struct evbuffer *source = conn_get_inbound(conn);
+  if (s->is_clientside) {
+    /* This loop should not be necessary, but we are currently not
+       enforcing the query-response pattern, so we can get more than
+       one response per request. */
+    do {
+      /* Linearize the buffer out past the longest possible
+         Content-Length header and subsequent blank line.  2**64 fits in
+         20 characters, and then we have two CRLFs; minus one for the
+         NUL in sizeof http_response_1. Note that this does _not_
+         guarantee that that much data is available. */
+
+      unsigned char *data = evbuffer_pullup(source, sizeof http_response_1 + 23);
+      size_t hlen = evbuffer_get_length(source);
+      if (hlen > sizeof http_response_1 + 23)
+        hlen = sizeof http_response_1 + 23;
+
+      /* Validate response headers. */
+      if (hlen < sizeof http_response_1 - 1)
+        return 0; // RECV_INCOMPLETE;
+      if (memcmp(data, http_response_1, sizeof http_response_1 - 1))
+        return -1; // RECV_BAD;
+
+      /* There should be an unsigned number immediately after the text of
+         http_response_1, followed by the four characters \r\n\r\n.
+         We may not have the complete number yet. */
+      unsigned char *p = data + sizeof http_response_1 - 1;
+      unsigned char *limit = data + hlen;
+      uint64_t clen = 0;
+      while (p < limit && '0' <= *p && *p <= '9') {
+        clen = clen*10 + *p - '0';
+        p++;
+      }
+      if (p+4 > limit)
+        return 0; // RECV_INCOMPLETE;
+      if (p[0] != '\r' || p[1] != '\n' || p[2] != '\r' || p[3] != '\n')
+        return -1; // RECV_BAD;
+
+      p += 4;
+      hlen = p - data;
+      /* Now we know how much data we're expecting after the blank line. */
+      if (evbuffer_get_length(source) < hlen + clen)
+        return 0; // RECV_INCOMPLETE;
+
+      /* we are go */
+      if (evbuffer_drain(source, hlen))
+        return -1; // RECV_BAD;
+
+      if (evbuffer_remove_buffer(source, dest, clen) != clen)
+        return -1; // RECV_BAD;
+
+    } while (evbuffer_get_length(source));
+
+    conn_expect_close(conn);
+    return 0; // RECV_GOOD;
+  } else {
+    /* We need a scratch buffer here because the contract is that if
+       we hit a decode error we *don't* write anything to 'dest'. */
+    struct evbuffer *scratch;
+
+    /* This loop should not be necessary either, but is, for the same
+       reason given above */
+    do {
+      /* Search for the second and third invariant bits of the query headers
+         we expect.  We completely ignore the contents of the Host header. */
+      struct evbuffer_ptr s2 = evbuffer_search(source, http_query_2,
+                                               sizeof http_query_2 - 1,
+                                               NULL);
+      if (s2.pos == -1) {
+        log_debug("Did not find second piece of HTTP query");
+        return 0; // RECV_INCOMPLETE;
+      }
+      struct evbuffer_ptr s3 = evbuffer_search(source, http_query_3,
+                                               sizeof http_query_3 - 1,
+                                               &s2);
+      if (s3.pos == -1) {
+        log_debug("Did not find third piece of HTTP query");
+        return 0; // RECV_INCOMPLETE;
+      }
+      obfs_assert(s3.pos + sizeof http_query_3 - 1
+                  <= evbuffer_get_length(source));
+
+      unsigned char *data = evbuffer_pullup(source, s2.pos);
+      if (memcmp(data, "GET /", sizeof "GET /"-1)) {
+        log_debug("Unexpected HTTP verb: %.*s", 5, data);
+        return -1; // RECV_BAD;
+      }
+
+      unsigned char *p = data + sizeof "GET /"-1;
+      unsigned char *limit = data + s2.pos;
+
+      scratch = evbuffer_new();
+      if (!scratch) return -1; // RECV_BAD;
+      if (evbuffer_expand(scratch, (limit - p)/2)) {
+        evbuffer_free(scratch);
+        return -1; // RECV_BAD;
+      }
+
+      unsigned char c, h, secondhalf = 0;
+      while (p < limit) {
+        if (!secondhalf) c = 0;
+        if ('0' <= *p && *p <= '9') h = *p - '0';
+        else if ('a' <= *p && *p <= 'f') h = *p - 'a' + 10;
+        else if ('A' <= *p && *p <= 'F') h = *p - 'A' + 10;
+        else if (*p == '=' && !secondhalf) {
+          p++;
+          continue;
+        } else {
+          evbuffer_free(scratch);
+          log_debug("Decode error: unexpected URI character %c", *p);
+          return -1; // RECV_BAD;
+        }
+
+        c = (c << 4) + h;
+        if (secondhalf)
+          evbuffer_add(scratch, &c, 1);
+        secondhalf = !secondhalf;
+        p++;
+      }
+
+      if (evbuffer_add_buffer(dest, scratch)) {
+        evbuffer_free(scratch);
+        log_debug("Failed to transfer buffer");
+        return -1; // RECV_BAD;
+      }
+      evbuffer_drain(source, s3.pos + sizeof http_query_3 - 1);
+      evbuffer_free(scratch);
+
+    } while (evbuffer_get_length(source));
+
+    conn_transmit_soon(conn, 100);
+    return 0; // RECV_GOOD;
+  }
+}
diff --git a/src/steg/x_http2.c b/src/steg/x_http2.c
new file mode 100644
index 0000000..0710fa7
--- /dev/null
+++ b/src/steg/x_http2.c
@@ -0,0 +1,700 @@
+/*  Copyright (c) 2011, SRI International
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+    * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+
+    * Neither the names of the copyright owners nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+    Contributors: Zack Weinberg, Vinod Yegneswaran
+    See LICENSE for other credits and copying information
+*/
+
+
+
+#include "util.h"
+#include "connections.h"
+#include "steg.h"
+#include "payloads.h"
+#include "cookies.h"
+#include "swfSteg.h"
+#include "pdfSteg.h"
+#include "jsSteg.h"
+
+#include <event2/buffer.h>
+#include <stdio.h>
+
+
+
+
+#define MIN_COOKIE_SIZE 128
+#define MAX_COOKIE_SIZE 2048
+
+
+int 
+x_http2_server_receive(steg_t *s, conn_t *conn, struct evbuffer *dest, struct evbuffer* source);
+
+int
+lookup_peer_name_from_ip(char* p_ip, char* p_name);
+
+
+static int has_peer_name = 0;
+static char peername[512];
+
+
+struct x_http2_steg_t
+{
+  steg_t super;
+  
+  int have_transmitted;
+  int have_received;
+  int type;
+};
+
+
+STEG_DEFINE_MODULE(x_http2,
+                   1024,  /* client-server max data rate - made up */
+                   10240, /* server-client max data rate - ditto */
+                   1,     /* max concurrent connections per IP */
+                   1);     /* max concurrent IPs */
+
+
+
+
+
+
+int x_http2_client_transmit (steg_t *s, struct evbuffer *source, conn_t *conn);
+
+void evbuffer_dump(struct evbuffer *buf, FILE *out);
+void buf_dump(unsigned char* buf, int len, FILE *out);
+
+
+
+void 
+evbuffer_dump(struct evbuffer *buf, FILE *out) 
+{
+  int nextent = evbuffer_peek(buf, SSIZE_MAX, 0, 0, 0);
+  struct evbuffer_iovec v[nextent];
+  int i;
+  const unsigned char *p, *limit;
+  
+  if (evbuffer_peek(buf, -1, 0, v, nextent) != nextent)
+    abort();
+  
+  for (i = 0; i < nextent; i++) {
+    p = v[i].iov_base;
+    limit = p + v[i].iov_len;
+    
+    putc('|', out);
+    while (p < limit) {
+      if (*p < 0x20 || *p >= 0x7F || *p == '\\' || *p == '|')
+	fprintf(out, "\\x%02x", *p);
+      else
+	putc(*p, out);
+      p++;
+    }
+  }
+  putc('|', out);
+}
+
+
+
+
+
+void 
+buf_dump(unsigned char* buf, int len, FILE *out) 
+{
+  int i=0;
+  putc('|', out);
+  while (i < len) {
+    if (buf[i] < 0x20 || buf[i] >= 0x7F || buf[i] == '\\' || buf[i]== '|')
+      fprintf(out, "\\x%02x", buf[i]);
+    else
+      putc(buf[i], out);
+    i++;
+  }
+  putc('|', out);
+  putc('\n', out);
+}
+
+
+
+
+
+steg_t * 
+x_http2_new(rng_t *rng, unsigned int is_clientside)
+{
+
+  STEG_NEW(x_http2, state, rng, is_clientside);
+
+  if (is_clientside)
+    load_payloads("traces/client.out");
+  else {
+    load_payloads("traces/server.out");
+    init_JS_payload_pool(HTTP_MSG_BUF_SIZE, TYPE_HTTP_RESPONSE, JS_MIN_AVAIL_SIZE);
+    //    init_PDF_payload_pool(HTTP_MSG_BUF_SIZE, TYPE_HTTP_RESPONSE, PDF_MIN_AVAIL_SIZE);
+    init_SWF_payload_pool(HTTP_MSG_BUF_SIZE, TYPE_HTTP_RESPONSE, 0);
+  }
+
+
+  /* if there were extra stuff to fill in, you would do it here */
+  return upcast_steg(state);
+}
+
+void
+x_http2_del(steg_t *s)
+{
+  x_http2_steg_t *state = downcast_steg(s);
+
+  STEG_DEL(s);
+
+  /* if there were extra stuff to deallocate, you would do it here */
+  free(state);
+}
+
+
+// x_http2_detect determines if a packet should be processed by the http2 steg module 
+unsigned int
+x_http2_detect(conn_t *conn)
+{
+  struct evbuffer *buf = conn_get_inbound(conn);
+  unsigned char *data;
+
+  // return 0;
+/*****
+ Here is a list of HTTP response codes extracted from the server-portals.out trace
+
+7369 HTTP/1.1 200 OK
+ 470 HTTP/1.1 302 Found
+ 350 HTTP/1.1 304 Not Modified
+ 212 HTTP/1.1 302 Moved Temporarily
+ 184 HTTP/1.1 204 No Content
+ 451 HTTP/1.0 200 OK
+  36 HTTP/1.0 204 No Content
+  21 HTTP/1.1 301 Moved Permanently
+  19 HTTP/1.1 302 Object moved
+  15 HTTP/1.1 404 Not Found
+
+   7 HTTP/1.0 304 Not Modified
+   6 HTTP/1.1 302 Redirect
+   3 HTTP/1.0 200 Ok
+   2 HTTP/1.1 303 Object Moved
+   2 HTTP/1.0 301 Moved Permanently
+   2 HTTP/1.0 302 Moved Temporarily
+   2 HTTP/1.0 400 Bad request
+   2 HTTP/1.0 403 Forbidden
+   1 HTTP/1.0 404 Not Found
+   1 HTTP/1.1 200
+   1 HTTP/1.1 302 FOUND
+   1 HTTP/1.1 304
+   1 HTTP/1.1 400 Bad Request
+   1 HTTP/1.1 403 Forbidden
+   1 HTTP/1.1 503 Service Unavailable.
+ *****/
+
+  // The first part of a valid HTTP response should be of the form
+  // HTTP/1.x nnn
+
+  if (evbuffer_get_length(buf) >= 12) {
+    data = evbuffer_pullup(buf, 12);
+    
+    if (data != NULL &&
+         ((!memcmp(data, "HTTP/1.1 200", 12)) ||
+          (!memcmp(data, "HTTP/1.1 302", 12)) ||
+          (!memcmp(data, "HTTP/1.1 304", 12)) ||
+          (!memcmp(data, "HTTP/1.1 204", 12)) ||
+          (!memcmp(data, "HTTP/1.0 200", 12)) ||
+          (!memcmp(data, "HTTP/1.0 204", 12)) ||
+          (!memcmp(data, "HTTP/1.1 301", 12)) ||
+          (!memcmp(data, "HTTP/1.1 302", 12)) ||
+          (!memcmp(data, "HTTP/1.1 404", 12)))) {
+      log_debug("x_http2_detect: valid response");
+      return 1;
+    }
+  }
+
+
+
+
+
+  // SC: if we are only interested in jsSteg, we may want to
+  // consider HTTP/1.1 and HTTP/1.0 responses whose code is 200 only
+
+  // check to see if this is a valid HTTP request
+  //
+  // the following is for HTTP requests used by the http2 steg module
+  // The client always transmits "GET /" followed by at least four
+  // characters that are either lowercase hex digits or equals
+  // signs, so we need nine bytes of incoming data.
+
+
+
+  if (evbuffer_get_length(buf) >= 9) {
+    data = evbuffer_pullup(buf, 9);
+    if (data != NULL && (!memcmp(data, "GET /", 5) || !memcmp(data, "POST /", 5) || !memcmp(data, "Cookie", 6))) {
+      log_debug("x_http2_detect: valid request");
+      return 1;
+    }
+  }
+ 
+  log_debug("x_http2_detect: didn't find either HTTP request or response");
+  /* Didn't find either the client or the server pattern. */
+  return 0;
+}
+
+size_t
+x_http2_transmit_room(steg_t *s, conn_t *conn)
+{
+  unsigned int mjc;
+
+  if (downcast_steg(s)->have_transmitted)
+    /* can't send any more on this connection */
+    return 0;
+  
+
+  if (s->is_clientside) {
+    /* per http://www.boutell.com/newfaq/misc/urllength.html,
+       IE<9 can handle no more than 2048 characters in the path
+       component of a URL; we're not talking to IE, but this limit
+       means longer paths look fishy; we hex-encode the path, so
+       we have to cut the number in half. */
+    return (MIN_COOKIE_SIZE + rand() % (MAX_COOKIE_SIZE - MIN_COOKIE_SIZE)) / 4;
+    // return 1024;
+  } 
+  else {
+
+    if (!downcast_steg(s)->have_received)
+      return 0;
+
+    switch(downcast_steg(s)->type) {
+
+    case HTTP_CONTENT_SWF:
+      return 1024;
+
+    case HTTP_CONTENT_JAVASCRIPT:
+      mjc = get_max_JS_capacity() / 2;
+      if (mjc > 1024) {
+	// it should be 1024 + ...., but seems like we need to be a little bit smaller (chopper bug?)
+	int rval = 512 + rand()%(mjc - 1024);  
+	//	fprintf(stderr, "returning rval %d, mjc  %d\n", rval, mjc);
+	return rval;
+      }
+      log_warn("js capacity too small\n");
+      exit(-1);
+
+    case HTTP_CONTENT_PDF:
+      // return 1024 + rand()%(get_max_PDF_capacity() - 1024)
+      return PDF_MIN_AVAIL_SIZE;
+    }
+
+    return SIZE_MAX;
+  }
+}
+
+
+
+
+
+
+int
+lookup_peer_name_from_ip(char* p_ip, char* p_name)  {
+  struct addrinfo* ailist;
+  struct addrinfo* aip;
+  struct addrinfo hint;
+  char buf[128];
+     
+  hint.ai_flags = AI_CANONNAME;
+  hint.ai_family = 0;
+  hint.ai_socktype = 0;
+  hint.ai_protocol = 0;
+  hint.ai_addrlen = 0;
+  hint.ai_canonname = NULL;
+  hint.ai_addr = NULL;
+  hint.ai_next = NULL;
+  
+  strcpy(buf, p_ip);
+  buf[strchr(buf, ':') - buf] = 0;
+
+  
+  if (getaddrinfo(buf, NULL, &hint, &ailist)) {
+    fprintf(stderr, "error: getaddrinfo() %s\n", p_ip);
+    exit(1);
+  }
+ 
+  for (aip = ailist; aip != NULL; aip = aip->ai_next) {
+    char buf[512];
+    if (getnameinfo(aip->ai_addr, sizeof(struct sockaddr), buf, 512, NULL, 0, 0) == 0) {
+      sprintf(p_name, "%s", buf);
+      return 1;
+    }
+  }
+  
+  return 0;
+}
+
+
+
+
+
+
+
+
+int 
+x_http2_client_transmit (steg_t *s, struct evbuffer *source, conn_t *conn) {
+
+  /* On the client side, we have to embed the data in a GET query somehow;
+     the only plausible places to put it are the URL and cookies.  This
+     presently uses the URL. And it can't be binary. */
+  // struct evbuffer *scratch;
+  struct evbuffer_iovec *iv;
+  int i, nv;
+  struct evbuffer *dest = conn_get_outbound(conn);
+  size_t sbuflen = evbuffer_get_length(source);
+  char buf[10000];
+  unsigned char data[(int) sbuflen*2];
+  //  unsigned char outbuf[MAX_COOKIE_SIZE];
+
+  unsigned char outbuf[(int) sbuflen*8];
+  int datalen;
+
+
+  //  size_t sofar = 0;
+  size_t cookie_len;
+  
+
+  /* Convert all the data in 'source' to hexadecimal and write it to
+     'scratch'. Data is padded to a multiple of four characters with
+     equals signs. */
+
+
+  unsigned int len = 0;
+  unsigned int cnt = 0;
+
+
+
+  datalen = 0;    
+  cookie_len = 4 * sbuflen + rand() % 4;
+  
+
+  nv = evbuffer_peek(source, sbuflen, NULL, NULL, 0);
+  iv = xzalloc(sizeof(struct evbuffer_iovec) * nv);
+
+  if (evbuffer_peek(source, sbuflen, NULL, iv, nv) != nv) {
+    free(iv);
+    return -1;
+  }
+
+  // retry up to 10 times
+  while (!len) {
+    len = find_client_payload(buf, sizeof(buf), TYPE_HTTP_REQUEST);
+    if (cnt++ == 10) return -1;
+  }
+
+
+  if (has_peer_name == 0 && lookup_peer_name_from_ip((char*) conn->peername, peername))
+    has_peer_name = 1;
+
+  // if (find_uri_type(buf) != HTTP_CONTENT_SWF) {
+  //   fprintf(stderr, "%s\n", buf);
+  //   exit(-1);
+  // }
+    
+
+  
+  cnt = 0;
+  
+  for (i = 0; i < nv; i++) {
+    const unsigned char *p = iv[i].iov_base;
+    const unsigned char *limit = p + iv[i].iov_len;
+    char c;
+    while (p < limit && cnt < sbuflen) {
+      c = *p++;
+      data[datalen] = "0123456789abcdef"[(c & 0xF0) >> 4];
+      data[datalen+1] = "0123456789abcdef"[(c & 0x0F) >> 0];
+      datalen += 2;
+      cnt++;
+    }
+  }
+  
+  free(iv);
+
+  if (cookie_len < 4) cookie_len = 4;
+
+  datalen = gen_cookie_field(outbuf, cookie_len, data, datalen);
+  log_debug("CLIENT: sending cookie of length = %d %d\n", datalen, (int) cookie_len);
+  //  fprintf(stderr, "CLIENT: sending cookie of length = %d %d\n", datalen, (int) cookie_len);
+
+  if (datalen < 0) {
+    log_debug("cookie generation failed\n");
+    return -1;
+  }
+
+
+  if (evbuffer_add(dest, buf, strstr(buf, "\r\n") - buf + 2)  ||  // add uri field
+      evbuffer_add(dest, "Host: ", 6) ||
+      evbuffer_add(dest, peername, strlen(peername)) ||
+      evbuffer_add(dest, strstr(buf, "\r\n"), len - (unsigned int) (strstr(buf, "\r\n") - buf))  ||  // add everything but first line
+      evbuffer_add(dest, "Cookie: ", 8) ||
+      evbuffer_add(dest, outbuf, cookie_len) ||
+      evbuffer_add(dest, "\r\n\r\n", 4)) {
+      log_debug("error ***********************");
+      return -1;
+    }
+  
+  //  sofar += datalen/2;
+  evbuffer_drain(source, datalen/2);
+  
+  log_debug("CLIENT TRANSMITTED payload %d\n", (int) sbuflen);
+  
+  conn_cease_transmission(conn);
+
+  downcast_steg(s)->type = find_uri_type(buf);
+  downcast_steg(s)->have_transmitted = 1;
+  return 0;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+int
+x_http2_transmit(steg_t *s, struct evbuffer *source, conn_t *conn)
+{
+  //  struct evbuffer *dest = conn_get_outbound(conn);
+
+  //  fprintf(stderr, "in x_http2_ transmit %d\n", downcast_steg(s)->type);
+    
+
+
+  if (s->is_clientside) {
+        /* On the client side, we have to embed the data in a GET query somehow;
+       the only plausible places to put it are the URL and cookies.  This
+       presently uses the URL. And it can't be binary. */
+
+    return x_http2_client_transmit(s, source, conn); //@@
+  } 
+  else {
+    int rval = -1;
+    switch(downcast_steg(s)->type) {
+      
+    case HTTP_CONTENT_SWF: 
+      rval = x_http2_server_SWF_transmit(s, source, conn);
+      break;
+    case HTTP_CONTENT_JAVASCRIPT:
+      rval = x_http2_server_JS_transmit(s, source, conn);
+      break;
+
+    case HTTP_CONTENT_PDF:
+      rval = x_http2_server_PDF_transmit(s, source, conn);
+      break;
+    }
+
+    if (rval == 0) downcast_steg(s)->have_transmitted = 1;
+    return rval;
+  }
+}
+
+
+
+
+
+
+int 
+x_http2_server_receive(steg_t *s, conn_t *conn, struct evbuffer *dest, struct evbuffer* source) {
+
+  int cnt = 0;
+  unsigned char* data;
+  int type;
+
+  do {
+    struct evbuffer_ptr s2 = evbuffer_search(source, "\r\n\r\n", sizeof ("\r\n\r\n") -1 , NULL);
+    unsigned char* limit;
+    unsigned char *p;
+    int unwrapped_cookie_len;
+    struct evbuffer *scratch;
+    unsigned char c, h, secondhalf;
+    unsigned char buf[evbuffer_get_length(source)];
+
+
+    if (s2.pos == -1) {
+      log_debug("Did not find end of request %d", (int) evbuffer_get_length(source));
+      //      evbuffer_dump(source, stderr);
+      return RECV_INCOMPLETE;
+    }
+
+    log_debug("SERVER received request header of length %d", (int)s2.pos);
+
+    data = evbuffer_pullup(source, s2.pos);
+    if (data == NULL) {
+      log_debug("SERVER evbuffer_pullup fails");
+      return RECV_BAD;
+    }
+
+    limit = data + s2.pos;
+
+    type = find_uri_type((char *)data);
+    log_warn ("*** Got type %d", type);
+
+    /*    if (type != 3) {
+	  fprintf(stderr, "type != 3, %d, data = %s \n", find_uri_type2((char *) data), data);
+	  exit(-1);
+	  }*/
+
+    data = (unsigned char*) strstr((char*) data, "Cookie:");
+
+    if (data == NULL || memcmp(data, "Cookie:", sizeof "Cookie:"-1)) {
+      log_debug("Unexpected HTTP verb: %.*s", 5, data);
+      return RECV_BAD;
+    }
+
+    p = data + sizeof "Cookie: "-1;
+    unwrapped_cookie_len = unwrap_cookie(p, buf, (int) (limit - p));
+
+    log_debug("SERVER: received cookie of length = %d %d\n", unwrapped_cookie_len, (int) (limit-p));
+    //    buf_dump(buf, unwrapped_cookie_len, stderr);
+    //    fprintf(stderr, "==========================\n");
+    //    buf_dump(p, (int) (limit-p), stderr);
+
+    
+    //    log_debug("hello SERVER received %d cnt = %d\n", (int) (limit - p), cnt);
+    //     buf_dump(p, (int) (limit-p), stderr);
+
+    /* We need a scratch buffer here because the contract is that if
+       we hit a decode error we *don't* write anything to 'dest'. */
+    scratch = evbuffer_new();
+
+    if (!scratch) return RECV_BAD;
+
+
+    if (evbuffer_expand(scratch, unwrapped_cookie_len/2)) {
+      log_debug("Evbuffer expand failed \n");
+      evbuffer_free(scratch);
+      return RECV_BAD;
+    }
+    p = buf;
+
+
+    secondhalf = 0;
+    while ((int) (p - buf) < unwrapped_cookie_len) {
+      if (!secondhalf) c = 0;
+      if ('0' <= *p && *p <= '9') h = *p - '0';
+      else if ('a' <= *p && *p <= 'f') h = *p - 'a' + 10;
+      else if ('A' <= *p && *p <= 'F') h = *p - 'A' + 10;
+      else if (*p == '=' && !secondhalf) {
+	p++;
+	continue;
+      } else {
+	evbuffer_free(scratch);
+	log_debug("Decode error: unexpected URI characterasdfaf %d", *p);
+	return RECV_BAD;
+      }
+
+      c = (c << 4) + h;
+      if (secondhalf) {
+	evbuffer_add(scratch, &c, 1);
+	//	log_debug("adding to scratch");
+	cnt++;
+      }
+      secondhalf = !secondhalf;
+      p++;
+    }
+
+
+
+    if (evbuffer_add_buffer(dest, scratch)) {
+      evbuffer_free(scratch);
+      log_debug("Failed to transfer buffer");
+      return RECV_BAD;
+    } 
+    evbuffer_drain(source, s2.pos + sizeof("\r\n\r\n") - 1);
+    evbuffer_free(scratch);
+  } while (evbuffer_get_length(source));
+  
+
+  downcast_steg(s)->have_received = 1;
+  downcast_steg(s)->type = type;
+  //  fprintf(stderr, "SERVER RECEIVED payload %d %d\n", cnt, type);
+    
+  conn_transmit_soon(conn, 100);
+  return RECV_GOOD;
+}
+
+
+
+
+
+
+
+
+static int
+x_http2_receive(steg_t *s, conn_t *conn, struct evbuffer *dest)
+{
+  struct evbuffer *source = conn_get_inbound(conn);
+  // unsigned int type;
+  int rval = RECV_BAD;
+
+
+  if (s->is_clientside) {
+
+    //    fprintf(stderr, "client type = %d\n", downcast_steg(s)->type);
+
+    switch(downcast_steg(s)->type) {
+      
+    case HTTP_CONTENT_SWF: 
+      rval = x_http2_handle_client_SWF_receive(s, conn, dest, source);
+      break;
+    case HTTP_CONTENT_JAVASCRIPT:
+      rval = x_http2_handle_client_JS_receive(s, conn, dest, source);
+      break;
+
+    case HTTP_CONTENT_PDF:
+      rval = x_http2_handle_client_PDF_receive(s, conn, dest, source);
+      break;
+    }
+
+    if (rval == RECV_GOOD) downcast_steg(s)->have_received = 1;
+    return rval;
+
+  } else {
+    return x_http2_server_receive(s, conn, dest, source);
+  }
+
+   
+}
diff --git a/src/steg/zpack.c b/src/steg/zpack.c
new file mode 100644
index 0000000..57cd2b2
--- /dev/null
+++ b/src/steg/zpack.c
@@ -0,0 +1,408 @@
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <time.h>
+#include <stdlib.h>
+#include "zlib.h"
+#include "zpack.h"
+
+
+#if defined(MSDOS) || defined(OS2) || defined(WIN32) || defined(__CYGWIN__)
+#  include <fcntl.h>
+#  include <io.h>
+#  define SET_BINARY_MODE(file) setmode(fileno(file), O_BINARY)
+#else
+#  define SET_BINARY_MODE(file)
+#endif
+
+#define CHUNK 16384
+
+/* Compress from file source to file dest until EOF on source.
+   def() returns Z_OK on success, Z_MEM_ERROR if memory could not be
+   allocated for processing, Z_STREAM_ERROR if an invalid compression
+   level is supplied, Z_VERSION_ERROR if the version of zlib.h and the
+   version of the library linked do not match, or Z_ERRNO if there is
+   an error reading or writing the files. */
+
+
+int def(char *source, int slen, char *dest, int dlen, int level)
+{
+  int ret, flush;
+  unsigned have;
+  z_stream strm;
+  unsigned char in[CHUNK];
+  unsigned char out[CHUNK];
+  int dlen_orig = dlen;
+
+  /* allocate deflate state */
+  strm.zalloc = Z_NULL;
+  strm.zfree = Z_NULL;
+  strm.opaque = Z_NULL;
+  ret = deflateInit(&strm, level);
+  if (ret != Z_OK)
+    return ret;
+
+  /* compress until end of file */
+  do {
+
+    if (slen > CHUNK)
+      strm.avail_in = CHUNK;
+    else
+      strm.avail_in = slen;
+
+    memcpy (in, source, strm.avail_in);
+    slen = slen - strm.avail_in;
+    source - source + strm.avail_in;
+
+    flush = (slen == 0) ? Z_FINISH : Z_NO_FLUSH;
+    strm.next_in = in;
+
+    /* run deflate() on input until output buffer not full, finish
+       compression if all of source has been read in */
+    do {
+      strm.avail_out = CHUNK;
+      strm.next_out = out;
+      ret = deflate(&strm, flush);    /* no bad return value */
+      assert(ret != Z_STREAM_ERROR);  /* state not clobbered */
+      have = CHUNK - strm.avail_out;
+
+      if ((unsigned int) dlen < have) {
+	fprintf(stderr, "dest buf too small!\n");
+	return Z_ERRNO;
+      }
+
+      memcpy(dest, out, have);
+      dest += have;
+      dlen = dlen - have;
+    } while (strm.avail_out == 0);
+    assert(strm.avail_in == 0);     /* all input will be used */
+
+    /* done when last data in file processed */
+  } while (flush != Z_FINISH);
+  assert(ret == Z_STREAM_END);        /* stream will be complete */
+
+  /* clean up and return */
+  (void)deflateEnd(&strm);
+
+  printf("hello here...\n");
+  return (dlen_orig - dlen);
+  //  return Z_OK;
+}
+
+/* Decompress from file source to file dest until stream ends or EOF.
+   inf() returns Z_OK on success, Z_MEM_ERROR if memory could not be
+   allocated for processing, Z_DATA_ERROR if the deflate data is
+   invalid or incomplete, Z_VERSION_ERROR if the version of zlib.h and
+   the version of the library linked do not match, or Z_ERRNO if there
+   is an error reading or writing the files. */
+
+
+
+
+int inf(char *source, int slen, char *dest, int dlen)
+{
+  int ret;
+  unsigned have;
+  z_stream strm;
+  unsigned char in[CHUNK];
+  unsigned char out[CHUNK];
+  int dlen_orig = dlen;
+
+
+  /* allocate inflate state */
+  strm.zalloc = Z_NULL;
+  strm.zfree = Z_NULL;
+  strm.opaque = Z_NULL;
+  strm.avail_in = 0;
+  strm.next_in = Z_NULL;
+  ret = inflateInit(&strm);
+  if (ret != Z_OK)
+    return ret;
+
+  /* decompress until deflate stream ends or end of file */
+  do {
+
+    if (slen == 0)
+      break;
+	
+    if (slen > CHUNK)
+      strm.avail_in = CHUNK;
+    else
+      strm.avail_in = slen;
+
+    memcpy(in, source, strm.avail_in);
+    slen = slen - strm.avail_in;
+    source = source + strm.avail_in;
+
+
+      
+    strm.next_in = in;
+
+    /* run inflate() on input until output buffer not full */
+    do {
+      strm.avail_out = CHUNK;
+      strm.next_out = out;
+      ret = inflate(&strm, Z_NO_FLUSH);
+      assert(ret != Z_STREAM_ERROR);  /* state not clobbered */
+      switch (ret) {
+      case Z_NEED_DICT:
+	ret = Z_DATA_ERROR;     /* and fall through */
+      case Z_DATA_ERROR:
+      case Z_MEM_ERROR:
+	(void)inflateEnd(&strm);
+	return ret;
+      }
+      have = CHUNK - strm.avail_out;
+
+
+      if ((unsigned int) dlen < have) {
+	fprintf(stderr, "dest buf too small!\n");
+	return Z_ERRNO;
+      }
+
+      memcpy(dest, out, have);
+      dest += have;
+      dlen = dlen - have;
+
+    } while (strm.avail_out == 0);
+
+    /* done when inflate() says it's done */
+  } while (ret != Z_STREAM_END);
+
+  /* clean up and return */
+  (void)inflateEnd(&strm);
+
+  if (ret == Z_STREAM_END)
+    return dlen_orig - dlen;
+  return Z_DATA_ERROR;
+}
+
+/* report a zlib or i/o error */
+void zerr(int ret)
+
+{
+  fputs("zpipe: ", stderr);
+  switch (ret) {
+  case Z_ERRNO:
+    if (ferror(stdin))
+      fputs("error reading stdin\n", stderr);
+    if (ferror(stdout))
+      fputs("error writing stdout\n", stderr);
+    break;
+  case Z_STREAM_ERROR:
+    fputs("invalid compression level\n", stderr);
+    break;
+  case Z_DATA_ERROR:
+    fputs("invalid or incomplete deflate data\n", stderr);
+    break;
+  case Z_MEM_ERROR:
+    fputs("out of memory\n", stderr);
+    break;
+  case Z_VERSION_ERROR:
+    fputs("zlib version mismatch!\n", stderr);
+  }
+}
+
+
+
+
+
+
+
+
+/* assumes that we know there is exactly 10 bytes of gzip header */
+
+int gzInflate(char *source, int slen, char *dest, int dlen)
+{
+  int ret;
+  unsigned have;
+  z_stream strm;
+  unsigned char in[CHUNK];
+  unsigned char out[CHUNK];
+  int dlen_orig = dlen;
+
+
+  /* allocate inflate state */
+  strm.zalloc = Z_NULL;
+  strm.zfree = Z_NULL;
+  strm.opaque = Z_NULL;
+  strm.avail_in = 0;
+  strm.next_in = Z_NULL;
+
+
+  ret = inflateInit2(&strm, -MAX_WBITS);
+  if (ret != Z_OK)
+    return ret;
+
+  source = source + 10;
+  slen -= 10;
+
+  /* decompress until deflate stream ends or end of file */
+  do {
+
+    if (slen == 0)
+      break;
+	
+    if (slen > CHUNK)
+      strm.avail_in = CHUNK;
+    else
+      strm.avail_in = slen;
+
+    memcpy(in, source, strm.avail_in);
+    slen = slen - strm.avail_in;
+    source = source + strm.avail_in;
+
+
+      
+    strm.next_in = in;
+
+    /* run inflate() on input until output buffer not full */
+    do {
+      strm.avail_out = CHUNK;
+      strm.next_out = out;
+      ret = inflate(&strm, Z_NO_FLUSH);
+      assert(ret != Z_STREAM_ERROR);  /* state not clobbered */
+      switch (ret) {
+      case Z_NEED_DICT:
+	ret = Z_DATA_ERROR;     /* and fall through */
+      case Z_DATA_ERROR:
+      case Z_MEM_ERROR:
+	(void)inflateEnd(&strm);
+	return ret;
+      }
+      have = CHUNK - strm.avail_out;
+
+      if ((unsigned int) dlen < have) {
+	fprintf(stderr, "dest buf too small!\n");
+	return Z_ERRNO;
+      }
+
+      memcpy(dest, out, have);
+      dest += have;
+      dlen = dlen - have;
+
+    } while (strm.avail_out == 0);
+
+    /* done when inflate() says it's done */
+  } while (ret != Z_STREAM_END);
+
+  /* clean up and return */
+  (void)inflateEnd(&strm);
+
+  if (ret == Z_STREAM_END)
+    return dlen_orig - dlen;
+  return Z_DATA_ERROR;
+}
+
+
+
+
+
+
+
+int gzDeflate(char* start, off_t insz, char *buf, off_t outsz, time_t mtime) {
+  unsigned char *c;
+  unsigned long crc;
+  z_stream z;
+
+  z.zalloc = Z_NULL;
+  z.zfree = Z_NULL;
+  z.opaque = Z_NULL;
+
+  if (Z_OK != deflateInit2(&z,
+			   Z_DEFAULT_COMPRESSION,
+			   Z_DEFLATED,
+			   -MAX_WBITS,  /* supress zlib-header */
+			   8,
+			   Z_DEFAULT_STRATEGY)) {
+    return -1;
+  }
+
+  z.next_in = (unsigned char *)start;
+  z.avail_in = insz;
+  z.total_in = 0;
+
+
+  /* write gzip header */
+
+  c = (unsigned char *) buf;
+  c[0] = 0x1f;
+  c[1] = 0x8b;
+  c[2] = Z_DEFLATED;
+  c[3] = 0; /* options */
+  c[4] = (mtime >>  0) & 0xff;
+  c[5] = (mtime >>  8) & 0xff;
+  c[6] = (mtime >> 16) & 0xff;
+  c[7] = (mtime >> 24) & 0xff;
+  c[8] = 0x00; /* extra flags */
+  c[9] = 0x03; /* UNIX */
+
+  z.next_out = c + 10;
+  z.avail_out = outsz - 10 - 8;
+  z.total_out = 0;
+
+  if (Z_STREAM_END != deflate(&z, Z_FINISH)) {
+    deflateEnd(&z);
+    return -1;
+  }
+
+
+  crc = generate_crc32c(start, insz);
+
+  c = (unsigned char *)buf + 10 + z.total_out; 
+
+  c[0] = (crc >>  0) & 0xff;
+  c[1] = (crc >>  8) & 0xff;
+  c[2] = (crc >> 16) & 0xff;
+  c[3] = (crc >> 24) & 0xff;
+  c[4] = (z.total_in >>  0) & 0xff;
+  c[5] = (z.total_in >>  8) & 0xff;
+  c[6] = (z.total_in >> 16) & 0xff;
+  c[7] = (z.total_in >> 24) & 0xff;
+
+
+
+  if (Z_OK != deflateEnd(&z)) {
+    return -1;
+  }
+
+  return 10 + z.total_out + 8;
+
+}
+
+
+
+
+
+/* compress or decompress from stdin to stdout */
+/* int main(int argc, char **argv) */
+/* { */
+/*   int ret; */
+/*   char buf1[32] = "abcasdfadfadfadf23fasdfa23sdfsdf"; */
+/*   char buf2[100]; */
+/*   char buf3[100]; */
+/*   int i; */
+
+/*   bzero(buf2, sizeof(buf2)); */
+/*   bzero(buf3, sizeof(buf3)); */
+  
+
+/*   //  ret = def(buf1, 3, buf2, 100,  Z_DEFAULT_COMPRESSION); */
+/*   ret = gzDeflate(buf1, sizeof(buf1), buf2, sizeof(buf2), time(NULL)); */
+/*   if (ret <= 0) */
+/*     zerr(ret); */
+
+/*   /\*  for (i=0; i < ret; i++) */
+/*     putc(buf2[i], stdout); */
+/*   *\/ */
+
+
+/*   //  printf("len = %d\n", ret); */
+
+/*   ret = gzInflate(buf2, ret, buf3, 100); */
+/*   if (ret <= 0) */
+/*     zerr(ret); */
+/*   printf("hello %s\n", buf3); */
+
+
+/* } */
diff --git a/src/steg/zpack.h b/src/steg/zpack.h
new file mode 100644
index 0000000..65cd28d
--- /dev/null
+++ b/src/steg/zpack.h
@@ -0,0 +1,14 @@
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <time.h>
+#include <stdlib.h>
+#include "zlib.h"
+
+
+int def(char *source, int slen, char *dest, int dlen, int level);
+int inf(char *source, int slen, char *dest, int dlen);
+void zerr(int ret);
+int gzInflate(char *source, int slen, char *dest, int dlen);
+int gzDeflate(char* start, off_t insz, char *buf, off_t outsz, time_t mtime);
+unsigned int generate_crc32c(char *buffer, size_t length);
diff --git a/start-client.csh b/start-client.csh
new file mode 100644
index 0000000..65f4465
--- /dev/null
+++ b/start-client.csh
@@ -0,0 +1,8 @@
+#!/bin/csh
+# ./obfsproxy --log-min-severity=debug x_dsteg socks 127.0.0.1:1080 x_http
+
+setenv EVENT_NOKQUEUE yes 
+#./obfsproxy --log-min-severity=debug chop socks 127.0.0.1:1080 127.0.0.1:8080 x_http2 127.0.0.1:8081 x_http2
+# ./obfsproxy --log-min-severity=warn chop socks 127.0.0.1:1080 127.0.0.1:8080 x_http2 127.0.0.1:8081 x_http2
+./obfsproxy --log-min-severity=error chop socks 127.0.0.1:1080 127.0.0.1:8080 x_http2 127.0.0.1:8081 x_http2
+
diff --git a/start-server.csh b/start-server.csh
new file mode 100644
index 0000000..50b781a
--- /dev/null
+++ b/start-server.csh
@@ -0,0 +1,6 @@
+#!/bin/csh
+setenv EVENT_NOKQUEUE yes 
+#./obfsproxy --log-min-severity=debug chop server 87.73.82.145:8080 127.0.0.1:8080 127.0.0.1:8081
+# ./obfsproxy --log-min-severity=warn chop server 87.73.82.145:8080 127.0.0.1:8080 127.0.0.1:8081
+./obfsproxy --log-min-severity=error chop server 87.73.82.145:8080 127.0.0.1:8080 127.0.0.1:8081
+
diff --git a/torrc b/torrc
new file mode 100644
index 0000000..ff27e61
--- /dev/null
+++ b/torrc
@@ -0,0 +1,12 @@
+SocksPort 9060 # what port to open for local application connections
+SocksListenAddress 127.0.0.1 # accept connections only from localhost
+
+SafeLogging 0
+Log info file ./info.log
+Log debug file ./debug.log
+
+Socks4Proxy 127.0.0.1:1080
+
+# Bridge 87.73.82.145:8080
+Bridge 127.0.0.1:8080
+UseBridges 1





More information about the tor-commits mailing list