/* apps/s_time.c */
/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
 * All rights reserved.
 */

#define NO_SHUTDOWN
//#define DEBUG 

/*-----------------------------------------
   s_time - SSL client connection timer program
   Written and donated by Larry Streepy <streepy@healthcare.com>
  -----------------------------------------*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>			// to support boolean
#include "common.h"				// common library between client and server
#define USE_SOCKETS
#include "apps.h"
#ifdef OPENSSL_NO_STDIO
#define APPS_WIN16
#endif
#include <openssl/x509.h>
#include <openssl/ssl.h>
#include <openssl/pem.h>
#include "s_apps.h"
#include <openssl/err.h>
#include <time.h>
#ifdef WIN32_STUFF
#include "winmain.h"
#include "wintext.h"
#endif
#if !defined(OPENSSL_SYS_MSDOS)
#include OPENSSL_UNISTD
#endif

#undef PROG
#define PROG s_time_main

#undef ioctl
#define ioctl ioctlsocket

#define SSL_CONNECT_NAME	"localhost:4433"

/*#define TEST_CERT "client.pem" */ /* no default cert. */

#undef BUFSIZZ
#define BUFSIZZ 1024*10

#define MYBUFSIZ 1024*8

#undef min
#undef max
#define min(a,b) (((a) < (b)) ? (a) : (b))
#define max(a,b) (((a) > (b)) ? (a) : (b))

#undef SECONDS
#define SECONDS	10
extern int verify_depth;
extern int verify_error;

static void s_time_usage(void);
static int parseArgs( int argc, char **argv );
static SSL *doConnection( SSL *scon, char *proto );
static void s_time_init(void);
static int read_proxy_list(char *, char ** ); 
static int check_SSL_write_error( SSL *, int, int );
static void slices_management( SSL *, SPP_SLICE **, SPP_PROXY ** ); 
static int send_GET_request( SSL * ); 
int tcp_connect(char *host, int port);


/***********************************************************************
 * Static data declarations
 */

/* static char *port=PORT_STR;*/
static char *host = SSL_CONNECT_NAME;
static char *t_cert_file = NULL;
static char *t_key_file = NULL;
static char *CApath = NULL;
static char *CAfile = NULL;
static char *tm_cipher = NULL;
static int tm_verify = SSL_VERIFY_NONE;
static int maxTime = SECONDS;
static SSL_CTX *tm_ctx = NULL;
static const SSL_METHOD *s_time_meth = NULL;
static char *s_www_path = NULL;
static long bytes_read = 0; 
static int st_bugs = 0;
static int perform = 0;
#ifdef FIONBIO
static int t_nbio = 0;
#endif
#ifdef OPENSSL_SYS_WIN32
static int exitNow = 0;                    // Set when it's time to exit main
#endif
static int slices_len = 0;                 // Number of slices (spp) 
static int r = 0;                          // Number of proxies with read access (spp)
static int w = 0;                          // Number of proxies with write access (spp)
static char *proto = "ssl";                // Protocol of choice (ssl; spp) 
static int N_proxies = 0;                  // number of proxies indicated
static char *filename = "proxyList";       // filename for proxy
static char **proxies_address;             // array of address for proxies
static bool reuse = false;                 // SSL session caching 
static int mySoc = NULL;

static void s_time_init(void)
	{
	host=SSL_CONNECT_NAME;
	t_cert_file=NULL;
	t_key_file=NULL;
	CApath=NULL;
	CAfile=NULL;
	tm_cipher=NULL;
	tm_verify = SSL_VERIFY_NONE;
	maxTime = SECONDS;
	tm_ctx=NULL;
	s_time_meth=NULL;
	s_www_path=NULL;
	bytes_read=0; 
	st_bugs=0;
	perform=0;

#ifdef FIONBIO
	t_nbio=0;
#endif
#ifdef OPENSSL_SYS_WIN32
	exitNow = 0;		/* Set when it's time to exit main */
#endif
	}

/***********************************************************************
 * usage - display usage message
 */
static void s_time_usage(void){
	static char umsg[] = "\
-time arg     - max number of seconds to collect data, default %d\n\
-verify arg   - turn on peer certificate verification, arg == depth\n\
-cert arg     - certificate file to use, PEM format assumed\n\
-key arg      - RSA file to use, PEM format assumed, key is in cert file\n\
                file if not specified by this option\n\
-CApath arg   - PEM format directory of CA's\n\
-CAfile arg   - PEM format file of CA's\n\
-cipher       - preferred cipher to use, play with 'openssl ciphers'\n\n";

	printf( "usage: s_time <args>\n\n" );

	printf("-connect host:port - host:port to connect to (default is %s)\n",SSL_CONNECT_NAME);
#ifdef FIONBIO
	printf("-nbio         - Run with non-blocking IO\n");
	//printf("-ssl2         - Just use SSLv2\n");
	//printf("-ssl3         - Just use SSLv3\n");
	printf("-bugs         - Turn on SSL bug compatibility\n");
	printf("-new          - Just time new connections\n");
	printf("-reuse        - Just time connection reuse\n");
	printf("-www page     - Retrieve 'page' from the server\n");
	printf("-time         - Test duration\n");
	printf("--------------------------------\n");
    printf("-proto        - Protocol requested [sll ; spp; fwd ; pln]\n");
	printf("-slice        - Number of slices requested\n"); 
	printf("-proxies      - Number of proxies\n"); 
    printf("-read         - Number of proxies with read access (per slice)\n"); 
    printf("-write        - Number of proxies with write access (per slice)\n");
#endif
	printf( umsg,SECONDS );
}

/***********************************************************************
 * parseArgs - Parse command line arguments and initialize data
 *
 * Returns 0 if ok, -1 on bad args
 */
static int parseArgs(int argc, char **argv){

    int badop = 0;
    verify_depth = 0;
    verify_error = X509_V_OK;

    argc--;
    argv++;

    while (argc >= 1) {
	if (strcmp(*argv,"-connect") == 0){

		if (--argc < 1) goto bad;
		host= *(++argv);

	} else if (strcmp(*argv,"-reuse") == 0){

		perform = 2;
		reuse = true; 

	} else if (strcmp(*argv,"-new") == 0){

		perform = 1;

	} else if( strcmp(*argv,"-verify") == 0) {
	    
		tm_verify=SSL_VERIFY_PEER|SSL_VERIFY_CLIENT_ONCE;
	    if (--argc < 1) goto bad;
	    verify_depth = atoi(*(++argv));
	    BIO_printf(bio_err,"verify depth is %d\n",verify_depth);

	} else if( strcmp(*argv,"-cert") == 0) {
	    
		if (--argc < 1) goto bad;
	    t_cert_file = *(++argv);

	} else if( strcmp(*argv,"-key") == 0) {

	    if (--argc < 1) goto bad;
	    t_key_file = *(++argv);

	} else if( strcmp(*argv,"-CApath") == 0) {

	    if (--argc < 1) goto bad;
	    CApath = *(++argv);

	} else if( strcmp(*argv,"-CAfile") == 0) {

	    if (--argc < 1) goto bad;
	    CAfile = *(++argv);

	} else if( strcmp(*argv,"-cipher") == 0) {

	    if (--argc < 1) goto bad;
	    tm_cipher= *(++argv);
	}
#ifdef FIONBIO
	else if(strcmp(*argv,"-nbio") == 0) {

	    t_nbio=1;

	}
#endif
	else if(strcmp(*argv,"-www") == 0) {

		if (--argc < 1) goto bad;
		s_www_path = *(++argv);
		if(strlen(s_www_path) > MYBUFSIZ-100){
			BIO_printf(bio_err,"-www option too long\n");
			badop=1;
		}
	} else if(strcmp(*argv,"-bugs") == 0){

	    st_bugs = 1;

	}

/*
#ifndef OPENSSL_NO_SSL2
	else if(strcmp(*argv,"-ssl2") == 0) {
	    s_time_meth = SSLv2_client_method();
	}
#endif
#ifndef OPENSSL_NO_SSL3
	else if(strcmp(*argv,"-ssl3") == 0) {
	    s_time_meth = SSLv3_client_method();
	}
#endif
*/
	// duration 
	else if( strcmp(*argv,"-time") == 0) {
	    if (--argc < 1) goto bad;
	    maxTime = atoi(*(++argv));
	}

	// protocol type
	else if( strcmp(*argv,"-proto") == 0) {
	    if (--argc < 1) goto bad;
		proto = *(++argv);

		// Added support for fwd (client side is regular SSL)
		if ((strcmp(proto, "fwd")) == 0){	
			proto = "ssl";
		}
		if ((strcmp(proto, "ssl")) == 0){	
		    s_time_meth = TLSv1_2_method();
		} else if ((strcmp(proto, "spp")) == 0){
		    s_time_meth = SPP_method(); 
		} else if ((strcmp(proto, "pln")) == 0){
		    s_time_meth = TLSv1_2_method(); 
		}
		 else {
	    	BIO_printf(bio_err, "Protocol %s not supported\n", proto); 
			goto bad; 
		}
	}

	// number of slices 
	else if( strcmp(*argv,"-slice") == 0) {
	    if (--argc < 1) goto bad;
	    slices_len = atoi(*(++argv));
	}

	// number of proxies with read access
	else if( strcmp(*argv,"-read") == 0) {
	    if (--argc < 1) goto bad;
	    r = atoi(*(++argv));
	}

	// number of proxies with write access
	else if( strcmp(*argv,"-write") == 0) {
	    if (--argc < 1) goto bad;
	    w = atoi(*(++argv));
	}

	else {
	    BIO_printf(bio_err,"unknown option %s\n",*argv);
	    badop=1;
	    break;
	}

	argc--;
	argv++;
    }

    // If no input from the user, perform=3 [2=reuse, 1=new]
	if (perform == 0){
		perform = 3;
	}

	// Exit if a bad option was given 
    if(badop) {
bad:
	s_time_usage();
	return -1;
    }

	// All good here
	return 0; 
}

/***********************************************************************
 * TIME - time functions
 */
#define START	0
#define STOP	1

// Function used to compute CPU (or process) time 
static double tm_Time_F(int s){
	return app_tminterval(s, 1);   //defined in [2833, /apps/appsc]
}

/***********************************************************************
 * 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 = line[0] - '0';
	
	// Close file
	fclose(fp);

	return N; 
}


/***********************************************************************
 * Read a proxy list from file and populate array of proxies
 */		
//void read_proxy_list(char *, SPP_PROXY **, SSL *); 
int read_proxy_list(char *file_name, char **proxies_address){
   	FILE *fp;					// pointer to file
	int count = 0;				// simple counters
	char line [128];			// maximum size of line to read from 
	bool firstLine = 1; 		// flag for first line in file
	int N; 						// local variable for number of proxies

	// 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));    
		strcpy(newLine, line);
		proxies_address[count] = newLine;
		
		// Move array index
		count++; 
	}
	
	// Close file
	fclose(fp);

	// all good; 
	return N; 
}


/***********************************************************************
 * Print proxy list 
 */	
void print_proxy_list(SPP_PROXY **proxies, int N){
	int i; 
	
	#ifdef DEBUG	
	printf("Print proxy list. There are %d available proxies.\r\n", N);	
	#endif 
	for (i = 0; i < N; i++){
		printf("Proxy %d -- %s\r\n", i, proxies[i]->address);
	}
}


/***********************************************************************
 * Check for SSL_write error (just write at this point)  
 */	
int check_SSL_write_error(SSL *ssl, int r, int request_len){
	
	switch(SSL_get_error(ssl, r)){
		case SSL_ERROR_NONE:
			if(request_len != r){
				printf("Incomplete write!");
				return -1; 
			}
			break;

		default:
			printf("SSL write problem");
			return -1; 
	}
	return 0; 
}

/***********************************************************************
 * Create slices and assign read and write rights to proxies 
 */	

void slices_management(SSL *ssl, SPP_SLICE **slice_set, SPP_PROXY ** proxies){

	// Create slices_n slices with incremental purpose 
	int i; 
	#ifdef DEBUG
	printf("[DEBUG] Generating %d slices\n", slices_len); 
	#endif
	for (i = 0;  i < slices_len; i++){
		char *newPurpose;  
		char str[30]; 
		sprintf (str, "slices_%d", i); 
		newPurpose = (char *)malloc(strlen(str));    
		strcpy(newPurpose, str);
		slice_set[i] = SPP_generate_slice(ssl, newPurpose); 
		#ifdef DEBUG
		printf("[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 = max(w, r);
	
	// 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 ("[DEBUG] Proxy %s assigned write access to slice-set (WRITE COUNT=%d)\n", proxies[i]->address, (i + 1)); 
				#endif
			}
		}
	}	
}

/***********************************************************************
 * Send a GET request (ssl and spp)
 */
int send_GET_request(SSL *scon){

		MS_STATIC char buf[1024*8];   //Buffer allocation 
		int i; 
		
		// Logging 
		#ifdef DEBUG
		printf("[DEBUG]  GET %s\n", s_www_path); 
		#endif 

		// Form HTTP GET request 
		BIO_snprintf(buf, sizeof buf, "GET %s HTTP/1.0\r\n\r\n", s_www_path);
		int request_len = strlen(buf); 
			
		// Send HTTP GET request (SPP) 
		if (strcmp(proto, "spp") == 0){	
			for (i = 0; i < scon->slices_len; i++){
				// currently writing same record -- differentiate per record in the future 
				int r = SPP_write_record(scon, buf, request_len, scon->slices[i]);
				if (check_SSL_write_error(scon, r, request_len) < 0){
					return -1;  
				}
			}	 
		}
			
		// Send HTTP GET request (SSL) 
		if (strcmp(proto, "ssl") == 0){
			#ifdef DEBUG
			printf("[DEBUG] Sending GET request %s\n", buf); 
			#endif 
			int r = SSL_write(scon, buf, request_len);
			if (check_SSL_write_error(scon, r, request_len) < 0){
				return -1; 
			}
		}
				// Send HTTP GET request (SSL) 
		if (strcmp(proto, "pln") == 0){
			#ifdef DEBUG
			printf("[DEBUG] Sending GET request (plain) %s\n", buf); 
			#endif 
			int r = write(scon, buf, request_len);
			if (r < 0){
				return -1; 
			}
		}

		// All good
		return 0; 
}

/***********************************************************************
 * Wait GET response (ssl and spp)
 */
int wait_GET_response(SSL* scon){

	MS_STATIC char buf[1024*8];   //Buffer allocation 
	int i = 0; 

	// Wait for HTTP response (SPP)
	if (strcmp(proto, "spp") == 0){				
		SPP_SLICE *slice;    // slice for SPP_read
		SPP_CTX *ctx;        // context pointer for SPP_read
		
		// check error for SSP_read_record
		while ((i = SPP_read_record(scon, buf, sizeof(buf), &slice, &ctx)) > 0){
			bytes_read += i;
			#ifdef DEBUG
			//printf("%s", buf); 
			printf("[DEBUG] GET response received (size=%lu)\n", bytes_read); 
			#endif 
		}
	}
	
	// Wait for HTTP response (SSL)
	if (strcmp(proto, "ssl") == 0){
		// check error for SSP_read_record
		while ((i = SSL_read(scon, buf, sizeof(buf))) > 0){
			bytes_read += i;
			#ifdef DEBUG
			//printf("%s", buf); 
			printf("[DEBUG] GET response received (size=%lu)\n", bytes_read); 
			#endif 
		}
	}
		// Wait for HTTP response (pln)
	if (strcmp(proto, "pln") == 0){
		// check error for SSP_read_record
		while ((i = read(scon, buf, sizeof(buf))) > 0){
			bytes_read += i;
			#ifdef DEBUG
			//printf("%s", buf); 
			printf("[DEBUG] GET response received (size=%lu)\n", bytes_read); 
			#endif 
		}
	}

	// All good 
	return 0; 
}


/***********************************************************************
 * MAIN - main processing area for client
 *			real name depends on MONOLITH
 */
int MAIN(int, char **);

int MAIN(int argc, char **argv){

	double totalTime = 0.0;
	int nConn = 0;
	SSL *scon = NULL;
	long finishtime = 0;
	int ret = 1, i;
	MS_STATIC char buf[1024*8];
	int ver;
	clock_t start, end;
	double cpu_time_used;

	apps_startup();
	s_time_init();

	// Outpur for errors
	if (bio_err == NULL){
		bio_err = BIO_new_fp(stderr, BIO_NOCLOSE);
	}

/*	
#if !defined(OPENSSL_NO_SSL2) && !defined(OPENSSL_NO_SSL3)
	s_time_meth = SSLv23_client_method();
#elif !defined(OPENSSL_NO_SSL3)
	s_time_meth = SSLv3_client_method();
#elif !defined(OPENSSL_NO_SSL2)
	s_time_meth = SSLv2_client_method();
#endif
*/

	// Parse command line arguments
	if( parseArgs( argc, argv ) < 0 ){
		goto end;
	}

	// Read proxy list 
	N_proxies = read_proxy_count(filename); 
	
	// Quick check on input parameters	
	if (r > N_proxies || w > N_proxies){
		printf ("The values for r and w need to be <= than the number of proxies\n"); 
		goto end; 
	}

	// Logging input parameters
	#ifdef DEBUG
	BIO_printf(bio_err, "[DEBUG] No. slices=%d No. proxies=%d read=%d write=%d proto=%s\n", slices_len, N_proxies, r, w, proto);
	#endif

	// Allocate memory for proxies address
	proxies_address  = malloc( N_proxies * sizeof (char*));
	read_proxy_list(filename, proxies_address); 

	#ifdef DEBUG
	int ii; 
	for (ii = 0; ii < N_proxies; ii++){
		printf("\t[DEBUG] Proxy %d: %s\n", ii, proxies_address[ii]); 
	}
	#endif

	
	OpenSSL_add_ssl_algorithms();
	if ((tm_ctx = SSL_CTX_new(s_time_meth)) == NULL){
		return(1);
	}

	// Quiet shutdown ...
	SSL_CTX_set_quiet_shutdown(tm_ctx, 1);

	// Set bug options
	if (st_bugs){
		SSL_CTX_set_options(tm_ctx,SSL_OP_ALL);
	}



	SSL_CTX_set_cipher_list(tm_ctx, tm_cipher);

	// Set certificate 
	if(!set_cert_stuff(tm_ctx,t_cert_file,t_key_file)){
		goto end;
	}

	SSL_load_error_strings();

	if ((!SSL_CTX_load_verify_locations(tm_ctx,CAfile,CApath)) || (!SSL_CTX_set_default_verify_paths(tm_ctx))){
		/* BIO_printf(bio_err,"error setting default verify locations\n"); */
		ERR_print_errors(bio_err);
		/* goto end; */
	}

	// If no cipher in input, use default one
	if (tm_cipher == NULL){
		tm_cipher = getenv("SSL_CIPHER");
	}

	// If no cipher at this point, throw an error 
	if (tm_cipher == NULL ) {
		fprintf( stderr, "No CIPHER specified\n" );
	}

    /* [Perform = {2=reuse, 1=new, 3=undefined}]
	if (!(perform & 1)){
		goto next;
	}
	*/
	
	// We are here if -new was used or nothing was specified 
	#ifdef DEBUG
	printf( "[DEBUG] Option=-new Collecting connection statistics for %d seconds\n", maxTime );
	#endif

	// Loop and time how long it takes to make connections
	bytes_read = 0;
	finishtime = (long) time (NULL) + maxTime;
	tm_Time_F(START);

	// Successively open and use connections until time expires
	start = clock();
	for (;;){
		// If time is done just break 
		if (finishtime < (long) time (NULL)){
			#ifdef DEBUG
			printf("[DEBUG] Time is up %lu\n", finishtime); 
			#endif 
			break;
		}
		/*
		// WIN32 specific stuff
#ifdef WIN32_STUFF
		if( flushWinMsgs(0) == -1 ){
			goto end;
		}

		if( waitingToDie || exitNow ){		
			goto end;
		}
#endif */
		// new connection (no re-use) 

		#ifdef DEBUG
		printf("[DEBUG] Connection %d ", nConn); 
		#endif 
		// Handshake
		if (! reuse){
			#ifdef DEBUG
			printf("(new)\n"); 
			#endif 
			scon = NULL; 
		}else{
			#ifdef DEBUG
			printf("(reuse)\n"); 
			#endif 
		}



		if( (scon = doConnection( scon, proto )) == NULL ){
			printf("Error: Could not establish connection\n");
			goto end;
		}

		// Ask for a file if indicated in input 
		if (s_www_path != NULL) {
			
			// Send GET request 
			if (send_GET_request(scon) < 0){
				printf("Error in sending GET request\n");
				goto end; 
			}
			
			// Wait for GET response 
			if (wait_GET_response(scon) < 0){
				printf("Error in receivign GET response\n");
				goto end; 
			}
		}

		nConn += 1;

		if (strcmp(proto, "pln") == 0){	
			#ifdef DEBUG
			printf("[DEBUG] Closing %d\n", scon);
			#endif 
			close((int)scon);
			#ifdef DEBUG
			printf("[DEBUG] Done %d\n", scon);
			#endif 
			scon = NULL;
			continue; //avoid all the tls closing...
		}

#ifdef NO_SHUTDOWN
		#ifdef DEBUG
		printf("[DEBUG] NO SSL shutdown\n"); 
		#endif 
		SSL_set_shutdown(scon, SSL_SENT_SHUTDOWN|SSL_RECEIVED_SHUTDOWN);
#else
		#ifdef DEBUG
		printf("[DEBUG] SSL shutdown\n"); 
		#endif 
		SSL_shutdown(scon);
#endif
		
		#ifdef DEBUG
		printf("[DEBUG] SSL shutdown2 -- ?\n"); 
		#endif 
		SHUTDOWN2(SSL_get_fd(scon));


		if (SSL_session_reused(scon)){
			ver = 'r';
		} else{
			ver = SSL_version(scon);
			if (ver == TLS1_VERSION)
				ver = 't';
			else if (ver == SSL3_VERSION)
				ver = '3';
			else if (ver == SSL2_VERSION)
				ver = '2';
			else
				ver = '*';
		}
		#ifdef DEBUG
		printf("[DEBUG] SSL version=%d\n", ver); 
		#endif 
		fflush(stdout);

		// Free session 
		SSL_free(scon);
		scon = NULL;
	}
	end = clock();
	cpu_time_used = ((double) (end - start)) / CLOCKS_PER_SEC;	
	totalTime += tm_Time_F(STOP); /* Add the time for this iteration */

	i = (int)((long)time(NULL) - finishtime + maxTime);
	//printf( "\n\n%d connections. CPU time=%.2fs; %.2f connections/user sec, bytes read %ld\n", nConn, totalTime, ((double)nConn/totalTime),bytes_read);
	printf( "\n\n%d connections. CPU time=%.5fs; %.2f connections/user sec, bytes read %ld\n", nConn, totalTime, ((double)nConn/cpu_time_used),bytes_read);
	printf( "%d connections in %ld real seconds, %ld bytes read per connection\n",nConn, (long)time(NULL) - finishtime + maxTime, bytes_read/nConn);
	//printf( "\n CPU time=%.5fs; %.5f connections/user sec [NEW]", cpu_time_used, ((double)nConn/cpu_time_used));


/* Now loop and time connections using the same session id over and over
next:
    // [Perform = {2=reuse, 1=new, 3=undefined}]
	if (!(perform & 2)){
		goto end;
	}
	#ifdef DEBUG 
	printf( "[DEBUG] Now timing with session id reuse.\n" );
	#endif 

	nConn = 0;
	totalTime = 0.0;

	finishtime=(long)time(NULL)+maxTime;

	printf( "starting\n" );
	bytes_read=0;
	tm_Time_F(START);
	scon = NULL; 

	for (;;){
		if (finishtime < (long)time(NULL)){
			break;
		}

#ifdef WIN32_STUFF
		if( flushWinMsgs(0) == -1 )
			goto end;

		if( waitingToDie || exitNow )	// we're dead 
			goto end;
#endif

	 	if( (doConnection( scon, proto )) == NULL )
			goto end;
		
		if (s_www_path) {
			// Send GET request 
			if (send_GET_request(scon) < 0){
				printf("Error in sending GET request\n");
				goto end; 
			}
		
			// Receive GET response
			if (wait_GET_response(scon) < 0){
				printf("Error in receiving GET response\n");
				goto end; 
			}

		}

#ifdef NO_SHUTDOWN
		SSL_set_shutdown(scon,SSL_SENT_SHUTDOWN|SSL_RECEIVED_SHUTDOWN);
#else
		SSL_shutdown(scon);
#endif
		SHUTDOWN2(SSL_get_fd(scon));
	
		nConn += 1;
		if (SSL_session_reused(scon))
			ver='r';
		else
			{
			ver = SSL_version(scon);
			if (ver == TLS1_VERSION)
				ver = 't';
			else if (ver == SSL3_VERSION)
				ver = '3';
			else if (ver == SSL2_VERSION)
				ver = '2';
			else
				ver = '*';
			}
		fflush(stdout);
		}
	totalTime += tm_Time_F(STOP); // Add the time for this iteration

	// Logging 
	printf( "\n\n%d connections in %.2fs; %.2f connections/user sec, bytes read %ld\n", nConn, totalTime, ((double)nConn/totalTime),bytes_read);
	printf( "%d connections in %ld real seconds, %ld bytes read per connection\n",nConn,(long)time(NULL)-finishtime+maxTime,bytes_read/nConn);
	//

	ret=0;
*/

end:
	if (scon != NULL){
		SSL_free(scon);
	}

	if (tm_ctx != NULL){
		SSL_CTX_free(tm_ctx);
		tm_ctx=NULL;
	}

	apps_shutdown();
	OPENSSL_EXIT(ret);
}

/* A simple error and exit routine*/
int err_exit(string)
  char *string;
  {
  	#ifdef DEBUG
    printf("[ERROR] %s\n",string);
	#endif

    fprintf(stderr,"%s\n",string);
    exit(0);
  }

/* Print SSL errors and exit*/
int berr_exit(string)
  char *string;
  {
    #ifdef DEBUG
	BIO_printf(bio_err,"%s\n",string);
    ERR_print_errors(bio_err);
	#endif
    exit(0);
  }



// 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(connect(sock,(struct sockaddr *)&addr, sizeof(addr))<0){
		err_exit("Couldn't connect socket");
	}
	#ifdef DEBUG
	printf("[DEBUG] Socket connected\n"); 
	#endif
	return sock;
}






/***********************************************************************
 * doConnection - make a connection
 * Args:
 *		scon	= earlier ssl connection for session id, or NULL
 * Returns:
 *		SSL *	= the connection pointer.
 */
static SSL *doConnection(SSL *scon, char *proto){
	BIO *conn;
	SSL *serverCon;
	int width, i=0;
	fd_set readfds;
	SPP_PROXY *proxies[N_proxies];
	SPP_SLICE *slice_set[slices_len];


	/* WARNING ONLY FOR TESTING, THIS OVERRIDES RETURN TYPE FOR PLN PROTOCOL */

	if ((strcmp(proto, "pln")) == 0){
		char * host_ip = strtok(strdup(host), ":");
		int host_port = atoi(strtok(NULL, ":")); 
		return tcp_connect(host_ip, host_port); // WARNING, THIS OVERRIDES RETURN TYPE. ONLY FOR TESTING
	}


	// what is this?
	if ((conn = BIO_new(BIO_s_connect())) == NULL){
		return(NULL);
	}

	BIO_set_conn_hostname(conn, host);
	#ifdef DEBUG
	printf("[DEBUG] Connecting to: %s  \n", host);
	#endif 


	// Create a new SSL* 
	if (scon == NULL){
		serverCon = SSL_new(tm_ctx);
		if ((strcmp(proto, "spp")) == 0){
			// Assign proxies
			int j; 
			for (j = 0; j < N_proxies; j++){
				proxies[j] = SPP_generate_proxy(serverCon, proxies_address[j]);
				#ifdef DEBUG
				printf("[DEBUG] Generating proxy: %s\n", proxies[j]->address);
				#endif 
			}
			// Generate and assign slices
			slices_management(serverCon, slice_set, proxies); 		
		}
	} 
	// Re-use SSL* passed as argument
	else {
		serverCon = scon;		
		// Get proxies and slices
		int temp_N; 
		int temp_S; 
		SPP_get_proxies(serverCon, proxies, &temp_N); 
		SPP_get_slices(serverCon, slice_set, &temp_S);		
		SSL_set_connect_state(serverCon);
	}

	SSL_set_bio(serverCon, conn, conn);

	// ok, lets connect -- weird 
	for(;;) {
		// Check here 
		if ((strcmp(proto, "spp")) == 0){
			i = SPP_connect(serverCon, slice_set, slices_len, proxies, N_proxies); 
		}
		if ((strcmp(proto, "ssl")) == 0){
			i = SSL_connect(serverCon);
		}		
		if (BIO_sock_should_retry(i)){
			BIO_printf(bio_err,"DELAY\n");

			i = SSL_get_fd(serverCon);
			width = i+1;
			FD_ZERO(&readfds);
			openssl_fdset(i, &readfds);
			/* Note: under VMS with SOCKETSHR the 2nd parameter
			 * is currently of type (int *) whereas under other
			 * systems it is (void *) if you don't have a cast it
			 * will choke the compiler: if you do have a cast then
			 * you can either go for (int *) or (void *).
			 */
			select(width, (void *)&readfds, NULL, NULL, NULL);
			continue;
		}
		break;
	}

	// Negative socket descriptor = error 
	if(i <= 0) {
		BIO_printf(bio_err,"ERROR\n");
		if (verify_error != X509_V_OK){
			BIO_printf(bio_err,"verify error:%s\n", X509_verify_cert_error_string(verify_error));
		} else {
			ERR_print_errors(bio_err);
		}
		if (scon == NULL){
			SSL_free(serverCon);
		}
		return NULL;
	}

	return serverCon;
}


