Only use the fallback mtu after 2 unsuccessful retransmissions if it is less
[openssl.git] / ssl / d1_lib.c
index db180f2ce239b24d6e8dcc99443615682b22eaef..308afff2d4ef2fe62a0176ea19241794f0ede5cf 100644 (file)
 #endif
 
 static void get_current_time(struct timeval *t);
+static void dtls1_set_handshake_header(SSL *s, int type, unsigned long len);
+static int dtls1_handshake_write(SSL *s);
 const char dtls1_version_str[]="DTLSv1" OPENSSL_VERSION_PTEXT;
 int dtls1_listen(SSL *s, struct sockaddr *client);
 
-SSL3_ENC_METHOD DTLSv1_enc_data={
-    dtls1_enc,
+const SSL3_ENC_METHOD DTLSv1_enc_data={
+       tls1_enc,
        tls1_mac,
        tls1_setup_key_block,
        tls1_generate_master_secret,
@@ -83,6 +85,30 @@ SSL3_ENC_METHOD DTLSv1_enc_data={
        TLS_MD_SERVER_FINISH_CONST,TLS_MD_SERVER_FINISH_CONST_SIZE,
        tls1_alert_code,
        tls1_export_keying_material,
+       SSL_ENC_FLAG_DTLS|SSL_ENC_FLAG_EXPLICIT_IV,
+       DTLS1_HM_HEADER_LENGTH,
+       dtls1_set_handshake_header,
+       dtls1_handshake_write   
+       };
+
+const SSL3_ENC_METHOD DTLSv1_2_enc_data={
+       tls1_enc,
+       tls1_mac,
+       tls1_setup_key_block,
+       tls1_generate_master_secret,
+       tls1_change_cipher_state,
+       tls1_final_finish_mac,
+       TLS1_FINISH_MAC_LENGTH,
+       tls1_cert_verify_mac,
+       TLS_MD_CLIENT_FINISH_CONST,TLS_MD_CLIENT_FINISH_CONST_SIZE,
+       TLS_MD_SERVER_FINISH_CONST,TLS_MD_SERVER_FINISH_CONST_SIZE,
+       tls1_alert_code,
+       tls1_export_keying_material,
+       SSL_ENC_FLAG_DTLS|SSL_ENC_FLAG_EXPLICIT_IV|SSL_ENC_FLAG_SIGALGS
+               |SSL_ENC_FLAG_SHA256_PRF|SSL_ENC_FLAG_TLS1_2_CIPHERS,
+       DTLS1_HM_HEADER_LENGTH,
+       dtls1_set_handshake_header,
+       dtls1_handshake_write   
        };
 
 long dtls1_default_timeout(void)
@@ -113,6 +139,9 @@ int dtls1_new(SSL *s)
                d1->cookie_len = sizeof(s->d1->cookie);
                }
 
+       d1->link_mtu = 0;
+       d1->mtu = 0;
+
        if( ! d1->unprocessed_rcds.q || ! d1->processed_rcds.q 
         || ! d1->buffered_messages || ! d1->sent_messages || ! d1->buffered_app_data.q)
                {
@@ -161,24 +190,25 @@ static void dtls1_clear_queues(SSL *s)
     while( (item = pqueue_pop(s->d1->buffered_messages)) != NULL)
         {
         frag = (hm_fragment *)item->data;
-        OPENSSL_free(frag->fragment);
-        OPENSSL_free(frag);
+        dtls1_hm_fragment_free(frag);
         pitem_free(item);
         }
 
     while ( (item = pqueue_pop(s->d1->sent_messages)) != NULL)
         {
         frag = (hm_fragment *)item->data;
-        OPENSSL_free(frag->fragment);
-        OPENSSL_free(frag);
+        dtls1_hm_fragment_free(frag);
         pitem_free(item);
         }
 
        while ( (item = pqueue_pop(s->d1->buffered_app_data.q)) != NULL)
                {
-               frag = (hm_fragment *)item->data;
-               OPENSSL_free(frag->fragment);
-               OPENSSL_free(frag);
+               rdata = (DTLS1_RECORD_DATA *) item->data;
+               if (rdata->rbuf.buf)
+                       {
+                       OPENSSL_free(rdata->rbuf.buf);
+                       }
+               OPENSSL_free(item->data);
                pitem_free(item);
                }
        }
@@ -196,6 +226,7 @@ void dtls1_free(SSL *s)
        pqueue_free(s->d1->buffered_app_data.q);
 
        OPENSSL_free(s->d1);
+       s->d1 = NULL;
        }
 
 void dtls1_clear(SSL *s)
@@ -206,6 +237,7 @@ void dtls1_clear(SSL *s)
        pqueue sent_messages;
        pqueue buffered_app_data;
        unsigned int mtu;
+       unsigned int link_mtu;
 
        if (s->d1)
                {
@@ -215,6 +247,7 @@ void dtls1_clear(SSL *s)
                sent_messages = s->d1->sent_messages;
                buffered_app_data = s->d1->buffered_app_data.q;
                mtu = s->d1->mtu;
+               link_mtu = s->d1->link_mtu;
 
                dtls1_clear_queues(s);
 
@@ -228,6 +261,7 @@ void dtls1_clear(SSL *s)
                if (SSL_get_options(s) & SSL_OP_NO_QUERY_MTU)
                        {
                        s->d1->mtu = mtu;
+                       s->d1->link_mtu = link_mtu;
                        }
 
                s->d1->unprocessed_rcds.q = unprocessed_rcds;
@@ -240,8 +274,10 @@ void dtls1_clear(SSL *s)
        ssl3_clear(s);
        if (s->options & SSL_OP_CISCO_ANYCONNECT)
                s->version=DTLS1_BAD_VER;
+       else if (s->method->version == DTLS_ANY_VERSION)
+               s->version=DTLS1_2_VERSION;
        else
-               s->version=DTLS1_VERSION;
+               s->version=s->method->version;
        }
 
 long dtls1_ctrl(SSL *s, int cmd, long larg, void *parg)
@@ -262,7 +298,45 @@ long dtls1_ctrl(SSL *s, int cmd, long larg, void *parg)
        case DTLS_CTRL_LISTEN:
                ret = dtls1_listen(s, parg);
                break;
-
+       case SSL_CTRL_CHECK_PROTO_VERSION:
+               /* For library-internal use; checks that the current protocol
+                * is the highest enabled version (according to s->ctx->method,
+                * as version negotiation may have changed s->method). */
+               if (s->version == s->ctx->method->version)
+                       return 1;
+               /* Apparently we're using a version-flexible SSL_METHOD
+                * (not at its highest protocol version). */
+               if (s->ctx->method->version == DTLS_method()->version)
+                       {
+#if DTLS_MAX_VERSION != DTLS1_2_VERSION
+#  error Code needs update for DTLS_method() support beyond DTLS1_2_VERSION.
+#endif
+                       if (!(s->options & SSL_OP_NO_DTLSv1_2))
+                               return s->version == DTLS1_2_VERSION;
+                       if (!(s->options & SSL_OP_NO_DTLSv1))
+                               return s->version == DTLS1_VERSION;
+                       }
+               return 0; /* Unexpected state; fail closed. */
+
+               /* Just one protocol version is supported so far;
+                * fail closed if the version is not as expected. */
+               return s->version == DTLS_MAX_VERSION;
+       case DTLS_CTRL_SET_LINK_MTU:
+               if (larg < (long)dtls1_link_min_mtu())
+                       return 0;
+               s->d1->link_mtu = larg;
+               return 1;
+       case DTLS_CTRL_GET_LINK_MIN_MTU:
+               return (long)dtls1_link_min_mtu();
+       case SSL_CTRL_SET_MTU:
+               /*
+                *  We may not have a BIO set yet so can't call dtls1_min_mtu()
+                *  We'll have to make do with dtls1_link_min_mtu() and max overhead
+                */
+               if (larg < (long)dtls1_link_min_mtu() - DTLS1_MAX_MTU_OVERHEAD)
+                       return 0;
+               s->d1->mtu = larg;
+               return larg;
        default:
                ret = ssl3_ctrl(s, cmd, larg, parg);
                break;
@@ -401,12 +475,17 @@ void dtls1_stop_timer(SSL *s)
 
 int dtls1_check_timeout_num(SSL *s)
        {
+       unsigned int mtu;
+
        s->d1->timeout.num_alerts++;
 
        /* Reduce MTU after 2 unsuccessful retransmissions */
-       if (s->d1->timeout.num_alerts > 2)
+       if (s->d1->timeout.num_alerts > 2
+                       && !(SSL_get_options(s) & SSL_OP_NO_QUERY_MTU))
                {
-               s->d1->mtu = BIO_ctrl(SSL_get_wbio(s), BIO_CTRL_DGRAM_GET_FALLBACK_MTU, 0, NULL);               
+               mtu = BIO_ctrl(SSL_get_wbio(s), BIO_CTRL_DGRAM_GET_FALLBACK_MTU, 0, NULL);
+               if(mtu < s->d1->mtu)
+                       s->d1->mtu = mtu;
                }
 
        if (s->d1->timeout.num_alerts > DTLS1_TMO_ALERT_COUNT)
@@ -458,7 +537,11 @@ static void get_current_time(struct timeval *t)
 
        GetSystemTime(&st);
        SystemTimeToFileTime(&st,&now.ft);
+#ifdef __MINGW32__
+       now.ul -= 116444736000000000ULL;
+#else
        now.ul -= 116444736000000000UI64;       /* re-bias to 1/1/1970 */
+#endif
        t->tv_sec  = (long)(now.ul/10000000);
        t->tv_usec = ((int)(now.ul%10000000))/10;
 #elif defined(OPENSSL_SYS_VMS)
@@ -484,3 +567,18 @@ int dtls1_listen(SSL *s, struct sockaddr *client)
        (void) BIO_dgram_get_peer(SSL_get_rbio(s), client);
        return 1;
        }
+
+static void dtls1_set_handshake_header(SSL *s, int htype, unsigned long len)
+       {
+       unsigned char *p = (unsigned char *)s->init_buf->data;
+       dtls1_set_message_header(s, p, htype, len, 0, len);
+       s->init_num = (int)len + DTLS1_HM_HEADER_LENGTH;
+       s->init_off = 0;
+       /* Buffer the message to handle re-xmits */
+       dtls1_buffer_message(s, 0);
+       }
+
+static int dtls1_handshake_write(SSL *s)
+       {
+       return dtls1_do_write(s, SSL3_RT_HANDSHAKE);
+       }