/* * Copyright (C) Telefonica 2015 * All rights reserved. * * Telefonica Proprietary Information. * * Contains proprietary/trade secret information which is the property of * Telefonica and must not be made available to, or copied or used by * anyone outside Telefonica without its written authorization. * * Authors: * Matteo Varvello et al. * * Description: * An SSL/SPP client that connects to an SSL/SPP through a bunch of * proxies. */ #include // to support boolean #include "common.h" // common library between client and server #include // thread support #define KEYFILE "client.pem" // client certificate #define PASSWORD "password" // unused now //#define DEBUG // verbose logging #define CONNECT_TIMEOUT 5 // socket connection timeout #define MAX_CONC_CLIENT 100 // max concurrent clients static char *host=HOST; static int port=PORT; static int require_server_auth = 1; static int clientID=0; // -- Moved up here just because of thread static SSL *ssl; // SSL instance static int plain_socket; static char *proto = "ssl"; // protocol to use (ssl ; spp) static int stats=0; // Report byte statistics boolean static int sizeCheck; static ExperimentInfo *experiment_info; // for printing stats at the end static int lines = 0; // number of lines in action file static long fSize = 0; //nagle stuff static int disable_nagle = 0; // allow acces to number of slices also for SSL static int slices_len = 0; // number of slices // Thread syncronization variables static int done = 0; static pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER; static pthread_cond_t c = PTHREAD_COND_INITIALIZER; static bool running = true; // Thread sync functions pthread_cond_wait(pthread_cond_t *c, pthread_mutex_t *m); pthread_cond_signal(pthread_cond_t *c); // thread exit void thr_exit() { pthread_mutex_lock(&m); done = 1; pthread_cond_signal(&c); pthread_mutex_unlock(&m); } // Join void thr_join() { pthread_mutex_lock(&m); while (done == 0){ pthread_cond_wait(&c, &m); } pthread_mutex_unlock(&m); } // function to plot statistics void print_stats(SSL *s); // Compute the size of a file to be served int calculate_file_size(char *filename){ FILE *fp; int sz = 0; // Open file fp = fopen(filename,"r"); // Check for errors while opening file if(fp == NULL){ printf("Error while opening file %s.\r\n", filename); exit(-1); } // Seek to the end of the file and ask for position fseek(fp, 0L, SEEK_END); sz = ftell(fp); //fseek(fp, 0L, SEEK_SET); // Close file fclose (fp); // Return file size #ifdef DEBUG printf ("[DEBUG] File requested is <<%s>> with size <<%d bytes>>\n", filename, sz); #endif return sz; } // Read counter of number of proxies in the provided list int read_proxy_count(char *file_name){ FILE *fp; // pointer to file int N; // number of proxies char line [128]; // maximum size of line to read from // Open file for reading fp = fopen(file_name,"r"); // CHheck for errors while opening file if( fp == NULL ){ printf("Error while opening file %s.\r\n", file_name); exit(-1); } // Read first line of file fgets ( line, sizeof line, fp ); // Convert char into integer N = atoi(line); #ifdef DEBUG printf("[DEBUG] Expected number of proxies is: %d\r\n", N); #endif // Close file fclose(fp); return N; } // Function to read a proxy list from file and populate array of proxies void read_proxy_list(char *file_name, SPP_PROXY **proxies){ FILE *fp; // pointer to file int count = 0; // simple counters bool firstLine = 1; // flag for first line in file char line [128]; // maximum size of line to read from // Open file for reading fp = fopen(file_name,"r"); // Check for errors while opening file if( fp == NULL ){ printf("Error while opening file %s.\r\n", file_name); exit(-1); } // Read file line-by-line while ( fgets ( line, sizeof line, fp ) != NULL ) { // Remove trailing newline (NOTE: strtoK is not thread safe) strtok(line, "\n"); // Handle first line as a special case if (firstLine){ firstLine = 0; continue; } // Generate a proxy from IP address just read char *newLine; newLine = (char *)malloc(strlen(line)+1); strcpy(newLine, line); proxies[count] = SPP_generate_proxy(ssl, newLine); count++; } // Close file fclose(fp); } // Print proxy list void print_proxy_list(SPP_PROXY **proxies, int N){ int i; if (N == 1){ printf("[DEBUG] There is %d available proxy (the final server):\r\n", N); }else{ printf("[DEBUG] There are %d available proxies:\r\n", N); } for (i = 0; i < N; i++){ printf("\t[PROXY] Proxy %d with address %s\r\n", i, proxies[i]->address); } } /* // Timeout for connect static int sTimeout = 0; // Handler for alarm raised by TCP connect static void AlarmHandler(int sig) { sTimeout = 1; } */ // 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))){ berr_exit("Couldn't resolve host"); } #ifdef DEBUG printf("[DEBUG] Host %s resolved\n", host); #endif 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){ err_exit("Couldn't create socket"); } #ifdef DEBUG printf("[DEBUG] Socket correctly created\n"); #endif if (disable_nagle == 1) set_nagle(sock, 1); if(connect(sock,(struct sockaddr *)&addr, sizeof(addr))<0){ err_exit("Couldn't connect socket"); } #ifdef DEBUG printf("[DEBUG] Socket connected\n"); #endif /* Check solution below if we need to support a timeout -- to debug signal(SIGALRM, AlarmHandler); sTimeout = 0; alarm(CONNECT_TIMEOUT); if ( connect(sock, (struct sockaddr *) &addr, sizeof(addr)) ){ if ( sTimeout ){ err_exit("timeout connecting stream socket"); } } sTimeout = 0; alarm(CONNECT_TIMEOUT); */ return sock; } /* Check that the common name matches the host name*/ void check_cert(char *host){ X509 *peer; char peer_CN[256]; long res = SSL_get_verify_result(ssl); if ((res == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT) || (res == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN)){ #ifdef DEBUG printf("[DEBUG] Self signed certificate accepted\n"); #endif } else { if(res != X509_V_OK) berr_exit("Certificate doesn't verify\n"); } /*Check the cert chain. The chain length is automatically checked by OpenSSL when we set the verify depth in the ctx */ /*Check the common name*/ peer=SSL_get_peer_certificate(ssl); X509_NAME_get_text_by_NID (X509_get_subject_name(peer), NID_commonName, peer_CN, 256); // WARNING NO VALIDATION Validate the hostname /* if (1) { printf("Peer_CN = %s\n", peer_CN); printf("Host = %s\n\n", host); //err_exit("Common name doesn't match host name"); } */ } // Check for SSL_write error (just write at this point) -- TO DO: check behavior per slice void check_SSL_write_error(int r, int request_len){ switch(SSL_get_error(ssl, r)){ case SSL_ERROR_NONE: if(request_len != r){ err_exit("Incomplete write!"); } break; default: berr_exit("SSL write problem"); } } // Perform a connect void doConnect (char *proto, int slices_len, int N_proxies, SPP_SLICE **slice_set, SPP_PROXY **proxies){ // SPP CONNECT if (strcmp(proto, "spp") == 0){ #ifdef DEBUG printf("[DEBUG] SPP_connect\n"); #endif if (SPP_connect(ssl, slice_set, slices_len, proxies, N_proxies) <= 0){ berr_exit("SPP connect error"); } } // SSL CONNECT if (strcmp(proto, "ssl") == 0){ #ifdef DEBUG printf("[DEBUG] SSL_connect\n"); #endif if(SSL_connect(ssl) <= 0) berr_exit("SSL connect error"); } // TO DO -- Check here if(require_server_auth){ #ifdef DEBUG printf("[DEBUG] Check certificate\n"); #endif check_cert(host); } } // Form and send GET void sendRequestBrowser(char *filename){ #ifdef DEBUG printf("[DEBUG] Inside sendRequestBrowser. Slices_len=%d\n", slices_len); #endif int r, j; int req_len_arr[50]; int request_len = 0; // extract list of sizes for requests and response char **s_Token; int size_alloc; int entries = TokenizeString(filename, &s_Token, &size_alloc, ';'); char *str = s_Token[0]; char *resp_sizes = s_Token[1]; for (j = 0; j < entries ; j++){ #ifdef VERBOSE printf("[DEBUG] Found string %s (length %d) - Tot entries: %d\n", s_Token[j], strlen(s_Token[j]), entries); #endif } // parse request sizes and save char **s_Token1; int countSlice = TokenizeString(str, &s_Token1, &size_alloc, '_'); #ifdef DEBUG printf("[DEBUG] String for request header is %s (length %d, %d slices)\n", str, strlen(str), countSlice); #endif for(j = 0; j < countSlice; j++){ req_len_arr[j] = atoi(s_Token1[j]); #ifdef VERBOSE printf("[VERBOSE]\tTokenized string is %s (value %d) - Tot entries:%d\n", s_Token1[j], req_len_arr[j], countSlice); #endif request_len += req_len_arr[j]; } // compute total response size (no need to save each size) fSize = 0; char **s_Token2; int count = TokenizeString(resp_sizes, &s_Token2, &size_alloc, '_'); #ifdef DEBUG printf("[DEBUG] String for response sizes is %s (length %d, %d slices)\n", resp_sizes, strlen(resp_sizes), count); #endif for(j = 0; j < count; j++){ fSize += atol(s_Token2[j]); #ifdef VERBOSE printf("[VERBOSE]\tTokenized string is %s (value %d) - Tot entries: %d\n", s_Token2[j], req_len_arr[j], count); #endif VERBOSE } // prepare common GET request char get_str[200]; memset(get_str, '0', sizeof(get_str)); int get_len; sprintf(get_str, "Get %s HTTP/1.1\r\nUser-Agent:SVA-%d\r\nHost: %s:%d\nPadding:", resp_sizes, clientID, host, port); get_len = strlen(get_str); #ifdef DEBUG printf ("[DEBUG] GET string (size %d):\n%s\n", get_len, get_str); #endif //prepare final request with appropriate padding char *request; int actual_request_len; char *padding=NULL; int toAllocate; if (strcmp(proto, "spp") == 0){ toAllocate = (req_len_arr[0] - get_len); } else { toAllocate = (request_len - get_len); } if (toAllocate < 0) { toAllocate = 0; } // Deal with the case when the GET is bigger than requested slice 0 if (toAllocate < 0){ toAllocate = 0; } // Logging #ifdef DEBUG printf ("[DEBUG] Allocating %d for padding\n", toAllocate + 1); #endif padding = (char*) malloc(toAllocate + 1); memset(padding, '?', toAllocate); padding[toAllocate] = 0; if (strcmp(proto, "spp") == 0){ actual_request_len = (req_len_arr[0] > strlen(get_str) ? req_len_arr[0] : strlen(get_str)) + 1; }else{ actual_request_len = (request_len > strlen(get_str) ? request_len : strlen(get_str)) + 1; } request = (char *)malloc(actual_request_len); // Copy get_str without \0 memcpy(request, get_str, strlen(get_str)); // Copy padding with \0 memcpy(request+strlen(get_str), padding, strlen(padding)+1); #ifdef DEBUG printf ("[DEBUG] Padded GET request (size %d):\n", strlen(request)); #endif #ifdef VERBOSE printf ("[DEBUG] Content:\n%s\n", request); #endif // SPP write if (strcmp(proto, "spp") == 0){ int i; //for (i = 0; i < ssl->slices_len; i++){ for (i = 0; i < countSlice; i++){ #ifdef DEBUG printf("[DEBUG] SPP_write (slice %d)\n", i); #endif if (i == 0){ #ifdef DEBUG printf ("[DEBUG] Send GET request with slice %d. Actual size %d -- size requested %d.\n", i, strlen(request), req_len_arr[i]); #endif r = SPP_write_record(ssl, request, strlen(request)+1, ssl->slices[i]); // req_len_arr[i] //r = SPP_write_record(ssl, request, strlen(request), ssl->slices[i]); #ifdef DEBUG printf ("[DEBUG] Send GET request with slice %d. Actual size %d -- size requested %d. SPP_write returned %d\n", i, strlen(request), req_len_arr[i], r); #endif // Check for errors //check_SSL_write_error(r, req_len_arr[i]); check_SSL_write_error(r, strlen(request)+1); #ifdef DEBUG printf("[DEBUG] Wrote %d bytes (on slice %d)\n", r, i); #endif }else{ // prepare fake request slices if needed if (req_len_arr[i] > 0){ #ifdef DEBUG printf ("[DEBUG] Send padding with slice %d (size %d)\n", i, req_len_arr[i]); #endif char *fake_request = (char*) malloc(req_len_arr[i] + 1); memset(fake_request, '?', req_len_arr[i]); fake_request[req_len_arr[i]] = 0; #ifdef VERBOSE printf ("[DEBUG] Prepared padding:\n%s\n", fake_request); #endif r = SPP_write_record(ssl, fake_request, req_len_arr[i], ssl->slices[i]); //r = SPP_write_record(ssl, fake_request, strlen(fake_request), ssl->slices[i]); // Check for errors //check_SSL_write_error(r, req_len_arr[i]); check_SSL_write_error(r, strlen(fake_request)); // logging #ifdef DEBUG printf("[DEBUG] Wrote %d bytes (on slice %d)\n", r, i); #endif // free memory free(fake_request); }else{ #ifdef DEBUG printf("[DEBUG] Passing here - i=%d, tot=%d\n", i, ssl->slices_len); #endif } } } } // SSL write else if (strcmp(proto, "ssl") == 0){ #ifdef DEBUG printf("[DEBUG] SSL_write\n"); #endif //r = SSL_write(ssl, request, request_len); r = SSL_write(ssl, request, strlen(request)); //check_SSL_write_error(r, request_len); check_SSL_write_error(r, strlen(request)); } // socket write else if (strcmp(proto, "pln") == 0){ #ifdef DEBUG printf("[DEBUG] Plain socket write\n"); #endif //r = write(plain_socket, request, strlen(request)); r = write(plain_socket, request, request_len); experiment_info->app_bytes_written += r; if ( r <= 0 ){ printf("[ERROR] Something went wrong with writing to the socket!\n"); } #ifdef DEBUG printf("[DEBUG] Request sent. %d bytes\n", r); #endif } // free memory free(padding); free(request); for (j = 0; j < entries ; j++){ free (s_Token[j]); } for (j = 0; j < countSlice ; j++){ free (s_Token1[j]); } for (j = 0; j < count ; j++){ free (s_Token2[j]); } free(s_Token); free(s_Token1); free(s_Token2); } // Form and send GET void sendRequest(char *filename){ char request[200]; int request_len; // Form the request memset(request, '0', sizeof(request)); sprintf(request, "Get %s HTTP/1.1\r\nUser-Agent:SVA-%d\r\nHost: %s:%d\r\n\r\n", filename, clientID, host, port); request_len = strlen(request); // SPP write if (strcmp(proto, "spp") == 0){ #ifdef DEBUG printf("[DEBUG] SPP_write\n"); #endif // Use slice 0 as default for sending (HEADER) int r = SPP_write_record(ssl, request, request_len, ssl->slices[0]); #ifdef DEBUG printf("[DEBUG] Wrote %d bytes\n", r); #endif check_SSL_write_error(r, request_len); } // SSL write else if (strcmp(proto, "ssl") == 0){ #ifdef DEBUG printf("[DEBUG] SSL_write\n"); #endif int r = SSL_write(ssl, request, request_len); check_SSL_write_error(r, request_len); } // socket write else if (strcmp(proto, "pln") == 0){ #ifdef DEBUG printf("[DEBUG] Plain socket write\n"); #endif int r = write(plain_socket, request, request_len); experiment_info->app_bytes_written += r; if ( r <= 0 ) { printf("Something went wrong with writing to the socket!\n"); } #ifdef DEBUG printf("[DEBUG] Request sent. %d bytes\n", r); #endif } } // Read file line by line with timing information static void *browser_replay(void *ptr){ char line[300]; //int previous_time = 0; // current/previous time FILE *fp; // pointer to file int curr_line = 0; // keep track of line read #ifdef DEBUG printf("[DEBUG] Read file timing\n"); #endif // Retrive filename from (void *) char *file_name = (char *)ptr; // Open file for reading fp = fopen(file_name,"r"); // Check for errors while opening file if( fp == NULL ){ printf("Error while opening file %s.\r\n", file_name); return NULL; } // Read file line-by-line while ( fgets ( line, sizeof line, fp ) != NULL && curr_line < lines) { double time; //double duration; char file_request[128]; // Current line read in the file curr_line ++; // Remove trailing newline (NOTE: strtoK is not thread safe) strtok(line, "\n"); #ifdef DEBUG printf("[DEBUG] Read line is <<%s>>\n", line); #endif // Parse line //sscanf(line, "%lf %lf", &time, &duration); sscanf(line, "%lf %s", &time, file_request); // Logging #ifdef DEBUG //printf("[DEBUG] Extracted values are: %f -- %f\n", time, duration); printf("[DEBUG] Extracted values are: %f -- %s\n", time, file_request); #endif /* Original code to relay timing information from traces // Compute sleeping time double time_to_sleep = time - previous_time; #ifdef DEBUG printf("[DEBUG] Sleeping for %f\n", time_to_sleep); #endif sleep(time_to_sleep); */ // Send HTTP GET #ifdef DEBUG printf("[DEBUG] Sending GET request for <<%s>> bytes of data\n", file_request); #endif if (curr_line == lines){ #ifdef DEBUG printf("[DEBUG] File has %d GET and we sent %d. Reading thread is done\n", lines, curr_line); #endif running = false; } sendRequestBrowser(file_request); // Wait on main thread to have received requested data unless we are done if (curr_line < lines){ #ifdef DEBUG printf("[DEBUG] Wait on main thread"); #endif thr_join(); } done = 0; /* Not used // Save current time previous_time = time; */ } #ifdef DEBUG printf("[DEBUG] Closing file\n"); #endif // Close file fclose(fp); // All good return NULL; } // Emulate browser behavior based on some input traces static int http_complex(char *proto, char *fn, struct timeval *tvEnd){ int r; char buf[BUFSIZZ]; long bytes_read = 0; // Thread for browser-like behavior pthread_t reading_thread; if(pthread_create(&reading_thread, NULL, browser_replay, fn)) { fprintf(stderr, "Error creating thread\n"); return -1; }else{ #ifdef DEBUG printf("[DEBUG] Reading thread started!\n"); #endif } // Now read the server's response, assuming that it's terminated by a close while(1){ // SPP read if (strcmp(proto, "spp") == 0){ #ifdef DEBUG printf("[DEBUG] Waiting on SPP_read...\n"); #endif SPP_SLICE *slice; // slice for SPP_read SPP_CTX *ctx; // context pointer for SPP_read r = SPP_read_record(ssl, buf, BUFSIZZ, &slice, &ctx); } // SSL read else if (strcmp(proto, "ssl") == 0){ #ifdef DEBUG printf("[DEBUG] Waiting on SSL_read...\n"); #endif r = SSL_read(ssl, buf, BUFSIZZ); } // TCP read else if (strcmp(proto, "pln") == 0){ r = read(plain_socket, buf, BUFSIZZ); experiment_info->app_bytes_read += r; } // Check for errors in read if (strcmp(proto, "spp") == 0 || strcmp(proto, "ssl") == 0){ switch(SSL_get_error(ssl, r)){ case SSL_ERROR_NONE: break; case SSL_ERROR_ZERO_RETURN: berr_exit("SSL error zero return"); case SSL_ERROR_SYSCALL: berr_exit("SSL Error: Premature close"); default: berr_exit("SSL read problem"); } } #ifdef DEBUG printf("Read %d bytes\n", r); #endif // Write buf to stdout #ifdef VERBOSE printf("[DEBUG] Received:\n%s\n\n", buf); #endif // Update counter of bytes read bytes_read += r; //if ( r <= 0 || bytes_read == fSize){ #ifdef DEBUG printf("[DEBUG] File transfer stats %d -- %ld\n", bytes_read, fSize); #endif if (bytes_read == fSize){ #ifdef DEBUG printf("[DEBUG] File transfer done - signaling to other thread\n"); #endif bytes_read = 0; if (running){ thr_exit(); }else{ #ifdef DEBUG printf("[DEBUG] Reading thread is done, so here we are done too\n"); #endif break; } } } // Measure time gettimeofday(tvEnd, NULL); // Shutdown connection #ifdef DEBUG printf("[DEBUG] Shutdown was requested -- HERE\n"); #endif r = SSL_shutdown(ssl); if( !r ){ shutdown(SSL_get_fd(ssl), 1); r = SSL_shutdown(ssl); } switch(r){ case 1: break; // Success case 0: case -1: default: #ifdef DEBUG printf ("Shutdown failed with code %d\n", r); #endif berr_exit("Shutdown failed"); } // Print byte statistics if (stats){ print_stats(ssl); } // Free ssl SSL_free(ssl); // All good return(0); } // Send HTTP get and wait for response (SSL/SPP) static int http_request(char *filename, char *proto, bool requestingFile, struct timeval *tvEnd){ char buf[BUFSIZZ]; int r; int len; long bytes_read = 0; // Compute expected data size fSize = atoi(filename); if (fSize == 0 && filename[0] != '0'){ if (requestingFile){ fSize = calculate_file_size(filename); }else{ fSize = strlen("HTTP/1.0 200 OK\r\n"); } } sizeCheck = fSize; experiment_info->file_size = fSize; // Request file (either by name or by size) if (requestingFile){ sendRequest(filename); } // Now read the server's response, assuming that it's terminated by a close while(1){ // SPP read if (strcmp(proto, "spp") == 0){ /* #ifdef DEBUG printf("[DEBUG] SPP_read\n"); #endif */ SPP_SLICE *slice; // slice for SPP_read SPP_CTX *ctx; // context pointer for SPP_read r = SPP_read_record(ssl, buf, BUFSIZZ, &slice, &ctx); #ifdef DEBUG printf("[INFO] Read %d bytes\n", r); #endif if (ssl->read_stats.app_bytes == fSize){ printf("[INFO] Read %d bytes as expected (fSize=%d). Stopping timer\n", ssl->read_stats.app_bytes, fSize); // Stop the timer here (avoid shutdown crap) gettimeofday(tvEnd, NULL); #ifdef VERBOSE fwrite(buf, 1, len, stdout); #endif break; //goto shutdown; } switch(SSL_get_error(ssl, r)){ case SSL_ERROR_NONE: len = r; break; case SSL_ERROR_ZERO_RETURN: goto shutdown; case SSL_ERROR_SYSCALL: fprintf(stderr, "SSL Error: Premature close\n"); goto done; default: berr_exit("SSL read problem"); } } // SSL read else if (strcmp(proto, "ssl") == 0){ /* #ifdef DEBUG printf("[DEBUG] SSL_read\n"); #endif */ r = SSL_read(ssl, buf, BUFSIZZ); #ifdef DEBUG printf("[DEBUG] Read %d bytes\n", r); #endif if (ssl->read_stats.app_bytes == fSize){ printf("[INFO] Read %d bytes as expected (fSize=%d). Stopping timer\n", ssl->read_stats.app_bytes, fSize); gettimeofday(tvEnd, NULL); // Write buf to stdout #ifdef VERBOSE fwrite(buf, 1, len, stdout); #endif break; } switch(SSL_get_error(ssl, r)){ case SSL_ERROR_NONE: len = r; break; case SSL_ERROR_ZERO_RETURN: goto shutdown; case SSL_ERROR_SYSCALL: fprintf(stderr, "SSL Error: Premature close\n"); goto done; default: berr_exit("SSL read problem"); } } // SSL read else if (strcmp(proto, "pln") == 0){ #ifdef DEBUG printf("[DEBUG] Waiting to read bytes in plain mode\n"); #endif r = read(plain_socket, buf, BUFSIZZ); experiment_info->app_bytes_read += r; bytes_read += r; #ifdef DEBUG printf("[DEBUG] Read %d bytes\n", r); #endif if ( r <= 0 || bytes_read == fSize) /* done reading */ { #ifdef DEBUG printf("[DEBUG] File transfer done, done reading socket...\n"); #endif gettimeofday(tvEnd, NULL); // Write buf to stdout #ifdef VERBOSE fwrite(buf, 1, len, stdout); #endif goto done; } } // Write buf to stdout #ifdef VERBOSE fwrite(buf, 1, len, stdout); #endif } shutdown: #ifdef DEBUG printf("[DEBUG] Shutdown was requested\n"); #endif r = SSL_shutdown(ssl); if( !r ){ shutdown(SSL_get_fd(ssl), 1); r = SSL_shutdown(ssl); } switch(r){ case 1: break; // Success case 0: case -1: default: #ifdef DEBUG printf ("Shutdown failed with code %d\n", r); #endif berr_exit("Shutdown failed"); } done: if (stats){ print_stats(ssl); } SSL_free(ssl); return(0); } // report "BYTE STATISITICS" void print_stats(SSL *s) { int total_read, total_write, app_read, app_write; if (strcmp(proto, "pln") == 0) { total_read = app_read = experiment_info->app_bytes_read; total_write = app_write = experiment_info->app_bytes_written; } else { total_read = s->read_stats.bytes; total_write = s->write_stats.bytes; app_read = s->read_stats.app_bytes; app_write = s->write_stats.app_bytes; } printf("[RESULTS] BYTE STATISITICS:\n"); printf("[RESULTS] Bytes read: %d\n", total_read); printf("[RESULTS] Application bytes read: %d [Expected %d]\n", app_read, sizeCheck); printf("[RESULTS] Block padding bytes read: %d\n", s->read_stats.pad_bytes); printf("[RESULTS] Header bytes read: %d\n", s->read_stats.header_bytes); printf("[RESULTS] Handshake bytes read: %d\n", s->read_stats.handshake_bytes); printf("[RESULTS] MAC bytes read: %d\n", s->read_stats.mac_bytes); printf("[RESULTS] Alert bytes read: %d\n", s->read_stats.alert_bytes); printf("[RESULTS] Bytes write: %d\n", total_write); printf("[RESULTS] Application bytes write: %d\n", app_write); printf("[RESULTS] Block padding bytes write: %d\n", s->write_stats.pad_bytes); printf("[RESULTS] Header bytes write: %d\n", s->write_stats.header_bytes); printf("[RESULTS] Handshake bytes write: %d\n", s->write_stats.handshake_bytes); printf("[RESULTS] MAC bytes write: %d\n", s->write_stats.mac_bytes); printf("[RESULTS] Alert bytes write: %d\n", s->write_stats.alert_bytes); // In one line (so it's easy for plotting script). // num_slices num_mboxes file_size total app_total padding_total header_total handshake_total MAC_total alert_bytes printf("[RESULTS] ByteStatsSummary %d %d %d %d %d %d %d %d %d %d\n", experiment_info->num_slices, experiment_info->num_proxies, experiment_info->file_size, total_read + total_write, app_read + app_write, s->read_stats.pad_bytes + s->write_stats.pad_bytes, s->read_stats.header_bytes + s->write_stats.header_bytes, s->read_stats.handshake_bytes + s->write_stats.handshake_bytes, s->read_stats.mac_bytes + s->write_stats.mac_bytes, s->read_stats.alert_bytes + s->write_stats.alert_bytes); } // Usage function void usage(void){ printf("usage: wclient -s -r -w -i -f -o -a -c -b\n"); printf("-s: number of slices requested (min 1)\n"); printf("-r: number of proxies with read access (per slice)\n"); printf("-w: number of proxies with write access (per slice)\n"); printf("-i: integrity check\n"); printf("-f: file for http GET (either via (require file to exhist both at server and client[for testing reasons]) or via )\n"); printf("-o: {1=test handshake ; 2=200 OK ; 3=file transfer ; 4=browser-like behavior}\n"); printf("-a: action file for browser-like behavior\n"); printf("-c: protocol chosen (ssl ; spp; pln; fwd; spp-mod; ssl-mod; fwd-mod; pln-mod)\n"); printf("-b: report byte statistics\n"); exit(-1); } /* Compute a time difference - NOTE: Return 1 if the difference is negative, otherwise 0 [now defined in the library] int timeval_subtract(struct timeval *result, struct timeval *t2, struct timeval *t1) { long int diff = (t2->tv_usec + 1000000 * t2->tv_sec) - (t1->tv_usec + 1000000 * t1->tv_sec); result->tv_sec = diff / 1000000; result->tv_usec = diff % 1000000; return (diff<0); } */ // Main function int main(int argc, char **argv){ SSL_CTX *ctx; // SSL context BIO *sbio; int sock; // socket descriptor extern char *optarg; // user input parameters int c; // user iput from getopt char *filename = "proxyList"; // filename for proxy int r = 0, w = 0; // slice related parameters char *file_requested = "index.html"; // file requeste for HTTP GET SPP_SLICE **slice_set; // slice array SPP_PROXY **proxies; // proxy array int N_proxies = 0; // number of proxies in path int action = 0; // specify client/server behavior (handshake, 200OK, serve file, browser-like) char *file_action = NULL; // file action to use for browser-liek behavior struct timeval tvBeginConnect; struct timeval tvEndConnect; struct timeval tvBegin, tvEnd; struct timeval tvConnect, tvDuration; // time structures for handshake duration experiment_info = (ExperimentInfo*)malloc(sizeof(ExperimentInfo)); experiment_info->app_bytes_read = 0; experiment_info->app_bytes_written = 0; #ifdef DEBUG printf("\n\n******************** CLIENT STARTING ********************\n"); #endif // Handle user input parameters while((c = getopt(argc, argv, "s:r:w:i:f:c:o:a:b:")) != -1){ switch(c){ // Number of slices case 's': if(! (slices_len = atoi(optarg) )){ err_exit("Bogus number of slices specified (no. of slices need to be > 1)"); } experiment_info->num_slices = slices_len; break; // Number of proxies with read access (per slice) case 'r': r = atoi(optarg); break; // Number of proxies with write access (per slice) case 'w': w = atoi(optarg); break; // Integrity check requested case 'i': require_server_auth = 0; break; // Protocol chosen case 'c': if(! (proto = strdup(optarg) )){ err_exit("Out of memory"); } if (strcmp(proto, "spp_mod") == 0){ proto = "spp"; disable_nagle = 1; } if (strcmp(proto, "ssl_mod") == 0){ proto = "ssl"; disable_nagle = 1; } if (strcmp(proto, "fwd_mod") == 0){ proto = "fwd"; disable_nagle = 1; } if (strcmp(proto, "pln_mod") == 0){ proto = "pln"; disable_nagle = 1; } if (strcmp(proto, "fwd") == 0){ proto = "ssl"; } break; // File requested for HTTP GET case 'f': if(! (file_requested = strdup(optarg) )){ err_exit("Out of memory"); } break; // Client/Server behavior case 'o': action = atoi(optarg); if (action == 2) { action = 3; file_requested = "1"; } break; // Action file // NOTE: necessary only if -o 4, i.e., browser-like behavior case 'a': if(! (file_action = strdup(optarg) )){ err_exit("Out of memory"); } break; // Print byte statistics case 'b': stats = atoi(optarg); break; // default case default: usage(); break; } } // Read number of proxy from file N_proxies = read_proxy_count(filename); experiment_info->num_proxies = N_proxies - 1; // Check that input parameters are correct #ifdef DEBUG printf("[DEBUG] Parameters count: %d\n", argc); #endif if (argc == 1){ usage(); } if ((strcmp(proto, "spp") == 0) && slices_len < 1){ printf("No. of slices need to be > 0"); usage(); } if (action < 1 || action > 4){ usage(); } if ((strcmp(proto, "spp") != 0) && (strcmp(proto, "ssl") != 0) && (strcmp(proto, "pln") != 0)){ printf("Protocol type specified (%s) is not supported. Supported protocols are: spp, ssl, pln\n", proto); usage(); } if (N_proxies == 0){ printf ("At least one proxy needs to be defined, i.e., the final server\n"); usage(); } if (r > N_proxies || w > N_proxies){ printf ("The values for r and w need to be <= than the number of proxies\n"); usage(); } if(action == 4){ if (file_action == NULL){ printf ("Action file (-a path_to_file) is required with -o 4\n"); usage(); }else{ FILE *fp; fp = fopen(file_action,"r"); int ch=0; // Check for errors while opening file if(fp == NULL){ printf("Error while opening file %s.\r\n", file_action); exit(-1); } while( ! feof(fp)){ ch = fgetc(fp); if(ch == '\n'){ lines++; } } fclose(fp); } } // Generate a clientID time_t t; srand((unsigned) time(&t)); clientID = rand() % MAX_CONC_CLIENT; // Construct string for client/server behavior char *temp_str; if (action == 1){ temp_str = "handshake_only"; } if (action == 2){ temp_str = "200_OK"; } if (action == 3){ temp_str = "serve_file"; } if (action == 4){ temp_str = "browser_like"; } // Logging input parameters #ifdef DEBUG printf("[DEBUG] CLIENT-ID=%d host=%s port=%d slices=%d read=%d write=%d n_proxies=%d proto=%s action=%d(%s)\n", clientID, host, port, slices_len, r, w, N_proxies, proto, action, temp_str); #endif // Build SSL context ctx = initialize_ctx(KEYFILE, PASSWORD, proto); ssl = SSL_new(ctx); // Allocate memory for proxies proxies = malloc( N_proxies * sizeof (SPP_PROXY*)); // Read proxy list read_proxy_list(filename, proxies); // Print proxy list #ifdef DEBUG print_proxy_list(proxies, N_proxies); #endif // Start timer for SPP (do not count slice creation for SSL instead) if (strcmp(proto, "spp") == 0){ gettimeofday(&tvBegin, NULL); } // Create slices_n slices with incremental purpose slice_set = malloc( slices_len * sizeof (SPP_SLICE*)); #ifdef DEBUG printf("[DEBUG] Generating %d slices\n", slices_len); #endif int i; for (i = 0; i < slices_len; i++){ char *newPurpose; char str[30]; sprintf (str, "slices_%d", (i + 2)); newPurpose = (char *)malloc(strlen(str)+1); strcpy(newPurpose, str); slice_set[i] = SPP_generate_slice(ssl, newPurpose); #ifdef DEBUG printf("\t[DEBUG] Generated slices %d with purpose %s\n", slice_set[i]->slice_id, slice_set[i]->purpose); #endif } // Assign write access to proxies for all slices // Find MAX between r and w int MAX = r; if (w > r) MAX = w; // Iterate among proxies for (i = 0; i < MAX ; i++){ // assign read access if requested if (i < r){ if (SPP_assign_proxy_read_slices(ssl, proxies[i], slice_set, slices_len) == 1 ) { #ifdef DEBUG printf ("[DEBUG] Proxy %s assigned read access to slice-set (READ_COUNT=%d)\n", proxies[i]->address, (i + 1)); #endif } } // assign write access if requested if (i < w){ if (SPP_assign_proxy_write_slices(ssl, proxies[i], slice_set, slices_len) == 1 ) { #ifdef DEBUG printf ("Proxy %s correctly assigned write access to slice-set (WRITE COUNT=%d)\n", proxies[i]->address, (i + 1)); #endif } } } // Start timer for "ssl" and "pln" if (strcmp(proto, "ssl") == 0 || strcmp(proto, "pln") == 0){ gettimeofday(&tvBegin, NULL); } // TCP Connect char* address = (char*)malloc(strlen(proxies[0]->address) + 1); // Large enough for string+\0 memcpy(address, proxies[0]->address, strlen(proxies[0]->address) + 1); host = strtok(address, ":"); port = atoi(strtok(NULL, ":")); #ifdef DEBUG printf("[DEBUG] Opening socket to host: %s, port %d\n", host, port); #endif sock = tcp_connect(host, port); plain_socket = sock; // Connect TCP socket to SSL socket // don't init ssl for unencrypted data... if (strcmp(proto, "pln") != 0) { sbio = BIO_new_socket(sock, BIO_NOCLOSE); SSL_set_bio(ssl, sbio, sbio); } // SSL Connect gettimeofday(&tvBeginConnect, NULL); if (strcmp(proto, "pln") != 0){ doConnect (proto, slices_len, N_proxies, slice_set, proxies); } // Measure duration of "ssl"/"spp" connect (it does not apply to "pln" of course) gettimeofday(&tvEndConnect, NULL); timeval_subtract(&tvConnect, &tvEndConnect, &tvBegin); // Switch across possible client-server behavior // // NOTE: here we can add more complex strategies switch(action){ // Handshake only case 1: break; // Send simple request, wait for 200 OK case 2: http_request(file_requested, proto, false, &tvEnd); break; // Send HTTP GET request and wait for file to be received case 3: http_request(file_requested, proto, true, &tvEnd); break; // Send several GET request following a browser-like behavior case 4: http_complex(proto, file_action, &tvEnd); break; // Unknown option default: usage(); break; } // Compute duration of action if (action > 1){ timeval_subtract(&tvDuration, &tvEnd, &tvBegin); } // Remove SSL context destroy_ctx(ctx); // Clode socket close(sock); //Free memory #ifdef DEBUG printf("[DEBUG] Freeing memory\n"); #endif for (i = 0; i < N_proxies ; i++){ free(proxies[i]); } for (i = 0; i < slices_len; i++){ free(slice_set[i]); } free(proxies); free(slice_set); free(experiment_info); // Report time statistics if (action > 1){ printf("[RESULTS] No_Slices %d Action %s_%d Duration %ld.%06ld\n", slices_len, temp_str, sizeCheck, tvDuration.tv_sec, tvDuration.tv_usec); }else{ printf("[RESULTS] No_Slices %d Action %s Handshake_Dur %ld.%06ld\n", slices_len, temp_str, tvConnect.tv_sec, tvConnect.tv_usec); } // All good return 0; }