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

Reply via email to