This patch fixes a bug when checking if the additional timer for handshake messages is expired. The timeval structs were not compared in the right way. According to the RFC 4347, timers should be initialized with 1 second and doubled after each timeout up to 60 seconds. This behavior is also added, instead of always using the same value. After receiving or sending sucessfully, the corresponding timer is set to its initial value. Timers are always used and the default initial values are 1 second, so the user doesn't have to activate the timers manually. However, he is able to use the known BIO_cntl options to modify the initial values. In the end, the receiving socket timeout is adjusted if the timer for an expected handshake message will expire earlier.
--- crypto/bio/bss_dgram.c 2009-04-14 17:13:35.000000000 +0200 +++ crypto/bio/bss_dgram.c 2009-05-08 11:50:11.000000000 +0200 @@ -108,8 +108,11 @@ unsigned int connected; unsigned int _errno; unsigned int mtu; - struct timeval hstimeoutdiff; - struct timeval hstimeout; + struct timeval initialtrcvtimeout; + struct timeval initialtsndtimeout; + struct timeval rcvtimeout; + struct timeval sndtimeout; + struct timeval nextrcvtimeout; } bio_dgram_data; BIO_METHOD *BIO_s_datagram(void) @@ -137,9 +140,19 @@ if (data == NULL) return 0; memset(data, 0x00, sizeof(bio_dgram_data)); - bi->ptr = data; - + bi->ptr = data; bi->flags=0; + + /* initialize timeouts */ + memset(&(data->initialtrcvtimeout), 0, sizeof(struct timeval)); + data->initialtrcvtimeout.tv_sec = 1; + memset(&(data->initialtsndtimeout), 0, sizeof(struct timeval)); + data->initialtsndtimeout.tv_sec = 1; + memset(&(data->rcvtimeout), 0, sizeof(struct timeval)); + data->rcvtimeout.tv_sec = 1; + memset(&(data->sndtimeout), 0, sizeof(struct timeval)); + data->sndtimeout.tv_sec = 1; + return(1); } @@ -174,7 +187,8 @@ static int dgram_read(BIO *b, char *out, int outl) { - int ret=0; + int ret=0, timeout = 0; + struct timeval settimeout; bio_dgram_data *data = (bio_dgram_data *)b->ptr; struct sockaddr peer; @@ -184,6 +198,62 @@ { clear_socket_error(); memset(&peer, 0x00, peerlen); + + /* Set timeout */ + memcpy(&settimeout, &(data->rcvtimeout), sizeof(struct timeval)); + + /* If the time left for a handshake message is smaller than the + * socket timeout, set the socket timeout to the remaining time. + */ + if (data->nextrcvtimeout.tv_sec > 0 || data->nextrcvtimeout.tv_usec > 0) + { + struct timeval curtime, cmptime; +#ifdef OPENSSL_SYS_WIN32 + struct _timeb tb; + _ftime(&tb); + curtime.tv_sec = (long)tb.time; + curtime.tv_usec = (long)tb.millitm * 1000; +#else + gettimeofday(&curtime, NULL); +#endif + memcpy(&cmptime, &(data->nextrcvtimeout), sizeof(struct timeval)); + cmptime.tv_sec = cmptime.tv_sec - curtime.tv_sec; + cmptime.tv_usec = cmptime.tv_usec - curtime.tv_usec; + if (cmptime.tv_usec < 0) + { + cmptime.tv_sec--; + cmptime.tv_usec += 1000000; + } + if (cmptime.tv_sec < 0) + { + cmptime.tv_sec = 0; + cmptime.tv_usec = 1; + } + + if (cmptime.tv_sec < settimeout.tv_sec || + (cmptime.tv_sec == settimeout.tv_sec && + cmptime.tv_usec <= settimeout.tv_usec)) + { + memcpy(&settimeout, &cmptime, sizeof(struct timeval)); + } + + } + +#if defined(SO_RCVTIMEO) +#ifdef OPENSSL_SYS_WINDOWS + { + timeout = settimeout.tv_sec * 1000 + settimeout.tv_usec/1000; + if (setsockopt(b->num, SOL_SOCKET, SO_RCVTIMEO, + (void*)&timeout, sizeof(timeout)) < 0) + { perror("setsockopt"); ret = -1; } + } +#else + if ( setsockopt(b->num, SOL_SOCKET, SO_RCVTIMEO, &settimeout, + sizeof(struct timeval)) < 0) + { perror("setsockopt"); ret = -1; } +#endif +#endif + /* Last arg in recvfrom is signed on some platforms and * unsigned on others. It is of type socklen_t on some * but this is not universal. Cast to (void *) to avoid @@ -202,11 +272,10 @@ BIO_set_retry_read(b); data->_errno = get_last_socket_error(); } - memset(&(data->hstimeout), 0, sizeof(struct timeval)); } else { - if (data->hstimeout.tv_sec > 0 || data->hstimeout.tv_usec > 0) + if (data->nextrcvtimeout.tv_sec > 0 || data- >nextrcvtimeout.tv_usec > 0) { struct timeval curtime; #ifdef OPENSSL_SYS_WIN32 @@ -218,26 +287,57 @@ gettimeofday(&curtime, NULL); #endif - if (curtime.tv_sec >= data->hstimeout.tv_sec && - curtime.tv_usec >= data->hstimeout.tv_usec) + if (curtime.tv_sec > data->nextrcvtimeout.tv_sec || + (curtime.tv_sec == data->nextrcvtimeout.tv_sec && + curtime.tv_usec >= data->nextrcvtimeout.tv_usec)) { data->_errno = EAGAIN; ret = -1; - memset(&(data->hstimeout), 0, sizeof(struct timeval)); } } } + + /* Timeout occured, double timer */ + if (data->_errno == EAGAIN) + { + memset(&(data->nextrcvtimeout), 0, sizeof(struct timeval)); + data->rcvtimeout.tv_sec *= 2; + if (data->rcvtimeout.tv_sec > 60) + { + data->rcvtimeout.tv_sec = 60; + } + } + /* Message received, reset timeout? */ + else if (data->nextrcvtimeout.tv_sec == 0 && data- >nextrcvtimeout.tv_usec == 0) + { + memset(&(data->nextrcvtimeout), 0, sizeof(struct timeval)); + memcpy(&(data->rcvtimeout), &(data->initialtrcvtimeout), sizeof(struct timeval)); + } } return(ret); } static int dgram_write(BIO *b, const char *in, int inl) { - int ret; + int ret, timeout = 0; bio_dgram_data *data = (bio_dgram_data *)b->ptr; clear_socket_error(); - if ( data->connected ) + /* Set timeout */ +#if defined(SO_SNDTIMEO) +#ifdef OPENSSL_SYS_WINDOWS + timeout = data->sndtimeout.tv_sec * 1000 + data->sndtimeout.tv_usec/ 1000; + if (setsockopt(b->num, SOL_SOCKET, SO_SNDTIMEO, + (void*)&timeout, sizeof(timeout)) < 0) + { perror("setsockopt"); ret = -1; } +#else + if ( setsockopt(b->num, SOL_SOCKET, SO_SNDTIMEO, &data->sndtimeout, + sizeof(struct timeval)) < 0) + { perror("setsockopt"); ret = -1; } +#endif +#endif + + if ( data->connected ) ret=writesocket(b->num,in,inl); else #if defined(NETWARE_CLIB) && defined(NETWARE_BSDSOCK) @@ -261,6 +361,23 @@ #endif } } + + /* Timeout occured, double timer */ + if (data->_errno == EAGAIN) + { + data->sndtimeout.tv_sec *= 2; + if (data->sndtimeout.tv_sec > 60) + { + data->sndtimeout.tv_sec = 60; + } +printf("send timeout, timer set to %d sec, %d usec\n", data- >sndtimeout.tv_sec, data->sndtimeout.tv_usec); + } + /* Message received, reset timeout */ + else + { + memcpy(&(data->sndtimeout), &(data->initialtsndtimeout), sizeof(struct timeval)); + } + return(ret); } @@ -381,101 +498,39 @@ #ifdef OPENSSL_SYS_WIN32 struct _timeb tb; _ftime(&tb); - data->hstimeout.tv_sec = (long)tb.time; - data->hstimeout.tv_usec = (long)tb.millitm * 1000; + data->nextrcvtimeout.tv_sec = (long)tb.time; + data->nextrcvtimeout.tv_usec = (long)tb.millitm * 1000; #else - gettimeofday(&(data->hstimeout), NULL); + gettimeofday(&(data->nextrcvtimeout), NULL); #endif - data->hstimeout.tv_sec += data->hstimeoutdiff.tv_sec; - data->hstimeout.tv_usec += data->hstimeoutdiff.tv_usec; - if (data->hstimeout.tv_usec >= 1000000) + data->nextrcvtimeout.tv_sec += data->rcvtimeout.tv_sec; + data->nextrcvtimeout.tv_usec += data->rcvtimeout.tv_usec; + if (data->nextrcvtimeout.tv_usec >= 1000000) { - data->hstimeout.tv_sec++; - data->hstimeout.tv_usec -= 1000000; + data->nextrcvtimeout.tv_sec++; + data->nextrcvtimeout.tv_usec -= 1000000; } } else { - memset(&(data->hstimeout), 0, sizeof(struct timeval)); + memset(&(data->nextrcvtimeout), 0, sizeof(struct timeval)); + memcpy(&(data->rcvtimeout), &(data->initialtrcvtimeout), sizeof(struct timeval)); } break; -#if defined(SO_RCVTIMEO) case BIO_CTRL_DGRAM_SET_RECV_TIMEOUT: -#ifdef OPENSSL_SYS_WINDOWS - { - struct timeval *tv = (struct timeval *)ptr; - int timeout = tv->tv_sec * 1000 + tv->tv_usec/1000; - if (setsockopt(b->num, SOL_SOCKET, SO_RCVTIMEO, - (void*)&timeout, sizeof(timeout)) < 0) - { perror("setsockopt"); ret = -1; } - } -#else - if ( setsockopt(b->num, SOL_SOCKET, SO_RCVTIMEO, ptr, - sizeof(struct timeval)) < 0) - { perror("setsockopt"); ret = -1; } -#endif - memcpy(&(data->hstimeoutdiff), ptr, sizeof(struct timeval)); + memcpy(&(data->initialtrcvtimeout), ptr, sizeof(struct timeval)); break; case BIO_CTRL_DGRAM_GET_RECV_TIMEOUT: -#ifdef OPENSSL_SYS_WINDOWS - { - int timeout, sz = sizeof(timeout); - struct timeval *tv = (struct timeval *)ptr; - if (getsockopt(b->num, SOL_SOCKET, SO_RCVTIMEO, - (void*)&timeout, &sz) < 0) - { perror("getsockopt"); ret = -1; } - else - { - tv->tv_sec = timeout / 1000; - tv->tv_usec = (timeout % 1000) * 1000; - ret = sizeof(*tv); - } - } -#else - if ( getsockopt(b->num, SOL_SOCKET, SO_RCVTIMEO, - ptr, (void *)&ret) < 0) - { perror("getsockopt"); ret = -1; } -#endif + memcpy(ptr, &(data->initialtrcvtimeout), sizeof(struct timeval)); + ret = sizeof(struct timeval); break; -#endif -#if defined(SO_SNDTIMEO) case BIO_CTRL_DGRAM_SET_SEND_TIMEOUT: -#ifdef OPENSSL_SYS_WINDOWS - { - struct timeval *tv = (struct timeval *)ptr; - int timeout = tv->tv_sec * 1000 + tv->tv_usec/1000; - if (setsockopt(b->num, SOL_SOCKET, SO_SNDTIMEO, - (void*)&timeout, sizeof(timeout)) < 0) - { perror("setsockopt"); ret = -1; } - } -#else - if ( setsockopt(b->num, SOL_SOCKET, SO_SNDTIMEO, ptr, - sizeof(struct timeval)) < 0) - { perror("setsockopt"); ret = -1; } -#endif + memcpy(&(data->initialtsndtimeout), ptr, sizeof(struct timeval)); break; case BIO_CTRL_DGRAM_GET_SEND_TIMEOUT: -#ifdef OPENSSL_SYS_WINDOWS - { - int timeout, sz = sizeof(timeout); - struct timeval *tv = (struct timeval *)ptr; - if (getsockopt(b->num, SOL_SOCKET, SO_SNDTIMEO, - (void*)&timeout, &sz) < 0) - { perror("getsockopt"); ret = -1; } - else - { - tv->tv_sec = timeout / 1000; - tv->tv_usec = (timeout % 1000) * 1000; - ret = sizeof(*tv); - } - } -#else - if ( getsockopt(b->num, SOL_SOCKET, SO_SNDTIMEO, - ptr, (void *)&ret) < 0) - { perror("getsockopt"); ret = -1; } -#endif + memcpy(ptr, &(data->initialtsndtimeout), sizeof(struct timeval)); + ret = sizeof(struct timeval); break; -#endif case BIO_CTRL_DGRAM_GET_SEND_TIMER_EXP: /* fall-through */ case BIO_CTRL_DGRAM_GET_RECV_TIMER_EXP: ______________________________________________________________________ OpenSSL Project http://www.openssl.org Development Mailing List openssl-dev@openssl.org Automated List Manager majord...@openssl.org