[openssl-commits] [openssl] master update

Matt Caswell matt at openssl.org
Wed Sep 23 12:55:54 UTC 2015


The branch master has been updated
       via  373dc6e196835c06f31ff34cd188471f296126c1 (commit)
       via  468f043ece0e7e262ee6166ae6ec1f7683d82220 (commit)
       via  d8249e99b9477ca059d5ed016fa49389bc0eb9e9 (commit)
       via  ca7256fbd947870101220735dc375862ec7eb9b1 (commit)
       via  35d15a3952d50f243451c5f9fce1e2d9b88b67bb (commit)
       via  fd4e98ec8423cbcfc09aef62de2b9b0108c875c6 (commit)
       via  912c89c529de78de807f58072ae77456b4a251b5 (commit)
       via  e3d0dae7cf8363ca462ac425b72c7bb31c3b4b7a (commit)
       via  01b7851aa27aa144372f5484da916be042d9aa4f (commit)
      from  1556d21850aabf31c554d3c6de2363979a965a9f (commit)


- Log -----------------------------------------------------------------
commit 373dc6e196835c06f31ff34cd188471f296126c1
Author: Matt Caswell <matt at openssl.org>
Date:   Wed Sep 23 12:57:34 2015 +0100

    Sanity check cookie_len
    
    Add a sanity check that the cookie_len returned by app_gen_cookie_cb is
    valid.
    
    Reviewed-by: Andy Polyakov <appro at openssl.org>

commit 468f043ece0e7e262ee6166ae6ec1f7683d82220
Author: Matt Caswell <matt at openssl.org>
Date:   Wed Sep 23 12:40:09 2015 +0100

    Clarify DTLSv1_listen documentation
    
    Clarify that user code is required to allocate sufficient space for the
    addressing scheme in use in the call to DTLSv1_listen.
    
    Reviewed-by: Andy Polyakov <appro at openssl.org>

commit d8249e99b9477ca059d5ed016fa49389bc0eb9e9
Author: Matt Caswell <matt at openssl.org>
Date:   Wed Sep 23 10:02:18 2015 +0100

    Fix s_server DTLSv1_listen issues
    
    Use sockaddr_storage not sockaddr for the client IP address to allow for
    IPv6.
    Also fixed a section of code which was conditional on OPENSSL_NO_DTLS1
    which should not have been.
    
    Reviewed-by: Andy Polyakov <appro at openssl.org>

commit ca7256fbd947870101220735dc375862ec7eb9b1
Author: Matt Caswell <matt at openssl.org>
Date:   Fri Apr 10 14:05:19 2015 +0100

    Add DTLSv1_listen documentation
    
    Adds a new man page to cover the DTLSv1_listen() function.
    
    Reviewed-by: Andy Polyakov <appro at openssl.org>

commit 35d15a3952d50f243451c5f9fce1e2d9b88b67bb
Author: Matt Caswell <matt at openssl.org>
Date:   Fri Apr 10 13:10:05 2015 +0100

    Add -listen documentation
    
    This commit adds documentation for the new -listen option to s_server. Along
    the way it also adds documentation for -dtls, -dtls1 and -dtls1_2 which was
    missing.
    
    Reviewed-by: Andy Polyakov <appro at openssl.org>

commit fd4e98ec8423cbcfc09aef62de2b9b0108c875c6
Author: Matt Caswell <matt at openssl.org>
Date:   Thu Apr 9 10:01:05 2015 +0100

    Add support for DTLSv1_listen in s_server
    
    DTLSv1_listen is a commonly used function within DTLS solutions for
    listening for new incoming connections. This commit adds support to s_server
    for using it.
    
    Reviewed-by: Andy Polyakov <appro at openssl.org>

commit 912c89c529de78de807f58072ae77456b4a251b5
Author: Matt Caswell <matt at openssl.org>
Date:   Thu Apr 9 23:31:35 2015 +0100

    Remove remaining old listen code
    
    The old implementation of DTLSv1_listen which has now been replaced still
    had a few vestiges scattered throughout the code. This commit removes them.
    
    Reviewed-by: Andy Polyakov <appro at openssl.org>

commit e3d0dae7cf8363ca462ac425b72c7bb31c3b4b7a
Author: Matt Caswell <matt at openssl.org>
Date:   Mon Sep 14 22:49:35 2015 +0100

    DTLSv1_listen rewrite
    
    The existing implementation of DTLSv1_listen() is fundamentally flawed. This
    function is used in DTLS solutions to listen for new incoming connections
    from DTLS clients. A client will send an initial ClientHello. The server
    will respond with a HelloVerifyRequest containing a unique cookie. The
    client the responds with a second ClientHello - which this time contains the
    cookie.
    
    Once the cookie has been verified then DTLSv1_listen() returns to user code,
    which is typically expected to continue the handshake with a call to (for
    example) SSL_accept().
    
    Whilst listening for incoming ClientHellos, the underlying BIO is usually in
    an unconnected state. Therefore ClientHellos can come in from *any* peer.
    The arrival of the first ClientHello without the cookie, and the second one
    with it, could be interspersed with other intervening messages from
    different clients.
    
    The whole purpose of this mechanism is as a defence against DoS attacks. The
    idea is to avoid allocating state on the server until the client has
    verified that it is capable of receiving messages at the address it claims
    to come from. However the existing DTLSv1_listen() implementation completely
    fails to do this. It attempts to super-impose itself on the standard state
    machine and reuses all of this code. However the standard state machine
    expects to operate in a stateful manner with a single client, and this can
    cause various problems.
    
    A second more minor issue is that the return codes from this function are
    quite confused, with no distinction made between fatal and non-fatal errors.
    Most user code treats all errors as non-fatal, and simply retries the call
    to DTLSv1_listen().
    
    This commit completely rewrites the implementation of DTLSv1_listen() and
    provides a stand alone implementation that does not rely on the existing
    state machine. It also provides more consistent return codes.
    
    Reviewed-by: Andy Polyakov <appro at openssl.org>

commit 01b7851aa27aa144372f5484da916be042d9aa4f
Author: Matt Caswell <matt at openssl.org>
Date:   Mon Sep 14 22:36:04 2015 +0100

    Add BIO_CTRL_DGRAM_SET_PEEK_MODE
    
    Add the ability to peek at a message from the DTLS read BIO. This is needed
    for the DTLSv1_listen rewrite.
    
    Reviewed-by: Andy Polyakov <appro at openssl.org>

-----------------------------------------------------------------------

Summary of changes:
 apps/s_server.c           |  43 +++++-
 crypto/bio/bss_dgram.c    |   9 +-
 doc/apps/s_server.pod     |  19 +++
 doc/ssl/DTLSv1_listen.pod |  95 ++++++++++++
 doc/ssl/ssl.pod           |   3 +-
 include/openssl/bio.h     |   2 +
 include/openssl/ssl.h     |   5 +
 ssl/d1_both.c             |  26 ++--
 ssl/d1_lib.c              | 386 +++++++++++++++++++++++++++++++++++++++++++++-
 ssl/d1_srvr.c             |  75 ++++-----
 ssl/record/rec_layer_d1.c |  11 +-
 ssl/record/record.h       |   5 +-
 ssl/record/ssl3_record.c  |  19 +--
 ssl/ssl_err.c             |   6 +
 ssl/ssl_locl.h            |  11 +-
 15 files changed, 627 insertions(+), 88 deletions(-)
 create mode 100644 doc/ssl/DTLSv1_listen.pod

diff --git a/apps/s_server.c b/apps/s_server.c
index bd38373..ec73964 100644
--- a/apps/s_server.c
+++ b/apps/s_server.c
@@ -262,6 +262,7 @@ static long socket_mtu;
 #ifndef OPENSSL_NO_DTLS1
 static int cert_chain = 0;
 #endif
+static int dtlslisten = 0;
 
 static BIO *serverinfo_in = NULL;
 static const char *s_serverinfo_file = NULL;
@@ -807,7 +808,7 @@ typedef enum OPTION_choice {
     OPT_SRPUSERSEED, OPT_REV, OPT_WWW, OPT_UPPER_WWW, OPT_HTTP,
     OPT_SSL3,
     OPT_TLS1_2, OPT_TLS1_1, OPT_TLS1, OPT_DTLS, OPT_DTLS1,
-    OPT_DTLS1_2, OPT_TIMEOUT, OPT_MTU, OPT_CHAIN,
+    OPT_DTLS1_2, OPT_TIMEOUT, OPT_MTU, OPT_CHAIN, OPT_LISTEN,
     OPT_ID_PREFIX, OPT_RAND, OPT_SERVERNAME, OPT_SERVERNAME_FATAL,
     OPT_CERT2, OPT_KEY2, OPT_NEXTPROTONEG, OPT_ALPN, OPT_JPAKE,
     OPT_SRTP_PROFILES, OPT_KEYMATEXPORT, OPT_KEYMATEXPORTLEN,
@@ -937,6 +938,8 @@ OPTIONS s_server_options[] = {
     {"timeout", OPT_TIMEOUT, '-', "Enable timeouts"},
     {"mtu", OPT_MTU, 'p', "Set link layer MTU"},
     {"chain", OPT_CHAIN, '-', "Read a certificate chain"},
+    {"listen", OPT_LISTEN, '-',
+     "Listen for a DTLS ClientHello with a cookie and then connect"},
 #endif
 #ifndef OPENSSL_NO_DH
     {"no_dhe", OPT_NO_DHE, '-', "Disable ephemeral DH"},
@@ -1369,6 +1372,9 @@ int s_server_main(int argc, char *argv[])
         case OPT_CHAIN:
             cert_chain = 1;
             break;
+        case OPT_LISTEN:
+            dtlslisten = 1;
+            break;
 #else
         case OPT_DTLS:
         case OPT_DTLS1:
@@ -1376,6 +1382,7 @@ int s_server_main(int argc, char *argv[])
         case OPT_TIMEOUT:
         case OPT_MTU:
         case OPT_CHAIN:
+        case OPT_LISTEN:
             break;
 #endif
         case OPT_ID_PREFIX:
@@ -1434,6 +1441,11 @@ int s_server_main(int argc, char *argv[])
         BIO_printf(bio_err, "Can't use -HTTP, -www or -WWW with DTLS\n");
         goto end;
     }
+
+    if (dtlslisten && socket_type != SOCK_DGRAM) {
+        BIO_printf(bio_err, "Can only use -listen with DTLS\n");
+        goto end;
+    }
 #endif
 
     if (unix_path && (socket_type != SOCK_STREAM)) {
@@ -2383,8 +2395,32 @@ static int init_ssl_connection(SSL *con)
     unsigned next_proto_neg_len;
 #endif
     unsigned char *exportedkeymat;
+    struct sockaddr_storage client;
+
+#ifndef OPENSSL_NO_DTLS1
+    if(dtlslisten) {
+        i = DTLSv1_listen(con, &client);
+        if (i > 0) {
+            BIO *wbio;
+            int fd;
+
+            wbio = SSL_get_wbio(con);
+            if(wbio) {
+                BIO_get_fd(wbio, &fd);
+            }
+
+            if(!wbio || connect(fd, (struct sockaddr *)&client,
+                                sizeof(struct sockaddr_storage))) {
+                BIO_printf(bio_err, "ERROR - unable to connect\n");
+                return 0;
+            }
+            dtlslisten = 0;
+            i = SSL_accept(con);
+        }
+    } else
+#endif
+        i = SSL_accept(con);
 
-    i = SSL_accept(con);
 #ifdef CERT_CB_TEST_RETRY
     {
         while (i <= 0 && SSL_get_error(con, i) == SSL_ERROR_WANT_X509_LOOKUP
@@ -2412,7 +2448,8 @@ static int init_ssl_connection(SSL *con)
 #endif
 
     if (i <= 0) {
-        if (BIO_sock_should_retry(i)) {
+        if ((dtlslisten && i == 0)
+                || (!dtlslisten && BIO_sock_should_retry(i))) {
             BIO_printf(bio_s_out, "DELAY\n");
             return (1);
         }
diff --git a/crypto/bio/bss_dgram.c b/crypto/bio/bss_dgram.c
index 91474fc..e7371c9 100644
--- a/crypto/bio/bss_dgram.c
+++ b/crypto/bio/bss_dgram.c
@@ -169,6 +169,7 @@ typedef struct bio_dgram_data_st {
     unsigned int mtu;
     struct timeval next_timeout;
     struct timeval socket_timeout;
+    unsigned int peekmode;
 } bio_dgram_data;
 
 # ifndef OPENSSL_NO_SCTP
@@ -367,6 +368,7 @@ static int dgram_read(BIO *b, char *out, int outl)
 {
     int ret = 0;
     bio_dgram_data *data = (bio_dgram_data *)b->ptr;
+    int flags = 0;
 
     struct {
         /*
@@ -392,7 +394,9 @@ static int dgram_read(BIO *b, char *out, int outl)
         clear_socket_error();
         memset(&sa.peer, 0, sizeof(sa.peer));
         dgram_adjust_rcv_timeout(b);
-        ret = recvfrom(b->num, out, outl, 0, &sa.peer.sa, (void *)&sa.len);
+        if (data->peekmode)
+            flags = MSG_PEEK;
+        ret = recvfrom(b->num, out, outl, flags, &sa.peer.sa, (void *)&sa.len);
         if (sizeof(sa.len.i) != sizeof(sa.len.s) && sa.len.i == 0) {
             OPENSSL_assert(sa.len.s <= sizeof(sa.peer));
             sa.len.i = (int)sa.len.s;
@@ -923,6 +927,9 @@ static long dgram_ctrl(BIO *b, int cmd, long num, void *ptr)
     case BIO_CTRL_DGRAM_GET_MTU_OVERHEAD:
         ret = dgram_get_mtu_overhead(data);
         break;
+    case BIO_CTRL_DGRAM_SET_PEEK_MODE:
+        data->peekmode = (unsigned int)num;
+        break;
     default:
         ret = 0;
         break;
diff --git a/doc/apps/s_server.pod b/doc/apps/s_server.pod
index 567df2c..3fd9a81 100644
--- a/doc/apps/s_server.pod
+++ b/doc/apps/s_server.pod
@@ -67,6 +67,10 @@ B<openssl> B<s_server>
 [B<-no_tmp_rsa>]
 [B<-ssl3>]
 [B<-tls1>]
+[B<-dtls>]
+[B<-dtls1>]
+[B<-dtls1_2>]
+[B<-listen>]
 [B<-no_ssl3>]
 [B<-no_tls1>]
 [B<-no_dhe>]
@@ -283,6 +287,21 @@ these options disable the use of certain SSL or TLS protocols. By default
 the initial handshake uses a method which should be compatible with all
 servers and permit them to use SSL v3 or TLS as appropriate.
 
+=item B<-dtls>, B<-dtls1>, B<-dtls1_2>
+
+these options make s_server use DTLS protocols instead of TLS. With B<-dtls>
+s_server will negotiate any supported DTLS protcol version, whilst B<-dtls1> and
+B<-dtls1_2> will only support DTLS1.0 and DTLS1.2 respectively.
+
+=item B<-listen>
+
+this option can only be used in conjunction with one of the DTLS options above.
+With this option s_server will listen on a UDP port for incoming connections.
+Any ClientHellos that arrive will be checked to see if they have a cookie in
+them or not. Any without a cookie will be responded to with a
+HelloVerifyRequest. If a ClientHello with a cookie is received then s_server
+will connect to that peer and complete the handshake.
+
 =item B<-bugs>
 
 there are several known bug in SSL and TLS implementations. Adding this
diff --git a/doc/ssl/DTLSv1_listen.pod b/doc/ssl/DTLSv1_listen.pod
new file mode 100644
index 0000000..d5f5a52
--- /dev/null
+++ b/doc/ssl/DTLSv1_listen.pod
@@ -0,0 +1,95 @@
+=pod
+
+=head1 NAME
+
+DTLSv1_listen - listen for incoming DTLS connections.
+
+=head1 SYNOPSIS
+
+ #include <openssl/ssl.h>
+
+ int DTLSv1_listen(SSL *ssl, struct sockaddr *peer);
+
+=head1 DESCRIPTION
+
+DTLSv1_listen() listens for new incoming DTLS connections. If a ClientHello is
+received that does not contain a cookie, then DTLSv1_listen() responds with a
+HelloVerifyRequest. If a ClientHello is received with a cookie that is verified
+then control is returned to user code to enable the handshake to be completed
+(for example by using SSL_accept()).
+
+=head1 NOTES
+
+Datagram based protocols can be susceptible to Denial of Service attacks. A
+DTLS attacker could, for example, submit a series of handshake initiation
+requests that cause the server to allocate state (and possibly perform
+cryptographic operations) thus consuming server resources. The attacker could
+also (with UDP) quite simply forge the source IP address in such an attack.
+
+As a counter measure to that DTLS includes a stateless cookie mechanism. The
+idea is that when a client attempts to connect to a server it sends a
+ClientHello message. The server responds with a HelloVerifyRequest which
+contains a unique cookie. The client then resends the ClientHello, but this time
+includes the cookie in the message thus proving that the client is capable of
+receiving messages sent to that address. All of this can be done by the server
+without allocating any state, and thus without consuming expensive resources.
+
+OpenSSL implements this capability via the DTLSv1_listen() function. The B<ssl>
+parameter should be a newly allocated SSL object with its read and write BIOs
+set, in the same way as might be done for a call to SSL_accept(). Typically the
+read BIO will be in an "unconnected" state and thus capable of receiving
+messages from any peer.
+
+When a ClientHello is received that contains a cookie that has been verified,
+then DTLSv1_listen() will return with the B<ssl> parameter updated into a state
+where the handshake can be continued by a call to (for example) SSL_accept().
+Additionally the B<struct sockaddr> location pointed to by B<peer> will be
+filled in with details of the peer that sent the ClientHello. It is the calling
+code's responsibility to ensure that the B<peer> location is sufficiently large
+to accommodate the addressing scheme in use. For example this might be done by
+allocating space for a struct sockaddr_storage and casting the pointer to it to
+a struct sockaddr * for the call to DTLSv1_listen(). Typically user code is
+expected to "connect" the underlying socket to the peer and continue the
+handshake in a connected state.
+
+Prior to calling DTLSv1_listen() user code must ensure that cookie generation
+and verification callbacks have been set up using
+SSL_CTX_set_cookie_generate_cb() and SSL_CTX_set_cookie_verify_cb()
+respectively.
+
+Since DTLSv1_listen() operates entirely statelessly whilst processing incoming
+ClientHellos it is unable to process fragmented messages (since this would
+require the allocation of state). An implication of this is that DTLSv1_listen()
+B<only> supports ClientHellos that fit inside a single datagram.
+
+=head1 RETURN VALUES
+
+From OpenSSL 1.1.0 a return value of >= 1 indicates success. In this instance
+the B<peer> value will be filled in and the B<ssl> object set up ready to
+continue the handshake.
+
+A return value of 0 indicates a non-fatal error. This could (for
+example) be because of non-blocking IO, or some invalid message having been
+received from a peer. Errors may be placed on the OpenSSL error queue with
+further information if appropriate. Typically user code is expected to retry the
+call to DTLSv1_listen() in the event of a non-fatal error. Any old errors on the
+error queue will be cleared in the subsequent call.
+
+A return value of <0 indicates a fatal error. This could (for example) be
+because of a failure to allocate sufficient memory for the operation.
+
+Prior to OpenSSL 1.1.0 fatal and non-fatal errors both produce return codes
+<= 0 (in typical implementations user code treats all errors as non-fatal),
+whilst return codes >0 indicate success.
+
+=head1 SEE ALSO
+
+L<SSL_get_error(3)|SSL_get_error(3)>, L<SSL_accept(3)|SSL_accept(3)>,
+L<ssl(3)|ssl(3)>, L<bio(3)|bio(3)>
+
+=head1 HISTORY
+
+DTLSv1_listen() was added in OpenSSL 0.9.8. Its return codes were clarified in
+OpenSSL 1.1.0.
+
+=cut
diff --git a/doc/ssl/ssl.pod b/doc/ssl/ssl.pod
index 6443de7..695a13c 100644
--- a/doc/ssl/ssl.pod
+++ b/doc/ssl/ssl.pod
@@ -743,7 +743,8 @@ L<SSL_SESSION_get_time(3)>,
 L<d2i_SSL_SESSION(3)>,
 L<SSL_CTX_set_psk_client_callback(3)>,
 L<SSL_CTX_use_psk_identity_hint(3)>,
-L<SSL_get_psk_identity(3)>
+L<SSL_get_psk_identity(3)>,
+L<DTLSv1_listen(3)|DTLSv1_listen(3)>
 
 =head1 HISTORY
 
diff --git a/include/openssl/bio.h b/include/openssl/bio.h
index 2da93bd..75d052f 100644
--- a/include/openssl/bio.h
+++ b/include/openssl/bio.h
@@ -178,6 +178,8 @@ extern "C" {
 
 # define BIO_CTRL_DGRAM_GET_MTU_OVERHEAD   49
 
+# define BIO_CTRL_DGRAM_SET_PEEK_MODE      50
+
 # ifndef OPENSSL_NO_SCTP
 /* SCTP stuff */
 #  define BIO_CTRL_DGRAM_SCTP_SET_IN_HANDSHAKE    50
diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h
index 1334eb6..192640e 100644
--- a/include/openssl/ssl.h
+++ b/include/openssl/ssl.h
@@ -1913,6 +1913,7 @@ void ERR_load_SSL_strings(void);
 # define SSL_F_DTLS1_GET_RECORD                           254
 # define SSL_F_DTLS1_HANDLE_TIMEOUT                       297
 # define SSL_F_DTLS1_HEARTBEAT                            305
+# define SSL_F_DTLS1_LISTEN                               350
 # define SSL_F_DTLS1_OUTPUT_CERT_CHAIN                    255
 # define SSL_F_DTLS1_PREPROCESS_FRAGMENT                  288
 # define SSL_F_DTLS1_PROCESS_OUT_OF_SEQ_MESSAGE           256
@@ -2166,6 +2167,7 @@ void ERR_load_SSL_strings(void);
 # define SSL_R_COMPRESSION_ID_NOT_WITHIN_PRIVATE_RANGE    307
 # define SSL_R_COMPRESSION_LIBRARY_ERROR                  142
 # define SSL_R_CONNECTION_TYPE_NOT_SET                    144
+# define SSL_R_COOKIE_GEN_CALLBACK_FAILURE                400
 # define SSL_R_COOKIE_MISMATCH                            308
 # define SSL_R_DATA_BETWEEN_CCS_AND_FINISHED              145
 # define SSL_R_DATA_LENGTH_TOO_LONG                       146
@@ -2189,6 +2191,7 @@ void ERR_load_SSL_strings(void);
 # define SSL_R_ERROR_IN_RECEIVED_CIPHER_LIST              151
 # define SSL_R_EXCESSIVE_MESSAGE_SIZE                     152
 # define SSL_R_EXTRA_DATA_IN_MESSAGE                      153
+# define SSL_R_FRAGMENTED_CLIENT_HELLO                    401
 # define SSL_R_GOT_A_FIN_BEFORE_A_CCS                     154
 # define SSL_R_GOT_NEXT_PROTO_BEFORE_A_CCS                355
 # define SSL_R_GOT_NEXT_PROTO_WITHOUT_EXTENSION           356
@@ -2201,6 +2204,7 @@ void ERR_load_SSL_strings(void);
 # define SSL_R_INVALID_COMPRESSION_ALGORITHM              341
 # define SSL_R_INVALID_NULL_CMD_NAME                      385
 # define SSL_R_INVALID_PURPOSE                            278
+# define SSL_R_INVALID_SEQUENCE_NUMBER                    402
 # define SSL_R_INVALID_SERVERINFO_DATA                    388
 # define SSL_R_INVALID_SRP_USERNAME                       357
 # define SSL_R_INVALID_STATUS_RESPONSE                    328
@@ -2250,6 +2254,7 @@ void ERR_load_SSL_strings(void);
 # define SSL_R_NO_SHARED_SIGATURE_ALGORITHMS              376
 # define SSL_R_NO_SRTP_PROFILES                           359
 # define SSL_R_NO_VERIFY_CALLBACK                         194
+# define SSL_R_NO_VERIFY_COOKIE_CALLBACK                  403
 # define SSL_R_NULL_SSL_CTX                               195
 # define SSL_R_NULL_SSL_METHOD_PASSED                     196
 # define SSL_R_OLD_SESSION_CIPHER_NOT_RETURNED            197
diff --git a/ssl/d1_both.c b/ssl/d1_both.c
index 52b7304..02a464e 100644
--- a/ssl/d1_both.c
+++ b/ssl/d1_both.c
@@ -454,15 +454,26 @@ long dtls1_get_message(SSL *s, int st1, int stn, int mt, long max, int *ok)
      * absence of an optional handshake message
      */
     if (s->s3->tmp.reuse_message) {
-        s->s3->tmp.reuse_message = 0;
         if ((mt >= 0) && (s->s3->tmp.message_type != mt)) {
             al = SSL_AD_UNEXPECTED_MESSAGE;
             SSLerr(SSL_F_DTLS1_GET_MESSAGE, SSL_R_UNEXPECTED_MESSAGE);
             goto f_err;
         }
         *ok = 1;
-        s->init_msg = s->init_buf->data + DTLS1_HM_HEADER_LENGTH;
+
+
+        /*
+         * Messages reused from dtls1_listen also have the record header in
+         * the buffer which we need to skip over.
+         */
+        if (s->s3->tmp.reuse_message == DTLS1_SKIP_RECORD_HEADER) {
+            s->init_msg = s->init_buf->data + DTLS1_HM_HEADER_LENGTH
+                          + DTLS1_RT_HEADER_LENGTH;
+        } else {
+            s->init_msg = s->init_buf->data + DTLS1_HM_HEADER_LENGTH;
+        }
         s->init_num = (int)s->s3->tmp.message_size;
+        s->s3->tmp.reuse_message = 0;
         return s->init_num;
     }
 
@@ -519,9 +530,8 @@ long dtls1_get_message(SSL *s, int st1, int stn, int mt, long max, int *ok)
 
     memset(msg_hdr, 0, sizeof(*msg_hdr));
 
-    /* Don't change sequence numbers while listening */
-    if (!s->d1->listen)
-        s->d1->handshake_read_seq++;
+    s->d1->handshake_read_seq++;
+
 
     s->init_msg = s->init_buf->data + DTLS1_HM_HEADER_LENGTH;
     return s->init_num;
@@ -935,8 +945,7 @@ dtls1_get_message_fragment(SSL *s, int st1, int stn, int mt, long max, int *ok)
      * While listening, we accept seq 1 (ClientHello with cookie)
      * although we're still expecting seq 0 (ClientHello)
      */
-    if (msg_hdr.seq != s->d1->handshake_read_seq
-        && !(s->d1->listen && msg_hdr.seq == 1))
+    if (msg_hdr.seq != s->d1->handshake_read_seq)
         return dtls1_process_out_of_seq_message(s, &msg_hdr, ok);
 
     if (frag_len && frag_len < len)
@@ -1289,8 +1298,7 @@ void dtls1_set_message_header(SSL *s, unsigned char *p,
                                         unsigned long frag_off,
                                         unsigned long frag_len)
 {
-    /* Don't change sequence numbers while listening */
-    if (frag_off == 0 && !s->d1->listen) {
+    if (frag_off == 0) {
         s->d1->handshake_write_seq = s->d1->next_handshake_write_seq;
         s->d1->next_handshake_write_seq++;
     }
diff --git a/ssl/d1_lib.c b/ssl/d1_lib.c
index d3b582a..4bdf90a 100644
--- a/ssl/d1_lib.c
+++ b/ssl/d1_lib.c
@@ -499,23 +499,395 @@ static void get_current_time(struct timeval *t)
 #endif
 }
 
+
+#define LISTEN_SUCCESS              2
+#define LISTEN_SEND_VERIFY_REQUEST  1
+
+
 int dtls1_listen(SSL *s, struct sockaddr *client)
 {
-    int ret;
+    int next, n, ret = 0, clearpkt = 0;
+    unsigned char cookie[DTLS1_COOKIE_LENGTH];
+    unsigned char seq[SEQ_NUM_SIZE];
+    unsigned char *data, *p, *buf;
+    unsigned long reclen, fragoff, fraglen, msglen;
+    unsigned int rectype, versmajor, msgseq, msgtype, clientvers, cookielen;
+    BIO *rbio, *wbio;
+    BUF_MEM *bufm;
+    struct sockaddr_storage tmpclient;
+    PACKET pkt, msgpkt, msgpayload, session, cookiepkt;
 
     /* Ensure there is no state left over from a previous invocation */
     if (!SSL_clear(s))
         return -1;
 
+    ERR_clear_error();
+
+    rbio = SSL_get_rbio(s);
+    wbio = SSL_get_wbio(s);
+
+    if(!rbio || !wbio) {
+        SSLerr(SSL_F_DTLS1_LISTEN, SSL_R_BIO_NOT_SET);
+        return -1;
+    }
+
+    /*
+     * We only peek at incoming ClientHello's until we're sure we are going to
+     * to respond with a HelloVerifyRequest. If its a ClientHello with a valid
+     * cookie then we leave it in the BIO for dtls1_accept to handle.
+     */
+    BIO_ctrl(SSL_get_rbio(s), BIO_CTRL_DGRAM_SET_PEEK_MODE, 1, NULL);
+
+    /*
+     * Note: This check deliberately excludes DTLS1_BAD_VER because that version
+     * requires the MAC to be calculated *including* the first ClientHello
+     * (without the cookie). Since DTLSv1_listen is stateless that cannot be
+     * supported. DTLS1_BAD_VER must use cookies in a stateful manner (e.g. via
+     * SSL_accept)
+     */
+    if ((s->version & 0xff00) != (DTLS1_VERSION & 0xff00)) {
+        SSLerr(SSL_F_DTLS1_LISTEN, SSL_R_UNSUPPORTED_SSL_VERSION);
+        return -1;
+    }
+
+    if (s->init_buf == NULL) {
+        if ((bufm = BUF_MEM_new()) == NULL) {
+            SSLerr(SSL_F_DTLS1_LISTEN, ERR_R_MALLOC_FAILURE);
+            return -1;
+        }
+
+        if (!BUF_MEM_grow(bufm, SSL3_RT_MAX_PLAIN_LENGTH)) {
+            BUF_MEM_free(bufm);
+            SSLerr(SSL_F_DTLS1_LISTEN, ERR_R_MALLOC_FAILURE);
+            return -1;
+        }
+        s->init_buf = bufm;
+    }
+    buf = (unsigned char *)s->init_buf->data;
+
+    do {
+        /* Get a packet */
+
+        clear_sys_error();
+        /*
+         * Technically a ClientHello could be SSL3_RT_MAX_PLAIN_LENGTH
+         * + DTLS1_RT_HEADER_LENGTH bytes long. Normally init_buf does not store
+         * the record header as well, but we do here. We've set up init_buf to
+         * be the standard size for simplicity. In practice we shouldn't ever
+         * receive a ClientHello as long as this. If we do it will get dropped
+         * in the record length check below.
+         */
+        n = BIO_read(rbio, buf, SSL3_RT_MAX_PLAIN_LENGTH);
+
+        if (n <= 0) {
+            if(BIO_should_retry(rbio)) {
+                /* Non-blocking IO */
+                goto end;
+            }
+            return -1;
+        }
+
+        /* If we hit any problems we need to clear this packet from the BIO */
+        clearpkt = 1;
+
+        if (!PACKET_buf_init(&pkt, buf, n)) {
+            SSLerr(SSL_F_DTLS1_LISTEN, ERR_R_INTERNAL_ERROR);
+            return -1;
+        }
+
+        /*
+         * Parse the received record. If there are any problems with it we just
+         * dump it - with no alert. RFC6347 says this "Unlike TLS, DTLS is
+         * resilient in the face of invalid records (e.g., invalid formatting,
+         * length, MAC, etc.).  In general, invalid records SHOULD be silently
+         * discarded, thus preserving the association; however, an error MAY be
+         * logged for diagnostic purposes."
+         */
+
+        /* this packet contained a partial record, dump it */
+        if (n < DTLS1_RT_HEADER_LENGTH) {
+            SSLerr(SSL_F_DTLS1_LISTEN, SSL_R_RECORD_TOO_SMALL);
+            goto end;
+        }
+
+        if (s->msg_callback)
+            s->msg_callback(0, 0, SSL3_RT_HEADER, buf,
+                            DTLS1_RT_HEADER_LENGTH, s, s->msg_callback_arg);
+
+        /* Get the record header */
+        if (!PACKET_get_1(&pkt, &rectype)
+            || !PACKET_get_1(&pkt, &versmajor)) {
+            SSLerr(SSL_F_DTLS1_LISTEN, SSL_R_LENGTH_MISMATCH);
+            goto end;
+        }
+
+        if (rectype != SSL3_RT_HANDSHAKE)  {
+            SSLerr(SSL_F_DTLS1_LISTEN, SSL_R_UNEXPECTED_MESSAGE);
+            goto end;
+        }
+
+        /*
+         * Check record version number. We only check that the major version is
+         * the same.
+         */
+        if (versmajor != DTLS1_VERSION_MAJOR) {
+            SSLerr(SSL_F_DTLS1_LISTEN, SSL_R_BAD_PROTOCOL_VERSION_NUMBER);
+            goto end;
+        }
+
+        if (!PACKET_forward(&pkt, 1)
+            /* Save the sequence number: 64 bits, with top 2 bytes = epoch */
+            || !PACKET_copy_bytes(&pkt, seq, SEQ_NUM_SIZE)
+            || !PACKET_get_length_prefixed_2(&pkt, &msgpkt)
+            || PACKET_remaining(&pkt) != 0) {
+            SSLerr(SSL_F_DTLS1_LISTEN, SSL_R_LENGTH_MISMATCH);
+            goto end;
+        }
+
+        /* This is an initial ClientHello so the epoch has to be 0 */
+        if (seq[0] != 0 || seq[1] != 0) {
+            SSLerr(SSL_F_DTLS1_LISTEN, SSL_R_UNEXPECTED_MESSAGE);
+            goto end;
+        }
+
+        /* Get a pointer to the raw message for the later callback */
+        data = PACKET_data(&msgpkt);
+
+        /* Finished processing the record header, now process the message */
+        if (!PACKET_get_1(&msgpkt, &msgtype)
+            || !PACKET_get_net_3(&msgpkt, &msglen)
+            || !PACKET_get_net_2(&msgpkt, &msgseq)
+            || !PACKET_get_net_3(&msgpkt, &fragoff)
+            || !PACKET_get_net_3(&msgpkt, &fraglen)
+            || !PACKET_get_sub_packet(&msgpkt, &msgpayload, msglen)
+            || PACKET_remaining(&msgpkt) != 0) {
+            SSLerr(SSL_F_DTLS1_LISTEN, SSL_R_LENGTH_MISMATCH);
+            goto end;
+        }
+
+        if (msgtype != SSL3_MT_CLIENT_HELLO) {
+            SSLerr(SSL_F_DTLS1_LISTEN, SSL_R_UNEXPECTED_MESSAGE);
+            goto end;
+        }
+
+        /* Message sequence number can only be 0 or 1 */
+        if(msgseq > 2) {
+            SSLerr(SSL_F_DTLS1_LISTEN, SSL_R_INVALID_SEQUENCE_NUMBER);
+            goto end;
+        }
+
+        /* We don't support a fragmented ClientHello whilst listening */
+        if (fragoff != 0 || fraglen != msglen) {
+            SSLerr(SSL_F_DTLS1_LISTEN, SSL_R_FRAGMENTED_CLIENT_HELLO);
+            goto end;
+        }
+
+        if (s->msg_callback)
+            s->msg_callback(0, s->version, SSL3_RT_HANDSHAKE, data,
+                            msglen + DTLS1_HM_HEADER_LENGTH, s,
+                            s->msg_callback_arg);
+
+        if (!PACKET_get_net_2(&msgpayload, &clientvers)) {
+            SSLerr(SSL_F_DTLS1_LISTEN, SSL_R_LENGTH_MISMATCH);
+            goto end;
+        }
+
+        /*
+         * Verify client version is supported
+         */
+        if ((clientvers > (unsigned int)s->method->version &&
+                              s->method->version != DTLS_ANY_VERSION)) {
+            SSLerr(SSL_F_DTLS1_LISTEN, SSL_R_WRONG_VERSION_NUMBER);
+            goto end;
+        }
+
+        if (!PACKET_forward(&msgpayload, SSL3_RANDOM_SIZE)
+            || !PACKET_get_length_prefixed_1(&msgpayload, &session)
+            || !PACKET_get_length_prefixed_1(&msgpayload, &cookiepkt)) {
+            SSLerr(SSL_F_DTLS1_LISTEN, SSL_R_LENGTH_MISMATCH);
+            goto end;
+        }
+
+        /*
+         * Check if we have a cookie or not. If not we need to send a
+         * HelloVerifyRequest.
+         */
+        if (PACKET_remaining(&cookiepkt) == 0) {
+            next = LISTEN_SEND_VERIFY_REQUEST;
+        } else {
+            /*
+             * We have a cookie, so lets check it.
+             */
+            if (s->ctx->app_verify_cookie_cb == NULL) {
+                SSLerr(SSL_F_DTLS1_LISTEN, SSL_R_NO_VERIFY_COOKIE_CALLBACK);
+                /* This is fatal */
+                return -1;
+            }
+            if (PACKET_remaining(&cookiepkt) > sizeof(s->d1->rcvd_cookie)
+                || s->ctx->app_verify_cookie_cb(s, PACKET_data(&cookiepkt),
+                    PACKET_remaining(&cookiepkt)) == 0) {
+                /*
+                 * We treat invalid cookies in the same was as no cookie as
+                 * per RFC6347
+                 */
+                next = LISTEN_SEND_VERIFY_REQUEST;
+            } else {
+                /* Cookie verification succeeded */
+                next = LISTEN_SUCCESS;
+            }
+        }
+
+        if (next == LISTEN_SEND_VERIFY_REQUEST) {
+            /*
+             * There was no cookie in the ClientHello so we need to send a
+             * HelloVerifyRequest. If this fails we do not worry about trying
+             * to resend, we just drop it.
+             */
+
+            /*
+             * Dump the read packet, we don't need it any more. Ignore return
+             * value
+             */
+            BIO_ctrl(SSL_get_rbio(s), BIO_CTRL_DGRAM_SET_PEEK_MODE, 0, NULL);
+            BIO_read(rbio, buf, SSL3_RT_MAX_PLAIN_LENGTH);
+            BIO_ctrl(SSL_get_rbio(s), BIO_CTRL_DGRAM_SET_PEEK_MODE, 1, NULL);
+
+            /* Generate the cookie */
+            if (s->ctx->app_gen_cookie_cb == NULL ||
+                s->ctx->app_gen_cookie_cb(s, cookie, &cookielen) == 0 ||
+                cookielen > 255) {
+                SSLerr(SSL_F_DTLS1_LISTEN, SSL_R_COOKIE_GEN_CALLBACK_FAILURE);
+                /* This is fatal */
+                return -1;
+            }
+
+            p = &buf[DTLS1_RT_HEADER_LENGTH];
+            msglen = dtls1_raw_hello_verify_request(p + DTLS1_HM_HEADER_LENGTH,
+                                                    cookie, cookielen);
+
+            *p++ = DTLS1_MT_HELLO_VERIFY_REQUEST;
+
+            /* Message length */
+            l2n3(msglen, p);
+
+            /* Message sequence number is always 0 for a HelloVerifyRequest */
+            s2n(0, p);
+
+            /*
+             * We never fragment a HelloVerifyRequest, so fragment offset is 0
+             * and fragment length is message length
+             */
+            l2n3(0, p);
+            l2n3(msglen, p);
+
+            /* Set reclen equal to length of whole handshake message */
+            reclen = msglen + DTLS1_HM_HEADER_LENGTH;
+
+            /* Add the record header */
+            p = buf;
+
+            *(p++) = SSL3_RT_HANDSHAKE;
+            /*
+             * Special case: for hello verify request, client version 1.0 and we
+             * haven't decided which version to use yet send back using version
+             * 1.0 header: otherwise some clients will ignore it.
+             */
+            if (s->method->version == DTLS_ANY_VERSION) {
+                *(p++) = DTLS1_VERSION >> 8;
+                *(p++) = DTLS1_VERSION & 0xff;
+            } else {
+                *(p++) = s->version >> 8;
+                *(p++) = s->version & 0xff;
+            }
+
+            /*
+             * Record sequence number is always the same as in the received
+             * ClientHello
+             */
+            memcpy(p, seq, SEQ_NUM_SIZE);
+            p += SEQ_NUM_SIZE;
+
+            /* Length */
+            s2n(reclen, p);
+
+            /*
+             * Set reclen equal to length of whole record including record
+             * header
+             */
+            reclen += DTLS1_RT_HEADER_LENGTH;
+
+            if (s->msg_callback)
+                s->msg_callback(1, 0, SSL3_RT_HEADER, buf,
+                                DTLS1_RT_HEADER_LENGTH, s, s->msg_callback_arg);
+
+            /*
+             * This is unneccessary if rbio and wbio are one and the same - but
+             * maybe they're not.
+             */
+            if(BIO_dgram_get_peer(rbio, &tmpclient) <= 0
+               || BIO_dgram_set_peer(wbio, &tmpclient) <= 0) {
+                SSLerr(SSL_F_DTLS1_LISTEN, ERR_R_INTERNAL_ERROR);
+                goto end;
+            }
+
+            if (BIO_write(wbio, buf, reclen) < (int)reclen) {
+                if(BIO_should_retry(wbio)) {
+                    /*
+                     * Non-blocking IO...but we're stateless, so we're just
+                     * going to drop this packet.
+                     */
+                    goto end;
+                }
+                return -1;
+            }
+
+            if (BIO_flush(wbio) <= 0) {
+                if(BIO_should_retry(wbio)) {
+                    /*
+                     * Non-blocking IO...but we're stateless, so we're just
+                     * going to drop this packet.
+                     */
+                    goto end;
+                }
+                return -1;
+            }
+        }
+    } while (next != LISTEN_SUCCESS);
+
+    /*
+     * Set expected sequence numbers to continue the handshake.
+     */
+    s->d1->handshake_read_seq = 1;
+    s->d1->handshake_write_seq = 1;
+    s->d1->next_handshake_write_seq = 1;
+    DTLS_RECORD_LAYER_set_write_sequence(&s->rlayer, seq);
+
+    /*
+     * We are doing cookie exchange, so make sure we set that option in the
+     * SSL object
+     */
     SSL_set_options(s, SSL_OP_COOKIE_EXCHANGE);
-    s->d1->listen = 1;
 
-    ret = SSL_accept(s);
-    if (ret <= 0)
-        return ret;
+    /*
+     * Put us into the "init" state so that dtls1_accept doesn't clear our
+     * state
+     */
+    s->state = SSL_ST_ACCEPT;
+
+    if(BIO_dgram_get_peer(rbio, client) <= 0) {
+        SSLerr(SSL_F_DTLS1_LISTEN, ERR_R_INTERNAL_ERROR);
+        return -1;
+    }
 
-    (void)BIO_dgram_get_peer(SSL_get_rbio(s), client);
-    return 1;
+    ret = 1;
+    clearpkt = 0;
+end:
+    BIO_ctrl(SSL_get_rbio(s), BIO_CTRL_DGRAM_SET_PEEK_MODE, 0, NULL);
+    if (clearpkt) {
+        /* Dump this packet. Ignore return value */
+        BIO_read(rbio, buf, SSL3_RT_MAX_PLAIN_LENGTH);
+    }
+    return ret;
 }
 
 static int dtls1_set_handshake_header(SSL *s, int htype, unsigned long len)
diff --git a/ssl/d1_srvr.c b/ssl/d1_srvr.c
index f56bf5a..e32c4c1 100644
--- a/ssl/d1_srvr.c
+++ b/ssl/d1_srvr.c
@@ -165,7 +165,6 @@ int dtls1_accept(SSL *s)
     unsigned long alg_k;
     int ret = -1;
     int new_state, state, skip = 0;
-    int listen;
 #ifndef OPENSSL_NO_SCTP
     unsigned char sctpauthkey[64];
     char labelbuffer[sizeof(DTLS1_SCTP_AUTH_LABEL)];
@@ -180,8 +179,6 @@ int dtls1_accept(SSL *s)
     else if (s->ctx->info_callback != NULL)
         cb = s->ctx->info_callback;
 
-    listen = s->d1->listen;
-
     /* init things to blank */
     s->in_handshake++;
     if (!SSL_in_init(s) || SSL_in_before(s)) {
@@ -189,7 +186,6 @@ int dtls1_accept(SSL *s)
             return -1;
     }
 
-    s->d1->listen = listen;
 #ifndef OPENSSL_NO_SCTP
     /*
      * Notify SCTP BIO socket to enter handshake mode and prevent stream
@@ -327,28 +323,6 @@ int dtls1_accept(SSL *s)
                 s->state = SSL3_ST_SW_SRVR_HELLO_A;
 
             s->init_num = 0;
-
-            /*
-             * Reflect ClientHello sequence to remain stateless while
-             * listening
-             */
-            if (listen) {
-                DTLS_RECORD_LAYER_resync_write(&s->rlayer);
-            }
-
-            /* If we're just listening, stop here */
-            if (listen && s->state == SSL3_ST_SW_SRVR_HELLO_A) {
-                ret = 2;
-                s->d1->listen = 0;
-                /*
-                 * Set expected sequence numbers to continue the handshake.
-                 */
-                s->d1->handshake_read_seq = 2;
-                s->d1->handshake_write_seq = 1;
-                s->d1->next_handshake_write_seq = 1;
-                goto end;
-            }
-
             break;
 
         case DTLS1_ST_SW_HELLO_VERIFY_REQUEST_A:
@@ -883,40 +857,55 @@ int dtls1_accept(SSL *s)
     return (ret);
 }
 
-int dtls1_send_hello_verify_request(SSL *s)
+unsigned int dtls1_raw_hello_verify_request(unsigned char *buf,
+                                            unsigned char *cookie,
+                                            unsigned char cookie_len)
 {
     unsigned int msg_len;
-    unsigned char *msg, *buf, *p;
+    unsigned char *p;
+
+    p = buf;
+    /* Always use DTLS 1.0 version: see RFC 6347 */
+    *(p++) = DTLS1_VERSION >> 8;
+    *(p++) = DTLS1_VERSION & 0xFF;
+
+    *(p++) = (unsigned char)cookie_len;
+    memcpy(p, cookie, cookie_len);
+    p += cookie_len;
+    msg_len = p - buf;
+
+    return msg_len;
+}
+
+
+int dtls1_send_hello_verify_request(SSL *s)
+{
+    unsigned int len;
+    unsigned char *buf;
 
     if (s->state == DTLS1_ST_SW_HELLO_VERIFY_REQUEST_A) {
         buf = (unsigned char *)s->init_buf->data;
 
-        msg = p = &(buf[DTLS1_HM_HEADER_LENGTH]);
-        /* Always use DTLS 1.0 version: see RFC 6347 */
-        *(p++) = DTLS1_VERSION >> 8;
-        *(p++) = DTLS1_VERSION & 0xFF;
-
         if (s->ctx->app_gen_cookie_cb == NULL ||
             s->ctx->app_gen_cookie_cb(s, s->d1->cookie,
-                                      &(s->d1->cookie_len)) == 0) {
+                                      &(s->d1->cookie_len)) == 0 ||
+            s->d1->cookie_len > 255) {
             SSLerr(SSL_F_DTLS1_SEND_HELLO_VERIFY_REQUEST,
-                   ERR_R_INTERNAL_ERROR);
+                   SSL_R_COOKIE_GEN_CALLBACK_FAILURE);
             s->state = SSL_ST_ERR;
             return 0;
         }
 
-        *(p++) = (unsigned char)s->d1->cookie_len;
-        memcpy(p, s->d1->cookie, s->d1->cookie_len);
-        p += s->d1->cookie_len;
-        msg_len = p - msg;
+        len = dtls1_raw_hello_verify_request(&buf[DTLS1_HM_HEADER_LENGTH],
+                                             s->d1->cookie, s->d1->cookie_len);
 
-        dtls1_set_message_header(s, buf,
-                                 DTLS1_MT_HELLO_VERIFY_REQUEST, msg_len, 0,
-                                 msg_len);
+        dtls1_set_message_header(s, buf, DTLS1_MT_HELLO_VERIFY_REQUEST, len, 0,
+                                 len);
+        len += DTLS1_HM_HEADER_LENGTH;
 
         s->state = DTLS1_ST_SW_HELLO_VERIFY_REQUEST_B;
         /* number of bytes to write */
-        s->init_num = p - buf;
+        s->init_num = len;
         s->init_off = 0;
     }
 
diff --git a/ssl/record/rec_layer_d1.c b/ssl/record/rec_layer_d1.c
index 74796be..d7d0093 100644
--- a/ssl/record/rec_layer_d1.c
+++ b/ssl/record/rec_layer_d1.c
@@ -226,6 +226,12 @@ void DTLS_RECORD_LAYER_resync_write(RECORD_LAYER *rl)
     memcpy(rl->write_sequence, rl->read_sequence, sizeof(rl->write_sequence));
 }
 
+
+void DTLS_RECORD_LAYER_set_write_sequence(RECORD_LAYER *rl, unsigned char *seq)
+{
+    memcpy(rl->write_sequence, seq, SEQ_NUM_SIZE);
+}
+
 static int have_handshake_fragment(SSL *s, int type, unsigned char *buf,
                                    int len, int peek);
 
@@ -505,11 +511,6 @@ int dtls1_read_bytes(SSL *s, int type, int *recvd_type, unsigned char *buf,
         }
     }
 
-    if (s->d1->listen && rr->type != SSL3_RT_HANDSHAKE) {
-        SSL3_RECORD_set_length(rr, 0);
-        goto start;
-    }
-
     /* we now have a packet which can be read and processed */
 
     if (s->s3->change_cipher_spec /* set when we receive ChangeCipherSpec,
diff --git a/ssl/record/record.h b/ssl/record/record.h
index 5c8fead..9637071 100644
--- a/ssl/record/record.h
+++ b/ssl/record/record.h
@@ -288,8 +288,8 @@ typedef struct record_layer_st {
     int wpend_ret;
     const unsigned char *wpend_buf;
 
-    unsigned char read_sequence[8];
-    unsigned char write_sequence[8];
+    unsigned char read_sequence[SEQ_NUM_SIZE];
+    unsigned char write_sequence[SEQ_NUM_SIZE];
     
     DTLS_RECORD_LAYER *d;
 } RECORD_LAYER;
@@ -346,6 +346,7 @@ void DTLS_RECORD_LAYER_clear(RECORD_LAYER *rl);
 void DTLS_RECORD_LAYER_set_saved_w_epoch(RECORD_LAYER *rl, unsigned short e);
 void DTLS_RECORD_LAYER_clear(RECORD_LAYER *rl);
 void DTLS_RECORD_LAYER_resync_write(RECORD_LAYER *rl);
+void DTLS_RECORD_LAYER_set_write_sequence(RECORD_LAYER *rl, unsigned char *seq);
 __owur int dtls1_read_bytes(SSL *s, int type, int *recvd_type,
                             unsigned char *buf, int len, int peek);
 __owur int dtls1_write_bytes(SSL *s, int type, const void *buf, int len);
diff --git a/ssl/record/ssl3_record.c b/ssl/record/ssl3_record.c
index 1fa1710..7383f13 100644
--- a/ssl/record/ssl3_record.c
+++ b/ssl/record/ssl3_record.c
@@ -1508,18 +1508,8 @@ int dtls1_get_record(SSL *s)
     /* Only do replay check if no SCTP bio */
     if (!BIO_dgram_is_sctp(SSL_get_rbio(s))) {
 #endif
-        /*
-         * Check whether this is a repeat, or aged record. Don't check if
-         * we're listening and this message is a ClientHello. They can look
-         * as if they're replayed, since they arrive from different
-         * connections and would be dropped unnecessarily.
-         */
-        if (!(s->d1->listen && rr->type == SSL3_RT_HANDSHAKE &&
-              RECORD_LAYER_get_packet_length(&s->rlayer)
-                  > DTLS1_RT_HEADER_LENGTH &&
-              RECORD_LAYER_get_packet(&s->rlayer)[DTLS1_RT_HEADER_LENGTH]
-                  == SSL3_MT_CLIENT_HELLO) &&
-            !dtls1_record_replay_check(s, bitmap)) {
+        /* Check whether this is a repeat, or aged record. */
+        if (!dtls1_record_replay_check(s, bitmap)) {
             rr->length = 0;
             RECORD_LAYER_reset_packet_length(&s->rlayer); /* dump this record */
             goto again;         /* get another record */
@@ -1535,11 +1525,10 @@ int dtls1_get_record(SSL *s)
     /*
      * If this record is from the next epoch (either HM or ALERT), and a
      * handshake is currently in progress, buffer it since it cannot be
-     * processed at this time. However, do not buffer anything while
-     * listening.
+     * processed at this time.
      */
     if (is_next_epoch) {
-        if ((SSL_in_init(s) || s->in_handshake) && !s->d1->listen) {
+        if ((SSL_in_init(s) || s->in_handshake)) {
             if (dtls1_buffer_record
                 (s, &(DTLS_RECORD_LAYER_get_unprocessed_rcds(&s->rlayer)),
                 rr->seq_num) < 0)
diff --git a/ssl/ssl_err.c b/ssl/ssl_err.c
index 21836d8..447bac6 100644
--- a/ssl/ssl_err.c
+++ b/ssl/ssl_err.c
@@ -87,6 +87,7 @@ static ERR_STRING_DATA SSL_str_functs[] = {
     {ERR_FUNC(SSL_F_DTLS1_GET_RECORD), "dtls1_get_record"},
     {ERR_FUNC(SSL_F_DTLS1_HANDLE_TIMEOUT), "dtls1_handle_timeout"},
     {ERR_FUNC(SSL_F_DTLS1_HEARTBEAT), "dtls1_heartbeat"},
+    {ERR_FUNC(SSL_F_DTLS1_LISTEN), "DTLS1_LISTEN"},
     {ERR_FUNC(SSL_F_DTLS1_OUTPUT_CERT_CHAIN), "dtls1_output_cert_chain"},
     {ERR_FUNC(SSL_F_DTLS1_PREPROCESS_FRAGMENT), "DTLS1_PREPROCESS_FRAGMENT"},
     {ERR_FUNC(SSL_F_DTLS1_PROCESS_OUT_OF_SEQ_MESSAGE),
@@ -406,6 +407,8 @@ static ERR_STRING_DATA SSL_str_reasons[] = {
      "compression id not within private range"},
     {ERR_REASON(SSL_R_COMPRESSION_LIBRARY_ERROR), "compression library error"},
     {ERR_REASON(SSL_R_CONNECTION_TYPE_NOT_SET), "connection type not set"},
+    {ERR_REASON(SSL_R_COOKIE_GEN_CALLBACK_FAILURE),
+     "cookie gen callback failure"},
     {ERR_REASON(SSL_R_COOKIE_MISMATCH), "cookie mismatch"},
     {ERR_REASON(SSL_R_DATA_BETWEEN_CCS_AND_FINISHED),
      "data between ccs and finished"},
@@ -440,6 +443,7 @@ static ERR_STRING_DATA SSL_str_reasons[] = {
      "error in received cipher list"},
     {ERR_REASON(SSL_R_EXCESSIVE_MESSAGE_SIZE), "excessive message size"},
     {ERR_REASON(SSL_R_EXTRA_DATA_IN_MESSAGE), "extra data in message"},
+    {ERR_REASON(SSL_R_FRAGMENTED_CLIENT_HELLO), "fragmented client hello"},
     {ERR_REASON(SSL_R_GOT_A_FIN_BEFORE_A_CCS), "got a fin before a ccs"},
     {ERR_REASON(SSL_R_GOT_NEXT_PROTO_BEFORE_A_CCS),
      "got next proto before a ccs"},
@@ -455,6 +459,7 @@ static ERR_STRING_DATA SSL_str_reasons[] = {
      "invalid compression algorithm"},
     {ERR_REASON(SSL_R_INVALID_NULL_CMD_NAME), "invalid null cmd name"},
     {ERR_REASON(SSL_R_INVALID_PURPOSE), "invalid purpose"},
+    {ERR_REASON(SSL_R_INVALID_SEQUENCE_NUMBER), "invalid sequence number"},
     {ERR_REASON(SSL_R_INVALID_SERVERINFO_DATA), "invalid serverinfo data"},
     {ERR_REASON(SSL_R_INVALID_SRP_USERNAME), "invalid srp username"},
     {ERR_REASON(SSL_R_INVALID_STATUS_RESPONSE), "invalid status response"},
@@ -510,6 +515,7 @@ static ERR_STRING_DATA SSL_str_reasons[] = {
      "no shared sigature algorithms"},
     {ERR_REASON(SSL_R_NO_SRTP_PROFILES), "no srtp profiles"},
     {ERR_REASON(SSL_R_NO_VERIFY_CALLBACK), "no verify callback"},
+    {ERR_REASON(SSL_R_NO_VERIFY_COOKIE_CALLBACK), "no verify cookie callback"},
     {ERR_REASON(SSL_R_NULL_SSL_CTX), "null ssl ctx"},
     {ERR_REASON(SSL_R_NULL_SSL_METHOD_PASSED), "null ssl method passed"},
     {ERR_REASON(SSL_R_OLD_SESSION_CIPHER_NOT_RETURNED),
diff --git a/ssl/ssl_locl.h b/ssl/ssl_locl.h
index 32e6338..b7c4fe7 100644
--- a/ssl/ssl_locl.h
+++ b/ssl/ssl_locl.h
@@ -1376,6 +1376,12 @@ typedef struct ssl3_state_st {
 /* Max MTU overhead we know about so far is 40 for IPv6 + 8 for UDP */
 #  define DTLS1_MAX_MTU_OVERHEAD                   48
 
+/*
+ * Flag used in message reuse to indicate the buffer contains the record
+ * header as well as the the handshake message header.
+ */
+#  define DTLS1_SKIP_RECORD_HEADER                 2
+
 struct dtls1_retransmit_state {
     EVP_CIPHER_CTX *enc_write_ctx; /* cryptographic state */
     EVP_MD_CTX *write_hash;     /* used for mac generation */
@@ -1425,8 +1431,6 @@ typedef struct dtls1_state_st {
     /* Buffered (sent) handshake records */
     pqueue sent_messages;
 
-    /* Is set when listening for new connections with dtls1_listen() */
-    unsigned int listen;
     unsigned int link_mtu;      /* max on-the-wire DTLS packet size */
     unsigned int mtu;           /* max DTLS packet size */
     struct hm_header_st w_msg_hdr;
@@ -1990,6 +1994,9 @@ void dtls1_start_timer(SSL *s);
 void dtls1_stop_timer(SSL *s);
 __owur int dtls1_is_timer_expired(SSL *s);
 void dtls1_double_timeout(SSL *s);
+__owur unsigned int dtls1_raw_hello_verify_request(unsigned char *buf,
+                                                   unsigned char *cookie,
+                                                   unsigned char cookie_len);
 __owur int dtls1_send_newsession_ticket(SSL *s);
 __owur unsigned int dtls1_min_mtu(SSL *s);
 __owur unsigned int dtls1_link_min_mtu(void);


More information about the openssl-commits mailing list