/******************************************************************************/ /** \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; void Sender() { while(1) { string buf(EXCHANGE_STRING); int len = 0; do { lock_guard lock(WriteReadMutex); len+=SSL_write(SSLHandler, buf.c_str()+len, buf.size()-len); // for debugging re-neg cout << "SSL STATE: " << SSL_state_string(SSLHandler) << endl; } while( len != static_cast(buf.size()) ); } }; void Client::receive() { char buf[MAX_PACKET_SIZE]; cout << "R " << endl; // TODO: this way it takes 100% CPU, some signal would be usefull memset(buf,'\0',MAX_PACKET_SIZE); int len_rcv = 0; { lock_guard lock(WriteReadMutex); len_rcv = SSL_read(SSLHandler, buf, MAX_PACKET_SIZE); } 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 << 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); 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 if( CharsRead > RENEG_INIT_LEN ) { CharsRead = 0; renegotiate(); } receive(); } } } void Client::renegotiate() { lock_guard lock_reads(WriteReadMutex); 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; }