/******************************************************************************/ /** \Author Krzysztof Kwiatkowski \File client.cpp \Description The SSL client which connects to the server.cpp and initiates renegotitaion after RENEG_INIT_LEN chars exchanged with the server *******************************************************************************/ #include "client.h" #include #include "defs.h" #include #include #include #include #include #include #include #include #include #include #include #include using namespace std; using namespace boost; SSL* SSLHandler = 0; int CharsRead = 0; // with this you can block sender thread during renegotiation mutex WriteReadMutex; bool handle_error_code(int& len, SSL* SSLHandler, int code, const char* func) { switch( SSL_get_error( SSLHandler, code ) ) { case SSL_ERROR_NONE: len+=code; return false; case SSL_ERROR_ZERO_RETURN: cout << "CONNETION CLOSE ON WRITE" << endl; break; case SSL_ERROR_WANT_READ: cout << func << " WANT READ" << endl; return true; break; case SSL_ERROR_WANT_WRITE: cout << func << " WANT WRITE" << endl; return true; break; case SSL_ERROR_SYSCALL: cout << func << " ESYSCALL" << endl; // exit(1); break; case SSL_ERROR_SSL: cout << func << " ESSL" << endl; return true; exit(1); break; default: cout << func << " SOMETHING ELSE" << endl; return true; exit(1); } return true; } void Sender() { while(1) { string buf(EXCHANGE_STRING); int len = 0; do { while( 1 ) { lock_guard lock(WriteReadMutex); cout << "SSL_write: start" << endl; int code = SSL_write(SSLHandler, buf.c_str()+len, buf.size()-len); // cout << "SSL_write: stop" << endl; handle_error_code(len, SSLHandler, code, "SSL_write"); } // for debugging re-neg cout << "SSL STATE: " << SSL_state_string(SSLHandler) << endl; } while( len != static_cast(buf.size()) ); sleep(10); } }; void Client::receive() { char buf[MAX_PACKET_SIZE]; // TODO: this way it takes 100% CPU, some signal would be usefull memset(buf,'\0',MAX_PACKET_SIZE); int len_rcv = 0; { bool flag = true; while( flag ) { lock_guard lock(WriteReadMutex); // cout << "SSL_read: start" << endl; len_rcv = SSL_read(SSLHandler, buf, MAX_PACKET_SIZE); // cout << "SSL_read: stop" << endl; flag = handle_error_code(len_rcv, SSLHandler, len_rcv, "SSL_read"); } } if( len_rcv != 0 ) { CharsRead += len_rcv; // dirty thing - if it has \n on the end - remove it if( buf[len_rcv-1] == '\n' ) buf[len_rcv-1] = '\0'; cout << "RCVD: " << buf << endl; } else { cout << "Closing connection " << _handler << endl; ::close(_handler); } } void Client::connect() { lock_guard lock(WriteReadMutex); struct sockaddr_in echoserver; _handler = socket(AF_INET, SOCK_STREAM, 0); memset(&echoserver, 0, sizeof(echoserver)); echoserver.sin_family = AF_INET; echoserver.sin_addr.s_addr = inet_addr(IP); echoserver.sin_port = htons(PORT); /* Establish connection */ if ( 0 > ::connect(_handler, (struct sockaddr *) &echoserver, sizeof(echoserver)) ) { throw runtime_error("Can't connect to the server"); } SSLHandler = SSL_new(_ctx); // if socket is blocking you can set this and forget about looking at SSL_get_error code on I/O calls // long mode = SSL_CTX_set_mode(_ctx, SSL_MODE_AUTO_RETRY); // if( ( mode & SSL_MODE_AUTO_RETRY) != SSL_MODE_AUTO_RETRY ) // { // throw runtime_error("SSL_MODE_AUTO_RETRY couldn't be set"); // } SSL_set_fd(SSLHandler, _handler); if( SSL_connect(SSLHandler) <= 0) { cerr << "Can't setup SSL session" << endl; exit(1); } // bug reproduces even if call is blocking, so not need to uncommet this line int opts = fcntl(_handler,F_GETFL); opts = opts & ( ~O_NONBLOCK ); fcntl(_handler, F_SETFL, opts); } void Client::start() { // start sender thread first _sender =new thread( Sender ); struct timeval tv; // go to select loop while(1) { // wait timer for select tv.tv_sec = 0; tv.tv_usec = 10; fd_set fd_read; FD_ZERO(&fd_read); FD_SET(_handler, &fd_read); select(_handler+1, &fd_read, NULL, NULL, (struct timeval *)&tv); if( FD_ISSET(_handler, &fd_read ) ) { // this should be in other thread but... it works #ifdef _RENEG_ON_ if( CharsRead > RENEG_INIT_LEN ) { CharsRead = 0; renegotiate(); } #endif receive(); } } } void Client::renegotiate() { lock_guard lock_reads(WriteReadMutex); if( SSL_renegotiate_pending(SSLHandler) == false ) { cout << "Starting SSL renegotiation on SSL" << "client (initiating by SSL client)" << endl; cout << "SSL State: " << SSL_state_string(SSLHandler) << endl; if(SSL_renegotiate(SSLHandler) <= 0){ cerr << "SSL_renegotiate() failed. STATE: " << SSL_state_string(SSLHandler) << endl; ERR_print_errors_fp(stderr); exit(1); } } // cout << "SSL State: " << SSL_state_string(SSLHandler) << endl; // if(SSL_do_handshake(SSLHandler) <= 0){ // cerr << "SSL_do_handshake() failed. STATE: " // << SSL_state_string(SSLHandler) << endl; // ERR_print_errors_fp(stderr); // exit(1); // } } void Client::init() { sslInit(); } // --- MAIN --- // int main() { try { Client client; client.init(); client.connect(); client.start(); } catch(std::runtime_error& e) { cerr << "ERROR " << e.what() << endl; } catch(...) { cerr << "Unknown exception" << endl; } return 0; }