#include "common.h" #include #include #include #include #include #include #include #include #include #include #include #define KEYFILE "server.pem" #define CA_FILE "ca.pem" #define PASSWORD "password" #define DHFILE "dh1024.pem" #define BUFSIZZ 20000 #ifdef DEBUG #define DEBUG_PRINT(...) do{ fprintf( stdout, __VA_ARGS__ ); } while( false ) #else #define DEBUG_PRINT(...) do{ } while ( false ) #endif static int disable_nagle = 0; char* middlebox_id = "Middlebox1"; char* handler_command = NULL; //"./mboxHandler.sh"; int tcp_listen(int port) { int sock; struct sockaddr_in sin; int val=1; if((sock=socket(AF_INET,SOCK_STREAM,0))<0) { printf("[MBOX ERROR] Couldn't make socket\n"); exit(-1); } memset(&sin,0,sizeof(sin)); sin.sin_addr.s_addr=INADDR_ANY; sin.sin_family=AF_INET; sin.sin_port=htons(port); setsockopt(sock,SOL_SOCKET,SO_REUSEADDR, &val,sizeof(val)); /* if (disable_nagle == 1) set_nagle(sock, 1); */ if(bind(sock,(struct sockaddr *)&sin, sizeof(sin))<0) { printf("[MBOX] Couldn't bind\n"); exit(-1); } listen(sock,5); DEBUG_PRINT("[MBOX] Listening at port: %d\n", port); return(sock); } // TCP connect function int tcp_connect(char *host, int port) { struct hostent *hp; struct sockaddr_in addr; int sock; // Resolve host if(!(hp = gethostbyname(host))) { printf("[MBOX ERROR] Couldn't resolve host %s\n", host); exit(-1); } DEBUG_PRINT("[MBOX DEBUG] Host %s resolved to %s port %d\n", host, hp->h_addr_list[0], port); memset(&addr, 0, sizeof(addr)); addr.sin_addr = *(struct in_addr*) hp->h_addr_list[0]; addr.sin_family = AF_INET; addr.sin_port = htons(port); if((sock=socket(AF_INET,SOCK_STREAM, IPPROTO_TCP))<0) { printf("[MBOX ERROR] Couldn't create socket\n"); } DEBUG_PRINT("[MBOX DEBUG] Socket created\n"); /* if (disable_nagle == 1) set_nagle(sock, 1); */ if(connect(sock,(struct sockaddr *)&addr, sizeof(addr))<0) { printf("[MBOX ERROR] Couldn't connect socket\n"); exit(-1); } DEBUG_PRINT("[MBOX DEBUG] Socket connected\n"); return sock; } void load_dh_params(SSL_CTX *ctx, char *file) { DH *ret=0; BIO *bio; if ((bio=BIO_new_file(file,"r")) == NULL) { printf ("[MBOX ERROR] Couldn't open DH file %s\n", file); exit(0); } ret = PEM_read_bio_DHparams(bio, NULL, NULL, NULL); BIO_free(bio); if(SSL_CTX_set_tmp_dh(ctx,ret) < 0) { printf ("[MBOX ERROR] Couldn't set DH parameters\n"); exit(0); } } // ------------------------------------------------------------- // Check for SSL_write error (just write at this point) // TO DO: check behavior per slice // ------------------------------------------------------------- void check_SSL_write_error(SSL *ssl, int r, int request_len) { int errorCode = SSL_get_error(ssl, r); switch(errorCode) { case SSL_ERROR_NONE: if(request_len != r) { printf("[MBOX ERROR] Incomplete write!"); exit(-1); } break; default: { printf("[MBOX ERROR] SSL write error %d", errorCode); exit(-1); } } } SSL* create_SSL_connection(char *address, char* method) { DEBUG_PRINT("[MBOX DEBUG] Creating new spp conection to: %s\n", address); SSL_CTX *ctx; // SSL context SSL *new_ssl; // SSL context BIO *sbio; // ? int sock; // socket char* ipv4 = strtok(strdup(address), ":"); // ip int port = atoi(strtok(NULL, ":")); // port DEBUG_PRINT("[MBOX DEBUG] Connecting to next hop: %s %d\n", ipv4, port); int status = COMMON_InitializeSSLCtx(&ctx, KEYFILE, PASSWORD, CA_FILE, ID_MIDDLEBOX_MIN ); new_ssl = SSL_new(ctx); sock = tcp_connect(ipv4, port); sbio = BIO_new_socket(sock, BIO_NOCLOSE); SSL_set_bio(new_ssl, sbio, sbio); return new_ssl; } /* Just creates a new SSL instance but it does not connect ! */ SSL* SPP_Callback(SSL *ssl, char *address) { return create_SSL_connection(address, "middlebox"); } /* This will terminate but NOT destroy a socket */ int shut_down_connections(SSL* ssl) { int r; int socket; // get socket socket = SSL_get_fd(ssl); DEBUG_PRINT("[MBOX DEBUG] Shutting down connection!\n"); r = SSL_shutdown(ssl); if (r == 0) { DEBUG_PRINT("[MBOX DEBUG] r=0 trying again to shutdown\n"); shutdown(socket, 1); r = SSL_shutdown(ssl); } // Verify that all went good switch(r) { case 1: DEBUG_PRINT("[MBOX DEBUG] Succesfully shut down connection!\n"); break; // Success case 0: case -1: default: // Error printf("[MBOX ERROR] Connection shutdown failed\n"); } // FIXME -- this should not be need it but ... shutdown(SSL_get_fd(ssl), SHUT_WR); // close here close(socket); // All good return 0; } cJSON* create_slice_record(char* direction, char* data, int dataLen, SPP_SLICE* slice) { cJSON* sliceRecord = cJSON_CreateObject(); cJSON_AddStringToObject(sliceRecord, "direction", direction); cJSON_AddStringToObject(sliceRecord, "middleboxid", middlebox_id); cJSON_AddNumberToObject(sliceRecord, "slice", slice->slice_id); cJSON_AddStringToObject(sliceRecord, "slicePurpose", slice->purpose); cJSON_AddNumberToObject(sliceRecord, "readAccess", slice->read_access); cJSON_AddNumberToObject(sliceRecord, "writeAccess", slice->write_access); if (slice->read_access) { char* dataStr = malloc(dataLen + 1); memcpy(dataStr, data, dataLen); dataStr[dataLen] = 0; cJSON_AddStringToObject(sliceRecord, "data", dataStr); free(dataStr); } else { char* printableStr = NULL; COMMON_Base64Encode(data, dataLen, &printableStr); cJSON_AddStringToObject(sliceRecord, "data", printableStr); } return sliceRecord; } cJSON* call_slice_handler (char* inputJsonFile) { if (!handler_command) return NULL; DEBUG_PRINT("[MBOX DEBUG] Calling response handler with file %s\n", inputJsonFile); char commandBuf[256]; sprintf(commandBuf, "%s %s", handler_command, inputJsonFile); int bufLen = 0; char* response = COMMON_CallExternalProcess(commandBuf, &bufLen); DEBUG_PRINT ("[MBOX DEBUG] Received %lu bytes from handler (%s)\n", strlen(response), response); cJSON* returnJson = cJSON_Parse(response); if (returnJson) return returnJson; printf ("[MBOX ERROR] Could not parse json from command handler %s - received { %s }\n", handler_command, response); return NULL; } /* Handles data from previous hop and forwards them to the next one. TODO: in theory the two handlers can be merged in a single function... */ int handle_previous_hop_data(SSL* prev_ssl, SSL* next_ssl) { int r,w; long status; char buf[BUFSIZZ]; SPP_SLICE *slice; SPP_CTX *ctx; // Read HTTP GET (assuming a single read is enough) while(1) { DEBUG_PRINT("[MBOX DEBUG PREVHOP] Waiting to read data from previous hop\n"); r = SPP_read_record(prev_ssl, buf, BUFSIZZ, &slice, &ctx); DEBUG_PRINT("[MBOX DEBUG PREVHOP] SPP_read_record returned\n"); status = SSL_get_error(prev_ssl, r); if (status == SSL_ERROR_ZERO_RETURN || status != SSL_ERROR_NONE) { printf ("[MBOX ERROR PREVHOP] Error code %ld\n", status); ERR_print_errors_fp(stdout); char tempBuf[100]; ERR_error_string(status, tempBuf); printf("[MBOX ERROR PREVHOP] Connection with previous hop closed, exiting previous hop handler (error %s)\n", tempBuf); break; } else if (status != SSL_ERROR_NONE) { printf("[MBOX ERROR PREVHOP] SSL read error code %lu", status); exit(-1); } DEBUG_PRINT("[MBOX DEBUG PREVHOP] Data received (from previous hop) (length %d, slice %d: %s):\n", r, slice->slice_id, slice->purpose); cJSON* sliceRecord = create_slice_record("ClientToServer", buf, r, slice); char* printStr = cJSON_Print(sliceRecord); printf("[MBOX PREVHOP] Received data (%d bytes)\n", r); printf("%s\n", printStr); free(printStr); char* jsonFileName = COMMON_WriteJSONFile(sliceRecord, middlebox_id); cJSON* modifiedSlice = NULL; if (slice->write_access) modifiedSlice = call_slice_handler(jsonFileName); if (modifiedSlice) { char* modifiedBuf = cJSON_GetObjectItem(modifiedSlice, "data")->valuestring; int newSliceID = cJSON_GetObjectItem(modifiedSlice, "slice")->valueint; DEBUG_PRINT("[MBOX DEBUG PREVHOP] Forwarding record to next hop modifed by command handler\n"); w = SPP_forward_record(next_ssl, modifiedBuf, strlen(modifiedBuf) , SPP_get_slice_by_id(next_ssl, newSliceID), ctx, 1); check_SSL_write_error(next_ssl, w, strlen(modifiedBuf)); } else { DEBUG_PRINT("[MBOX DEBUG PREVHOP] Forwarding record to next hop unmodified\n"); w = SPP_forward_record(next_ssl, buf,r , slice, ctx, 0); check_SSL_write_error(next_ssl, w, r); } free (jsonFileName); cJSON_Delete(sliceRecord); //this is probably not necessary... if(r == 0 ) { break; } } // NEW SHUT DOWN DEBUG_PRINT("[MBOX DEBUG PREVHOP] Shutting down next hop\n"); shut_down_connections(next_ssl); DEBUG_PRINT("[MBOX DEBUG PREVHOP] Next hop shut down\n"); return(0); } int handle_next_hop_data(SSL* prev_ssl, SSL* next_ssl) { int r,w,status; char buf[BUFSIZZ]; SPP_SLICE *slice; SPP_CTX *ctx; // Read HTTP GET (assuming a single read is enough) while(1) { DEBUG_PRINT("[MBOX DEBUG NEXTHOP] Waiting to read data from next hop\n"); r = SPP_read_record(next_ssl, buf, BUFSIZZ, &slice, &ctx); status = SSL_get_error(next_ssl, r); if (status == SSL_ERROR_ZERO_RETURN || status != SSL_ERROR_NONE) { printf("[MBOX ERROR NEXTHOP] Connection with next hop closed, exiting next hop handler and also closing previous hop connection\n"); break; } else if (status != SSL_ERROR_NONE) { printf("[MBOX ERROR NEXTHOP] SSL read error code %d", status); exit(-1); } DEBUG_PRINT("[MBOX DEBUG NEXTHOP] Data received (from previous hop) (length %d bytes, slice %d: %s):\n", r, slice->slice_id, slice->purpose); cJSON* sliceRecord = create_slice_record("ServerToClient", buf, r, slice); char* printStr = cJSON_Print(sliceRecord); printf("[MBOX NEXTHOP] Received data (%d bytes)\n", r); printf("%s\n", printStr); COMMON_WriteJSONFile(sliceRecord, middlebox_id); free(printStr); w = SPP_forward_record(prev_ssl, buf,r , slice, ctx, 0); check_SSL_write_error(prev_ssl, w, r); // In theory this is not necessary if(r == 0 ) { break; } } DEBUG_PRINT("[MBOX DEBUG NEXTHOP] Triggering connection with previous hop to close too\n"); return(0); } int handle_data(SSL* prev_ssl, SSL* next_ssl) { pid_t next_handler; int status; DEBUG_PRINT("[MBOX DEBUG] Initializing data handlers\n"); next_handler = fork(); if(next_handler == 0) { //child process // handle traffic from client handle_previous_hop_data(prev_ssl, next_ssl); DEBUG_PRINT("[MBOX DEBUG] Exiting previous hop handler\n"); //TODO: wait for child before returning exit(0); } else { //parent handle_next_hop_data(prev_ssl, next_ssl); DEBUG_PRINT("[MBOX DEBUG] Exiting next hop handler\n"); } DEBUG_PRINT("[MBOX DEBUG] Waiting previous hop handler to quit before quiting data handler\n"); wait(&status); //CLEAN UP close(SSL_get_fd(next_ssl)); close(SSL_get_fd(prev_ssl)); //CLEAN UP SSL_free(next_ssl); //SSL_free(prev_ssl); DEBUG_PRINT("[MBOX DEBUG] Exiting data handler!\n"); return 0; } int main(int argc, char **argv) { int sock, s; BIO *sbio; SSL_CTX *ctx; SSL *ssl; int r; pid_t pid; int port = 8423; char *prxy_address = "127.0.0.1:8423"; SSL* ssl_next = NULL; char buf[BUFSIZZ]; int ret; if (argc < 4) { printf ("Usage: middlebox port proxy-address midlebox-id [command handler...]\n"); exit(0); } port = atoi(argv[1]); prxy_address = argv[2]; middlebox_id = argv[3]; DEBUG_PRINT("[MBOX DEBUG] port=%d prxy_address=%s id=%s\n", port, prxy_address, middlebox_id); if (argc > 4) { char tempBuf[1024]; memset(tempBuf, 0, 1024); int written = 0; for (int i = 4; i < argc; i++) { written += snprintf(tempBuf + written, 1024 - written, "%s ", argv[i]); } handler_command = strdup(tempBuf); DEBUG_PRINT("[MOX DEBUG] Handler command = %s\n", handler_command); } else { DEBUG_PRINT("[MBOX DEBUG] Handler command not set\n"); } // TO-DO: Actually check the return value of this e.g. if the files don't exist int status = COMMON_InitializeSSLCtx(&ctx, KEYFILE, PASSWORD, CA_FILE, ID_MIDDLEBOX_MIN ); load_dh_params(ctx,DHFILE); // Socket in listen state sock = tcp_listen(port); // Wait for client request int nConn = 0; while(1) { // keep track of number of connections nConn++; if((s = accept(sock, 0, 0)) < 0) { printf("[MBOX ERROR] Problem socket accept\n"); } signal(SIGCHLD, SIG_IGN); // fork a new proces if((pid = fork())) { close(s); } else { sbio = BIO_new_socket(s, BIO_NOCLOSE); ssl = SSL_new(ctx); SSL_set_bio(ssl, sbio, sbio); if ((r = SPP_proxy(ssl, prxy_address, SPP_Callback, &ssl_next)) <= 0) { printf ("[MBOX ERROR] SPP proxy error"); } else { DEBUG_PRINT("[MBOX DEBUG] SPP proxy OK\n"); } // Set server URL //char* targetServercopy = "127.0.0.1:4433"; //ssl->spp_server_address = targetServercopy; handle_data(ssl, ssl_next); // Close socket close(s); // Exit child thread exit(0); } } // Clean context COMMON_DestroyCtx(ctx); // Exit exit(0); }