#include <stdio.h>
#include "ssl_locl.h"
#include <openssl/buffer.h>
#include <openssl/rand.h>
#include <openssl/objects.h>
#include <openssl/evp.h>

//#define DEBUG
// They need to be global here to work 
/* Matteo -- START */	
struct timeval currTime;      // keep current time  
struct timeval prevTime;      // keep previous time (to compute time passed)
struct timeval originTime;    // keep previous time (to compute time passed)
/* Matteo -- END*/

static const SSL_METHOD *spp_get_proxy_method(int ver);
static const SSL_METHOD *spp_get_proxy_method(int ver)
	{
	if (ver == SPP_VERSION)
		return SPP_proxy_method();
	return NULL;
	}

IMPLEMENT_spp_meth_func(SPP_VERSION, SPP_proxy_method,
			spp_proxy_accept,
			spp_proxy_connect,
			spp_get_proxy_method)

int spp_proxy_connect(SSL *s) {
    BUF_MEM *buf=NULL;
    unsigned long Time=(unsigned long)time(NULL);
    void (*cb)(const SSL *ssl,int type,int val)=NULL;
    int ret= -1;
	

    RAND_add(&Time,sizeof(Time),0);
    ERR_clear_error();
    clear_sys_error();

    if (s->info_callback != NULL)
        cb=s->info_callback;
    else if (s->ctx->info_callback != NULL)
        cb=s->ctx->info_callback;

    s->in_handshake++;
    if (!SSL_in_init(s) || SSL_in_before(s)) SSL_clear(s); 

#ifndef OPENSSL_NO_HEARTBEATS
	/* If we're awaiting a HeartbeatResponse, pretend we
	 * already got and don't await it anymore, because
	 * Heartbeats don't make sense during handshakes anyway.
	 */
	if (s->tlsext_hb_pending) {
            s->tlsext_hb_pending = 0;
            s->tlsext_hb_seq++;
        }
#endif

    for (;;) {
        switch(s->state) {
            case SSL_ST_RENEGOTIATE:
                SSLerr(SSL_F_SSL3_CONNECT, ERR_R_INTERNAL_ERROR);
                ret = -1;
                goto end;
                
            case SSL_ST_BEFORE:
            case SSL_ST_CONNECT:
            case SSL_ST_BEFORE|SSL_ST_CONNECT:
            case SSL_ST_OK|SSL_ST_CONNECT:
				#ifdef DEBUG
				log_time("Connecting next session\n", &currTime, &prevTime, &originTime); 
				#endif
                s->server=0; /* We are a proxy */
                s->proxy=1;
                
                if (cb != NULL) cb(s,SSL_CB_HANDSHAKE_START,1);

                if ((s->version & 0xff00 ) != 0x0600) { /* SPP major version is 6 */
                    SSLerr(SSL_F_SSL3_CONNECT, ERR_R_INTERNAL_ERROR);
                    ret = -1;
                    goto end;
                }
				
                s->version=SPP_VERSION;
                s->type=SSL_ST_CONNECT;

                if (s->init_buf == NULL) {
                    if ((buf=BUF_MEM_new()) == NULL) {
                        ret= -1;
                        goto end;
                    }
                    if (!BUF_MEM_grow(buf,SSL3_RT_MAX_PLAIN_LENGTH)) {
                        ret= -1;
                        goto end;
                    }
                    s->init_buf=buf;
                    buf=NULL;
                }

                if (!ssl3_setup_buffers(s)) { ret= -1; goto end; }

                /* setup buffing BIO */
                if (!ssl_init_wbio_buffer(s,0)) { ret= -1; goto end; }

                /* don't push the buffering BIO quite yet */

                ssl3_init_finished_mac(s);

                s->state=SSL3_ST_CW_CLNT_HELLO_A;
                s->ctx->stats.sess_connect++;
                s->init_num=0;

                ret=1;
                goto end;
                
            default:
                SSLerr(SSL_F_SSL3_CONNECT,SSL_R_UNKNOWN_STATE);
                ret= -1;
                goto end;
        }
    }
end:
    return(ret);
}

char * spp_next_proxy_address(SSL *s) {
    int i;
    //printf("Searching for proxy (%s) in proxy list %d\n", s->proxy_address, (int)s->proxies_len);
    for (i = 0; i < s->proxies_len; i++) {
        //printf("Comparing with %s ...\n", s->proxies[i]->address);
        if (strcmp(s->proxies[i]->address, s->proxy_address) == 0) {
            //printf("Match\n");
            s->proxy_id = s->proxies[i]->proxy_id;
            break;
        }
    }
    // End the proxy list
    if (i < s->proxies_len-1) {
        //printf("Found next proxy %s\n", s->proxies[i+1]->address);
        return s->proxies[i+1]->address;
    } else if (i == s->proxies_len-1) {
        // Last proxy, return server
        //printf("Found server %s\n", s->spp_server_address);
        return s->spp_server_address;
    } else {
        //printf("Not found\n");
        return NULL;
    }
}

int spp_initialize_ssl(SSL *s, SSL *n) {
    int i;
    // Initialize the new SSL state
    s->other_ssl = n;
    n->other_ssl = s;
    n->proxy_id = s->proxy_id;
    n->session = s->session;
    
    /* Copy proxies and slices */
    //printf("Proxy list is:\n");
    n->proxies_len = s->proxies_len;
    for (i = 0; i < s->proxies_len; i++) {
        n->proxies[i] = (SPP_PROXY*)OPENSSL_malloc(sizeof(SPP_PROXY));
        spp_init_proxy(n->proxies[i]);
        n->proxies[i]->proxy_id = s->proxies[i]->proxy_id;
        n->proxies[i]->address = s->proxies[i]->address;
        //printf("%d: %s\n", n->proxies[i]->proxy_id, n->proxies[i]->address);
    }
    n->slices_len = s->slices_len;
    for (i = 0; i < s->slices_len; i++) {
        n->slices[i] = (SPP_SLICE*)OPENSSL_malloc(sizeof(SPP_SLICE));
        spp_init_slice(n->slices[i]);
        n->slices[i]->slice_id = s->slices[i]->slice_id;
        n->slices[i]->purpose = s->slices[i]->purpose;
    }
    return 1;
}

void spp_proxies_count(SSL *s, int *ahead, int *behind) {
    int i;
    for (i = 0; i < s->proxies_len; i++) {
        if (s->proxies[i]->proxy_id == s->proxy_id) {
            break;
        }
    }
    *ahead = i;
    *behind = s->proxies_len - i - 1;
}

/* The below functions are essentially identical except for an offset.
 * The read and write calls provided by openssl for handshake messages 
 * are inconsistent in whether the 4 byte header is included or not. */
int spp_forward_message(SSL *to, SSL*from) {
    // When receiving a message, the helper functions automatically strip the header.
    // Meant, from->init_msg = &(init_buf->data[init_off])+4
    // So, keep the same pointer location into init_buf->data but increase the init_num value by 4.
    to->init_num = from->init_num + 4;
    to->init_off = from->init_off;
    memcpy(&(to->init_buf->data[to->init_off]), &(from->init_buf->data[from->init_off]), to->init_num);    
    return(ssl3_do_write(to,SSL3_RT_HANDSHAKE));
}
int spp_duplicate_message(SSL *to, SSL*from) {
    to->init_num = from->init_num;
    to->init_off = from->init_off;
    memcpy(&(to->init_buf->data[to->init_off]), &(from->init_buf->data[from->init_off]), to->init_num);    
    return(ssl3_do_write(to,SSL3_RT_HANDSHAKE));
}

/* Grab a handshake message from one state and forward it to the other. */
int get_proxy_msg(SSL *s, int st1, int stn, int msg, int forward) {
    int n, ok;
    n=s->method->ssl_get_message(s,
        st1,
        stn,
        msg,
        SSL3_RT_MAX_PLAIN_LENGTH,
        &ok);
    //printf("ok=%d\n", ok);
    if (!ok) return n;
#if 0
    printf("Got handshake message, len=%d, type=%d\n", n, s->s3->tmp.message_type);
    spp_print_buffer(s->init_msg, s->init_num);
#endif
    if (forward)
        return spp_forward_message(s->other_ssl, s);
    return 1;
}

long spp_get_finished(SSL *s, int st1, int stn)
	{
	unsigned char *p;
	int i=0,al;

	p=(unsigned char *)s->init_buf->data;

	if (s->state == st1) {
            while (1) {
                i=s->method->ssl_read_bytes(s,SSL3_RT_HANDSHAKE, &p[0],SSL3_RT_MAX_PLAIN_LENGTH, 0);
                if (i <= 0) {
                    s->rwstate=SSL_READING;
                    return i;
                }
                s->init_num=i;
                s->init_off=0;
                
/*#ifdef DEBUG
                printf("Received during finished: ");
                spp_print_buffer(p, s->init_num);       
#endif*/
                
                if (s->s3->change_cipher_spec) {
                    break;
                }
                
                // Forward the message along
                i=spp_duplicate_message(s->other_ssl, s);
                if (i<=0) goto err;
            }
            s->state=stn;
        }
/*#ifdef DEBUG
        printf("Got finished\n");
#endif*/

        s->s3->tmp.message_type = SSL3_MT_FINISHED;

        // Send to opposite.
        // First, send change cipher spec
        s->other_ssl->state=SSL3_ST_CW_CHANGE_A;
        i=ssl3_send_change_cipher_spec(s->other_ssl, SSL3_ST_CW_CHANGE_A, SSL3_ST_CW_CHANGE_B);
        if (i<=0) goto err;
        // Then send finished
        i=spp_duplicate_message(s->other_ssl, s);
        if (i<=0) goto err;

	return s->init_num;
f_err:
	ssl3_send_alert(s,SSL3_AL_FATAL,al);
err:
	return(-1);
	}

int spp_process_server_hello(SSL *s) {
    STACK_OF(SSL_CIPHER) *sk;
    const SSL_CIPHER *c;
    unsigned char *p,*d;
    int i,al;
    unsigned int j;
    long n;
#ifndef OPENSSL_NO_COMP
    SSL_COMP *comp;
#endif

    n=s->init_num;

    if ( s->s3->tmp.message_type != SSL3_MT_SERVER_HELLO) {
        al=SSL_AD_UNEXPECTED_MESSAGE;
        SSLerr(SSL_F_SSL3_GET_SERVER_HELLO,SSL_R_BAD_MESSAGE_TYPE);
        goto f_err;
    }

    d=p=(unsigned char *)s->init_msg;

    /* Read the version number */
    if ((p[0] != (s->version>>8)) || (p[1] != (s->version&0xff))) {
        SSLerr(SSL_F_SSL3_GET_SERVER_HELLO,SSL_R_WRONG_SSL_VERSION);
        s->version=(s->version&0xff00)|p[1];
        al=SSL_AD_PROTOCOL_VERSION;
        goto f_err;
    }
    p+=2;

    /* load the server hello data */
    /* load the server random */
    memcpy(s->s3->server_random,p,SSL3_RANDOM_SIZE);
    memcpy(s->other_ssl->s3->server_random,p,SSL3_RANDOM_SIZE);
    p+=SSL3_RANDOM_SIZE;

    /* get the session-id */
    j= *(p++);

    if ((j > sizeof s->session->session_id) || (j > SSL3_SESSION_ID_SIZE)) {
        al=SSL_AD_ILLEGAL_PARAMETER;
        SSLerr(SSL_F_SSL3_GET_SERVER_HELLO,SSL_R_SSL3_SESSION_ID_TOO_LONG);
        goto f_err;
    }

#ifndef OPENSSL_NO_TLSEXT
	/* check if we want to resume the session based on external pre-shared secret */
    if (s->version >= TLS1_VERSION && s->tls_session_secret_cb) {
        SSL_CIPHER *pref_cipher=NULL;
        s->session->master_key_length=sizeof(s->session->master_key);
        if (s->tls_session_secret_cb(s, s->session->master_key,
                &s->session->master_key_length,
                NULL, &pref_cipher,
                s->tls_session_secret_cb_arg)) {
                    s->session->cipher = pref_cipher ?
                        pref_cipher : ssl_get_cipher_by_char(s, p+j);
                    s->s3->flags |= SSL3_FLAGS_CCS_OK;
                    s->other_ssl->s3->flags |= SSL3_FLAGS_CCS_OK;
        }
        s->other_ssl->session->master_key_length=s->session->master_key_length;
        s->other_ssl->session->cipher=s->session->cipher;
    }
#endif /* OPENSSL_NO_TLSEXT */

    if (j != 0 && j == s->session->session_id_length
        && memcmp(p,s->session->session_id,j) == 0) {
            if(s->sid_ctx_length != s->session->sid_ctx_length
               || memcmp(s->session->sid_ctx,s->sid_ctx,s->sid_ctx_length)) {
                /* actually a client application bug */
                al=SSL_AD_ILLEGAL_PARAMETER;
                SSLerr(SSL_F_SSL3_GET_SERVER_HELLO,SSL_R_ATTEMPT_TO_REUSE_SESSION_IN_DIFFERENT_CONTEXT);
                goto f_err;
            }
            s->other_ssl->s3->flags |= SSL3_FLAGS_CCS_OK;
            s->s3->flags |= SSL3_FLAGS_CCS_OK;
            s->hit=s->other_ssl->hit=1;
    } else {
        /* a miss or crap from the other end */
        /* If we were trying for session-id reuse, make a new
         * SSL_SESSION so we don't stuff up other people */
        s->hit=s->other_ssl->hit=0;
        if (s->session->session_id_length > 0) {
            if (!ssl_get_new_session(s,0)) {
                al=SSL_AD_INTERNAL_ERROR;
                goto f_err;
            }
            s->other_ssl->session=s->session;
        }
        s->session->session_id_length=j;
        memcpy(s->session->session_id,p,j); /* j could be 0 */
    }
    p+=j;
    
    
    c=ssl_get_cipher_by_char(s,p);
    if (c == NULL) {
        /* unknown cipher */
        al=SSL_AD_ILLEGAL_PARAMETER;
        SSLerr(SSL_F_SSL3_GET_SERVER_HELLO,SSL_R_UNKNOWN_CIPHER_RETURNED);
        goto f_err;
    }
    /* TLS v1.2 only ciphersuites require v1.2 or later */
    if ((c->algorithm_ssl & SSL_TLSV1_2) && (TLS1_get_version(s) < TLS1_2_VERSION)) {
        al=SSL_AD_ILLEGAL_PARAMETER;
        SSLerr(SSL_F_SSL3_GET_SERVER_HELLO,SSL_R_WRONG_CIPHER_RETURNED);
        goto f_err;
    }
#ifndef OPENSSL_NO_SRP
    if (((c->algorithm_mkey & SSL_kSRP) || (c->algorithm_auth & SSL_aSRP)) && !(s->srp_ctx.srp_Mask & SSL_kSRP)) {
        al=SSL_AD_ILLEGAL_PARAMETER;
        SSLerr(SSL_F_SSL3_GET_SERVER_HELLO,SSL_R_WRONG_CIPHER_RETURNED);
        goto f_err;
    }
#endif /* OPENSSL_NO_SRP */
    p+=ssl_put_cipher_by_char(s,NULL,NULL);
    sk=ssl_get_ciphers_by_id(s);
    i=sk_SSL_CIPHER_find(sk,c);
    if (i < 0) {
        /* we did not say we would use this cipher */
        al=SSL_AD_ILLEGAL_PARAMETER;
        SSLerr(SSL_F_SSL3_GET_SERVER_HELLO,SSL_R_WRONG_CIPHER_RETURNED);
        goto f_err;
    }

    /* Depending on the session caching (internal/external), the cipher
     * and/or cipher_id values may not be set. Make sure that
     * cipher_id is set and use it for comparison. */
    if (s->session->cipher)
        s->session->cipher_id = s->session->cipher->id;
    if (s->hit && (s->session->cipher_id != c->id)) {
        /* Workaround is now obsolete */
#if 0
        if (!(s->options &
            SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG))
#endif
        {
            al=SSL_AD_ILLEGAL_PARAMETER;
            SSLerr(SSL_F_SSL3_GET_SERVER_HELLO,SSL_R_OLD_SESSION_CIPHER_NOT_RETURNED);
            goto f_err;
        }
    }
    s->s3->tmp.new_cipher=s->other_ssl->s3->tmp.new_cipher=c;
    /* lets get the compression algorithm */
    /* COMPRESSION */
#ifdef OPENSSL_NO_COMP
    if (*(p++) != 0) {
        al=SSL_AD_ILLEGAL_PARAMETER;
        SSLerr(SSL_F_SSL3_GET_SERVER_HELLO,SSL_R_UNSUPPORTED_COMPRESSION_ALGORITHM);
        goto f_err;
    }
    /* If compression is disabled we'd better not try to resume a session
     * using compression.
     */
    if (s->session->compress_meth != 0) {
        al=SSL_AD_INTERNAL_ERROR;
        SSLerr(SSL_F_SSL3_GET_SERVER_HELLO,SSL_R_INCONSISTENT_COMPRESSION);
        goto f_err;
    }
#else
    j= *(p++);
    if (s->hit && j != s->session->compress_meth) {
        al=SSL_AD_ILLEGAL_PARAMETER;
        SSLerr(SSL_F_SSL3_GET_SERVER_HELLO,SSL_R_OLD_SESSION_COMPRESSION_ALGORITHM_NOT_RETURNED);
        goto f_err;
    }
    
    if (j == 0)
        comp=NULL;
    else if (s->options & SSL_OP_NO_COMPRESSION) {
        al=SSL_AD_ILLEGAL_PARAMETER;
        SSLerr(SSL_F_SSL3_GET_SERVER_HELLO,SSL_R_COMPRESSION_DISABLED);
        goto f_err;
    } else
        comp=ssl3_comp_find(s->ctx->comp_methods,j);
	
    if ((j != 0) && (comp == NULL)) {
        al=SSL_AD_ILLEGAL_PARAMETER;
        SSLerr(SSL_F_SSL3_GET_SERVER_HELLO,SSL_R_UNSUPPORTED_COMPRESSION_ALGORITHM);
        goto f_err;
    } else {
        s->s3->tmp.new_compression=s->other_ssl->s3->tmp.new_compression=comp;
    }
#endif

#ifndef OPENSSL_NO_TLSEXT
    /* TLS extensions*/
    if (s->version >= SSL3_VERSION) {
        if (!ssl_parse_serverhello_tlsext(s,&p,d,n, &al)) {
            /* 'al' set by ssl_parse_serverhello_tlsext */
            SSLerr(SSL_F_SSL3_GET_SERVER_HELLO,SSL_R_PARSE_TLSEXT);
            goto f_err; 
        }
        if (ssl_check_serverhello_tlsext(s) <= 0) {
            SSLerr(SSL_F_SSL3_GET_SERVER_HELLO,SSL_R_SERVERHELLO_TLSEXT);
            goto err;
        }
    }
#endif

    if (p != (d+n)) {
        /* wrong packet length */
        al=SSL_AD_DECODE_ERROR;
        SSLerr(SSL_F_SSL3_GET_SERVER_HELLO,SSL_R_BAD_PACKET_LENGTH);
        goto f_err;
    }

    return(1);
f_err:
    ssl3_send_alert(s,SSL3_AL_FATAL,al);
err:
    return(-1);
}


/* Function checks to make sure that this message is for this proxy before 
 * calling get_proxy_material_ext which puts a shitload more memory on the stack. */
int get_proxy_material(SSL *s, int server) {
    unsigned char *key_mat_envelope = NULL;
    int id;

    key_mat_envelope = (unsigned char *)s->init_msg;
    
    /* Get the ID */
    n1s(key_mat_envelope, id);
    if (id != s->proxy_id) {
        /* Material not intended for this proxy */
        return 1;
    } else {
        return get_proxy_material_ext(s, server);
    }
}
int get_proxy_material_ext(SSL *s, int server) {
    unsigned char key_mat[SSL3_RT_MAX_PLAIN_LENGTH];
    int *key_mat_len = 0;
    EVP_PKEY *private_key = NULL;
    unsigned char *key_mat_envelope = NULL, *d;
    int encrypted_envelope_key_len = 0;
    unsigned char *envelope_iv;
    //unsigned char encrypted_envelope_key[128] = {0};
    unsigned char *encrypted_envelope_key;
    int encrypted_key_mat_len = 0;
    unsigned char *encrypted_key_mat; /* HACK size... */
    int n, shared_secret_len;
    unsigned char *shared_secret=NULL;
    
    key_mat_envelope = d = (unsigned char *)s->init_msg;
    n = s->init_num;
    key_mat_envelope++; // Skip id
    
    private_key = (server ? s->other_ssl->cert->pkeys[SSL_PKEY_RSA_ENC].privatekey : s->cert->pkeys[SSL_PKEY_RSA_ENC].privatekey); 

    /* get length of encrypted envelope key */
    n2l3(key_mat_envelope, encrypted_envelope_key_len);
    if (encrypted_envelope_key_len > 128)
        goto err;
    /* now pull out the encrypted envelope key */
    /* Memcpy unnecessary. */
    //memcpy(encrypted_envelope_key, key_mat_envelope, encrypted_envelope_key_len); // );
    encrypted_envelope_key = key_mat_envelope;
    // printf("encrypted envelope key:\n");
    // spp_print_buffer(encrypted_envelope_key, encrypted_envelope_key_len);

    /* advance pointer! */
    key_mat_envelope += encrypted_envelope_key_len;

    /* read iv */
    // printf("reading iv\n");
    //memcpy(envelope_iv, key_mat_envelope, EVP_MAX_IV_LENGTH);
    envelope_iv = key_mat_envelope;
    // printf("envelope_iv:\n");
    // // printf("%s\n", envelope_iv);
    // spp_print_buffer(envelope_iv, EVP_MAX_IV_LENGTH);
    /* advance pointer! */
    key_mat_envelope += EVP_MAX_IV_LENGTH;

    /* get legnth of encrypted key material */
    n2l3(key_mat_envelope, encrypted_key_mat_len);

    /* pull the encrypted key material out! */
    //memcpy(encrypted_key_mat, key_mat_envelope, encrypted_key_mat_len);
    encrypted_key_mat = key_mat_envelope;
    // printf("Encrypted key material:\n");
    // spp_print_buffer(encrypted_key_mat, encrypted_key_mat_len);
    key_mat_envelope += encrypted_key_mat_len;
    
    /* Confirm the message is the correct size. */
    if (n != (key_mat_envelope - d)) {
        printf("Message size wrong, header:%d, actual:%d\n", n, (key_mat_envelope - d));
        goto err;
    }

    //printf("opening envelope!\n");

    key_mat_len = envelope_open(
        private_key,
        encrypted_key_mat,
        encrypted_key_mat_len,
        encrypted_envelope_key,
        encrypted_envelope_key_len,
        envelope_iv,
        key_mat,
        &shared_secret,
        &shared_secret_len
        );
    
    /* Proxy actually has no reason to save the secret, cleanse and free */
    if (shared_secret != NULL) { 
        OPENSSL_cleanse(shared_secret,shared_secret_len);
        OPENSSL_free(shared_secret);
    }
    

    /*printf("key mat len: %d\n", key_mat_len);
    spp_print_buffer(key_mat, key_mat_len);*/
    
    return spp_proxy_unpack_mat(s, key_mat, key_mat_len, server);
err:
    printf("Error in get_proxy_material_ext\n");
    return -1;
}

int spp_proxy_unpack_mat(SSL *s, unsigned char *p, long n, int server) {
    int slice_id, len;
    SPP_SLICE *slice, *slice2;
    unsigned char *param=p;
    
    /* More to read */
    while (p-param < n) {
        n1s(p, slice_id);
        //printf("Slice %d received\n", slice_id);
        slice = SPP_get_slice_by_id(s, slice_id);
        slice2 = SPP_get_slice_by_id(s->other_ssl, slice_id);
        if (slice == NULL)        
            goto err;
        
        n2s(p, len);
        if (len > EVP_MAX_KEY_LENGTH)
            goto err;
        if (len > 0) {
            if (server) {
                memcpy(slice->other_read_mat, p, len);
                memcpy(slice2->other_read_mat, p, len);
            } else {
                memcpy(slice->read_mat, p, len);
                memcpy(slice2->read_mat, p, len);
            }
            p += len;
            slice->read_access = 1;
            slice2->read_access = 1;
        }
        
        n2s(p, len);
        if (len > EVP_MAX_KEY_LENGTH)
            goto err;
        if (len> 0) {
            if (server) {
                memcpy(slice->other_write_mat, p, len);
                memcpy(slice2->other_write_mat, p, len);
            } else {
                memcpy(slice->write_mat, p, len);
                memcpy(slice2->write_mat, p, len);
            }
            p += len;
            slice->write_access = 1;
            slice2->write_access = 1;
        }
        
    }
    /* Should now have read the full message. */
    if (p-param != n) {
        printf("Did not read the whole message, %d != %d\n", p-param, n);
        goto err;
    }
    
    //printf("Done\n");
    return 1;
err:
    printf("Error reading proxy key material\n");
    return(-1);
}

int spp_proxy_accept(SSL *s) {
    SSL *next_st=NULL;
    BUF_MEM *buf;
    char *address;
    SPP_PROXY *proxy=NULL, *this_proxy;
    unsigned long alg_k,Time=(unsigned long)time(NULL);
    void (*cb)(const SSL *ssl,int type,int val)=NULL;
    int ahead,behind;
    int ret= -1,i;
    int new_state,state,skip=0;
    /* Matteo -- START 
	struct timeval currTime;      // keep current time  
	struct timeval prevTime;      // keep previous time (to compute time passed)
	struct timeval originTime;    // keep previous time (to compute time passed)
	/* Matteo -- END*/

    RAND_add(&Time,sizeof(Time),0);
    ERR_clear_error();
    clear_sys_error();

    if (s->info_callback != NULL)
            cb=s->info_callback;
    else if (s->ctx->info_callback != NULL)
            cb=s->ctx->info_callback;

    /* init things to blank */
    s->in_handshake++;
    if (!SSL_in_init(s) || SSL_in_before(s)) SSL_clear(s);

    if (s->cert == NULL) {
        SSLerr(SSL_F_SSL3_ACCEPT,SSL_R_NO_CERTIFICATE_SET);
        return(-1);
    }

#ifndef OPENSSL_NO_HEARTBEATS
    /* If we're awaiting a HeartbeatResponse, pretend we
     * already got and don't await it anymore, because
     * Heartbeats don't make sense during handshakes anyway.
     */
    if (s->tlsext_hb_pending) {
        s->tlsext_hb_pending = 0;
        s->tlsext_hb_seq++;
    }
#endif

    for (;;) {
        state=s->state;
        if (next_st != NULL)
            next_st->state=s->state;

        switch (s->state) {
            case SSL_ST_RENEGOTIATE:
                s->renegotiate=1;
                /* s->state=SSL_ST_ACCEPT; */

            case SSL_ST_BEFORE:
            case SSL_ST_ACCEPT:
            case SSL_ST_BEFORE|SSL_ST_ACCEPT:
            case SSL_ST_OK|SSL_ST_ACCEPT:
                                // Initialize timers
				gettimeofday(&currTime, NULL);
				gettimeofday(&prevTime, NULL);
				gettimeofday(&originTime, NULL);
                                #ifdef DEBUG
				log_time("Before handshake\n", &currTime, &prevTime, &originTime); 
				#endif
                s->server = 1;
                s->proxy = 1;

                if (cb != NULL) cb(s,SSL_CB_HANDSHAKE_START,1);

                if ((s->version>>8) != 6) {
                    SSLerr(SSL_F_SSL3_ACCEPT, ERR_R_INTERNAL_ERROR);
                    return -1;
                }
                s->type=SSL_ST_ACCEPT;

                if (s->init_buf == NULL) {
                    if ((buf=BUF_MEM_new()) == NULL) {
                        ret= -1;
                        goto end;
                    }
                    if (!BUF_MEM_grow(buf,SSL3_RT_MAX_PLAIN_LENGTH)) {
                        ret= -1;
                        goto end;
                    }
                    s->init_buf=buf;
                }

                if (!ssl3_setup_buffers(s)) {
                    ret= -1;
                    goto end;
                }

                s->init_num=0;
                s->s3->flags &= ~SSL3_FLAGS_SGC_RESTART_DONE;

                if (s->state != SSL_ST_RENEGOTIATE) {
                    /* Ok, we now need to push on a buffering BIO so that
                     * the output is sent in a way that TCP likes :-)
                     */
                    if (!ssl_init_wbio_buffer(s,1)) { ret= -1; goto end; }
				
                    ssl3_init_finished_mac(s);
                    s->state=SSL3_ST_SR_CLNT_HELLO_A;
                    s->ctx->stats.sess_accept++;
                } else if (!s->s3->send_connection_binding && !(s->options & SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION)) {
                    /* Server attempting to renegotiate with
                     * client that doesn't support secure
                     * renegotiation.
                     */
                    SSLerr(SSL_F_SSL3_ACCEPT, SSL_R_UNSAFE_LEGACY_RENEGOTIATION_DISABLED);
                    ssl3_send_alert(s,SSL3_AL_FATAL,SSL_AD_HANDSHAKE_FAILURE);
                    ret = -1;
                    goto end;
                } else {
                    /* s->state == SSL_ST_RENEGOTIATE,
                     * we will just send a HelloRequest */
                    s->ctx->stats.sess_accept_renegotiate++;
                    s->state=SSL3_ST_SW_HELLO_REQ_A;
                }
                break;

            case SSL3_ST_SR_CLNT_HELLO_A:
            case SSL3_ST_SR_CLNT_HELLO_B:
            case SSL3_ST_SR_CLNT_HELLO_C:
                s->shutdown=0;
                if (s->rwstate != SSL_X509_LOOKUP) {
                    ret=ssl3_get_client_hello(s);
				#ifdef DEBUG
				log_time("Received client hello\n", &currTime, &prevTime, &originTime); 
				#endif
                    if (ret <= 0) goto end;
                }
                
                // Process locally and call application to start new connection
                if ((address = spp_next_proxy_address(s)) == NULL)
                    goto end;
                //printf("Next address is %s\n", address);
                                #ifdef DEBUG
				log_time("Passing control to application\n", &currTime, &prevTime, &originTime); 
				#endif
                if ((next_st = s->proxy_func(s, address)) == NULL)
                    goto end;
                                #ifdef DEBUG
				log_time("Application returned\n", &currTime, &prevTime, &originTime); 
				#endif
                //printf("Callback returned\n");
                
                if (SSL_connect(next_st) <= 0)
                    goto end;
                
                spp_initialize_ssl(s, next_st);
                this_proxy = SPP_get_proxy_by_id(s, s->proxy_id);
                
                // Forward the message on.
                ret=spp_forward_message(next_st, s);
                if (ret <= 0) goto end;
                
                s->renegotiate = 2;
                s->state=SSL3_ST_CR_SRVR_HELLO_A;
                s->init_num=next_st->init_num=0;
                
                //Turn on buffering for the remainder of the handshake
                if (next_st->bbio != next_st->wbio)
                    next_st->wbio=BIO_push(next_st->bbio,next_st->wbio);
                break;
                
            case SSL3_ST_CR_SRVR_HELLO_A:
            case SSL3_ST_CR_SRVR_HELLO_B:
                // Must read from the hello to determine what ciphers are in use.
                ret=get_proxy_msg(next_st, SSL3_ST_CR_SRVR_HELLO_A, SSL3_ST_CR_SRVR_HELLO_B, -1,1);
                                #ifdef DEBUG
				log_time("Received server hello\n", &currTime, &prevTime, &originTime); 
				#endif
                if (ret <= 0) goto end;
                ret=spp_process_server_hello(next_st);
		if (ret <= 0) goto end;

                s->state=SSL3_ST_CR_CERT_A;
                s->init_num=next_st->init_num=0;
                break;

            case SSL3_ST_CR_CERT_A:
            case SSL3_ST_CR_CERT_B:
                ret=get_proxy_msg(next_st, SSL3_ST_CR_CERT_A, SSL3_ST_CR_CERT_B, SSL3_MT_CERTIFICATE,1);
                                #ifdef DEBUG
				log_time("Received server certificate\n", &currTime, &prevTime, &originTime); 
				#endif
                if (ret <= 0) goto end;

                s->state=SSL3_ST_CR_KEY_EXCH_A;
                //SSL3_ST_CR_SRVR_DONE_A; //SSL3_ST_CR_KEY_EXCH_A; Sent in DH, not RSA ciphersuites
                s->init_num=next_st->init_num=0;
                break;

            case SSL3_ST_CR_KEY_EXCH_A:
            case SSL3_ST_CR_KEY_EXCH_B:
                ret=get_proxy_msg(next_st, SSL3_ST_CR_KEY_EXCH_A, SSL3_ST_CR_KEY_EXCH_B, SSL3_MT_SERVER_KEY_EXCHANGE,1);
                                #ifdef DEBUG
				log_time("Received server key exchange\n", &currTime, &prevTime, &originTime); 
				#endif
                if (ret <= 0) goto end;

                s->state=SSL3_ST_CR_SRVR_DONE_A;
                s->init_num=next_st->init_num=0;

                /* at this point we check that we have the
                 * required stuff from the server */
                /*if (!ssl3_check_cert_and_algorithm(next_st)) {
                    ret= -1;
                    goto end;
                }*/
                break;

            case SSL3_ST_CR_SRVR_DONE_A:
            case SSL3_ST_CR_SRVR_DONE_B:
                ret=get_proxy_msg(next_st, SSL3_ST_CR_SRVR_DONE_A, SSL3_ST_CR_SRVR_DONE_B, SSL3_MT_SERVER_DONE, 1);
                                #ifdef DEBUG
				log_time("Received server done\n", &currTime, &prevTime, &originTime); 
				#endif
                if (ret <= 0) goto end;

                if (s->s3->tmp.cert_req)
                    goto end;
                else if ((proxy = spp_get_next_proxy(s, NULL, 0)) != NULL && proxy->proxy_id != s->proxy_id) {
                    /* Received certs from proxies between this proxy and the server. */
                    //s->state=SPP_ST_SW_AHEAD_FLUSH;
                    //s->s3->tmp.next_state=SPP_ST_PR_BEHIND_A;
                    s->state=SPP_ST_PR_BEHIND_A;
                } else {
                    /* Send our certificate. */
                    //s->state=SPP_ST_SW_AHEAD_FLUSH;
                    //s->s3->tmp.next_state=SSL3_ST_SW_CERT_A;
                    s->state=SSL3_ST_SW_CERT_A;
                }
                s->init_num=next_st->init_num=0;

                break;
                
            case SSL3_ST_SW_CERT_A:
            case SSL3_ST_SW_CERT_B:
                /* Check if it is anon DH or anon ECDH, */
                /* normal PSK or KRB5 or SRP */
                if (!(s->s3->tmp.new_cipher->algorithm_auth & (SSL_aNULL|SSL_aKRB5|SSL_aSRP))
                    && !(s->s3->tmp.new_cipher->algorithm_mkey & SSL_kPSK)) {
                                #ifdef DEBUG
				log_time("Sending proxy certificate\n", &currTime, &prevTime, &originTime); 
				#endif
                        ret=ssl3_send_server_certificate(s); //OK
                        if (ret <= 0) goto end;
                        ret=spp_duplicate_message(next_st, s);
                        if (ret <= 0) goto end;
                } else
                    skip=1;

                s->state=SSL3_ST_SW_KEY_EXCH_A;
                s->init_num=next_st->init_num=0;
                break;

            case SSL3_ST_SW_KEY_EXCH_A:
            case SSL3_ST_SW_KEY_EXCH_B:
                alg_k = s->s3->tmp.new_cipher->algorithm_mkey;

                /* clear this, it may get reset by
                 * send_server_key_exchange */
                if ((s->options & SSL_OP_EPHEMERAL_RSA)
#ifndef OPENSSL_NO_KRB5
                    && !(alg_k & SSL_kKRB5)
#endif /* OPENSSL_NO_KRB5 */
                    )
                    /* option SSL_OP_EPHEMERAL_RSA sends temporary RSA key
                     * even when forbidden by protocol specs
                     * (handshake may fail as clients are not required to
                     * be able to handle this) */
                    s->s3->tmp.use_rsa_tmp=1;
                else
                    s->s3->tmp.use_rsa_tmp=0;


                /* only send if a DH key exchange, fortezza or
                 * RSA but we have a sign only certificate
                 *
                 * PSK: may send PSK identity hints
                 *
                 * For ECC ciphersuites, we send a serverKeyExchange
                 * message only if the cipher suite is either
                 * ECDH-anon or ECDHE. In other cases, the
                 * server certificate contains the server's
                 * public key for key exchange.
                 */
                if (s->s3->tmp.use_rsa_tmp
                /* PSK: send ServerKeyExchange if PSK identity
                 * hint if provided */
#ifndef OPENSSL_NO_PSK
                    || ((alg_k & SSL_kPSK) && s->ctx->psk_identity_hint)
#endif
#ifndef OPENSSL_NO_SRP
                    /* SRP: send ServerKeyExchange */
                    || (alg_k & SSL_kSRP)
#endif
                    || (alg_k & (SSL_kDHr|SSL_kDHd|SSL_kEDH))
                    || (alg_k & SSL_kEECDH)
                    || ((alg_k & SSL_kRSA)
                        && (s->cert->pkeys[SSL_PKEY_RSA_ENC].privatekey == NULL
                            || (SSL_C_IS_EXPORT(s->s3->tmp.new_cipher)
                                && EVP_PKEY_size(s->cert->pkeys[SSL_PKEY_RSA_ENC].privatekey)*8 > SSL_C_EXPORT_PKEYLENGTH(s->s3->tmp.new_cipher)
                                )
                            )
                        )
                    )
                        {
                                #ifdef DEBUG
				log_time("Sending proxy key exchange\n", &currTime, &prevTime, &originTime); 
				#endif
                        ret=ssl3_send_server_key_exchange(s); //OK
                        if (ret <= 0) goto end;
                        ret=spp_duplicate_message(next_st, s);
                        if (ret <= 0) goto end;
                        }
                else
                        skip=1;

                s->state=SSL3_ST_SW_SRVR_DONE_A;
                s->init_num=next_st->init_num=0;
                break;
                
            case SSL3_ST_SW_SRVR_DONE_A:
            case SSL3_ST_SW_SRVR_DONE_B:
                                #ifdef DEBUG
				log_time("Sending proxy done\n", &currTime, &prevTime, &originTime); 
				#endif
                ret=ssl3_send_server_done(s); //OK
                if (ret <= 0) goto end;
                ret=spp_duplicate_message(next_st, s);
                if (ret <= 0) goto end;

                s->init_num=next_st->init_num=0;
                
                // Receive and forward the certificates for proxies between us and the server
                if ((proxy = spp_get_next_proxy(s, this_proxy, 0)) != NULL) {
                    // Flush our cert in both directions
                    // Need to receive and forward certificates from proxies between this proxy and the client
                    s->state=SPP_ST_SW_AHEAD_FLUSH;
                    s->s3->tmp.next_state=SPP_ST_PR_AHEAD_A;
                } else {
                    // No proxies between us and client, wait for client key exchange
                    s->state=SPP_ST_SW_AHEAD_FLUSH;
                    s->s3->tmp.next_state=SSL3_ST_SR_KEY_EXCH_A;
                }
                break;

            case SPP_ST_PR_BEHIND_A:
            case SPP_ST_PR_BEHIND_B:
                                #ifdef DEBUG
                //printf("Receiving message from behind proxy %d\n", proxy->proxy_id);
				log_time("Waiting for messages from proxies behind\n", &currTime, &prevTime, &originTime); 
				#endif
                while (proxy != NULL) {
                    next_st->state=SPP_ST_PR_BEHIND_A;
                    ret=get_proxy_msg(next_st, SPP_ST_PR_BEHIND_A, SPP_ST_PR_BEHIND_B, -1, 1);
                                #ifdef DEBUG
				log_time("Proxy message received\n", &currTime, &prevTime, &originTime); 
				#endif
                    if (ret <= 0) goto end;
                    s->init_num=next_st->init_num=0;
                    if (next_st->s3->tmp.message_type == SSL3_MT_SERVER_DONE) {
                        if ((proxy = spp_get_next_proxy(s, proxy, 0)) == NULL || proxy->proxy_id == s->proxy_id) {
                            s->state=SPP_ST_SW_AHEAD_FLUSH;
                            s->s3->tmp.next_state=SSL3_ST_SW_CERT_A;
                            break;
                        }
                        //printf("Receiving message from behind proxy %d\n", proxy->proxy_id);
                    }
                }
                
                break;
                
            case SPP_ST_PR_AHEAD_A:
            case SPP_ST_PR_AHEAD_B:
                                #ifdef DEBUG
//printf("Receiving message from ahead proxy %d\n", proxy->proxy_id);
				log_time("Waiting for messages from proxies ahead\n", &currTime, &prevTime, &originTime); 
				#endif
                while (proxy != NULL) {
                    s->state=SPP_ST_PR_AHEAD_A;
                    ret=get_proxy_msg(s, SPP_ST_PR_AHEAD_A, SPP_ST_PR_AHEAD_B, -1, 1);
                                #ifdef DEBUG
				log_time("Proxy message received\n", &currTime, &prevTime, &originTime); 
				#endif
                    if (ret <= 0) goto end;
                    s->init_num=next_st->init_num=0;
                    if (s->s3->tmp.message_type == SSL3_MT_SERVER_DONE) {
                        if ((proxy = spp_get_next_proxy(s, proxy, 0)) == NULL) {
                            //s->state=SPP_ST_CW_BEHIND_FLUSH;
                            //s->s3->tmp.next_state=SSL3_ST_SR_KEY_EXCH_A;
                            s->state=SSL3_ST_SR_KEY_EXCH_A;
                            break;
                        } 
                        //printf("Receiving message from ahead proxy %d\n", proxy->proxy_id);
                    }
                }
                
                break;
		
            case SSL3_ST_SW_FLUSH:
                /* This code originally checked to see if
                 * any data was pending using BIO_CTRL_INFO
                 * and then flushed. This caused problems
                 * as documented in PR#1939. The proposed
                 * fix doesn't completely resolve this issue
                 * as buggy implementations of BIO_CTRL_PENDING
                 * still exist. So instead we just flush
                 * unconditionally.
                 */

                s->rwstate=SSL_WRITING;
                if (BIO_flush(s->wbio) <= 0) {
                    ret= -1;
                    goto end;
                }
                s->rwstate=SSL_NOTHING;
                next_st->rwstate=SSL_WRITING;
                if (BIO_flush(next_st->wbio) <= 0) {
                    ret= -1;
                    goto end;
                }
                next_st->rwstate=SSL_NOTHING;

                s->state=s->s3->tmp.next_state;
                break;
                
            case SPP_ST_SW_AHEAD_FLUSH:
                /* Flush between the proxy and the client
                 */

                s->rwstate=SSL_WRITING;
                if (BIO_flush(s->wbio) <= 0) {
                    ret= -1;
                    goto end;
                }
                s->rwstate=SSL_NOTHING;

                s->state=s->s3->tmp.next_state;
                break;
                
            case SPP_ST_CW_BEHIND_FLUSH:
                /* Flush between the proxy and the server
                 */

                next_st->rwstate=SSL_WRITING;
                if (BIO_flush(next_st->wbio) <= 0) {
                    ret= -1;
                    goto end;
                }
                next_st->rwstate=SSL_NOTHING;

                s->state=s->s3->tmp.next_state;
                break;
                
            case SSL3_ST_SR_KEY_EXCH_A:
            case SSL3_ST_SR_KEY_EXCH_B:
                                #ifdef DEBUG
				log_time("Waiting for client key exchange\n", &currTime, &prevTime, &originTime); 
				#endif
                ret=get_proxy_msg(s, SSL3_ST_SR_KEY_EXCH_A, SSL3_ST_SR_KEY_EXCH_B, SSL3_MT_CLIENT_KEY_EXCHANGE,1);
                                #ifdef DEBUG
				log_time("Received client key exchange\n", &currTime, &prevTime, &originTime); 
				#endif
                if (ret <= 0) goto end;
                
                s->state=SPP_ST_CR_PRXY_MAT_A;
                s->init_num=next_st->init_num=0;
                break;
                
            case SPP_ST_CR_PRXY_MAT_A:
            case SPP_ST_CR_PRXY_MAT_B:
                                #ifdef DEBUG
				log_time("Receiving client key material\n", &currTime, &prevTime, &originTime); 
				#endif
                // Receive proxy key material for each proxy and the server
                // Pass all messages on
                for (i = 0; i <= s->proxies_len; i++) {
                    s->state = next_st->state = SPP_ST_CR_PRXY_MAT_A;
                    ret=get_proxy_msg(s, SPP_ST_CR_PRXY_MAT_A, SPP_ST_CR_PRXY_MAT_B, SPP_MT_PROXY_KEY_MATERIAL,1);                    
                    if (ret <= 0) goto end;
                    ret=get_proxy_material(s, 0); // From client
                    if (ret <= 0) goto end;
                    s->init_num=next_st->init_num=0;
                }
                
#if defined(OPENSSL_NO_TLSEXT) || defined(OPENSSL_NO_NEXTPROTONEG)
                s->state=SSL3_ST_SR_FINISHED_A;
#else
                if (s->s3->next_proto_neg_seen)
                    s->state=SSL3_ST_SR_NEXT_PROTO_A;
                else
                    s->state=SSL3_ST_SR_FINISHED_A;
#endif
                //s->s3->tmp.next_state=SPP_ST_SR_PRXY_MAT_A;
                //s->state=SSL3_ST_SW_FLUSH;
                break;
                
            case SPP_ST_SR_PRXY_MAT_A:
            case SPP_ST_SR_PRXY_MAT_B:
                                #ifdef DEBUG
				log_time("Receiving server key material\n", &currTime, &prevTime, &originTime); 
				#endif
                // Receive proxy key material for each proxy and the server
                // Pass all messages on
                for (i = 0; i <= s->proxies_len; i++) {
                    //printf("Waiting for proxy material message\n");
                    s->state = next_st->state = SPP_ST_SR_PRXY_MAT_A;
                    ret=get_proxy_msg(next_st, SPP_ST_SR_PRXY_MAT_A, SPP_ST_SR_PRXY_MAT_B, SPP_MT_PROXY_KEY_MATERIAL, 1);
                    if (ret <= 0) goto end;
                    //printf("Got material message\n");
                    ret=get_proxy_material(next_st, 1); // From server
                    if (ret <= 0) goto end;
                    s->init_num=next_st->init_num=0;
                }
                
#ifndef OPENSSL_NO_TLSEXT
                if (s->tlsext_ticket_expected)
                    s->state=SSL3_ST_CR_FINISHED_A;
                else
#endif
                    s->state=SSL3_ST_CR_FINISHED_A;
                break;

#if !defined(OPENSSL_NO_TLSEXT) && !defined(OPENSSL_NO_NEXTPROTONEG)
            case SSL3_ST_SR_NEXT_PROTO_A:
            case SSL3_ST_SR_NEXT_PROTO_B:
                                #ifdef DEBUG
				log_time("Receiving next proto\n", &currTime, &prevTime, &originTime); 
				#endif
                ret=get_proxy_msg(s, SSL3_ST_SR_NEXT_PROTO_A, SSL3_ST_SR_NEXT_PROTO_B, SSL3_MT_NEXT_PROTO,1);
                if (ret <= 0) goto end;
                s->init_num=next_st->init_num=0;
                s->state=SSL3_ST_SR_FINISHED_A;
                break;
#endif

            case SSL3_ST_SR_FINISHED_A:
            case SSL3_ST_SR_FINISHED_B:
                                #ifdef DEBUG
				log_time("Waiting for finished from client\n", &currTime, &prevTime, &originTime); 
				#endif
                s->s3->flags |= SSL3_FLAGS_CCS_OK;
                ret=spp_get_finished(s, SSL3_ST_SR_FINISHED_A, SSL3_ST_SR_FINISHED_B);
                if (ret <= 0) goto end;
                s->init_num=next_st->init_num=0;
                
                s->state=SPP_ST_CW_BEHIND_FLUSH;
                if (s->hit)
                    s->s3->tmp.next_state=SSL_ST_OK;
                else
                    s->s3->tmp.next_state=SPP_ST_SR_PRXY_MAT_A;

                
                /* Cipher state should actually be changed before the finished message.
                 * Since proxies do not actually validate the Finished message, however,
                 * it may be done after. */
                /*if (!s->method->ssl3_enc->change_cipher_state(s, SSL3_CHANGE_CIPHER_SERVER_READ)) {
                    ret= -1;
                    goto end;
                }*/ /* Done when receiving the change cipher spec message */
                next_st->session->cipher=s->s3->tmp.new_cipher;
                if (!next_st->method->ssl3_enc->setup_key_block(next_st))
                        { ret= -1; goto end; }
                if (!next_st->method->ssl3_enc->change_cipher_state(next_st, SSL3_CHANGE_CIPHER_CLIENT_WRITE)) {
                    ret= -1;
                    goto end;
                }
                break;
                
            case SSL3_ST_SW_SESSION_TICKET_A:
            case SSL3_ST_SW_SESSION_TICKET_B:
                                #ifdef DEBUG
				log_time("Waiting for session ticket\n", &currTime, &prevTime, &originTime); 
				#endif
                ret=get_proxy_msg(next_st, SSL3_ST_SW_SESSION_TICKET_A, SSL3_ST_SW_SESSION_TICKET_B, SSL3_MT_NEWSESSION_TICKET,1);
                if (ret <= 0) goto end;
                s->state=SSL3_ST_CR_FINISHED_A;
                s->init_num=next_st->init_num=0;
                break;
                
            case SSL3_ST_CR_FINISHED_A:
            case SSL3_ST_CR_FINISHED_B:
                                #ifdef DEBUG
				log_time("Waiting for finished from server\n", &currTime, &prevTime, &originTime); 
				#endif
                next_st->s3->flags |= SSL3_FLAGS_CCS_OK;
                ret=spp_get_finished(next_st, SSL3_ST_CR_FINISHED_A, SSL3_ST_CR_FINISHED_B);
                if (ret <= 0) goto end;
                s->init_num=next_st->init_num=0;
                
                s->s3->tmp.next_state=SSL_ST_OK;
                s->state=SPP_ST_SW_AHEAD_FLUSH;
                
                s->session->cipher=s->s3->tmp.new_cipher;
                if (!s->method->ssl3_enc->setup_key_block(s))
                        { ret= -1; goto end; }
                if (!s->method->ssl3_enc->change_cipher_state(s, SSL3_CHANGE_CIPHER_SERVER_WRITE)) {
                    ret= -1;
                    goto end;
                }
                /*if (!next_st->method->ssl3_enc->change_cipher_state(next_st, SSL3_CHANGE_CIPHER_CLIENT_READ)) {
                    ret= -1;
                    goto end;
                }*/ /* Done when receiving the change cipher spec message */
                
                /* Setup the slices now that we have the necessary state. */
                ret = spp_init_slices_st(s, SSL3_CHANGE_CIPHER_SERVER_WRITE);
                if (ret <= 0) goto end;
                ret = spp_init_slices_st(s, SSL3_CHANGE_CIPHER_SERVER_READ);
                if (ret <= 0) goto end;
                ret = spp_init_slices_st(next_st, SSL3_CHANGE_CIPHER_CLIENT_WRITE);
                if (ret <= 0) goto end;
                ret = spp_init_slices_st(next_st, SSL3_CHANGE_CIPHER_CLIENT_READ);
                if (ret <= 0) goto end;
                break;

            case SSL_ST_OK:
                                #ifdef DEBUG
				log_time("Handshake OK\n", &currTime, &prevTime, &originTime); 
				#endif
                next_st->state=SSL_ST_OK;
                /* clean a few things up */
                ssl3_cleanup_key_block(s);
                ssl3_cleanup_key_block(next_st);

                BUF_MEM_free(s->init_buf);
                s->init_buf=NULL;
                BUF_MEM_free(next_st->init_buf);
                next_st->init_buf=NULL;

                /* remove buffering on output */
                ssl_free_wbio_buffer(s);
                if (!(next_st->s3->flags & SSL3_FLAGS_POP_BUFFER))
                ssl_free_wbio_buffer(next_st);

                s->init_num=0;

                /* skipped if we just sent a HelloRequest */
                if (s->renegotiate == 2) {
                    s->renegotiate=0;
                    s->new_session=0;

                    ssl_update_cache(s,SSL_SESS_CACHE_SERVER);

                    s->ctx->stats.sess_accept_good++;
                    /* s->server=1; */
                    s->handshake_func=spp_proxy_accept;

                    if (cb != NULL) cb(s,SSL_CB_HANDSHAKE_DONE,1);
                }
                
                next_st->init_num=0;
                next_st->renegotiate=0;
                next_st->new_session=0;

                next_st->handshake_func=spp_proxy_connect;

                ret = 1;
                goto end;
                /* break; */

            default:
                SSLerr(SSL_F_SSL3_ACCEPT,SSL_R_UNKNOWN_STATE);
                ret= -1;
                goto end;
                /* break; */
        }
		
        if (!s->s3->tmp.reuse_message && !skip) {
            if (s->debug) {
                if ((ret=BIO_flush(s->wbio)) <= 0)
                    goto end;
            }

            if ((cb != NULL) && (s->state != state)) {
                new_state=s->state;
                s->state=state;
                cb(s,SSL_CB_ACCEPT_LOOP,1);
                s->state=new_state;
            }
        }
        skip=0;
    }
end:
                    #ifdef DEBUG
                    log_time("Handshake end\n", &currTime, &prevTime, &originTime); 
                    #endif
    /* BIO_flush(s->wbio); */

    s->s3->change_cipher_spec=0;
    s->in_handshake--;
    if (next_st != NULL) {
        next_st->in_handshake--;
        next_st->s3->change_cipher_spec=0;
    }
    if (cb != NULL)
            cb(s,SSL_CB_ACCEPT_EXIT,ret);
    return(ret);
}
