commit 0382170797b7ddadfd91b292d35da0a08627e57e Author: Zack Weinberg zackw@cmu.edu Date: Thu Dec 1 23:30:00 2011 +0000
Renames after the merge; drop the old x_http. --- Makefile.am | 17 +- run-autogen.csh | 3 - src/steg/cookies.c | 234 ------- src/steg/cookies.cc | 234 +++++++ src/steg/crc32.c | 82 --- src/steg/crc32.cc | 82 +++ src/steg/http.cc | 857 +++++++++++++++++++++++++ src/steg/jsSteg.c | 1163 --------------------------------- src/steg/jsSteg.cc | 1163 +++++++++++++++++++++++++++++++++ src/steg/payloads.c | 1669 ------------------------------------------------ src/steg/payloads.cc | 1669 ++++++++++++++++++++++++++++++++++++++++++++++++ src/steg/pdfSteg.c | 630 ------------------ src/steg/pdfSteg.cc | 630 ++++++++++++++++++ src/steg/swfSteg.c | 282 -------- src/steg/swfSteg.c.old | 264 -------- src/steg/swfSteg.cc | 282 ++++++++ src/steg/x_http.c.old | 337 ---------- src/steg/x_http.cc | 360 ----------- src/steg/x_http2.c | 857 ------------------------- src/steg/zpack.c | 408 ------------ src/steg/zpack.cc | 408 ++++++++++++ 21 files changed, 5333 insertions(+), 6298 deletions(-)
diff --git a/Makefile.am b/Makefile.am index ebf44d9..bbd89a6 100644 --- a/Makefile.am +++ b/Makefile.am @@ -19,15 +19,14 @@ PROTOCOLS = \ src/protocol/x_null.cc
STEGANOGRAPHERS = \ - src/steg/x_http.cc \ - 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 + src/steg/http.cc \ + src/steg/payloads.cc \ + src/steg/cookies.cc \ + src/steg/jsSteg.cc \ + src/steg/swfSteg.cc \ + src/steg/zpack.cc \ + src/steg/crc32.cc \ + src/steg/pdfSteg.cc
libstegotorus_a_SOURCES = \ src/connections.cc \ diff --git a/run-autogen.csh b/run-autogen.csh deleted file mode 100644 index 9fe8c44..0000000 --- a/run-autogen.csh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/csh -setenv PATH /usr/local/bin:`echo $PATH` -./autogen.sh diff --git a/src/steg/cookies.c b/src/steg/cookies.c deleted file mode 100644 index c773386..0000000 --- a/src/steg/cookies.c +++ /dev/null @@ -1,234 +0,0 @@ - -#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 == '%' || c == '+' || c == '{' || 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 == '%' || c == '+' || c == '{' || 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.cc b/src/steg/cookies.cc new file mode 100644 index 0000000..c773386 --- /dev/null +++ b/src/steg/cookies.cc @@ -0,0 +1,234 @@ + +#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 == '%' || c == '+' || c == '{' || 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 == '%' || c == '+' || c == '{' || 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/crc32.c b/src/steg/crc32.c deleted file mode 100644 index 7fdc847..0000000 --- a/src/steg/crc32.c +++ /dev/null @@ -1,82 +0,0 @@ -#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.cc b/src/steg/crc32.cc new file mode 100644 index 0000000..7fdc847 --- /dev/null +++ b/src/steg/crc32.cc @@ -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/http.cc b/src/steg/http.cc new file mode 100644 index 0000000..7377196 --- /dev/null +++ b/src/steg/http.cc @@ -0,0 +1,857 @@ +/* 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 24 +#define MAX_COOKIE_SIZE 1024 + + +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_uri_transmit (steg_t *s, struct evbuffer *source, conn_t *conn); +int x_http2_client_cookie_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); +int gen_uri_field(char* uri, unsigned int uri_sz, char* data, int datalen); + + +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_JS_payload_pool(HTTP_MSG_BUF_SIZE, TYPE_HTTP_RESPONSE, JS_MIN_AVAIL_SIZE, HTTP_CONTENT_HTML); + init_HTML_payload_pool(HTTP_MSG_BUF_SIZE, TYPE_HTTP_RESPONSE, HTML_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_HTML: + mjc = get_max_HTML_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_cookie_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; + } + + // debug + // log_warn("CLIENT HTTP request header:"); + // buf_dump((unsigned char*)buf, len, stderr); + + // 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, sizeof(buf)); + downcast_steg(s)->have_transmitted = 1; + return 0; +} + + + + +int gen_uri_field(char* uri, unsigned int uri_sz, char* data, int datalen) { + unsigned int so_far = 0; + uri[0] = 0; + + strcat(uri, "GET /"); + so_far = 5; + + while (datalen > 0) { + unsigned int r = rand() % 4; + + if (r == 1) { + r = rand() % 46; + if (r < 20) + uri[so_far++] = 'g' + r; + else + uri[so_far++] = 'A' + r - 20; + } + else { + uri[so_far++] = data[0]; + data++; + datalen--; + } + + + + r = rand() % 8; + + if (r == 0 && datalen > 0) + uri[so_far++] = '/'; + + if (r == 2 && datalen > 0) + uri[so_far++] = '_'; + + + if (so_far > uri_sz - 6) { + fprintf(stderr, "too small\n"); + return 0; + } + } + + switch(rand()%4){ + case 1: + memcpy(uri+so_far, ".htm ", 6); + break; + case 2: + memcpy(uri+so_far, ".html ", 7); + break; + case 3: + memcpy(uri+so_far, ".js ", 5); + break; + case 0: + memcpy(uri+so_far, ".swf ", 6); + break; + + } + + return strlen(uri); + +} + + + + + +int +x_http2_client_uri_transmit (steg_t *s, struct evbuffer *source, conn_t *conn) { + + + struct evbuffer *dest = conn_get_outbound(conn); + + + 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 datalen = 0; + int cnt = 0; + char data[2*slen]; + + char outbuf[1024]; + int len =0; + char buf[10000]; + + + if (has_peer_name == 0 && lookup_peer_name_from_ip((char*) conn->peername, peername)) + has_peer_name = 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) { + 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 c; + while (p < limit) { + c = *p++; + data[datalen++] = "0123456789abcdef"[(c & 0xF0) >> 4]; + data[datalen++] = "0123456789abcdef"[(c & 0x0F) >> 0]; + } + } + free(iv); + + + + do { + datalen = gen_uri_field(outbuf, sizeof(outbuf), data, datalen); + } while (datalen == 0); + + + + + // retry up to 10 times + while (!len) { + len = find_client_payload(buf, sizeof(buf), TYPE_HTTP_REQUEST); + if (cnt++ == 10) return -1; + } + + + // fprintf(stderr, "outbuf = %s\n", outbuf); + + if (evbuffer_add(dest, outbuf, datalen) || // add uri field + evbuffer_add(dest, "HTTP/1.1\r\nHost: ", 19) || + 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, "\r\n", 2)) { + log_debug("error ***********************"); + return -1; + } + + + + evbuffer_drain(source, slen); + conn_cease_transmission(conn); + downcast_steg(s)->type = find_uri_type(outbuf, sizeof(outbuf)); + 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. */ + + if (evbuffer_get_length(source) < 72) + return x_http2_client_uri_transmit(s, source, conn); //@@ + return x_http2_client_cookie_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, HTTP_CONTENT_JAVASCRIPT); + break; + + case HTTP_CONTENT_HTML: + rval = x_http2_server_JS_transmit(s, source, conn, HTTP_CONTENT_HTML); + 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 *p; + unsigned char c, h, secondhalf; + char outbuf[MAX_COOKIE_SIZE]; + int sofar = 0; + int cookie_mode = 0; + + + 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+4); + + if (data == NULL) { + log_debug("SERVER evbuffer_pullup fails"); + return RECV_BAD; + } + + + data[s2.pos+3] = 0; + + type = find_uri_type((char *)data, s2.pos+4); + + if (strstr((char*) data, "Cookie") != NULL) { + p = (unsigned char*) strstr((char*) data, "Cookie:") + sizeof "Cookie: "-1; + cookie_mode = 1; + } + else + p = data + sizeof "GET /" -1; + + + secondhalf = 0; + c = 0; + + + while (strncmp((char*) p, "\r\n", 2) != 0 && (cookie_mode != 0 || p[0] != '.')) { + if (!secondhalf) + c = 0; + if ('0' <= *p && *p <= '9') + h = *p - '0'; + else if ('a' <= *p && *p <= 'f') + h = *p - 'a' + 10; + else { + p++; + continue; + } + + c = (c << 4) + h; + if (secondhalf) { + outbuf[sofar++] = c; + cnt++; + } + secondhalf = !secondhalf; + p++; + } + + outbuf[sofar] = 0; + + if (secondhalf) { + fprintf(stderr, "incorrect cookie or uri recovery \n"); + exit(-1); + } + + + + if (evbuffer_add(dest, outbuf, sofar)) { + log_debug("Failed to transfer buffer"); + return RECV_BAD; + } + evbuffer_drain(source, s2.pos + sizeof("\r\n\r\n") - 1); + } 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: + case HTTP_CONTENT_HTML: + 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/jsSteg.c b/src/steg/jsSteg.c deleted file mode 100644 index 1486255..0000000 --- a/src/steg/jsSteg.c +++ /dev/null @@ -1,1163 +0,0 @@ -#include "payloads.h" -#include "jsSteg.h" -#include "cookies.h" - -void buf_dump(unsigned char* buf, int len, FILE *out); - - -/* - * 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; -} - - -/* - * isGzipContent(char *msg) - * - * If the HTTP header of msg specifies that the content is gzipped, - * this function returns 1; otherwise, it returns 0 - * - * Assumptions: - * msg is null terminated - * - */ -int isGzipContent (char *msg) { - char *ptr = msg, *end; - int gzipFlag = 0; - - if (!strstr(msg, "\r\n\r\n")) - return 0; - - while (1) { - end = strstr(ptr, "\r\n"); - if (end == NULL) { - break; - } - - if (!strncmp(ptr, "Content-Encoding: gzip", 22)) { - gzipFlag = 1; - break; - } - - if (!strncmp(end, "\r\n\r\n", 4)){ - break; - } - ptr = end+2; - } - - return gzipFlag; -} - - -/* - * findContentType(char *msg) - * - * If the HTTP header of msg specifies that the content type: - * case (content type) - * javascript: return HTTP_CONTENT_JAVASCRIPT - * pdf: return HTTP_CONTENT_PDF - * shockwave: return HTTP_CONTENT_SWF - * html: return HTTP_CONTENT_HTML - * otherwise: return 0 - * - * Assumptions: - * msg is null terminated - * - */ -int findContentType (char *msg) { - char *ptr = msg, *end; - - if (!strstr(msg, "\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)) { - return HTTP_CONTENT_JAVASCRIPT; - } - if (!strncmp(ptr+14, "text/html", 9)) { - return HTTP_CONTENT_HTML; - } - if (!strncmp(ptr+14, "application/pdf", 15) || - !strncmp(ptr+14, "application/x-pdf", 17)) { - return HTTP_CONTENT_PDF; - } - if (!strncmp(ptr+14, "application/x-shockwave-flash", strlen("application/x-shockwave-flash"))) { - return HTTP_CONTENT_SWF; - } - } - - if (!strncmp(end, "\r\n\r\n", 4)){ - break; - } - ptr = end+2; - } - - return 0; -} - - - -/* - * 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, unsigned int content_type) { - - struct evbuffer_iovec *iv; - int nv; - struct evbuffer *dest = conn_get_outbound(conn); - size_t sbuflen = evbuffer_get_length(source); - char *hend, *jsTemplate = NULL, *outbuf, *outbuf2; - char data[(int) sbuflen*2]; - char newHdr[MAX_RESP_HDR_SIZE]; - unsigned int datalen = 0, cnt = 0, mjs = 0; - int r, i, mode, jsLen, hLen, cLen, newHdrLen = 0, outbuf2len; - - int gzipMode = JS_GZIP_RESP; - - - log_debug("sbuflen = %d\n", (int) sbuflen); - - if (content_type != HTTP_CONTENT_JAVASCRIPT && - content_type != HTTP_CONTENT_HTML) { - log_warn("SERVER ERROR: Unknown content type (%d)", content_type); - return -1; - } - - // log_debug("SERVER: dumping data with length %d:", (int) sbuflen); - // evbuffer_dump(source, stderr); - - 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; - } - - if (content_type == HTTP_CONTENT_JAVASCRIPT) { - mjs = get_max_JS_capacity(); - } else if (content_type == HTTP_CONTENT_HTML) { - mjs = get_max_HTML_capacity(); - } - - if (mjs <= 0) { - log_warn("SERVER ERROR: No JavaScript found in jsTemplate"); - return -1; - } - - if (sbuflen > (size_t) mjs) { - log_warn("SERVER ERROR: jsTemplate cannot accommodate data %d %dn", - (int) sbuflen, (int) mjs); - return -1; - } - - // Convert data in 'source' to hexadecimal and write it to data - 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); - - log_debug("SERVER encoded data in hex string (len %d):", datalen); - // buf_dump((unsigned char*)data, datalen, stderr); - - - - if (get_payload(content_type, datalen, &jsTemplate, &jsLen) == 1) { - log_debug("SERVER found the applicable HTTP response template with size %d", jsLen); - } else { - log_warn("SERVER couldn't find the applicable HTTP response template"); - return -1; - } - - // log_debug("MJS %d %d", datalen, mjs); - if (jsTemplate == NULL) { - log_warn("NO suitable payload found %d %d", datalen, mjs); - return -1; - } - - // assumption: jsTemplate is null-terminated - hend = strstr(jsTemplate, "\r\n\r\n"); - if (hend == NULL) { - log_warn("Unable to find end of header in the HTTP template"); - return -1; - } - - mode = has_eligible_HTTP_content (jsTemplate, jsLen, HTTP_CONTENT_JAVASCRIPT); - - // log_debug("SERVER: using HTTP resp template of length = %d", jsLen); - // log_debug("HTTP resp tempmlate:"); - // buf_dump((unsigned char*)jsTemplate, jsLen, stderr); - - hLen = hend+4-jsTemplate; - cLen = jsLen - hLen; - outbuf = malloc(cLen); - if (outbuf == NULL) { - log_warn("malloc for outbuf fails"); - return -1; - } - - r = encodeHTTPBody(data, hend+4, outbuf, datalen, cLen, cLen, mode); - - if (r < 0 || ((unsigned int) r < datalen)) { - log_warn("SERVER ERROR: Incomplete data encoding"); - return -1; - } - - // work in progress - if (gzipMode == 1) { - // conservative estimate: - // sizeof outbuf2 = cLen + 10-byte for gzip header + 8-byte for crc - outbuf2 = malloc(cLen+18); - if (outbuf2 == NULL) { - log_warn("malloc for outbuf2 fails"); - return -1; - } - - outbuf2len = gzDeflate(outbuf, cLen, outbuf2, cLen+18, time(NULL)); - - if (outbuf2len <= 0) { - log_warn("gzDeflate for outbuf fails"); - free(outbuf2); - return -1; - } - free(outbuf); - - } else { - outbuf2 = outbuf; - outbuf2len = cLen; - } - - // outbuf2 points to the HTTP payload (of length outbuf2len) to be sent - - if (mode == CONTENT_JAVASCRIPT) { // JavaScript in HTTP body - newHdrLen = gen_response_header((char*) "application/x-javascript", gzipMode, - outbuf2len, newHdr, sizeof(newHdr)); - } else if (mode == CONTENT_HTML_JAVASCRIPT) { // JavaScript(s) embedded in HTML doc - newHdrLen = gen_response_header((char*) "text/html", gzipMode, - outbuf2len, newHdr, sizeof(newHdr)); - } else { // unknown mode - log_warn("SERVER ERROR: unknown mode for creating the HTTP response header"); - free(outbuf2); - return -1; - } - if (newHdrLen < 0) { - log_warn("SERVER ERROR: gen_response_header fails for jsSteg"); - free(outbuf2); - return -1; - } - - // newHdr points to the HTTP header (of length newHdrLen) to be sent - - if (evbuffer_add(dest, newHdr, newHdrLen)) { - log_warn("SERVER ERROR: evbuffer_add() fails for newHdr"); - free(outbuf2); - return -1; - } - - if (evbuffer_add(dest, outbuf2, outbuf2len)) { - log_warn("SERVER ERROR: evbuffer_add() fails for outbuf2"); - free(outbuf2); - return -1; - } - - evbuffer_drain(source, sbuflen); - - free(outbuf2); - 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]; - char buf2[HTTP_MSG_BUF_SIZE]; - - unsigned char *field, *fieldStart, *fieldEnd, *fieldValStart; - char *httpBody; - - int decCnt, fin, i, j, k, gzipMode=0, httpBodyLen, buf2len, contentType = 0; - ev_ssize_t r; - struct evbuffer * scratch; - 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); - // buf_dump((unsigned char*)respMsg, response_len, stderr); - // log_debug("HTTP response header:"); - // buf_dump((unsigned char*)respMsg, hdrLen+80, stderr); - - contentType = findContentType (respMsg); - if (contentType != HTTP_CONTENT_JAVASCRIPT && contentType != HTTP_CONTENT_HTML) { - log_warn("ERROR: Invalid content type (%d)", contentType); - return RECV_BAD; - } - - httpBody = respMsg + hdrLen; - httpBodyLen = response_len - hdrLen; - - gzipMode = isGzipContent(respMsg); - if (gzipMode) { - log_debug("gzip content encoding detected"); - buf2len = gzInflate(httpBody, httpBodyLen, buf2, HTTP_MSG_BUF_SIZE); - if (buf2len <= 0) { - log_warn("gzInflate for httpBody fails"); - fprintf(stderr, "gzInflate for httpBody fails"); - exit(-1); - return RECV_BAD; - } - buf2[buf2len] = 0; - httpBody = buf2; - httpBodyLen = buf2len; - } - - if (contentType == HTTP_CONTENT_JAVASCRIPT) { - decCnt = decodeHTTPBody(httpBody, data, httpBodyLen, HTTP_MSG_BUF_SIZE, - &fin, CONTENT_JAVASCRIPT); - } else { - decCnt = decodeHTTPBody(httpBody, data, httpBodyLen, HTTP_MSG_BUF_SIZE, - &fin, CONTENT_HTML_JAVASCRIPT); - } - 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) { - fprintf(stderr, "CLIENT ERROR: An odd number of hex characters received\n"); - // buf_dump((unsigned char*)data, decCnt, stderr); - return RECV_BAD; - } - - if (! isxString(data)) { - log_warn("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_warn("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_warn("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_warn("CLIENT ERROR: Failed to drain source"); - 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 - 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.cc b/src/steg/jsSteg.cc new file mode 100644 index 0000000..1486255 --- /dev/null +++ b/src/steg/jsSteg.cc @@ -0,0 +1,1163 @@ +#include "payloads.h" +#include "jsSteg.h" +#include "cookies.h" + +void buf_dump(unsigned char* buf, int len, FILE *out); + + +/* + * 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; +} + + +/* + * isGzipContent(char *msg) + * + * If the HTTP header of msg specifies that the content is gzipped, + * this function returns 1; otherwise, it returns 0 + * + * Assumptions: + * msg is null terminated + * + */ +int isGzipContent (char *msg) { + char *ptr = msg, *end; + int gzipFlag = 0; + + if (!strstr(msg, "\r\n\r\n")) + return 0; + + while (1) { + end = strstr(ptr, "\r\n"); + if (end == NULL) { + break; + } + + if (!strncmp(ptr, "Content-Encoding: gzip", 22)) { + gzipFlag = 1; + break; + } + + if (!strncmp(end, "\r\n\r\n", 4)){ + break; + } + ptr = end+2; + } + + return gzipFlag; +} + + +/* + * findContentType(char *msg) + * + * If the HTTP header of msg specifies that the content type: + * case (content type) + * javascript: return HTTP_CONTENT_JAVASCRIPT + * pdf: return HTTP_CONTENT_PDF + * shockwave: return HTTP_CONTENT_SWF + * html: return HTTP_CONTENT_HTML + * otherwise: return 0 + * + * Assumptions: + * msg is null terminated + * + */ +int findContentType (char *msg) { + char *ptr = msg, *end; + + if (!strstr(msg, "\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)) { + return HTTP_CONTENT_JAVASCRIPT; + } + if (!strncmp(ptr+14, "text/html", 9)) { + return HTTP_CONTENT_HTML; + } + if (!strncmp(ptr+14, "application/pdf", 15) || + !strncmp(ptr+14, "application/x-pdf", 17)) { + return HTTP_CONTENT_PDF; + } + if (!strncmp(ptr+14, "application/x-shockwave-flash", strlen("application/x-shockwave-flash"))) { + return HTTP_CONTENT_SWF; + } + } + + if (!strncmp(end, "\r\n\r\n", 4)){ + break; + } + ptr = end+2; + } + + return 0; +} + + + +/* + * 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, unsigned int content_type) { + + struct evbuffer_iovec *iv; + int nv; + struct evbuffer *dest = conn_get_outbound(conn); + size_t sbuflen = evbuffer_get_length(source); + char *hend, *jsTemplate = NULL, *outbuf, *outbuf2; + char data[(int) sbuflen*2]; + char newHdr[MAX_RESP_HDR_SIZE]; + unsigned int datalen = 0, cnt = 0, mjs = 0; + int r, i, mode, jsLen, hLen, cLen, newHdrLen = 0, outbuf2len; + + int gzipMode = JS_GZIP_RESP; + + + log_debug("sbuflen = %d\n", (int) sbuflen); + + if (content_type != HTTP_CONTENT_JAVASCRIPT && + content_type != HTTP_CONTENT_HTML) { + log_warn("SERVER ERROR: Unknown content type (%d)", content_type); + return -1; + } + + // log_debug("SERVER: dumping data with length %d:", (int) sbuflen); + // evbuffer_dump(source, stderr); + + 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; + } + + if (content_type == HTTP_CONTENT_JAVASCRIPT) { + mjs = get_max_JS_capacity(); + } else if (content_type == HTTP_CONTENT_HTML) { + mjs = get_max_HTML_capacity(); + } + + if (mjs <= 0) { + log_warn("SERVER ERROR: No JavaScript found in jsTemplate"); + return -1; + } + + if (sbuflen > (size_t) mjs) { + log_warn("SERVER ERROR: jsTemplate cannot accommodate data %d %dn", + (int) sbuflen, (int) mjs); + return -1; + } + + // Convert data in 'source' to hexadecimal and write it to data + 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); + + log_debug("SERVER encoded data in hex string (len %d):", datalen); + // buf_dump((unsigned char*)data, datalen, stderr); + + + + if (get_payload(content_type, datalen, &jsTemplate, &jsLen) == 1) { + log_debug("SERVER found the applicable HTTP response template with size %d", jsLen); + } else { + log_warn("SERVER couldn't find the applicable HTTP response template"); + return -1; + } + + // log_debug("MJS %d %d", datalen, mjs); + if (jsTemplate == NULL) { + log_warn("NO suitable payload found %d %d", datalen, mjs); + return -1; + } + + // assumption: jsTemplate is null-terminated + hend = strstr(jsTemplate, "\r\n\r\n"); + if (hend == NULL) { + log_warn("Unable to find end of header in the HTTP template"); + return -1; + } + + mode = has_eligible_HTTP_content (jsTemplate, jsLen, HTTP_CONTENT_JAVASCRIPT); + + // log_debug("SERVER: using HTTP resp template of length = %d", jsLen); + // log_debug("HTTP resp tempmlate:"); + // buf_dump((unsigned char*)jsTemplate, jsLen, stderr); + + hLen = hend+4-jsTemplate; + cLen = jsLen - hLen; + outbuf = malloc(cLen); + if (outbuf == NULL) { + log_warn("malloc for outbuf fails"); + return -1; + } + + r = encodeHTTPBody(data, hend+4, outbuf, datalen, cLen, cLen, mode); + + if (r < 0 || ((unsigned int) r < datalen)) { + log_warn("SERVER ERROR: Incomplete data encoding"); + return -1; + } + + // work in progress + if (gzipMode == 1) { + // conservative estimate: + // sizeof outbuf2 = cLen + 10-byte for gzip header + 8-byte for crc + outbuf2 = malloc(cLen+18); + if (outbuf2 == NULL) { + log_warn("malloc for outbuf2 fails"); + return -1; + } + + outbuf2len = gzDeflate(outbuf, cLen, outbuf2, cLen+18, time(NULL)); + + if (outbuf2len <= 0) { + log_warn("gzDeflate for outbuf fails"); + free(outbuf2); + return -1; + } + free(outbuf); + + } else { + outbuf2 = outbuf; + outbuf2len = cLen; + } + + // outbuf2 points to the HTTP payload (of length outbuf2len) to be sent + + if (mode == CONTENT_JAVASCRIPT) { // JavaScript in HTTP body + newHdrLen = gen_response_header((char*) "application/x-javascript", gzipMode, + outbuf2len, newHdr, sizeof(newHdr)); + } else if (mode == CONTENT_HTML_JAVASCRIPT) { // JavaScript(s) embedded in HTML doc + newHdrLen = gen_response_header((char*) "text/html", gzipMode, + outbuf2len, newHdr, sizeof(newHdr)); + } else { // unknown mode + log_warn("SERVER ERROR: unknown mode for creating the HTTP response header"); + free(outbuf2); + return -1; + } + if (newHdrLen < 0) { + log_warn("SERVER ERROR: gen_response_header fails for jsSteg"); + free(outbuf2); + return -1; + } + + // newHdr points to the HTTP header (of length newHdrLen) to be sent + + if (evbuffer_add(dest, newHdr, newHdrLen)) { + log_warn("SERVER ERROR: evbuffer_add() fails for newHdr"); + free(outbuf2); + return -1; + } + + if (evbuffer_add(dest, outbuf2, outbuf2len)) { + log_warn("SERVER ERROR: evbuffer_add() fails for outbuf2"); + free(outbuf2); + return -1; + } + + evbuffer_drain(source, sbuflen); + + free(outbuf2); + 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]; + char buf2[HTTP_MSG_BUF_SIZE]; + + unsigned char *field, *fieldStart, *fieldEnd, *fieldValStart; + char *httpBody; + + int decCnt, fin, i, j, k, gzipMode=0, httpBodyLen, buf2len, contentType = 0; + ev_ssize_t r; + struct evbuffer * scratch; + 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); + // buf_dump((unsigned char*)respMsg, response_len, stderr); + // log_debug("HTTP response header:"); + // buf_dump((unsigned char*)respMsg, hdrLen+80, stderr); + + contentType = findContentType (respMsg); + if (contentType != HTTP_CONTENT_JAVASCRIPT && contentType != HTTP_CONTENT_HTML) { + log_warn("ERROR: Invalid content type (%d)", contentType); + return RECV_BAD; + } + + httpBody = respMsg + hdrLen; + httpBodyLen = response_len - hdrLen; + + gzipMode = isGzipContent(respMsg); + if (gzipMode) { + log_debug("gzip content encoding detected"); + buf2len = gzInflate(httpBody, httpBodyLen, buf2, HTTP_MSG_BUF_SIZE); + if (buf2len <= 0) { + log_warn("gzInflate for httpBody fails"); + fprintf(stderr, "gzInflate for httpBody fails"); + exit(-1); + return RECV_BAD; + } + buf2[buf2len] = 0; + httpBody = buf2; + httpBodyLen = buf2len; + } + + if (contentType == HTTP_CONTENT_JAVASCRIPT) { + decCnt = decodeHTTPBody(httpBody, data, httpBodyLen, HTTP_MSG_BUF_SIZE, + &fin, CONTENT_JAVASCRIPT); + } else { + decCnt = decodeHTTPBody(httpBody, data, httpBodyLen, HTTP_MSG_BUF_SIZE, + &fin, CONTENT_HTML_JAVASCRIPT); + } + 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) { + fprintf(stderr, "CLIENT ERROR: An odd number of hex characters received\n"); + // buf_dump((unsigned char*)data, decCnt, stderr); + return RECV_BAD; + } + + if (! isxString(data)) { + log_warn("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_warn("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_warn("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_warn("CLIENT ERROR: Failed to drain source"); + 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 + 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/payloads.c b/src/steg/payloads.c deleted file mode 100644 index 2b82451..0000000 --- a/src/steg/payloads.c +++ /dev/null @@ -1,1669 +0,0 @@ -#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_HTML_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_HTML_capacity() { - return max_HTML_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); -} - - - -void gen_rfc_1123_expiry_date(char* buf, int buf_size) { - time_t t = time(NULL) + rand() % 10000; - struct tm *my_tm = gmtime(&t); - strftime(buf, buf_size, "Expires: %a, %d %b %Y %H:%M:%S GMT\r\n", my_tm); -} - - - - - -int gen_response_header(char* content_type, int gzip, int length, char* buf, int buflen) { - char* ptr; - - // conservative assumption here.... - if (buflen < 400) { - 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); - - sprintf(ptr, "Server: Apache\r\n"); - ptr = ptr + strlen(ptr); - - switch(rand() % 9) { - case 1: - sprintf(ptr, "Vary: Cookie\r\n"); - ptr = ptr + strlen(ptr); - break; - - case 2: - sprintf(ptr, "Vary: Accept-Encoding, User-Agent\r\n"); - ptr = ptr + strlen(ptr); - break; - - case 3: - sprintf(ptr, "Vary: *\r\n"); - ptr = ptr + strlen(ptr); - break; - - } - - - switch(rand() % 4) { - case 2: - gen_rfc_1123_expiry_date(ptr, buflen - (ptr - buf)); - ptr = ptr + strlen(ptr); - } - - - - - if (gzip) - sprintf(ptr, "Content-Length: %d\r\nContent-Encoding: gzip\r\nContent-Type: %s\r\n", length, content_type); - else - sprintf(ptr, "Content-Length: %d\r\nContent-Type: %s\r\n", length, content_type); - - ptr += strlen(ptr); - - switch(rand() % 4) { - case 2: - case 3: - case 4: - sprintf(ptr, "Connection: Keep-Alive\r\n\r\n"); - break; - default: - sprintf(ptr, "Connection: close\r\n\r\n"); - break; - } - - 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_orig, int buflen) { - - char* uri; - char* ext; - - char* buf = malloc(buflen+1); - char* uri_end; - - - memcpy(buf, buf_orig, buflen); - buf[buflen] = 0; - - - if (strncmp(buf, "GET", 3) != 0 - && strncmp(buf, "POST", 4) != 0) { - fprintf(stderr, "HERE %s\n", buf); - return -1; - } - - - - uri = strchr(buf, ' ') + 1; - - if (uri == NULL) { - fprintf(stderr, "Invalid URL\n"); - return -1; - } - - uri_end = strchr(uri, ' '); - - if (uri_end == NULL) { - fprintf(stderr, "unterminated uri\n"); - return -1; - } - - uri_end[0] = 0; - - - - - - ext = strrchr(uri, '/'); - - if (ext == NULL) { - fprintf(stderr, "no / in url: find_uri_type..."); - return -1; - } - - ext = strchr(ext, '.'); - - - if (ext == NULL || !strncmp(ext, ".html", 5) || !strncmp(ext, ".htm", 4) || !strncmp(ext, ".php", 4) - || !strncmp(ext, ".jsp", 4) || !strncmp(ext, ".asp", 4)) - return HTTP_CONTENT_HTML; - - - if (!strncmp(ext, ".js", 3) || !strncmp(ext, ".JS", 3)) - return HTTP_CONTENT_JAVASCRIPT; - - 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; - - - - free(buf); - return -1; - -} - -/* -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; - - // default type - return HTTP_CONTENT_HTML; - // return HTTP_CONTENT_JAVASCRIPT; - 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, p->length) != HTTP_CONTENT_SWF && - find_uri_type(inbuf, p->length) != HTTP_CONTENT_HTML && - find_uri_type(inbuf, p->length) != HTTP_CONTENT_JAVASCRIPT && - find_uri_type(inbuf, p->length) != HTTP_CONTENT_PDF) { - 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 regular expression matching algo - */ - - - -int skipJSPattern(char *cp, int len) { - int i,j; - - - char keywords [21][10]= {"function", "return", "var", "int", "random", "Math", "while", - "else", "for", "document", "write", "writeln", "true", - "false", "True", "False", "window", "indexOf", "navigator", "case", "if"}; - - - if (len < 1) return 0; - - // change the limit to 21 to enable if as a keyword - for (i=0; i < 20; i++) { - char* word = keywords[i]; - - if (len <= (int) strlen(word)) - continue; - - if (word[0] != cp[0]) - continue; - - for (j=1; j < (int) strlen(word); j++) { - if (isxdigit(word[j])) { - if (!isxdigit(cp[j])) - goto next_word; - else - continue; - } - - if (cp[j] != word[j]) - goto next_word; - } - if (!isalnum(cp[j])) - return strlen(word)+1; - - next_word: - continue; - } - - return 0; -} - - - - -/* 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 { - while (cp < (p+range) && isalnum_(*cp)) { - cp++; - if (isxdigit(*cp)) return (cp-p); - } - if (cp >= (p+range)) return -1; - // non-alnum_ found - // 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; - - while (cp < (p+range) && i != -1) { - - if (i == 0) { - if (isFirstWordChar) { - j = skipJSPattern(cp, p+range-cp); - if (j > 0) { - cp = cp+j; - } else { - 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); - if (! isalnum_(*cp)) { - isFirstWordChar = 1; - } - cp++; - } - } else { - cp += i; isFirstWordChar = 1; - } - i = offset2Alnum_(cp, p+range-cp); - - } // while - - // cannot find next usable hex char - return -1; - -} - -/* - * 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; - } - - if (len < buf + len - bp) { - fprintf(stderr, "HERE\n"); - } - 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; - } - - if (len < jsEnd - buf || len < jsEnd - bp) { - fprintf(stderr, "HERE2\n"); - } - - - j = offset2Hex(bp, jsEnd-bp, 1); - } // while (j != -1) - - if (buf + len < bp + 9) { - fprintf(stderr, "HERE3\n"); - } - - - 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, gzipFlag=0; - char* end, *cp; - -#ifdef DEBUG - fprintf(stderr, "TESTING availabilty of js in payload ... \n"); -#endif - - if (type != HTTP_CONTENT_JAVASCRIPT && - type != HTTP_CONTENT_HTML && - 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: gzip", 22)) { - gzipFlag = 1; - } else if (!strncmp(ptr, "Content-Encoding:", 17)) { // Content-Encoding that is not gzip - 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; gzipFlag=%d; ceFlag=%d; teFlag=%d; http304Flag=%d; clZeroFlag=%d\n", - tjFlag, thFlag, gzipFlag, ceFlag, teFlag, http304Flag, clZeroFlag); -#endif - - // if (type == HTTP_CONTENT_JAVASCRIPT) - if (type == HTTP_CONTENT_JAVASCRIPT || type == HTTP_CONTENT_HTML) { - // 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) - // or Content-Encoding that is not gzip - // if (teFlag) return 0; - if (teFlag || ceFlag) 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 that is not gzip - // if (teFlag) 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 == CONTENT_JAVASCRIPT) { - - 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_HTML_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_HTML; - - 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_HTML); - if (mode == CONTENT_HTML_JAVASCRIPT) { - - 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_HTML_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.cc b/src/steg/payloads.cc new file mode 100644 index 0000000..2b82451 --- /dev/null +++ b/src/steg/payloads.cc @@ -0,0 +1,1669 @@ +#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_HTML_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_HTML_capacity() { + return max_HTML_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); +} + + + +void gen_rfc_1123_expiry_date(char* buf, int buf_size) { + time_t t = time(NULL) + rand() % 10000; + struct tm *my_tm = gmtime(&t); + strftime(buf, buf_size, "Expires: %a, %d %b %Y %H:%M:%S GMT\r\n", my_tm); +} + + + + + +int gen_response_header(char* content_type, int gzip, int length, char* buf, int buflen) { + char* ptr; + + // conservative assumption here.... + if (buflen < 400) { + 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); + + sprintf(ptr, "Server: Apache\r\n"); + ptr = ptr + strlen(ptr); + + switch(rand() % 9) { + case 1: + sprintf(ptr, "Vary: Cookie\r\n"); + ptr = ptr + strlen(ptr); + break; + + case 2: + sprintf(ptr, "Vary: Accept-Encoding, User-Agent\r\n"); + ptr = ptr + strlen(ptr); + break; + + case 3: + sprintf(ptr, "Vary: *\r\n"); + ptr = ptr + strlen(ptr); + break; + + } + + + switch(rand() % 4) { + case 2: + gen_rfc_1123_expiry_date(ptr, buflen - (ptr - buf)); + ptr = ptr + strlen(ptr); + } + + + + + if (gzip) + sprintf(ptr, "Content-Length: %d\r\nContent-Encoding: gzip\r\nContent-Type: %s\r\n", length, content_type); + else + sprintf(ptr, "Content-Length: %d\r\nContent-Type: %s\r\n", length, content_type); + + ptr += strlen(ptr); + + switch(rand() % 4) { + case 2: + case 3: + case 4: + sprintf(ptr, "Connection: Keep-Alive\r\n\r\n"); + break; + default: + sprintf(ptr, "Connection: close\r\n\r\n"); + break; + } + + 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_orig, int buflen) { + + char* uri; + char* ext; + + char* buf = malloc(buflen+1); + char* uri_end; + + + memcpy(buf, buf_orig, buflen); + buf[buflen] = 0; + + + if (strncmp(buf, "GET", 3) != 0 + && strncmp(buf, "POST", 4) != 0) { + fprintf(stderr, "HERE %s\n", buf); + return -1; + } + + + + uri = strchr(buf, ' ') + 1; + + if (uri == NULL) { + fprintf(stderr, "Invalid URL\n"); + return -1; + } + + uri_end = strchr(uri, ' '); + + if (uri_end == NULL) { + fprintf(stderr, "unterminated uri\n"); + return -1; + } + + uri_end[0] = 0; + + + + + + ext = strrchr(uri, '/'); + + if (ext == NULL) { + fprintf(stderr, "no / in url: find_uri_type..."); + return -1; + } + + ext = strchr(ext, '.'); + + + if (ext == NULL || !strncmp(ext, ".html", 5) || !strncmp(ext, ".htm", 4) || !strncmp(ext, ".php", 4) + || !strncmp(ext, ".jsp", 4) || !strncmp(ext, ".asp", 4)) + return HTTP_CONTENT_HTML; + + + if (!strncmp(ext, ".js", 3) || !strncmp(ext, ".JS", 3)) + return HTTP_CONTENT_JAVASCRIPT; + + 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; + + + + free(buf); + return -1; + +} + +/* +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; + + // default type + return HTTP_CONTENT_HTML; + // return HTTP_CONTENT_JAVASCRIPT; + 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, p->length) != HTTP_CONTENT_SWF && + find_uri_type(inbuf, p->length) != HTTP_CONTENT_HTML && + find_uri_type(inbuf, p->length) != HTTP_CONTENT_JAVASCRIPT && + find_uri_type(inbuf, p->length) != HTTP_CONTENT_PDF) { + 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 regular expression matching algo + */ + + + +int skipJSPattern(char *cp, int len) { + int i,j; + + + char keywords [21][10]= {"function", "return", "var", "int", "random", "Math", "while", + "else", "for", "document", "write", "writeln", "true", + "false", "True", "False", "window", "indexOf", "navigator", "case", "if"}; + + + if (len < 1) return 0; + + // change the limit to 21 to enable if as a keyword + for (i=0; i < 20; i++) { + char* word = keywords[i]; + + if (len <= (int) strlen(word)) + continue; + + if (word[0] != cp[0]) + continue; + + for (j=1; j < (int) strlen(word); j++) { + if (isxdigit(word[j])) { + if (!isxdigit(cp[j])) + goto next_word; + else + continue; + } + + if (cp[j] != word[j]) + goto next_word; + } + if (!isalnum(cp[j])) + return strlen(word)+1; + + next_word: + continue; + } + + return 0; +} + + + + +/* 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 { + while (cp < (p+range) && isalnum_(*cp)) { + cp++; + if (isxdigit(*cp)) return (cp-p); + } + if (cp >= (p+range)) return -1; + // non-alnum_ found + // 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; + + while (cp < (p+range) && i != -1) { + + if (i == 0) { + if (isFirstWordChar) { + j = skipJSPattern(cp, p+range-cp); + if (j > 0) { + cp = cp+j; + } else { + 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); + if (! isalnum_(*cp)) { + isFirstWordChar = 1; + } + cp++; + } + } else { + cp += i; isFirstWordChar = 1; + } + i = offset2Alnum_(cp, p+range-cp); + + } // while + + // cannot find next usable hex char + return -1; + +} + +/* + * 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; + } + + if (len < buf + len - bp) { + fprintf(stderr, "HERE\n"); + } + 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; + } + + if (len < jsEnd - buf || len < jsEnd - bp) { + fprintf(stderr, "HERE2\n"); + } + + + j = offset2Hex(bp, jsEnd-bp, 1); + } // while (j != -1) + + if (buf + len < bp + 9) { + fprintf(stderr, "HERE3\n"); + } + + + 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, gzipFlag=0; + char* end, *cp; + +#ifdef DEBUG + fprintf(stderr, "TESTING availabilty of js in payload ... \n"); +#endif + + if (type != HTTP_CONTENT_JAVASCRIPT && + type != HTTP_CONTENT_HTML && + 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: gzip", 22)) { + gzipFlag = 1; + } else if (!strncmp(ptr, "Content-Encoding:", 17)) { // Content-Encoding that is not gzip + 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; gzipFlag=%d; ceFlag=%d; teFlag=%d; http304Flag=%d; clZeroFlag=%d\n", + tjFlag, thFlag, gzipFlag, ceFlag, teFlag, http304Flag, clZeroFlag); +#endif + + // if (type == HTTP_CONTENT_JAVASCRIPT) + if (type == HTTP_CONTENT_JAVASCRIPT || type == HTTP_CONTENT_HTML) { + // 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) + // or Content-Encoding that is not gzip + // if (teFlag) return 0; + if (teFlag || ceFlag) 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 that is not gzip + // if (teFlag) 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 == CONTENT_JAVASCRIPT) { + + 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_HTML_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_HTML; + + 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_HTML); + if (mode == CONTENT_HTML_JAVASCRIPT) { + + 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_HTML_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/pdfSteg.c b/src/steg/pdfSteg.c deleted file mode 100644 index 81efdf4..0000000 --- a/src/steg/pdfSteg.c +++ /dev/null @@ -1,630 +0,0 @@ -#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 - * - * escape indicates if a dangling delimiter1 has been - * seen in the previous invocation of removeDelimiter - */ -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) { - outbuf[cnt++] = ic1; - } else { - // lookahead 1 char - ic2 = *ibp; - // if the next char is delimiter1 - if (ic2 == delimiter1) { - outbuf[cnt++] = delimiter1; ibp++; - } else { // end-of-data pattern detected - *endFlag = 1; - 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; - - char newHdr[MAX_RESP_HDR_SIZE]; - int newHdrLen = 0; - - 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); - // } - - - newHdrLen = gen_response_header((char*) "application/pdf", 0, outbuflen, newHdr, sizeof(newHdr)); - if (newHdrLen < 0) { - log_warn("SERVER ERROR: gen_response_header fails for pdfSteg"); - return -1; - } - - if (evbuffer_add(dest, newHdr, newHdrLen)) { - log_warn("SERVER ERROR: evbuffer_add() fails for newHdr"); - return -1; - } - // 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); - - if (httpHdr == NULL) { - log_warn("CLIENT unable to pullup the complete HTTP body"); - return RECV_BAD; - } - - - 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); - - - 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.cc b/src/steg/pdfSteg.cc new file mode 100644 index 0000000..81efdf4 --- /dev/null +++ b/src/steg/pdfSteg.cc @@ -0,0 +1,630 @@ +#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 + * + * escape indicates if a dangling delimiter1 has been + * seen in the previous invocation of removeDelimiter + */ +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) { + outbuf[cnt++] = ic1; + } else { + // lookahead 1 char + ic2 = *ibp; + // if the next char is delimiter1 + if (ic2 == delimiter1) { + outbuf[cnt++] = delimiter1; ibp++; + } else { // end-of-data pattern detected + *endFlag = 1; + 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; + + char newHdr[MAX_RESP_HDR_SIZE]; + int newHdrLen = 0; + + 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); + // } + + + newHdrLen = gen_response_header((char*) "application/pdf", 0, outbuflen, newHdr, sizeof(newHdr)); + if (newHdrLen < 0) { + log_warn("SERVER ERROR: gen_response_header fails for pdfSteg"); + return -1; + } + + if (evbuffer_add(dest, newHdr, newHdrLen)) { + log_warn("SERVER ERROR: evbuffer_add() fails for newHdr"); + return -1; + } + // 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); + + if (httpHdr == NULL) { + log_warn("CLIENT unable to pullup the complete HTTP body"); + return RECV_BAD; + } + + + 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); + + + 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/swfSteg.c b/src/steg/swfSteg.c deleted file mode 100644 index ad3d5c8..0000000 --- a/src/steg/swfSteg.c +++ /dev/null @@ -1,282 +0,0 @@ -#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 deleted file mode 100644 index 833ea9f..0000000 --- a/src/steg/swfSteg.c.old +++ /dev/null @@ -1,264 +0,0 @@ -#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.cc b/src/steg/swfSteg.cc new file mode 100644 index 0000000..ad3d5c8 --- /dev/null +++ b/src/steg/swfSteg.cc @@ -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/x_http.c.old b/src/steg/x_http.c.old deleted file mode 100644 index 2799078..0000000 --- a/src/steg/x_http.c.old +++ /dev/null @@ -1,337 +0,0 @@ -/* 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_http.cc b/src/steg/x_http.cc deleted file mode 100644 index e1d9780..0000000 --- a/src/steg/x_http.cc +++ /dev/null @@ -1,360 +0,0 @@ -/* Copyright 2011 Zack Weinberg - See LICENSE for other credits and copying information -*/ - -#include "util.h" -#include "connections.h" -#include "steg.h" -#include "crypt.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. */ - -namespace { - struct x_http : steg_t - { - bool have_transmitted : 1; - bool have_received : 1; - STEG_DECLARE_METHODS(x_http); - }; -} - -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: "; - - -x_http::x_http() - : have_transmitted(false), have_received(false) -{} - -x_http::~x_http() -{} - -bool -x_http::detect(conn_t *conn) -{ - struct evbuffer *buf = conn_get_inbound(conn); - uint8_t *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 true; - } - - /* 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 true; - } - - /* Didn't find either the client or the server pattern. */ - return false; -} - -size_t -x_http::transmit_room(conn_t *) -{ - if (this->have_transmitted) - /* can't send any more on this connection */ - return 0; - - if (this->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 { - if (!this->have_received) - return 0; - /* no practical limit applies */ - return SIZE_MAX; - } -} - -int -x_http::transmit(struct evbuffer *source, conn_t *conn) -{ - struct evbuffer *dest = conn_get_outbound(conn); - - if (this->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 = (struct evbuffer_iovec *)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 uint8_t *p = (const uint8_t *)iv[i].iov_base; - const uint8_t *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); - this->have_transmitted = true; - 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, "%lu\r\n\r\n", - (unsigned long)evbuffer_get_length(source)) == -1) - return -1; - if (evbuffer_add_buffer(dest, source)) - return -1; - - conn_close_after_transmit(conn); - this->have_transmitted = true; - return 0; - } -} - -int -x_http::receive(conn_t *conn, struct evbuffer *dest) -{ - struct evbuffer *source = conn_get_inbound(conn); - if (this->is_clientside) { - /* 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. */ - - size_t hlen = evbuffer_get_length(source); - uint8_t *data, *p, *limit; - uint64_t clen; - - log_debug("x_http: %lu byte response stream available%s", - (unsigned long)hlen, - hlen >= sizeof http_response_1 - 1 ? "" : " (incomplete)"); - - if (this->have_received) { - log_warn("x_http: protocol error: multiple responses"); - return -1; - } - - if (hlen < sizeof http_response_1 - 1) - return 0; /* incomplete */ - - if (hlen > sizeof http_response_1 + 23) - hlen = sizeof http_response_1 + 23; - - data = evbuffer_pullup(source, hlen); - /* Validate response headers. */ - if (memcmp(data, http_response_1, sizeof http_response_1 - 1)) - return -1; - - /* 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. */ - p = data + sizeof http_response_1 - 1; - limit = data + hlen; - clen = 0; - while (p < limit && '0' <= *p && *p <= '9') { - clen = clen*10 + *p - '0'; - p++; - } - if (p+4 > limit) - return 0; /* incomplete */ - if (p[0] != '\r' || p[1] != '\n' || p[2] != '\r' || p[3] != '\n') - return -1; - - 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; /* incomplete */ - - /* we are go */ - if (evbuffer_drain(source, hlen)) - return -1; - - if ((uint64_t)evbuffer_remove_buffer(source, dest, clen) != clen) - return -1; - - log_debug("x_http: decoded %lu byte response", - (unsigned long)(hlen + clen)); - - if (evbuffer_get_length(source) > 0) { - log_warn("x_http: protocol error: extra response data"); - return -1; - } - - this->have_received = 1; - conn_expect_close(conn); - return 0; - } 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; - struct evbuffer_ptr s2, s3; - uint8_t *data, *p, *limit; - uint8_t c, h, secondhalf; - - log_debug("x_http: %lu byte query stream available", - (unsigned long)evbuffer_get_length(source)); - - if (this->have_received) { - log_warn("x_http: protocol error: multiple queries"); - return -1; - } - - /* Search for the second and third invariant bits of the query headers - we expect. We completely ignore the contents of the Host header. */ - s2 = evbuffer_search(source, http_query_2, - sizeof http_query_2 - 1, NULL); - if (s2.pos == -1) { - log_debug("x_http: did not find second piece of HTTP query"); - return 0; - } - s3 = evbuffer_search(source, http_query_3, - sizeof http_query_3 - 1, &s2); - if (s3.pos == -1) { - log_debug("x_http: did not find third piece of HTTP query"); - return 0; - } - log_assert(s3.pos + sizeof http_query_3 - 1 - <= evbuffer_get_length(source)); - - data = evbuffer_pullup(source, s2.pos); - if (memcmp(data, "GET /", sizeof "GET /"-1)) { - log_debug("x_http: unexpected HTTP verb: %.*s", 5, data); - return -1; - } - - p = data + sizeof "GET /"-1; - limit = data + s2.pos; - - scratch = evbuffer_new(); - if (!scratch) return -1; - if (evbuffer_expand(scratch, (limit - p)/2)) { - evbuffer_free(scratch); - return -1; - } - - 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("x_http: decode error: unexpected URI character %c", *p); - return -1; - } - - 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("x_http: failed to transfer buffer"); - return -1; - } - evbuffer_drain(source, s3.pos + sizeof http_query_3 - 1); - evbuffer_free(scratch); - log_debug("x_http: decoded %lu byte query", - (unsigned long)(s3.pos + sizeof http_query_3 - 1)); - - if (evbuffer_get_length(source) > 0) { - log_warn("x_http: protocol error: extra query data"); - return -1; - } - - this->have_received = 1; - conn_transmit_soon(conn, 2); - return 0; - } -} diff --git a/src/steg/x_http2.c b/src/steg/x_http2.c deleted file mode 100644 index 7377196..0000000 --- a/src/steg/x_http2.c +++ /dev/null @@ -1,857 +0,0 @@ -/* 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 24 -#define MAX_COOKIE_SIZE 1024 - - -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_uri_transmit (steg_t *s, struct evbuffer *source, conn_t *conn); -int x_http2_client_cookie_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); -int gen_uri_field(char* uri, unsigned int uri_sz, char* data, int datalen); - - -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_JS_payload_pool(HTTP_MSG_BUF_SIZE, TYPE_HTTP_RESPONSE, JS_MIN_AVAIL_SIZE, HTTP_CONTENT_HTML); - init_HTML_payload_pool(HTTP_MSG_BUF_SIZE, TYPE_HTTP_RESPONSE, HTML_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_HTML: - mjc = get_max_HTML_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_cookie_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; - } - - // debug - // log_warn("CLIENT HTTP request header:"); - // buf_dump((unsigned char*)buf, len, stderr); - - // 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, sizeof(buf)); - downcast_steg(s)->have_transmitted = 1; - return 0; -} - - - - -int gen_uri_field(char* uri, unsigned int uri_sz, char* data, int datalen) { - unsigned int so_far = 0; - uri[0] = 0; - - strcat(uri, "GET /"); - so_far = 5; - - while (datalen > 0) { - unsigned int r = rand() % 4; - - if (r == 1) { - r = rand() % 46; - if (r < 20) - uri[so_far++] = 'g' + r; - else - uri[so_far++] = 'A' + r - 20; - } - else { - uri[so_far++] = data[0]; - data++; - datalen--; - } - - - - r = rand() % 8; - - if (r == 0 && datalen > 0) - uri[so_far++] = '/'; - - if (r == 2 && datalen > 0) - uri[so_far++] = '_'; - - - if (so_far > uri_sz - 6) { - fprintf(stderr, "too small\n"); - return 0; - } - } - - switch(rand()%4){ - case 1: - memcpy(uri+so_far, ".htm ", 6); - break; - case 2: - memcpy(uri+so_far, ".html ", 7); - break; - case 3: - memcpy(uri+so_far, ".js ", 5); - break; - case 0: - memcpy(uri+so_far, ".swf ", 6); - break; - - } - - return strlen(uri); - -} - - - - - -int -x_http2_client_uri_transmit (steg_t *s, struct evbuffer *source, conn_t *conn) { - - - struct evbuffer *dest = conn_get_outbound(conn); - - - 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 datalen = 0; - int cnt = 0; - char data[2*slen]; - - char outbuf[1024]; - int len =0; - char buf[10000]; - - - if (has_peer_name == 0 && lookup_peer_name_from_ip((char*) conn->peername, peername)) - has_peer_name = 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) { - 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 c; - while (p < limit) { - c = *p++; - data[datalen++] = "0123456789abcdef"[(c & 0xF0) >> 4]; - data[datalen++] = "0123456789abcdef"[(c & 0x0F) >> 0]; - } - } - free(iv); - - - - do { - datalen = gen_uri_field(outbuf, sizeof(outbuf), data, datalen); - } while (datalen == 0); - - - - - // retry up to 10 times - while (!len) { - len = find_client_payload(buf, sizeof(buf), TYPE_HTTP_REQUEST); - if (cnt++ == 10) return -1; - } - - - // fprintf(stderr, "outbuf = %s\n", outbuf); - - if (evbuffer_add(dest, outbuf, datalen) || // add uri field - evbuffer_add(dest, "HTTP/1.1\r\nHost: ", 19) || - 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, "\r\n", 2)) { - log_debug("error ***********************"); - return -1; - } - - - - evbuffer_drain(source, slen); - conn_cease_transmission(conn); - downcast_steg(s)->type = find_uri_type(outbuf, sizeof(outbuf)); - 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. */ - - if (evbuffer_get_length(source) < 72) - return x_http2_client_uri_transmit(s, source, conn); //@@ - return x_http2_client_cookie_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, HTTP_CONTENT_JAVASCRIPT); - break; - - case HTTP_CONTENT_HTML: - rval = x_http2_server_JS_transmit(s, source, conn, HTTP_CONTENT_HTML); - 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 *p; - unsigned char c, h, secondhalf; - char outbuf[MAX_COOKIE_SIZE]; - int sofar = 0; - int cookie_mode = 0; - - - 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+4); - - if (data == NULL) { - log_debug("SERVER evbuffer_pullup fails"); - return RECV_BAD; - } - - - data[s2.pos+3] = 0; - - type = find_uri_type((char *)data, s2.pos+4); - - if (strstr((char*) data, "Cookie") != NULL) { - p = (unsigned char*) strstr((char*) data, "Cookie:") + sizeof "Cookie: "-1; - cookie_mode = 1; - } - else - p = data + sizeof "GET /" -1; - - - secondhalf = 0; - c = 0; - - - while (strncmp((char*) p, "\r\n", 2) != 0 && (cookie_mode != 0 || p[0] != '.')) { - if (!secondhalf) - c = 0; - if ('0' <= *p && *p <= '9') - h = *p - '0'; - else if ('a' <= *p && *p <= 'f') - h = *p - 'a' + 10; - else { - p++; - continue; - } - - c = (c << 4) + h; - if (secondhalf) { - outbuf[sofar++] = c; - cnt++; - } - secondhalf = !secondhalf; - p++; - } - - outbuf[sofar] = 0; - - if (secondhalf) { - fprintf(stderr, "incorrect cookie or uri recovery \n"); - exit(-1); - } - - - - if (evbuffer_add(dest, outbuf, sofar)) { - log_debug("Failed to transfer buffer"); - return RECV_BAD; - } - evbuffer_drain(source, s2.pos + sizeof("\r\n\r\n") - 1); - } 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: - case HTTP_CONTENT_HTML: - 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 deleted file mode 100644 index 63aa412..0000000 --- a/src/steg/zpack.c +++ /dev/null @@ -1,408 +0,0 @@ -#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.cc b/src/steg/zpack.cc new file mode 100644 index 0000000..63aa412 --- /dev/null +++ b/src/steg/zpack.cc @@ -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); */ + + +/* } */
tor-commits@lists.torproject.org