Browse Source

added stream ids and downstream replacement

cecylia 8 years ago
parent
commit
b48a863b7f
10 changed files with 964 additions and 208 deletions
  1. 51 0
      client/loadpage.js
  2. 10 0
      client/socks5proxy.c
  3. 38 19
      server/crypto.c
  4. 1 1
      server/crypto.h
  5. 123 50
      server/flow.c
  6. 23 5
      server/flow.h
  7. 678 110
      server/relay.c
  8. 22 2
      server/relay.h
  9. 14 17
      server/slitheen-proxy.c
  10. 4 4
      server/slitheen.h

+ 51 - 0
client/loadpage.js

@@ -0,0 +1,51 @@
+var fs = require('fs');
+var page = require('webpage').create();
+var initial_done = false;
+var initial_bytes = '';
+
+var output = fs.open("OUS_out", {mode: 'wb'});
+page.onResourceRequested = function(request, network) {
+	console.log('Request ' + JSON.stringify(request, undefined, 4));
+	if(!initial_done){
+		network.setHeader('X-Slitheen', initial_bytes);
+		initial_done = true;
+	} else if(fs.isFile("OUS_in")){
+		var bytes = fs.read("OUS_in");
+		if(bytes != ''){
+			fs.remove("OUS_in");
+			bytes.replace(/\r?\n|\r/g, "");
+			console.log('Read in '+bytes.length+ ' bytes:' + bytes);
+			network.setHeader('X-Slitheen', bytes);
+		}
+	}
+
+};
+
+//TODO: on partial resource data coming in
+page.onResourceReceived = function(response) {
+	console.log('Receive ' + JSON.stringify(response, undefined, 4));
+	if(response.contentType == "slitheen"){
+		console.log("WOOOOOOO\n");
+		fs.write("slitheen.out", response.body, 'a');
+	}
+};
+
+for(;;){
+	if(fs.isFile("OUS_in")){
+		var initial_bytes = fs.read("OUS_in");
+		if(initial_bytes != ''){
+			fs.remove("OUS_in");
+			initial_bytes.replace(/\r?\n|\r/g, "");
+			console.log('Read in '+initial_bytes.length+ ' bytes:' + initial_bytes);
+
+			page.open('https://cs.uwaterloo.ca', function(status) {
+			  console.log("Status: " + status);
+			  if(status === "success") {
+				page.render('example.png');
+			  }
+			  phantom.exit();
+			});
+			break;
+		}
+	}
+}

+ 10 - 0
client/socks5proxy.c

@@ -22,6 +22,9 @@ int main(void){
 	struct sockaddr_in address;
 	struct sockaddr_in remote_addr;
 	socklen_t addr_size;
+
+	mkfifo("OUS_in", 0666);
+	mkfifo("OUS_out", 0666);
 	if (!(listen_socket = socket(AF_INET, SOCK_STREAM, 0))){
 		printf("Error creating socket\n");
 		return 1;
@@ -146,6 +149,13 @@ int proxy_data(int sockfd){
 		goto err;
 	}
 
+	//send connect request to OUS
+	fd = open("OUS_in", O_WRONLY);
+
+	write(fd, "Hi", sizeof("Hi"));
+	close(fd);
+
+
 	//from this point on, this code will live on slitheen relay
 
     struct sockaddr_in dest;

+ 38 - 19
server/crypto.c

@@ -128,7 +128,6 @@ int encrypt(flow *f, uint8_t *input, uint8_t *output, int32_t len, int32_t incom
 		return 1;
 	}
 
-	//TODO: wrap my mind around this, might need 2 more
 	uint8_t *seq;
 	seq = (incoming) ? f->read_seq : f->write_seq;
 
@@ -163,6 +162,7 @@ int encrypt(flow *f, uint8_t *input, uint8_t *output, int32_t len, int32_t incom
 	buf[12] = len & 0xff;//len *0xff;
 	int32_t pad = EVP_CIPHER_CTX_ctrl(ds, EVP_CTRL_AEAD_TLS1_AAD,
 			13, buf); // = int32_t pad?
+	printf("pad: %d\n", pad);
 
 	if(enc)
 		len += pad;
@@ -170,6 +170,14 @@ int encrypt(flow *f, uint8_t *input, uint8_t *output, int32_t len, int32_t incom
 	int32_t n = EVP_Cipher(ds, p, p, len); //decrypt in place
 	if(n<0) return 0;
 
+#ifdef DEBUG
+	printf("decrypted data:\n");
+	for(int i=0; i< len; i++){
+		printf("%02x ", p[EVP_GCM_TLS_EXPLICIT_IV_LEN+i]);
+	}
+	printf("\n");
+#endif
+
 	if(!enc)
 		p[EVP_GCM_TLS_EXPLICIT_IV_LEN+n] = '\0';
 
@@ -233,6 +241,7 @@ int verify_finish_hash(flow *f, uint8_t *p, int32_t incoming){
  *  	0 on success, 1 on failure
  */
 int compute_master_secret(flow *f){
+	printf("Computing master secret (%x:%d -> %x:%d)...\n", f->src_ip.s_addr, f->src_port, f->dst_ip.s_addr, f->dst_port);
 	DH *dh_srvr = NULL;
 	DH *dh_clnt = NULL;
 	BN_CTX *ctx;
@@ -246,6 +255,7 @@ int compute_master_secret(flow *f){
 
     uint32_t l = dh_clnt->length ? dh_clnt->length : BN_num_bits(dh_clnt->p) - 1;
     int32_t bytes = (l+7) / 8;
+	printf("length of dh param: %d\n", bytes);
 
     uint8_t *buf = (uint8_t *)OPENSSL_malloc(bytes);
     if (buf == NULL){
@@ -256,18 +266,23 @@ int compute_master_secret(flow *f){
 	pub_key = BN_new();
 	priv_key = BN_new();
 
+	printf("key =");
+	for(int i=0; i< 16; i++)
+		printf(" %02x", f->key[i]);
+	printf("\n");
+
 	PRF(f->key, 16,
 		(uint8_t *) SLITHEEN_KEYGEN_CONST, SLITHEEN_KEYGEN_CONST_SIZE,
 		NULL, 0, NULL, 0, NULL, 0,
 		buf, bytes);
 
-#ifdef DEBUG
+//#ifdef DEBUG
 	printf("Generated the following rand bytes: ");
 	for(int i=0; i< bytes; i++){
 		printf(" %02x ", buf[i]);
 	}
 	printf("\n");
-#endif
+//#endif
 
     if (!BN_bin2bn(buf, bytes, priv_key))
 		return 1;
@@ -287,28 +302,28 @@ int compute_master_secret(flow *f){
     dh_clnt->priv_key = priv_key;
 
 	// Compute master key 
-	uint8_t *pre_master_secret = calloc(1, 256);//TODO: find right length
+	uint8_t *pre_master_secret = calloc(1, PRE_MASTER_MAX_LEN);//TODO: find right length
 
-	DH_compute_key(pre_master_secret, dh_srvr->pub_key, dh_clnt);
+	int32_t pre_master_len = DH_compute_key(pre_master_secret, dh_srvr->pub_key, dh_clnt);
 	
-	PRF(pre_master_secret, PRE_MASTER_LEN, (uint8_t *) TLS_MD_MASTER_SECRET_CONST, TLS_MD_MASTER_SECRET_CONST_SIZE, f->client_random, SSL3_RANDOM_SIZE, f->server_random, SSL3_RANDOM_SIZE, NULL, 0, f->master_secret, SSL3_MASTER_SECRET_SIZE);
+	PRF(pre_master_secret, pre_master_len, (uint8_t *) TLS_MD_MASTER_SECRET_CONST, TLS_MD_MASTER_SECRET_CONST_SIZE, f->client_random, SSL3_RANDOM_SIZE, f->server_random, SSL3_RANDOM_SIZE, NULL, 0, f->master_secret, SSL3_MASTER_SECRET_SIZE);
 
 	if(f->current_session != NULL)
 		memcpy(f->current_session, f->master_secret, SSL3_MASTER_SECRET_SIZE);
 
 #ifdef DEBUG
-    fprintf(stderr, "Premaster Secret:\n");
-    BIO_dump_fp(stderr, (char *)pre_master_secret, PRE_MASTER_LEN);
-    fprintf(stderr, "Client Random:\n");
-    BIO_dump_fp(stderr, (char *)f->client_random, SSL3_RANDOM_SIZE);
-    fprintf(stderr, "Server Random:\n");
-    BIO_dump_fp(stderr, (char *)f->server_random, SSL3_RANDOM_SIZE);
-    fprintf(stderr, "Master Secret:\n");
-    BIO_dump_fp(stderr, (char *)f->master_secret, SSL3_MASTER_SECRET_SIZE);
+    fprintf(stdout, "Premaster Secret:\n");
+    BIO_dump_fp(stdout, (char *)pre_master_secret, pre_master_len);
+    fprintf(stdout, "Client Random:\n");
+    BIO_dump_fp(stdout, (char *)f->client_random, SSL3_RANDOM_SIZE);
+    fprintf(stdout, "Server Random:\n");
+    BIO_dump_fp(stdout, (char *)f->server_random, SSL3_RANDOM_SIZE);
+    fprintf(stdout, "Master Secret:\n");
+    BIO_dump_fp(stdout, (char *)f->master_secret, SSL3_MASTER_SECRET_SIZE);
 #endif
 
 	//remove pre_master_secret from memory
-	memset(pre_master_secret, 0, PRE_MASTER_LEN);
+	memset(pre_master_secret, 0, PRE_MASTER_MAX_LEN);
 
 	free(pre_master_secret);
 	DH_free(dh_srvr);
@@ -464,25 +479,25 @@ int init_ciphers(flow *f){
 			key_block, total_len);
 
 #ifdef DEBUG
-	printf("master secret:\n");
+	printf("master secret: (%x:%d -> %x:%d)\n", f->src_ip.s_addr, f->src_port, f->dst_ip.s_addr, f->dst_port);
 	for(int i=0; i< SSL3_MASTER_SECRET_SIZE; i++){
 		printf("%02x ", f->master_secret[i]);
 	}
 	printf("\n");
 
-	printf("client random:\n");
+	printf("client random: (%x:%d -> %x:%d)\n", f->src_ip.s_addr, f->src_port, f->dst_ip.s_addr, f->dst_port);
 	for(int i=0; i< SSL3_RANDOM_SIZE; i++){
 		printf("%02x ", f->client_random[i]);
 	}
 	printf("\n");
 
-	printf("server random:\n");
+	printf("server random: (%x:%d -> %x:%d)\n", f->src_ip.s_addr, f->src_port, f->dst_ip.s_addr, f->dst_port);
 	for(int i=0; i< SSL3_RANDOM_SIZE; i++){
 		printf("%02x ", f->server_random[i]);
 	}
 	printf("\n");
 
-	printf("keyblock:\n");
+	printf("keyblock: (%x:%d -> %x:%d)\n", f->src_ip.s_addr, f->src_port, f->dst_ip.s_addr, f->dst_port);
 	for(int i=0; i< total_len; i++){
 		printf("%02x ", key_block[i]);
 	}
@@ -672,6 +687,10 @@ void check_handshake(struct packet_info *info, flow f){
 			}
 
 			memcpy(flow_ptr->client_random, hello_rand, SSL3_RANDOM_SIZE);
+			for(int i=0; i< SSL3_RANDOM_SIZE; i++){
+				printf("%02x ", hello_rand[i]);
+			}
+			printf("\n");
 			
 			printf("Saved new flow\n");
 

+ 1 - 1
server/crypto.h

@@ -23,7 +23,7 @@ int init_ciphers(flow *f);
 void update_context(flow *f, uint8_t *input, int32_t len, int32_t incoming, int32_t type, int32_t enc);
 void check_handshake(struct packet_info *info, flow f);
 
-#define PRE_MASTER_LEN 256
+#define PRE_MASTER_MAX_LEN 512
 
 #define SLITHEEN_KEYGEN_CONST "SLITHEEN_KEYGEN"
 #define SLITHEEN_KEYGEN_CONST_SIZE 15

+ 123 - 50
server/flow.c

@@ -6,22 +6,22 @@
 #include "flow.h"
 #include "crypto.h"
 #include "slitheen.h"
+#include "relay.h"
 
 static flow_table *table;
 static session_cache *sessions;
+data_queue *downstream_queue;
 
 /* Initialize the table of tagged flows */
-int init_flow_table(void) {
+int init_tables(void) {
 
-	table = malloc(sizeof(flow_table));
-	
-	table->table = (flow *) malloc(sizeof(flow)*MAX_FLOWS);
-	if( table->table == NULL){
-		fprintf(stderr, "malloc failed.\n");
-		return 1;
-	}
+	table = calloc(1, sizeof(flow_table));
+	table->first_entry = NULL;
 	table->len = 0;
-	table->max_len = MAX_FLOWS;
+
+	downstream_queue = calloc(1, sizeof(data_queue));
+	downstream_queue->first_block = NULL;
+	printf("initialized downstream queue\n");
 
 	return 0;
 }
@@ -29,15 +29,13 @@ int init_flow_table(void) {
 
 /* Add a new flow to the tagged flow table */
 flow *add_flow(flow newFlow) {
-	flow *ptr;
+	flow_entry *entry = calloc(1, sizeof(flow_entry));
+	flow *ptr = calloc(1, sizeof(flow));
+	entry->f = ptr;
+	entry->next = NULL;
 
-	if(table->len == table->max_len){
-		//grow_table();
-		NULL;
-	}
 	printf("there are %d flows in the table\n", table->len);
 
-	ptr = table->table + table->len;
 	newFlow.state = TLS_CLNT_HELLO;
 	newFlow.in_encrypted = 0;
 	newFlow.out_encrypted = 0;
@@ -47,6 +45,11 @@ flow *add_flow(flow newFlow) {
 	newFlow.packet_chain = NULL;
 	newFlow.censored_queue = NULL;
 	newFlow.outbox_len = 0;
+	newFlow.remaining_record_len = 0;
+	newFlow.remaining_response_len = 0;
+	newFlow.httpstate = PARSE_HEADER;
+	newFlow.replace_response = 0;
+
 
 	newFlow.finish_md_ctx = EVP_MD_CTX_create();
 	const EVP_MD *md = EVP_sha384();
@@ -54,8 +57,18 @@ flow *add_flow(flow newFlow) {
 
 	memset(newFlow.read_seq, 0, 8);
 	memset(newFlow.write_seq, 0, 8);
+
 	*ptr = newFlow;
 
+	flow_entry *last = table->first_entry;
+	if(last == NULL){
+		table->first_entry = entry;
+	} else {
+		for(int i=0; i< table->len-1; i++){
+			last = last->next;
+		}
+		last->next = entry;
+	}
 	table->len ++;
 
 	return ptr;
@@ -118,8 +131,20 @@ int update_flow(flow *f) {
 			p += RECORD_HEADER_LEN;
 
 			if((incoming && f->in_encrypted) || (!incoming && f->out_encrypted)){
+				printf("Decrypting finished (%d bytes) (%x:%d -> %x:%d)\n", record_len - RECORD_HEADER_LEN, f->src_ip.s_addr, f->src_port, f->dst_ip.s_addr, f->dst_port);
 				int32_t n = encrypt(f, p, p, record_len - RECORD_HEADER_LEN, incoming, 0x16, 0);
+				if(n<=0){
+					printf("Error decrypting finished  (%x:%d -> %x:%d)\n", f->src_ip.s_addr, f->src_port, f->dst_ip.s_addr, f->dst_port);
+				}
+				printf("Finished decrypted: (%x:%d -> %x:%d)\n", f->src_ip.s_addr, f->src_port, f->dst_ip.s_addr, f->dst_port);
 				p += EVP_GCM_TLS_EXPLICIT_IV_LEN;
+				
+				printf("record:\n");
+				for(int i=0; i< n; i++){
+					printf("%02x ", p[i]);
+				}
+				printf("\n");
+
 				update_context(f, p, n, incoming, 0x16, 0);
 				if(incoming) f->in_encrypted = 2;
 				else f->out_encrypted = 2;
@@ -129,12 +154,12 @@ int update_flow(flow *f) {
 
 			switch(f->state){
 				case TLS_CLNT_HELLO: 
+					printf("Received tagged client hello (%x:%d -> %x:%d)\n", f->src_ip.s_addr, f->src_port, f->dst_ip.s_addr, f->dst_port);
 					update_finish_hash(f, p);
 					check_session(f, p, HANDSHAKE_MESSAGE_LEN(handshake_hdr));
-					printf("Shaking hands...\n");
 					break;
 				case TLS_SERV_HELLO:
-					printf("Received server hello\n");
+					printf("Received server hello (%x:%d -> %x:%d)\n", f->src_ip.s_addr, f->src_port, f->dst_ip.s_addr, f->dst_port);
 					if(f->resume_session){
 						verify_session_id(f,p);
 					} else {
@@ -153,6 +178,7 @@ int update_flow(flow *f) {
 					update_finish_hash(f, p);
 					break;
 				case TLS_SRVR_KEYEX:
+					printf("Received server keyex\n");
 					update_finish_hash(f, p);
 
 					if(extract_parameters(f, p)){
@@ -178,6 +204,7 @@ int update_flow(flow *f) {
 					update_finish_hash(f, p);
 					break;
 				case TLS_FINISHED:
+					printf("Received finished (%x:%d -> %x:%d)\n", f->src_ip.s_addr, f->src_port, f->dst_ip.s_addr, f->dst_port);
 					verify_finish_hash(f,p, incoming);
 					update_finish_hash(f, p);
 					if((f->in_encrypted == 2) && (f->out_encrypted == 2)){
@@ -193,7 +220,7 @@ int update_flow(flow *f) {
 					}
 					break;
 				default:
-					printf("Error? %02x\n",p[0]);
+					printf("Error? (%x:%d -> %x:%d)...\n", f->src_ip.s_addr, f->src_port, f->dst_ip.s_addr, f->dst_port);
 					break;
 			}
 			break;
@@ -201,7 +228,7 @@ int update_flow(flow *f) {
 			printf("Application Data\n");
 			break;
 		case CCS:
-			printf("CCS\n");
+			printf("CCS (%x:%d -> %x:%d) \n", f->src_ip.s_addr, f->src_port, f->dst_ip.s_addr, f->dst_port);
 			if(incoming){
 				f->in_encrypted = 1;
 			} else {
@@ -214,16 +241,19 @@ int update_flow(flow *f) {
 		case ALERT:
 			p = record;
 			p += RECORD_HEADER_LEN;
-
-			encrypt(f, p, p, record_len - RECORD_HEADER_LEN, incoming, 0x16, 0);
-			p += EVP_GCM_TLS_EXPLICIT_IV_LEN;
-			printf("Alert: %02x %02x\n", p[0], p[1]);
+			if(((incoming) && (f->in_encrypted > 0)) || ((!incoming) && (f->out_encrypted > 0))){
+				encrypt(f, p, p, record_len - RECORD_HEADER_LEN, incoming, 0x16, 0);
+				p += EVP_GCM_TLS_EXPLICIT_IV_LEN;
+			}
+			printf("Alert (%x:%d -> %x:%d) %02x %02x \n", f->src_ip.s_addr, f->src_port, f->dst_ip.s_addr, f->dst_port, p[0], p[1]);
+			fflush(stdout);
 			break;
 		case HB:
 			printf("Heartbeat\n");
 			break;
 		default:
-			printf("Error: Not a Record\n");
+			printf("Error: Not a Record (%x:%d -> %x:%d)\n", f->src_ip.s_addr, f->src_port, f->dst_ip.s_addr, f->dst_port);
+			fflush(stdout);
 			//TODO: later figure this out, for now delete
 			f->packet_chain = f->packet_chain->next;
 
@@ -261,23 +291,42 @@ int update_flow(flow *f) {
  *  Output:
  *  	0 on success, 1 on failure
  */
-int remove_flow(int index) {
-	int i;
-	flow *ptr;
+int remove_flow(flow *f) {
+
+	flow_entry *entry = table->first_entry;
+	if(entry->f == f){
+		table->first_entry = entry->next;
+		free(entry->f);
+		free(entry);
+		printf("flow removed!\n");
+		fflush(stdout);
+		table->len --;
+		return 0;
+	}
 
-	if(index){
-		ptr = table->table + index -1;
-		for(i=0; i< table->len - index; i++){
-			ptr += i;
-			*ptr = *(ptr + 1);
+	flow_entry *next;
+	for(int i=0; i< table->len; i++){
+		if(entry->next != NULL){
+			next = entry->next;
+		} else {
+			printf("Flow not in table\n");
+			return 1;
 		}
-		table->len --;
-	} else {
-		return 1;
+
+		if(next->f == f){
+			entry->next = next->next;
+			free(next->f);
+			free(next);
+			printf("flow removed!\n");
+			table->len --;
+			return 0;
+		}
+
+		entry = next;
 	}
 
-	printf("flow removed!\n");
-	return 0;
+	printf("Should not get here!\n");
+	return 1;
 }
 
 /** Expands the flow table when we run out of space
@@ -294,44 +343,57 @@ int grow_table() {
  *  	observed: details for the observed flow
  *
  *  Output:
- *  	index of flow in table or 0 if it doesn't exist
+ *  	index of flow in table or -1 if it doesn't exist
  */
-int check_flow(flow observed){
+flow *check_flow(flow observed){
 	/* Loop through flows in table and see if it exists */
 	int i;
-	flow *candidate = table->table;
+	flow_entry *entry = table->first_entry;
+	flow *candidate;
+	if(entry == NULL)
+		return NULL;
 
 	/* Check first in this direction */
 	for(i=0; i<table->len; i++){
-		candidate += i;
+		if(entry == NULL){
+			printf("Error: entry is null\n");
+			break;
+		}
+		candidate = entry->f;
 		if(candidate->src_ip.s_addr == observed.src_ip.s_addr){
 			if(candidate->dst_ip.s_addr == observed.dst_ip.s_addr){
 				if(candidate->src_port == observed.src_port){
 					if(candidate->dst_port == observed.dst_port){
-						return i+1;
+						return candidate;
 					}
 				}
 			}
 		}
+		entry = entry->next;
 	}
 
 
-	candidate = table->table;
+	entry = table->first_entry;
 	/* Then in the other direction */
 	for(i=0; i<table->len; i++){
-		candidate += i;
+		if(entry == NULL){
+			printf("Error: entry is null\n");
+			break;
+		}
+		candidate = entry->f;
 		if(candidate->src_ip.s_addr == observed.dst_ip.s_addr){
 			if(candidate->dst_ip.s_addr == observed.src_ip.s_addr){
 				if(candidate->src_port == observed.dst_port){
 					if(candidate->dst_port == observed.src_port){
-						return i+1;
+						return candidate;
 					}
 				}
 			}
 		}
+		entry = entry->next;
 	}
 
-	return 0;
+	return NULL;
 }
 
 /** Returns the flow in the flow table at a specified index
@@ -341,7 +403,6 @@ int check_flow(flow observed){
  *
  *  Output:
  *  	the flow at the specified index
- */
 flow *get_flow(int index){
 	if(index < table->len){
 		return table->table+index;
@@ -349,6 +410,7 @@ flow *get_flow(int index){
 		return NULL;
 	}
 }
+ */
 
 int init_session_cache(void){
 	sessions = malloc(sizeof(session_cache));
@@ -396,6 +458,11 @@ int verify_session_id(flow *f, uint8_t *hs){
 				if(!memcmp(last->session_ticket, f->current_session->session_ticket, f->current_session->session_ticket_len)){
 					memcpy(f->master_secret, last->master_secret, SSL3_MASTER_SECRET_SIZE);
 					found = 1;
+					printf("Found new session ticket (%x:%d -> %x:%d)\n", f->src_ip.s_addr, f->src_port, f->dst_ip.s_addr, f->dst_port);
+					for(int i=0; i< last->session_ticket_len; i++){
+						printf("%02x ", last->session_ticket[i]);
+					}
+					printf("\n");
 				}
 				last = last->next;
 			}
@@ -438,6 +505,7 @@ int check_session(flow *f, uint8_t *hs, uint32_t len){
 		f->resume_session = 1;
 		memcpy(new_session->session_id, p, new_session->session_id_len);
 		new_session->next = NULL;
+		printf("Requested new session (%x:%d -> %x:%d)\n", f->src_ip.s_addr, f->src_port, f->dst_ip.s_addr, f->dst_port);
 
 		f->current_session = new_session;
 	}
@@ -512,7 +580,6 @@ int save_session_id(flow *f, uint8_t *hs){
 	if(new_session->session_id_len <= 0){
 		//if this value is zero, the session is non-resumable or the
 		//server will issue a NewSessionTicket handshake message
-		printf("USING TLS SESSION TICKET\n");
 		free(new_session);
 		return 0;
 	}
@@ -567,6 +634,7 @@ int save_session_ticket(flow *f, uint8_t *hs, uint32_t len){
 	
 	new_session->session_ticket_len = (p[0] << 8) + p[1];
 	printf("saving ticket of size %d\n", new_session->session_ticket_len);
+	fflush(stdout);
 	p += 2;
 
 	uint8_t *ticket = calloc(1, new_session->session_ticket_len);
@@ -579,9 +647,11 @@ int save_session_ticket(flow *f, uint8_t *hs, uint32_t len){
 	} else {
 		session *last = sessions->first_session;
 
-		for(int i=0; i< sessions->length; i++){
-			if(last == NULL)
+		for(int i=0; i< (sessions->length-1); i++){
+			if(last == NULL){
 				printf("UH OH: last is null?\n");
+				fflush(stdout);
+			}
 			last = last->next;
 		}
 		last->next = new_session;
@@ -594,14 +664,17 @@ int save_session_ticket(flow *f, uint8_t *hs, uint32_t len){
 		printf(" %02x", p[i]);
 	}
 	printf("\n");
+	fflush(stdout);
 
 	printf("Saved session master secret:");
 	for(int i=0; i< SSL3_MASTER_SECRET_SIZE; i++){
 		printf(" %02x", new_session->master_secret[i]);
 	}
 	printf("\n");
+	fflush(stdout);
 
 	printf("THERE ARE NOW %d saved sessions\n", sessions->length);
+	fflush(stdout);
 
 	return 0;
 }

+ 23 - 5
server/flow.h

@@ -35,8 +35,15 @@ typedef struct queue_block_st{
 	int32_t offset;
 	uint8_t *data;
 	struct queue_block_st *next;
+	uint8_t stream_id;
 } queue_block;
 
+typedef struct data_queue_st {
+	queue_block *first_block;
+} data_queue;
+
+extern data_queue *downstream_queue;
+
 typedef struct packet_st packet;
 
 typedef struct session_st {
@@ -89,26 +96,37 @@ typedef struct flow_st {
 	uint8_t read_seq[8];
 	uint8_t write_seq[8];
 
+	//for downstream processing
+	uint32_t remaining_record_len;
+	uint8_t httpstate;
+	uint32_t remaining_response_len;
+	uint8_t replace_response;
+
 	uint8_t *outbox;
 	int32_t outbox_len;
+	int32_t outbox_data_len;
 
 	//locking
 	//pthread_mutex_t flow_lock = PTHREAD_MUTEX_INITIALIZER;
 
 } flow;
 
+typedef struct flow_entry_st {
+	flow *f;
+	struct flow_entry_st *next;
+} flow_entry;
+
 typedef struct flow_table_st {
-	flow *table;
+	flow_entry *first_entry;
 	int len;
-	int max_len;
 } flow_table;
 
 
-int init_flow_table (void);
+int init_tables(void);
 flow *add_flow(flow newFlow);
 int update_flow(flow *f);
-int remove_flow(int index);
-int check_flow(flow observed);
+int remove_flow(flow *f);
+flow *check_flow(flow observed);
 flow *get_flow(int index);
 
 int init_session_cache (void);

+ 678 - 110
server/relay.c

@@ -8,6 +8,11 @@
 #include <netdb.h>
 #include <unistd.h>
 #include <pthread.h>
+#include <string.h>
+
+#include <openssl/bio.h>
+#include <openssl/evp.h>
+
 #include "relay.h"
 #include "slitheen.h"
 #include "flow.h"
@@ -58,7 +63,7 @@ int replace_packet(flow *f, struct packet_info *info){
 			f->seq_num += info->app_data_len;
 		/* if incoming, replace with data from queue */
 		//if(htonl(tcp_hdr->sequence_num) >= f->seq_num){
-			replace_contents(f, offset, info);
+			process_downstream(f, offset, info);
 		//}//TODO: need to do something about replaying packets (maybe store previously sent data??
 
 
@@ -79,10 +84,13 @@ int replace_packet(flow *f, struct packet_info *info){
 }
 
 /** Reads the HTTP header of upstream data and searches for
- *  a covert request in the x-ignore header. Sends this
+ *  a covert request in an x-slitheen header. Sends this
  *  request to the indicated site and saves the response to
  *  the censored queue
  *
+ *  TODO: change this to take SOCKS5 proxy connection and then relay data
+ *  	based on stream ID
+ *
  *  Inputs:
  *  	f: the tagged flow
  *  	info: the processed received packet
@@ -107,8 +115,10 @@ int read_header(flow *f, struct packet_info *info){
 	memcpy(decrypted_data, p, record_length);
 
 	if(!encrypt(f, decrypted_data, decrypted_data, record_length, 0, record_hdr->type, 0)){
-		fprintf(stdout,"decryption failed\n");
+		fprintf(stdout,"upstream decryption failed\n");
 		return 0;
+	} else {
+		fprintf(stdout, "upstream decryption succeeded\n");
 	}
 
 	if(record_hdr->type == 0x15){
@@ -120,64 +130,62 @@ int read_header(flow *f, struct packet_info *info){
 	}
 
 	//TODO: re-write this to take a SOCKS connection request
-
 	/* search through decrypted data for x-ignore */
-	regex_t r;
-	const char *regex_text = "x-ignore";
-	const char *match = (char *) decrypted_data;
-	if(regcomp(&r, regex_text, REG_EXTENDED|REG_NEWLINE)){
-		printf("could not compile regex\n");
+	char *header_ptr = strstr((const char *) decrypted_data, "X-Slitheen");
+	uint8_t *upstream_data;
+	if(header_ptr == NULL){
+		printf("UPSTREAM: No x-slitheen header found\n");
 		return 0;
 	}
-
-	regmatch_t m;
-	if(regexec(&r, match, 1, &m, 0)){
-		printf("Upstream data: no x-ignore header\n");
-		return 0;
-	} 
-	uint8_t *message = decrypted_data;
-	//remove escape characters
-	for(int i=m.rm_eo+2; i< strlen(match); i++){
-		if(match[i] != '\\'){
-			*(message++) = match[i];
-		} else if (match[i+1] == 'r') {
-			*(message++) = '\r';
-			i++;
-		} else if (match[i+1] == 'n') {
-			*(message++) = '\n';
-			i++;
-		}
-	}
-	*message = '\0';
-	message = decrypted_data;
-
-	regex_t r2;
-	const char *regex_text2 = "host";
-	const char *match2 = (char *) decrypted_data;
-	regmatch_t m2;
-	if(regcomp(&r2, regex_text2, REG_EXTENDED|REG_NEWLINE)){
-		printf("could not compile regex\n");
-		return 0;
+	printf("UPSTREAM: Found x-slitheen header\n");
+	fprintf(stdout,"UPSTREAM Flow: %x > %x (%s)\n", info->ip_hdr->src.s_addr, info->ip_hdr->dst.s_addr, (info->ip_hdr->src.s_addr != f->src_ip.s_addr)? "incoming":"outgoing");
+	fprintf(stdout, "Sequence number: %d\n", info->tcp_hdr->sequence_num);
+
+	header_ptr += strlen("X-Slitheen: ");
+	char *c = header_ptr;
+	while(*c != '\r'){
+		c++;
 	}
-	if(regexec(&r2, match2, 1, &m2, 0)){
-		printf("no host found\n");
-		return 0;
+	c++;
+	*c = '\0';
+	printf("upstream data: %s\n", header_ptr);
+
+	//b64 decode the data
+	int32_t decode_len = strlen(header_ptr);
+	if(header_ptr[decode_len-2] == '='){
+		decode_len = decode_len*3/4 - 2;
+	} else if(header_ptr[decode_len-1] == '='){
+		decode_len = decode_len*3/4 - 1;
+	} else {
+		decode_len = decode_len*3/4;
 	}
-	uint8_t server[50];
-	for(int i=m2.rm_eo+2; i< strlen(match2); i++){
-		if(match2[i] != '\n'){
-			server[i-m2.rm_eo-2] = match2[i];
-		} else {
-			server[i-m2.rm_eo-2] = '\0';
-			break;
-		}
+
+	upstream_data = calloc(1, decode_len + 1);
+
+	BIO *bio, *b64;
+	bio = BIO_new_mem_buf(header_ptr, -1);
+	b64 = BIO_new(BIO_f_base64());
+	bio = BIO_push(b64, bio);
+	BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL);
+
+	int32_t output_len = BIO_read(bio, upstream_data, strlen(header_ptr));
+	if(output_len != decode_len)
+		printf("UH OH, lens dont match\n");
+
+	printf("Decoded to get %d bytes:\n", output_len);
+	for(int i=0; i< output_len; i++){
+		printf("%02x ", upstream_data[i]);
 	}
+	printf("\n");
+	uint8_t stream_id = upstream_data[0];
+	p = upstream_data +1;
+
+	printf("Received bytes with stream id %d\n", stream_id);
 
-	printf("Collecting data from censored site: %s\n", server);
-	//If a thread for this stream id exists, get the thread info
-		//TODO: fill this in
+	//If a thread for this stream id exists, get the thread info and pipe data
+	//TODO: fill this in
 
-	//Else, spawn a thread to handle the proxy to this site
+	/*Else, spawn a thread to handle the proxy to this site*/
 	pthread_t *proxy_thread = calloc(1, sizeof(pthread_t));
 	int32_t pipefd[2];
 	if(pipe(pipefd) < 0){
@@ -187,26 +195,28 @@ int read_header(flow *f, struct packet_info *info){
 
 	struct proxy_thread_data *thread_data = 
 		calloc(1, sizeof(struct proxy_thread_data));
+	thread_data->initial_data = upstream_data;
 	thread_data->f = f;
-	memcpy(thread_data->server, server, 50);
-	printf("Collecting data from censored site: %s\n", thread_data->server);
 	thread_data->pipefd = pipefd[0];
 	
 	pthread_create(proxy_thread, NULL, proxy_covert_site, (void *) thread_data);
 
-	//save a reference to the proxy threads in the flow
+	//save a reference to the proxy threads in a global table
 	
 
-	//Now pipe information to the selected or created thread
-	//TODO: message may not be a string. fix.
-	int32_t bytes_written = write(pipefd[1], message,
+	/*int32_t bytes_written = write(pipefd[1], message,
 			strlen( (const char *) message));
 	if(bytes_written < strlen( (const char *) message)){
 		printf("failed to write all bytes to pipe\n");
-	}
+	}*/
 
 	return 0;
 
+err:
+	if(upstream_data != NULL){
+		free(upstream_data);
+	}
+	return 1;
 }
 
 /** Called by spawned pthreads in read_header to send upstream
@@ -218,7 +228,7 @@ int read_header(flow *f, struct packet_info *info){
  *  Input:
  *  	A struct that contains the following information:
  *  	- the tagged flow
- *  	- the name of the server
+ *  	- the initial upstream data (including connect request)
  *  	- the read end of the pipe
  *
  */
@@ -226,42 +236,113 @@ void *proxy_covert_site(void *data){
 
 	struct proxy_thread_data *thread_data =
 		(struct proxy_thread_data *) data;
-
 	flow *f = thread_data->f;
 
-	int32_t buffer_len = BUFSIZ;
-	uint8_t *buffer = calloc(1, BUFSIZ);
-	/* Send GET request to site */
-	printf("Collecting data from censored site: %s\n", thread_data->server);
+	uint8_t *p = thread_data->initial_data;
+	uint8_t stream_id = p[0];
+	p++;
+	struct socks_req *clnt_req = (struct socks_req *) p;
+	p += 4;
 
-	struct hostent *host;
-	host = gethostbyname((const char *) thread_data->server);
+	//see if it's a connect request
+	if(clnt_req->cmd != 0x01){
+		printf("Error: issued a non-connect command\n");
+		goto err;
+	}
 
-	if(host == NULL){
-		printf("gethostbyname failed\n");
-		return 0;
+    struct sockaddr_in dest;
+	dest.sin_family = AF_INET;
+	uint8_t domain_len;
+
+	switch(clnt_req->addr_type){
+	case 0x01:
+		//IPv4
+		dest.sin_addr.s_addr = *((uint32_t*) p);
+		printf("destination addr: %d\n", ntohl(dest.sin_addr.s_addr));
+		p += 4;
+		break;
+		
+	case 0x03:
+		//domain name
+		domain_len = p[0];
+		p++;
+		uint8_t *domain_name = calloc(1, domain_len+1);
+		memcpy(domain_name, p, domain_len);
+		domain_name[domain_len] = '\0';
+		struct hostent *host;
+		host = gethostbyname((const char *) domain_name);
+		dest.sin_addr = *((struct in_addr *) host->h_addr);
+		printf("destination addr: %d\n", ntohl(dest.sin_addr.s_addr));
+
+		p += domain_len;
+		free(domain_name);
+		break;
+	case 0x04:
+		//IPv6
+		goto err;//TODO: fix this
+		break;
 	}
 
-	int32_t handle = socket(AF_INET, SOCK_STREAM, 0);
-	if(handle < 0){
-		printf("error: constructing socket failed\n");
-		return 0;
+	//now set the port
+	dest.sin_port = *((uint16_t *) p);
+	printf("destination port: %d\n", ntohs(dest.sin_port));
+
+    int32_t handle = socket(AF_INET, SOCK_STREAM, 0);
+    if(handle < 0){
+        printf("error: constructing socket failed\n");
+		goto err;
+    }
+
+	struct sockaddr_in my_addr;
+	socklen_t my_addr_len = sizeof(my_addr);
+
+    int32_t error = connect (handle, (struct sockaddr *) &dest, sizeof (struct sockaddr));
+
+    if(error <0){
+        printf("error connecting\n");
+		goto err;
+    }
+
+	getsockname(handle, (struct sockaddr *) &my_addr, &my_addr_len);
+
+	uint8_t *response = calloc(1, 11);
+	//now send the reply to the client
+	response[0] = 0x05;
+	response[1] = 0x00;//TODO: make this accurate
+	response[2] = 0x00;
+	response[3] = 0x01;
+	*((uint32_t *) (response + 4)) = my_addr.sin_addr.s_addr;
+	*((uint16_t *) (response + 8)) = my_addr.sin_port;
+
+	printf("Bound to %x:%d\n", my_addr.sin_addr.s_addr, ntohs(my_addr.sin_port));
+	printf("Downstream response:\n");
+	for(int i=0; i< 10; i++){
+		printf("%02x ", response[i]);
 	}
-	
-	struct sockaddr_in dest;
-	dest.sin_family = AF_INET;
-	dest.sin_port = htons(80);
-	dest.sin_addr = *((struct in_addr *) host->h_addr);
-	bzero (&(dest.sin_zero), 8);
-	
-	int32_t error = connect (handle, (struct sockaddr *) &dest, sizeof (struct sockaddr));
-	
-	if(error <0){
-		printf("error connecting\n");
-		return 0;
+	printf("\n");
+
+	queue_block *new_block = calloc(1, sizeof(queue_block));
+	printf("Inserting new block into queue: %p\n", new_block);
+	fflush(stdout);
+	new_block->len = 10;
+	new_block->offset = 0;
+	new_block->data = response;
+	new_block->next = NULL;
+	new_block->stream_id = stream_id;
+	printf("downstream_queue is at %p\n", downstream_queue);
+	fflush(stdout);
+	if(downstream_queue->first_block == NULL){
+		downstream_queue->first_block = new_block;
+	}
+	else{
+		queue_block *last = downstream_queue->first_block;
+		while(last->next != NULL)
+			last = last->next;
+		last->next = new_block;
 	}
+	return 0; //slowly test
 
-	//now select on reading from the pipe and from the socket
+	/*now select on reading from the pipe and from the socket
 	for(;;){
 		fd_set readfds;
 		fd_set writefds;
@@ -315,7 +396,7 @@ void *proxy_covert_site(void *data){
 			if(bytes_read > 0){
 
 				//make a new queue block
-				queue_block *new_block = calloc(1, sizeof(queue_block));
+				new_block = calloc(1, sizeof(queue_block));
 				new_block->len = bytes_read;
 				new_block->offset = 0;
 				new_block->data = buf;
@@ -337,6 +418,13 @@ void *proxy_covert_site(void *data){
 	free(thread_data);
 	free(buffer);
 	close(handle);
+	*/
+	return 0;
+err:
+	if(thread_data->initial_data != NULL){
+		free(thread_data->initial_data);
+	}
+	close(handle);
 	return 0;
 }
 
@@ -355,14 +443,343 @@ void *proxy_covert_site(void *data){
  *  Output:
  *  	Returns 0 on sucess 
  */
-int replace_contents(flow *f, int32_t offset, struct packet_info *info){
+int process_downstream(flow *f, int32_t offset, struct packet_info *info){
+
+	printf("Processing downstream data\n");
+	uint8_t changed = 0;
+
+	uint8_t *p = info->app_data;
+	uint32_t remaining_packet_len = info->app_data_len;
+	printf("This packet has %d bytes of application data\n", remaining_packet_len);
+	printf("Sequence number: %u:%u\n", htonl(info->tcp_hdr->sequence_num), htonl(info->tcp_hdr->sequence_num)+ remaining_packet_len);
+	fflush(stdout);
+
+	if(f->remaining_record_len > 0){
+		//ignore bytes until the end of the record
+		if(f->remaining_record_len > remaining_packet_len){ //ignore entire packet
+			f->remaining_record_len -= remaining_packet_len;
+			printf("Ignoring %d bytes, %d bytes left to ignore\n", remaining_packet_len, f->remaining_record_len);
+			remaining_packet_len -= remaining_packet_len;
+		} else {
+			p += f->remaining_record_len;
+			printf("Ignoring %d bytes\n", f->remaining_record_len);
+			remaining_packet_len -= f->remaining_record_len;
+			f->remaining_record_len = 0;
+		}
+
+	}
+
+	printf("There are now %d bytes remaining\n", remaining_packet_len);
+	fflush(stdout);
+	while(remaining_packet_len > 0){ //while bytes remain in the packet
+		
+		struct record_header *record_hdr = (struct record_header*) p;
+		uint32_t record_len = RECORD_LEN(record_hdr);
+		printf("Record length: %d\n", record_len);
+
+		fprintf(stdout, "Record:\n");
+		for(int i=0; i< RECORD_HEADER_LEN; i++){
+			printf("%02x ", p[i]);
+		}
+		printf("\n");
+
+		p += RECORD_HEADER_LEN;
+		remaining_packet_len -= RECORD_HEADER_LEN;
+
+		uint8_t *record_ptr = p; //points to the beginning of record data
+		uint32_t remaining_record_len = record_len;
+
+		if(record_len > remaining_packet_len){
+			//for now, just forfeit this record
+			printf("Record is too long\n");
+			f->remaining_record_len = record_len - remaining_packet_len;
+			remaining_packet_len -= remaining_packet_len;
+
+			if(f->httpstate == PARSE_HEADER || f->httpstate == BEGIN_CHUNK || f->httpstate == END_CHUNK){
+				f->httpstate = FORFEIT_REST;
+			} else if( f->httpstate == MID_CONTENT || f->httpstate == MID_CHUNK){
+				f->remaining_response_len -= record_len - 24; //len of IV and padding
+				printf("remaining response len: %d\n", f->remaining_response_len);
+				if(f->remaining_response_len == 0){
+					if(f->httpstate == MID_CHUNK)
+						f->httpstate = END_CHUNK;
+					else 
+						f->httpstate = END_BODY;
+				}
+				if(f->remaining_response_len < 0){
+					f->remaining_response_len = 0;
+					f->httpstate = FORFEIT_REST;
+				}
+			}
+			break;
+		}
+
+
+		//now decrypt the record
+		printf("Decrypting record\n");
+		int32_t n = encrypt(f, record_ptr, record_ptr, record_len, 1,
+						record_hdr->type, 0);
+		if(n < 0){
+			//do something smarter here
+			fprintf(stdout,"application decryption failed\n");
+			return 0;
+		}
+		changed = 1;
+		printf("Decryption succeeded\n");
+		
+		printf("Bytes:\n");
+		for(int i=0; i< n; i++){
+			printf("%02x ", record_ptr[EVP_GCM_TLS_EXPLICIT_IV_LEN+i]);
+		}
+		printf("\n");
+		printf("Text:\n");
+		printf("%s\n", record_ptr+EVP_GCM_TLS_EXPLICIT_IV_LEN);
+
+		p += EVP_GCM_TLS_EXPLICIT_IV_LEN;
+		char *len_ptr, *needle;
+
+		remaining_record_len = n;
+
+		while(remaining_record_len > 0){
+
+			switch(f->httpstate){
+
+				case PARSE_HEADER:
+					//determine whether it's transfer encoded or otherwise
+					len_ptr = strstr((const char *) p, "Transfer-Encoding");
+					if(len_ptr != NULL){
+						if(!memcmp(len_ptr + 19, "chunked", 7)){
+							printf("SLITHEEN: transfer chunked\n");
+							//now find end of header
+							
+							len_ptr = strstr((const char *) p, "\r\n\r\n");
+							if(len_ptr != NULL){
+								f->httpstate = BEGIN_CHUNK;
+								remaining_record_len -= (((uint8_t *)len_ptr - p) + 4);
+								p = (uint8_t *) len_ptr + 4;
+								printf("remaining record len: %d\n", remaining_record_len);
+							}
+						}
+					} else {
+						len_ptr = strstr((const char *) p, "Content-Length");
+						if(len_ptr != NULL){
+							len_ptr += 15;
+							f->remaining_response_len = strtol((const char *) len_ptr, NULL, 10);
+							printf("SLITHEEN: Content-Length: %d\n", f->remaining_response_len);
+							len_ptr = strstr((const char *) p, "\r\n\r\n");
+							if(len_ptr != NULL){
+								f->httpstate = MID_CONTENT;
+								remaining_record_len -= (((uint8_t *)len_ptr - p) + 4);
+								p = (uint8_t *) len_ptr + 4;
+								printf("remaining record len: %d\n", remaining_record_len);
+							} else {
+								printf("UH OH\n");
+								remaining_record_len = 0;
+								f->httpstate = FORFEIT_REST;
+							}
+						} else {
+							f->httpstate = FORFEIT_REST;
+							remaining_record_len = 0;
+						}
+					}
+
+					//figure out what the content-type is
+					len_ptr = strstr((const char *) record_ptr, "Content-Type: image");
+					if(len_ptr != NULL){
+						printf("SLITHEEN: replaceable content\n");
+						f->replace_response = 1;
+						memcpy(len_ptr + 14, "slitheen", 8);
+						char *c = len_ptr + 14+8;
+						while(c[0] != '\r'){
+							c[0] = ' ';
+							c++;
+						}
+					} else {
+						printf("SLITHEEN: non-replaceable content\n");
+						f->replace_response = 0;
+					}
+					break;
+
+				case MID_CONTENT:
+					//check if content is replaceable
+					printf("In MID_CONTENT\n");
+					if(f->remaining_response_len > remaining_record_len){
+						if(f->replace_response){
+							fill_with_downstream(f, p, remaining_record_len);
+							printf("Replaced with:\n");
+							for(int i=0; i< remaining_record_len; i++){
+								printf("%02x ", p[i]);
+							}
+							printf("\n");
+						}
+						f->remaining_response_len -= remaining_record_len;
+						p += remaining_record_len;
+					
+						remaining_record_len = 0;
+						printf("%d bytes left of body\n", f->remaining_response_len);
+					} else {
+						if(f->replace_response){
+							fill_with_downstream(f, p, remaining_record_len);
+							printf("Replaced with:\n");
+							for(int i=0; i< remaining_record_len; i++){
+								printf("%02x ", p[i]);
+							}
+							printf("\n");
+						}
+						remaining_record_len -= f->remaining_response_len;
+						p += f->remaining_response_len;
+						f->httpstate = PARSE_HEADER;
+						f->remaining_response_len = 0;
+						printf("rem. response len = %d\n", f->remaining_response_len);
+					}
+					break;
+
+				case BEGIN_CHUNK:
+					printf("In BEGIN_CHUNK\n");
+
+					int32_t chunk_size = strtol((const char *) p, NULL, 16);
+					printf("chunk size: %x\n", chunk_size);
+					if(chunk_size == 0){
+						f->httpstate = END_BODY;
+					} else {
+						f->httpstate = MID_CHUNK;
+					}
+					f->remaining_response_len = chunk_size;
+					needle = strstr((const char *) p, "\r\n");
+					if(needle != NULL){
+						remaining_record_len -= ((uint8_t *) needle - p + 2);
+						p = (uint8_t *) needle + 2;
+						printf("remaining_record len = %d\n", remaining_record_len);
+					} else {
+						printf("UH OH\n");
+						remaining_record_len = 0;
+						f->httpstate = FORFEIT_REST;
+					}
+					break;
+
+				case MID_CHUNK:
+					printf("In MID_CHUNK\n");
+					if(f->remaining_response_len > remaining_record_len){
+						if(f->replace_response){
+							fill_with_downstream(f, p, remaining_record_len);
+							printf("Replaced with:\n");
+							for(int i=0; i< remaining_record_len; i++){
+								printf("%02x ", p[i]);
+							}
+							printf("\n");
+						}
+						f->remaining_response_len -= remaining_record_len;
+						p += remaining_record_len;
+					
+						remaining_record_len = 0;
+						printf("%d bytes left of chunk\n", f->remaining_response_len);
+					} else {
+						if(f->replace_response){
+							fill_with_downstream(f, p, remaining_record_len);
+							printf("Replaced with:\n");
+							for(int i=0; i< remaining_record_len; i++){
+								printf("%02x ", p[i]);
+							}
+							printf("\n");
+						}
+						remaining_record_len -= f->remaining_response_len;
+						p += f->remaining_response_len;
+						f->httpstate = END_CHUNK;
+						printf("rem. record len = %d\n", f->remaining_record_len);
+					}
+					break;
+
+				case END_CHUNK:
+					printf("In END_CHUNK\n");
+					needle = strstr((const char *) p, "\r\n");
+					if(needle != NULL){
+						f->httpstate = BEGIN_CHUNK;
+						p += 2;
+						remaining_record_len -= 2;
+					} else {
+						printf("UH OH\n");
+						remaining_record_len = 0;
+						f->httpstate = FORFEIT_REST;
+					}
+					break;
+
+				case END_BODY:
+					printf("In END_BODY\n");
+					needle = strstr((const char *) p, "\r\n");
+					if(needle != NULL){
+						f->httpstate = PARSE_HEADER;
+						p += 2;
+						remaining_record_len -= 2;
+					} else {
+						printf("UH OH\n");
+						remaining_record_len = 0;
+						f->httpstate = FORFEIT_REST;
+					}
+					break;
+
+				case FORFEIT_REST:
+					printf("In FORFEIT_REST\n");
+
+				case USE_REST:
+					remaining_record_len = 0;
+					break;
+
+				default:
+					break;
+
+			}
+		}
+
+		/*
+
+		if(f->remaining_response_len > 0){
+			if(f->ignore_response){
+				uint32_t skip_len = (f->remaining_response_len > remaining_record_len) ?
+					remaining_record_len : f->remaining_response_len;
+				p += skip_len;
+				remaining_record_len -= skip_len;
+				f->remaining_response_len -= skip_len;
+			} else {//replace with downstream data, for now ignore
+				uint32_t skip_len = (f->remaining_response_len > remaining_record_len) ?
+					remaining_record_len : f->remaining_response_len;
+				p += skip_len;
+				remaining_record_len -= skip_len;
+				f->remaining_response_len -= skip_len;
+			}
+		}
+
+		//cycle through all responses
+		while(remaining_record_len > 0){
+			uint32_t response_len = get_response_length(p);
 
-	printf("Replacing contents of downstream data\n");
+
+			response_len = remaining_record_len;
+			remaining_record_len -= response_len;
+			p += response_len;
+		}
+
+		*/
+		printf("Re-encrypting record\n");
+		if((n = encrypt(f, record_ptr, record_ptr,
+						n + EVP_GCM_TLS_EXPLICIT_IV_LEN, 1, record_hdr->type,
+						1)) < 0){
+			fprintf(stdout,"encryption failed\n");
+			return 0;
+		}
+		printf("Encryption successful!\n");
+
+		p = record_ptr + record_len;
+		remaining_packet_len -= record_len;
+
+	}
+
+/*
 
 	uint8_t *p = info->app_data;
 
 	int32_t tmp_len = info->app_data_len;
 
+	printf("remaining packet length: %d\n", tmp_len);
 	//step 1: replace record contents
 	//note: encrypted message will be original message size + EVP_GCM_TLS_EXPLICIT_IV_LEN + 16 byte pad
 	//first check to see if there's anything in the outbox
@@ -380,28 +797,61 @@ int replace_contents(flow *f, int32_t offset, struct packet_info *info){
 		}
 #endif
 
-		if(tmp_len >= f->outbox_len){
-			memcpy(p, f->outbox, f->outbox_len);
-
-			p += f->outbox_len;
-			tmp_len -= f->outbox_len;
-			f->outbox_len = 0;
-			free(f->outbox);
+		//If the outbox contains data we can't change, just forward it as is.
+
+		if(f->outbox_data_len > 0){
+			if(tmp_len >= f->outbox_data_len){
+				printf("There are %d bytes in the outbox\n", f->outbox_data_len);
+				memcpy(p, f->outbox, f->outbox_data_len);
+
+				p += f->outbox_data_len;
+				tmp_len -= f->outbox_data_len;
+				f->outbox_len -= f->outbox_data_len;
+				f->outbox_data_len = 0;
+				free(f->outbox);
+
+				//now see if there are remainders of the encrypted record to send...
+				if(tmp_len > 0){
+					if(tmp_len >= f->outbox_len){
+						p += f->outbox_len;
+						tmp_len -= f->outbox_len;
+						f->outbox_len = 0;
+					} else {
+						f->outbox_len -= tmp_len;
+						tmp_len -= tmp_len;
+					}
+				}
+			} else {
+				memcpy(p, f->outbox, tmp_len);
+				uint8_t *tmp = calloc(1, f->outbox_data_len - tmp_len);
+				f->outbox_data_len -= tmp_len;
+				f->outbox_len -= tmp_len;
+				memcpy(tmp, f->outbox + tmp_len, f->outbox_data_len);
+				free(f->outbox);
+				f->outbox = tmp;
+				tmp_len -= tmp_len;
+			}
 		} else {
-			memcpy(p, f->outbox, tmp_len);
-			uint8_t *tmp = calloc(1, f->outbox_len - tmp_len);
-			f->outbox_len -= tmp_len;
-			memcpy(tmp, f->outbox + tmp_len, f->outbox_len);
-			free(f->outbox);
-			f->outbox = tmp;
-			tmp_len -= tmp_len;
+			//all we have are ingored bytes
+			if(tmp_len >= f->outbox_len){
+				printf("ignoring %d bytes\n", f->outbox_len);
+				p += f->outbox_len;
+				tmp_len -= f->outbox_len;
+				f->outbox_len = 0;
+			} else {
+				printf("ignoring %d bytes\n", tmp_len);
+				f->outbox_len -= tmp_len;
+				tmp_len -= tmp_len;
+			}
 		}
 	}
 
 	while(tmp_len > 0){
+		printf("remaining packet length: %d\n", tmp_len);
 
 		struct record_header *record_hdr = (struct record_header*) p;
 		uint32_t record_length = RECORD_LEN(record_hdr);
+		printf("record length: %d\n", record_length);
 
 #ifdef DEBUG
 		fprintf(stdout, "Record:\n");
@@ -412,6 +862,18 @@ int replace_contents(flow *f, int32_t offset, struct packet_info *info){
 #endif
 
 		p += RECORD_HEADER_LEN;
+
+		if(record_length > tmp_len){
+			//the packet is smaller than the record, we'll just forfeit the data
+			printf("Cannot see all of record\n");
+			tmp_len -= RECORD_HEADER_LEN;
+			f->outbox_len = record_length - tmp_len;
+			f->outbox_data_len = 0;
+			printf("ignoring %d bytes\n", tmp_len);
+			break;
+
+		}
+
 		if(record_hdr->type != 0x17){
 			//TODO: might need to decrypt and re-encrypt
 			//printf("received non-application data\n");
@@ -420,6 +882,45 @@ int replace_contents(flow *f, int32_t offset, struct packet_info *info){
 			continue;
 		}
 
+		//We are at a record boundary: decrypt record and change "leaves"
+		if(!encrypt(f, p, p, record_length, 1, record_hdr->type, 0)){
+			fprintf(stdout,"decryption failed\n");
+			return 0;
+		}
+
+		printf("encryption successful\n");
+
+		//look for content type
+		const char *needle = "Content-Type";
+		const char *type = strstr((const char *) p, needle);
+		if(type == NULL){
+			printf("No content-type header\n");
+			tmp_len -= RECORD_HEADER_LEN + record_length;
+			p += record_length;
+			continue;
+		}
+		uint8_t *substr = calloc(1, 32);
+		memcpy(substr, type, 32);
+		if(strstr((const char *) substr, "img") == NULL){
+			//we're ignoring this
+			printf("This is not a leaf node\n");
+			tmp_len -= RECORD_HEADER_LEN + record_length;
+			p += record_length;
+			free(substr);
+			continue;
+		}
+		free(substr);
+		if(type != NULL){
+			while((*type != '\r') && (*type != ';')){
+				printf("%c", *type);
+				type ++;
+			}
+			printf("\n");
+		} else {
+			printf("No content-type header\n");
+		}
+
+
 		uint8_t *new_record = calloc(1, record_length);
 		memcpy(new_record, p, record_length);
 		uint8_t *tmp_p = new_record;
@@ -440,7 +941,7 @@ int replace_contents(flow *f, int32_t offset, struct packet_info *info){
 
 #ifdef DEBUG
 			printf("Censored queue is at %p.\n", f->censored_queue);
-			printf("This block as %d bytes left\n", block_length - offset);
+			printf("This block has %d bytes left\n", block_length - offset);
 			printf("We need %d bytes\n", remaining);
 #endif
 			
@@ -479,6 +980,7 @@ int replace_contents(flow *f, int32_t offset, struct packet_info *info){
 			printf("%02x ", tmp_p[EVP_GCM_TLS_EXPLICIT_IV_LEN+i]);
 		printf("\n");
 #endif
+reencrypt:
 
 		//step 3: encrypt new record
 		int32_t success;
@@ -491,12 +993,13 @@ int replace_contents(flow *f, int32_t offset, struct packet_info *info){
 		if(record_length +RECORD_HEADER_LEN > tmp_len){
 			//We have a partial record
 			memcpy(p, new_record, tmp_len - RECORD_HEADER_LEN);
-			f->outbox_len = record_length - (tmp_len - RECORD_HEADER_LEN);
+			f->outbox_data_len = record_length - (tmp_len - RECORD_HEADER_LEN);
 			//save left-overs in outbox
-			f->outbox = calloc(1, f->outbox_len);
+			f->outbox = calloc(1, f->outbox_data_len);
 			memcpy(f->outbox, new_record + (tmp_len - RECORD_HEADER_LEN),
-					f->outbox_len);
+					f->outbox_data_len);
 			free(new_record);
+			f->outbox_len = f->outbox_data_len;
 		} else {
 			memcpy(p, new_record, record_length);
 			free(new_record);
@@ -519,9 +1022,74 @@ int replace_contents(flow *f, int32_t offset, struct packet_info *info){
 		p += record_length;
 
 	}
+	*/
 	//step 4: recompute TCP checksum
-	tcp_checksum(info);
+	if(changed){
+		tcp_checksum(info);
+		printf("Computing checksum\n");
+		fflush(stdout);
+	}
+
+	return 0;
+}
+
+int fill_with_downstream(flow *f, uint8_t *data, int32_t length){
+
+	uint8_t *p = data;
+	int32_t remaining = length;
+	struct slitheen_header *sl_hdr;
+
+	//Fill as much as we can from the censored_queue
+	while((remaining > SLITHEEN_HEADER_LEN) && downstream_queue->first_block != NULL){
+		queue_block *first_block = downstream_queue->first_block;
+		int32_t block_length = first_block->len;
+		int32_t offset = first_block->offset;
 
+//#ifdef DEBUG
+		printf("Censored queue is at %p.\n", first_block);
+		printf("This block has %d bytes left\n", block_length - offset);
+		printf("We need %d bytes\n", remaining);
+//#endif
+		
+		sl_hdr = (struct slitheen_header *) p;
+		sl_hdr->stream_id = first_block->stream_id;
+		sl_hdr->len = 0x00;
+		sl_hdr->garbage = 0x00;
+		p += SLITHEEN_HEADER_LEN;
+		remaining -= SLITHEEN_HEADER_LEN;
+
+		if(block_length > offset + remaining){
+			//use part of the block, update offset
+			memcpy(p, first_block->data+offset, remaining);
+			first_block->offset += remaining;
+			p += remaining;
+			sl_hdr->len = remaining;
+			remaining -= remaining;
+		} else {
+			//use all of the block and free it
+			memcpy(p, first_block->data+offset, block_length - offset);
+
+			free(first_block->data);
+			downstream_queue->first_block = first_block->next;
+			free(first_block);
+
+			p += (block_length - offset);
+			sl_hdr->len = (block_length - offset);
+			remaining -= (block_length - offset);
+		}
+		sl_hdr->len = htons(sl_hdr->len);
+	}
+	//now, if we need more data, fill with garbage
+	if(remaining > SLITHEEN_HEADER_LEN ){
+		//TODO: note, we may also be receiving misordered packets. Take Ian's suggestion into account here
+		sl_hdr = (struct slitheen_header *) p;
+		sl_hdr->stream_id = 0x00;
+		remaining -= SLITHEEN_HEADER_LEN;
+		sl_hdr->len = remaining;
+		sl_hdr->garbage = remaining;
+		p += SLITHEEN_HEADER_LEN;
+		memset(p, 'A', remaining);
+	}
 	return 0;
 }
 

+ 22 - 2
server/relay.h

@@ -6,14 +6,34 @@
 
 struct proxy_thread_data {
 	flow *f;
-	uint8_t server[50];
+	uint8_t *initial_data;
 	int32_t pipefd;
 };
 
+struct socks_req {
+	uint8_t version;
+	uint8_t cmd;
+	uint8_t rsvd;
+	uint8_t addr_type;
+};
+
 int replace_packet(flow *f, struct packet_info *info);
-int replace_contents(flow *f, int32_t offset, struct packet_info *info);
+int process_downstream(flow *f, int32_t offset, struct packet_info *info);
 int read_header(flow *f, struct packet_info *info);
+uint32_t get_response_length(uint8_t *response);
+int fill_with_downstream(flow *f, uint8_t *data, int32_t length);
 uint16_t tcp_checksum(struct packet_info *info);
 
 void *proxy_covert_site(void *data);
+
+#define BEGIN_HEADER 0x10
+#define PARSE_HEADER 0x20
+#define MID_CONTENT 0x30
+#define BEGIN_CHUNK 0x40
+#define MID_CHUNK 0x50
+#define END_CHUNK 0x60
+#define END_BODY 0x70
+#define FORFEIT_REST 0x80
+#define USE_REST 0x90
+
 #endif /* _RELAY_H_ */

+ 14 - 17
server/slitheen-proxy.c

@@ -35,7 +35,7 @@ int main(int argc, char *argv[]){
 	snprintf(filter1, 33, "ether src host %s", macaddr);
 	snprintf(filter2, 33, "ether dst host %s", macaddr);
 
-	init_flow_table();
+	init_tables();
 	init_session_cache();
 
 	/* Create threads */
@@ -115,8 +115,8 @@ void got_packet(uint8_t *args, const struct pcap_pkthdr *header, const uint8_t *
 	memcpy(tmp_packet, packet, header->len);
 	extract_packet_headers(tmp_packet, info);
 
-	// Check to make sure it is an IP packet 
-	if(info->ip_hdr == NULL)
+	// Check to make sure it is a TCP packet 
+	if((info->ip_hdr == NULL) || (info->tcp_hdr == NULL))
 		goto end;
 
 	process_packet(info);
@@ -146,24 +146,21 @@ void process_packet(struct packet_info *info){
 
 	flow newFlow;
 
-	if (info->tcp_hdr != NULL){
+	newFlow.src_ip = info->ip_hdr->src;
+	newFlow.dst_ip = info->ip_hdr->dst;
+	newFlow.src_port = info->tcp_hdr->src_port;
+	newFlow.dst_port = info->tcp_hdr->dst_port;
 
-		newFlow.src_ip = info->ip_hdr->src;
-		newFlow.dst_ip = info->ip_hdr->dst;
-		newFlow.src_port = info->tcp_hdr->src_port;
-		newFlow.dst_port = info->tcp_hdr->dst_port;
+	newFlow.seq_num = info->tcp_hdr->sequence_num;
 
-		newFlow.seq_num = info->tcp_hdr->sequence_num;
-
-		/* Checks to see if this is a possibly tagged hello msg */
-		if ((info->record_hdr != NULL) && (info->record_hdr->type == HS)){ /* This is a TLS handshake */
-			check_handshake(info, newFlow);
-		}
+	/* Checks to see if this is a possibly tagged hello msg */
+	if ((info->record_hdr != NULL) && (info->record_hdr->type == HS)){ /* This is a TLS handshake */
+		check_handshake(info, newFlow);
 	}
 
 	/* Now if flow is in table, update state */
-	if((index = check_flow(newFlow))){
-		flow *observed = get_flow(index-1);
+	flow *observed;
+	if((observed = check_flow(newFlow)) != NULL){
 	
 		if(observed->application){
 			replace_packet(observed, info);
@@ -182,7 +179,7 @@ void process_packet(struct packet_info *info){
 		/* Update TCP state */
 		if(info->tcp_hdr->flags & (FIN | RST) ){
 			/* Remove flow from table, connection ended */
-			remove_flow(index);
+			remove_flow(observed);
 		}
 
 	}

+ 4 - 4
server/slitheen.h

@@ -70,12 +70,12 @@ struct packet_info {
 	uint32_t app_data_len;
 };
 
-struct slitheen_header {
-	u_char marker; /* 0x01 means censored data, 0x02 means dummy data */
-	u_char version; /* For now 0x01 */
+struct __attribute__((__packed__)) slitheen_header {
+	u_char stream_id; /* determines which stream the data is from */
 	u_short len;
+	u_short garbage;
 };
-#define SLITHEEN_HEADER_LEN 4
+#define SLITHEEN_HEADER_LEN 5
 
 struct __attribute__((__packed__)) record_header {
 	u_char type;