PR: 1997
[openssl.git] / ssl / d1_lib.c
index 070bb1ec184bc31a678e52a240fbc184de332d6b..8039740555f2b388168a3250a277da20e237a62d 100644 (file)
  */
 
 #include <stdio.h>
+#define USE_SOCKETS
 #include <openssl/objects.h>
 #include "ssl_locl.h"
 
-const char *dtls1_version_str="DTLSv1" OPENSSL_VERSION_PTEXT;
+#ifdef OPENSSL_SYS_WIN32
+#include <sys/timeb.h>
+#endif
+
+static void get_current_time(struct timeval *t);
+const char dtls1_version_str[]="DTLSv1" OPENSSL_VERSION_PTEXT;
 
 SSL3_ENC_METHOD DTLSv1_enc_data={
     dtls1_enc,
@@ -84,11 +90,6 @@ long dtls1_default_timeout(void)
        return(60*60*2);
        }
 
-IMPLEMENT_dtls1_meth_func(dtls1_base_method,
-                       ssl_undefined_function,
-                       ssl_undefined_function,
-                       ssl_bad_method)
-
 int dtls1_new(SSL *s)
        {
        DTLS1_STATE *d1;
@@ -103,6 +104,7 @@ int dtls1_new(SSL *s)
        d1->processed_rcds.q=pqueue_new();
        d1->buffered_messages = pqueue_new();
        d1->sent_messages=pqueue_new();
+       d1->buffered_app_data.q=pqueue_new();
 
        if ( s->server)
                {
@@ -110,12 +112,13 @@ int dtls1_new(SSL *s)
                }
 
        if( ! d1->unprocessed_rcds.q || ! d1->processed_rcds.q 
-        || ! d1->buffered_messages || ! d1->sent_messages)
+        || ! d1->buffered_messages || ! d1->sent_messages || ! d1->buffered_app_data.q)
                {
         if ( d1->unprocessed_rcds.q) pqueue_free(d1->unprocessed_rcds.q);
         if ( d1->processed_rcds.q) pqueue_free(d1->processed_rcds.q);
         if ( d1->buffered_messages) pqueue_free(d1->buffered_messages);
                if ( d1->sent_messages) pqueue_free(d1->sent_messages);
+               if ( d1->buffered_app_data.q) pqueue_free(d1->buffered_app_data.q);
                OPENSSL_free(d1);
                return (0);
                }
@@ -164,11 +167,200 @@ void dtls1_free(SSL *s)
         }
        pqueue_free(s->d1->sent_messages);
 
+       while ( (item = pqueue_pop(s->d1->buffered_app_data.q)) != NULL)
+               {
+               frag = (hm_fragment *)item->data;
+               OPENSSL_free(frag->fragment);
+               OPENSSL_free(frag);
+               pitem_free(item);
+               }
+       pqueue_free(s->d1->buffered_app_data.q);
+
        OPENSSL_free(s->d1);
        }
 
 void dtls1_clear(SSL *s)
        {
        ssl3_clear(s);
-       s->version=DTLS1_VERSION;
+       if (s->options & SSL_OP_CISCO_ANYCONNECT)
+               s->version=DTLS1_BAD_VER;
+       else
+               s->version=DTLS1_VERSION;
+       }
+
+long dtls1_ctrl(SSL *s, int cmd, long larg, void *parg)
+       {
+       int ret=0;
+
+       switch (cmd)
+               {
+       case DTLS_CTRL_GET_TIMEOUT:
+               if (dtls1_get_timeout(s, (struct timeval*) parg) != NULL)
+                       {
+                       ret = 1;
+                       }
+               break;
+       case DTLS_CTRL_HANDLE_TIMEOUT:
+               ret = dtls1_handle_timeout(s);
+               break;
+
+       default:
+               ret = ssl3_ctrl(s, cmd, larg, parg);
+               break;
+               }
+       return(ret);
+       }
+
+/*
+ * As it's impossible to use stream ciphers in "datagram" mode, this
+ * simple filter is designed to disengage them in DTLS. Unfortunately
+ * there is no universal way to identify stream SSL_CIPHER, so we have
+ * to explicitly list their SSL_* codes. Currently RC4 is the only one
+ * available, but if new ones emerge, they will have to be added...
+ */
+const SSL_CIPHER *dtls1_get_cipher(unsigned int u)
+       {
+       const SSL_CIPHER *ciph = ssl3_get_cipher(u);
+
+       if (ciph != NULL)
+               {
+               if (ciph->algorithm_enc == SSL_RC4)
+                       return NULL;
+               }
+
+       return ciph;
+       }
+
+void dtls1_start_timer(SSL *s)
+       {
+       /* If timer is not set, initialize duration with 1 second */
+       if (s->d1->next_timeout.tv_sec == 0 && s->d1->next_timeout.tv_usec == 0)
+               {
+               s->d1->timeout_duration = 1;
+               }
+       
+       /* Set timeout to current time */
+       get_current_time(&(s->d1->next_timeout));
+
+       /* Add duration to current time */
+       s->d1->next_timeout.tv_sec += s->d1->timeout_duration;
+       BIO_ctrl(SSL_get_rbio(s), BIO_CTRL_DGRAM_SET_NEXT_TIMEOUT, 0, &(s->d1->next_timeout));
+       }
+
+struct timeval* dtls1_get_timeout(SSL *s, struct timeval* timeleft)
+       {
+       struct timeval timenow;
+
+       /* If no timeout is set, just return NULL */
+       if (s->d1->next_timeout.tv_sec == 0 && s->d1->next_timeout.tv_usec == 0)
+               {
+               return NULL;
+               }
+
+       /* Get current time */
+       get_current_time(&timenow);
+
+       /* If timer already expired, set remaining time to 0 */
+       if (s->d1->next_timeout.tv_sec < timenow.tv_sec ||
+               (s->d1->next_timeout.tv_sec == timenow.tv_sec &&
+                s->d1->next_timeout.tv_usec <= timenow.tv_usec))
+               {
+               memset(timeleft, 0, sizeof(struct timeval));
+               return timeleft;
+               }
+
+       /* Calculate time left until timer expires */
+       memcpy(timeleft, &(s->d1->next_timeout), sizeof(struct timeval));
+       timeleft->tv_sec -= timenow.tv_sec;
+       timeleft->tv_usec -= timenow.tv_usec;
+       if (timeleft->tv_usec < 0)
+               {
+               timeleft->tv_sec--;
+               timeleft->tv_usec += 1000000;
+               }
+
+       return timeleft;
+       }
+
+int dtls1_is_timer_expired(SSL *s)
+       {
+       struct timeval timeleft;
+
+       /* Get time left until timeout, return false if no timer running */
+       if (dtls1_get_timeout(s, &timeleft) == NULL)
+               {
+               return 0;
+               }
+
+       /* Return false if timer is not expired yet */
+       if (timeleft.tv_sec > 0 || timeleft.tv_usec > 0)
+               {
+               return 0;
+               }
+
+       /* Timer expired, so return true */     
+       return 1;
+       }
+
+void dtls1_double_timeout(SSL *s)
+       {
+       s->d1->timeout_duration *= 2;
+       if (s->d1->timeout_duration > 60)
+               s->d1->timeout_duration = 60;
+       dtls1_start_timer(s);
        }
+
+void dtls1_stop_timer(SSL *s)
+       {
+       /* Reset everything */
+       memset(&(s->d1->next_timeout), 0, sizeof(struct timeval));
+       s->d1->timeout_duration = 1;
+       BIO_ctrl(SSL_get_rbio(s), BIO_CTRL_DGRAM_SET_NEXT_TIMEOUT, 0, &(s->d1->next_timeout));
+       }
+
+int dtls1_handle_timeout(SSL *s)
+       {
+       DTLS1_STATE *state;
+
+       /* if no timer is expired, don't do anything */
+       if (!dtls1_is_timer_expired(s))
+               {
+               return 0;
+               }
+
+       dtls1_double_timeout(s);
+       state = s->d1;
+       state->timeout.num_alerts++;
+       if ( state->timeout.num_alerts > DTLS1_TMO_ALERT_COUNT)
+               {
+               /* fail the connection, enough alerts have been sent */
+               SSLerr(SSL_F_DTLS1_READ_FAILED,SSL_R_READ_TIMEOUT_EXPIRED);
+               return 0;
+               }
+
+       state->timeout.read_timeouts++;
+       if ( state->timeout.read_timeouts > DTLS1_TMO_READ_COUNT)
+               {
+               state->timeout.read_timeouts = 1;
+               }
+
+       dtls1_start_timer(s);
+       return dtls1_retransmit_buffered_messages(s);
+       }
+
+static void get_current_time(struct timeval *t)
+{
+#ifdef OPENSSL_SYS_WIN32
+       struct _timeb tb;
+       _ftime(&tb);
+       t->tv_sec = (long)tb.time;
+       t->tv_usec = (long)tb.millitm * 1000;
+#elif defined(OPENSSL_SYS_VMS)
+       struct timeb tb;
+       ftime(&tb);
+       t->tv_sec = (long)tb.time;
+       t->tv_usec = (long)tb.millitm * 1000;
+#else
+       gettimeofday(t, NULL);
+#endif
+}