uint64_t OsTimeInUs(OsContext* aContext) { struct timeval now, diff, adjustedNow; OsMutexLock(aContext->iMutex); gettimeofday(&now, NULL); /* if time has moved backwards, calculate by how much and add this to aContext->iTimeAdjustment */ if (now.tv_sec < aContext->iPrevTime.tv_sec || (now.tv_sec == aContext->iPrevTime.tv_sec && now.tv_usec < aContext->iPrevTime.tv_usec)) { diff = subtractTimeval(&aContext->iPrevTime, &now); fprintf(stderr, "WARNING: clock moved backwards by %llu.%03llusecs\n", (unsigned long long)diff.tv_sec, (unsigned long long)(diff.tv_usec/1000)); aContext->iTimeAdjustment = addTimeval(&aContext->iTimeAdjustment, &diff); } aContext->iPrevTime = now; /* stash current time to allow the next call to spot any backwards move */ adjustedNow = addTimeval(&now, &aContext->iTimeAdjustment); /* add any previous backwards moves to the time */ diff = subtractTimeval(&adjustedNow, &aContext->iStartTime); /* how long since we started, ignoring any backwards moves */ OsMutexUnlock(aContext->iMutex); return (uint64_t)diff.tv_sec * 1000000 + diff.tv_usec; }
/** * Get time difference between first and current function call. * @return time diff */ static timeval getTimeDiff() { static timeval firstTime; static bool firstRun = true; // on first function call get time if (firstRun) { gettimeofday(&firstTime, NULL); firstRun = false; } // get current time timeval curTime, time; gettimeofday(&curTime, NULL); // subtract time on first run from curent time subtractTimeval(&time, &curTime, &firstTime); return time; }
int redisContextConnectSSL(redisContext *c, const char *addr, int port, char* certfile, char* certdir, struct timeval *timeout) { struct timeval start_time; int has_timeout = 0; int is_nonblocking = 0; c->ssl.sd = -1; c->ssl.ctx = NULL; c->ssl.ssl = NULL; c->ssl.bio = NULL; // Set up a SSL_CTX object, which will tell our BIO object how to do its work SSL_CTX* ctx = SSL_CTX_new(TLSv1_1_client_method()); c->ssl.ctx = ctx; // Create a SSL object pointer, which our BIO object will provide. SSL* ssl; // Create our BIO object for SSL connections. BIO* bio = BIO_new_ssl_connect(ctx); c->ssl.bio = bio; // Failure? if (bio == NULL) { char errorbuf[1024]; __redisSetError(c,REDIS_ERR_OTHER,"SSL Error: Error creating BIO!\n"); ERR_error_string(1024,errorbuf); __redisSetError(c,REDIS_ERR_OTHER,errorbuf); // We need to free up the SSL_CTX before we leave. cleanupSSL( &c->ssl ); return REDIS_ERR; } // Makes ssl point to bio's SSL object. BIO_get_ssl(bio, &ssl); c->ssl.ssl = ssl; // Set the SSL to automatically retry on failure. SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY); char* connect_str = (char *)calloc( 1, strlen( addr ) + 10 ); sprintf( connect_str, "%s:%d", addr, port ); c->ssl.conn_str = connect_str; // We're connection to the REDIS server at host:port BIO_set_conn_hostname(bio, connect_str); SSL_CTX_load_verify_locations(ctx, certfile, certdir); // Are we supposed to be blocking or non-blocking? if( (c->flags & REDIS_BLOCK) == 0) { is_nonblocking = 1; BIO_set_nbio(bio,1); } // What about a timeout? // If we're blocking and have a timeout, we need to handle that specially if( NULL != timeout ) { BIO_set_nbio(bio,1); has_timeout = 1; gettimeofday(&start_time, NULL); } while(1) { struct timeval cur_time, elapsed_time; if (has_timeout) { gettimeofday(&cur_time, NULL); elapsed_time = subtractTimeval( cur_time, start_time ); if (compareTimeval( elapsed_time, *timeout) > 0) { char errorbuf[1024]; __redisSetError(c,REDIS_ERR_OTHER,"SSL Error: Connection timed out."); ERR_error_string(1024,errorbuf); __redisSetError(c,REDIS_ERR_OTHER,errorbuf); cleanupSSL( &(c->ssl) ); return REDIS_ERR; } } // Same as before, try to connect. if (BIO_do_connect(bio) <= 0 ) { if( BIO_should_retry( bio ) ) { // We need to retry. } else { char errorbuf[1024]; __redisSetError(c,REDIS_ERR_OTHER,"SSL Error: Failed to connect"); ERR_error_string(1024,errorbuf); __redisSetError(c,REDIS_ERR_OTHER,errorbuf); cleanupSSL( &(c->ssl) ); return REDIS_ERR; } } else { // connect is done... break; } if( has_timeout ) { // Do select and seelct on it int result; int fd = BIO_get_fd( bio, NULL ); struct timeval time_left; if (has_timeout) { time_left = subtractTimeval( *timeout, elapsed_time ); } fd_set readset, writeset; FD_ZERO(&readset); FD_SET(fd, &readset); FD_ZERO(&writeset); FD_SET(fd, &writeset); do { result = select( fd+1, &readset, &writeset, NULL, &time_left); } while( result == -1 && errno == EINTR ); } } while(1) { struct timeval cur_time, elapsed_time; if (has_timeout) { gettimeofday(&cur_time, NULL); elapsed_time = subtractTimeval( cur_time, start_time ); if (compareTimeval( elapsed_time, *timeout) > 0) { char errorbuf[1024]; __redisSetError(c,REDIS_ERR_OTHER,"SSL Error: Connection timed out."); ERR_error_string(1024,errorbuf); __redisSetError(c,REDIS_ERR_OTHER,errorbuf); cleanupSSL( &(c->ssl) ); return REDIS_ERR; } } // Now we need to do the SSL handshake, so we can communicate. if (BIO_do_handshake(bio) <= 0) { if( BIO_should_retry( bio ) ) { // We need to retry. } else { char errorbuf[1024]; __redisSetError(c,REDIS_ERR_OTHER,"SSL Error: handshake failure"); ERR_error_string(1024,errorbuf); __redisSetError(c,REDIS_ERR_OTHER,errorbuf); cleanupSSL( &(c->ssl) ); return REDIS_ERR; } } else { // handshake is done... break; } if( has_timeout ) { // Do select and seelct on it int result; int fd = BIO_get_fd( bio, NULL ); struct timeval time_left; if (has_timeout) { time_left = subtractTimeval( *timeout, elapsed_time ); } fd_set readset, writeset; FD_ZERO(&readset); FD_SET(fd, &readset); FD_ZERO(&writeset); FD_SET(fd, &writeset); do { result = select( fd+1, &readset, &writeset, NULL, &time_left); } while( result == -1 && errno == EINTR ); } } long verify_result = SSL_get_verify_result(ssl); if( verify_result == X509_V_OK) { X509* peerCertificate = SSL_get_peer_certificate(ssl); char commonName [512]; X509_NAME * name = X509_get_subject_name(peerCertificate); X509_NAME_get_text_by_NID(name, NID_commonName, commonName, 512); // TODO: Add a redis config parameter for the common name to check for. // Since Redis connections are generally done with an IP address directly, // we can't just assume that the name we get on the addr is an actual name. // // if(strcasecmp(commonName, "BradBroerman") != 0) { // __redisSetError(c,REDIS_ERR_OTHER,"SSL Error: Error validating cert common name.\n\n" ); // cleanupSSL( &(c->ssl) ); // return REDIS_ERR; } else { char errorbuf[1024]; __redisSetError(c,REDIS_ERR_OTHER,"SSL Error: Error retrieving peer certificate.\n" ); ERR_error_string(1024,errorbuf); __redisSetError(c,REDIS_ERR_OTHER,errorbuf); cleanupSSL( &(c->ssl) ); return REDIS_ERR; } BIO_set_nbio(bio,is_nonblocking); return REDIS_OK; }