[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