diff --git a/include/mbedtls/config.h b/include/mbedtls/config.h
index f8a99f2e5..243678c62 100644
--- a/include/mbedtls/config.h
+++ b/include/mbedtls/config.h
@@ -1293,6 +1293,19 @@
  */
 #define MBEDTLS_SSL_ALL_ALERT_MESSAGES
 
+/**
+ * \def MBEDTLS_SSL_RECORD_CHECKING
+ *
+ * Enable the API mbedtls_ssl_check_record() which allows to check the
+ * validity, freshness and authenticity of an incoming record without
+ * modifying the externally visible state of the SSL context.
+ *
+ * See mbedtls_ssl_check_record() for more information.
+ *
+ * Uncomment to enable support for record checking.
+ */
+#define MBEDTLS_SSL_RECORD_CHECKING
+
 /**
  * \def MBEDTLS_SSL_DTLS_CONNECTION_ID
  *
diff --git a/include/mbedtls/ssl.h b/include/mbedtls/ssl.h
index 9e5fb2dbb..edb0a4699 100644
--- a/include/mbedtls/ssl.h
+++ b/include/mbedtls/ssl.h
@@ -1898,6 +1898,54 @@ void mbedtls_ssl_set_mtu( mbedtls_ssl_context *ssl, uint16_t mtu );
 void mbedtls_ssl_conf_read_timeout( mbedtls_ssl_config *conf, uint32_t timeout );
 #endif /* !MBEDTLS_SSL_CONF_READ_TIMEOUT */
 
+#if defined(MBEDTLS_SSL_RECORD_CHECKING)
+/**
+ * \brief          Check whether a buffer contains a valid, fresh
+ *                 and authentic record (DTLS only).
+ *
+ *                 This function does not change the user-visible state
+ *                 of the SSL context. Its sole purpose is to provide
+ *                 an indication of the legitimacy of an incoming record.
+ *
+ *                 This can be useful e.g. in distributed server environments
+ *                 using the DTLS Connection ID feature, in which connections
+ *                 might need to be passed between service instances on a change
+ *                 of peer address, but where such disruptive operations should
+ *                 only happen after the validity of incoming records has been
+ *                 confirmed.
+ *
+ * \param ssl      The SSL context to use.
+ * \param buf      The address of the buffer holding the record to be checked.
+ *                 This must be an R/W buffer of length \p buflen Bytes.
+ * \param buflen   The length of \p buf in Bytes.
+ *
+ * \note           This routine only checks whether the provided buffer begins
+ *                 with a valid, fresh and authentic record, but does not check
+ *                 potential data following the initial record. In particular,
+ *                 it is possible to pass DTLS datagrams containing multiple
+ *                 records, in which case only the first record is checked.
+ *
+ * \note           This function modifies the input buffer \p buf. If you need
+ *                 to preserve the original record, you have to maintain a copy.
+ *
+ * \return         \c 0 if the record is valid, fresh and authentic.
+ * \return         MBEDTLS_ERR_SSL_INVALID_MAC if the check completed
+ *                 successfully but the record was found to be not authentic.
+ * \return         MBEDTLS_ERR_SSL_INVALID_RECORD if the check completed
+ *                 successfully but the record was found to be invalid for
+ *                 a reason different from authenticity checking.
+ * \return         MBEDTLS_ERR_SSL_UNEXPECTED_RECORD if the check completed
+ *                 successfully but the record was found to be unexpected
+ *                 in the state of the SSL context, including replayed records.
+ * \return         Another negative error code on different kinds of failure.
+ *                 In this case, the SSL context becomes unusable and needs
+ *                 to be freed or reset before reuse.
+ */
+int mbedtls_ssl_check_record( mbedtls_ssl_context const *ssl,
+                              unsigned char *buf,
+                              size_t buflen );
+#endif /* MBEDTLS_SSL_RECORD_CHECKING */
+
 #if !defined(MBEDTLS_SSL_CONF_SET_TIMER) && \
     !defined(MBEDTLS_SSL_CONF_GET_TIMER)
 /**
diff --git a/library/ssl_tls.c b/library/ssl_tls.c
index 3e78e5d95..d04602027 100644
--- a/library/ssl_tls.c
+++ b/library/ssl_tls.c
@@ -122,6 +122,18 @@ static void ssl_update_out_pointers( mbedtls_ssl_context *ssl,
                                      mbedtls_ssl_transform *transform );
 static void ssl_update_in_pointers( mbedtls_ssl_context *ssl );
 
+#if defined(MBEDTLS_SSL_RECORD_CHECKING)
+int mbedtls_ssl_check_record( mbedtls_ssl_context const *ssl,
+                              unsigned char *buf,
+                              size_t buflen )
+{
+    ((void) ssl);
+    ((void) buf);
+    ((void) buflen);
+    return( MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE );
+}
+#endif /* MBEDTLS_SSL_RECORD_CHECKING */
+
 #define SSL_DONT_FORCE_FLUSH 0
 #define SSL_FORCE_FLUSH      1
 
diff --git a/library/version_features.c b/library/version_features.c
index 51ce7801d..102b52109 100644
--- a/library/version_features.c
+++ b/library/version_features.c
@@ -438,6 +438,9 @@ static const char *features[] = {
 #if defined(MBEDTLS_SSL_ALL_ALERT_MESSAGES)
     "MBEDTLS_SSL_ALL_ALERT_MESSAGES",
 #endif /* MBEDTLS_SSL_ALL_ALERT_MESSAGES */
+#if defined(MBEDTLS_SSL_RECORD_CHECKING)
+    "MBEDTLS_SSL_RECORD_CHECKING",
+#endif /* MBEDTLS_SSL_RECORD_CHECKING */
 #if defined(MBEDTLS_SSL_DTLS_CONNECTION_ID)
     "MBEDTLS_SSL_DTLS_CONNECTION_ID",
 #endif /* MBEDTLS_SSL_DTLS_CONNECTION_ID */
diff --git a/programs/ssl/query_config.c b/programs/ssl/query_config.c
index 0f7251d45..cf10ba18d 100644
--- a/programs/ssl/query_config.c
+++ b/programs/ssl/query_config.c
@@ -1210,6 +1210,14 @@ int query_config( const char *config )
     }
 #endif /* MBEDTLS_SSL_ALL_ALERT_MESSAGES */
 
+#if defined(MBEDTLS_SSL_RECORD_CHECKING)
+    if( strcmp( "MBEDTLS_SSL_RECORD_CHECKING", config ) == 0 )
+    {
+        MACRO_EXPANSION_TO_STR( MBEDTLS_SSL_RECORD_CHECKING );
+        return( 0 );
+    }
+#endif /* MBEDTLS_SSL_RECORD_CHECKING */
+
 #if defined(MBEDTLS_SSL_DTLS_CONNECTION_ID)
     if( strcmp( "MBEDTLS_SSL_DTLS_CONNECTION_ID", config ) == 0 )
     {
diff --git a/programs/ssl/ssl_client2.c b/programs/ssl/ssl_client2.c
index f0de38153..f6bdc567d 100644
--- a/programs/ssl/ssl_client2.c
+++ b/programs/ssl/ssl_client2.c
@@ -490,7 +490,8 @@ static void my_debug( void *ctx, int level,
  * Test recv/send functions that make sure each try returns
  * WANT_READ/WANT_WRITE at least once before sucesseding
  */
-static int my_recv( void *ctx, unsigned char *buf, size_t len )
+
+static int delayed_recv( void *ctx, unsigned char *buf, size_t len )
 {
     static int first_try = 1;
     int ret;
@@ -507,7 +508,7 @@ static int my_recv( void *ctx, unsigned char *buf, size_t len )
     return( ret );
 }
 
-static int my_send( void *ctx, const unsigned char *buf, size_t len )
+static int delayed_send( void *ctx, const unsigned char *buf, size_t len )
 {
     static int first_try = 1;
     int ret;
@@ -527,6 +528,137 @@ static int my_send( void *ctx, const unsigned char *buf, size_t len )
           MBEDTLS_SSL_CONF_SEND &&
           MBEDTLS_SSL_CONF_RECV_TIMEOUT */
 
+typedef struct
+{
+    mbedtls_ssl_context *ssl;
+    mbedtls_net_context *net;
+} io_ctx_t;
+
+#if defined(MBEDTLS_SSL_RECORD_CHECKING)
+static int ssl_check_record( mbedtls_ssl_context const *ssl,
+                             unsigned char const *buf, size_t len )
+{
+    int ret;
+    unsigned char *tmp_buf;
+
+    tmp_buf = mbedtls_calloc( 1, len );
+    if( tmp_buf == NULL )
+        return( MBEDTLS_ERR_SSL_ALLOC_FAILED );
+    memcpy( tmp_buf, buf, len );
+
+    ret = mbedtls_ssl_check_record( ssl, tmp_buf, len );
+    if( ret != MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE )
+    {
+        int ret_repeated;
+
+        /* Test-only: Make sure that mbedtls_ssl_check_record()
+         *            doesn't alter state. */
+        memcpy( tmp_buf, buf, len ); /* Restore buffer */
+        ret_repeated = mbedtls_ssl_check_record( ssl, tmp_buf, len );
+        if( ret != ret_repeated )
+        {
+            mbedtls_printf( "mbedtls_ssl_check_record() returned inconsistent results.\n" );
+            return( -1 );
+        }
+
+        switch( ret )
+        {
+            case 0:
+                break;
+
+            case MBEDTLS_ERR_SSL_INVALID_RECORD:
+                if( opt.debug_level > 1 )
+                    mbedtls_printf( "mbedtls_ssl_check_record() detected invalid record.\n" );
+                break;
+
+            case MBEDTLS_ERR_SSL_INVALID_MAC:
+                if( opt.debug_level > 1 )
+                    mbedtls_printf( "mbedtls_ssl_check_record() detected unauthentic record.\n" );
+                break;
+
+            case MBEDTLS_ERR_SSL_UNEXPECTED_RECORD:
+                if( opt.debug_level > 1 )
+                    mbedtls_printf( "mbedtls_ssl_check_record() detected unexpected record.\n" );
+                break;
+
+            default:
+                mbedtls_printf( "mbedtls_ssl_check_record() failed fatally with -%#04x.\n", -ret );
+                return( -1 );
+        }
+
+        /* Regardless of the outcome, forward the record to the stack. */
+    }
+
+    mbedtls_free( tmp_buf );
+
+    return( 0 );
+}
+#endif /* MBEDTLS_SSL_RECORD_CHECKING */
+
+static int recv_cb( void *ctx, unsigned char *buf, size_t len )
+{
+    io_ctx_t *io_ctx = (io_ctx_t*) ctx;
+    size_t recv_len;
+    int ret;
+
+    if( opt.nbio == 2 )
+        ret = delayed_recv( io_ctx->net, buf, len );
+    else
+        ret = mbedtls_net_recv( io_ctx->net, buf, len );
+    if( ret < 0 )
+        return( ret );
+    recv_len = (size_t) ret;
+
+    if( opt.transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM )
+    {
+        /* Here's the place to do any datagram/record checking
+         * in between receiving the packet from the underlying
+         * transport and passing it on to the TLS stack. */
+#if defined(MBEDTLS_SSL_RECORD_CHECKING)
+        if( ssl_check_record( io_ctx->ssl, buf, recv_len ) != 0 )
+            return( -1 );
+#endif /* MBEDTLS_SSL_RECORD_CHECKING */
+    }
+
+    return( (int) recv_len );
+}
+
+static int recv_timeout_cb( void *ctx, unsigned char *buf, size_t len,
+                            uint32_t timeout )
+{
+    io_ctx_t *io_ctx = (io_ctx_t*) ctx;
+    int ret;
+    size_t recv_len;
+
+    ret = mbedtls_net_recv_timeout( io_ctx->net, buf, len, timeout );
+    if( ret < 0 )
+        return( ret );
+    recv_len = (size_t) ret;
+
+    if( opt.transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM )
+    {
+        /* Here's the place to do any datagram/record checking
+         * in between receiving the packet from the underlying
+         * transport and passing it on to the TLS stack. */
+#if defined(MBEDTLS_SSL_RECORD_CHECKING)
+        if( ssl_check_record( io_ctx->ssl, buf, recv_len ) != 0 )
+            return( -1 );
+#endif /* MBEDTLS_SSL_RECORD_CHECKING */
+    }
+
+    return( (int) recv_len );
+}
+
+static int send_cb( void *ctx, unsigned char const *buf, size_t len )
+{
+    io_ctx_t *io_ctx = (io_ctx_t*) ctx;
+
+    if( opt.nbio == 2 )
+        return( delayed_send( io_ctx->net, buf, len ) );
+
+    return( mbedtls_net_send( io_ctx->net, buf, len ) );
+}
+
 #if defined(MBEDTLS_X509_CRT_PARSE_C)
 static unsigned char peer_crt_info[1024];
 
@@ -761,6 +893,7 @@ int main( int argc, char *argv[] )
 {
     int ret = 0, len, tail_len, i, written, frags, retry_left;
     mbedtls_net_context server_fd;
+    io_ctx_t io_ctx;
 
     unsigned char buf[MAX_REQUEST_SIZE + 1];
 
@@ -1928,12 +2061,10 @@ int main( int argc, char *argv[] )
 #if !defined(MBEDTLS_SSL_CONF_RECV) && \
     !defined(MBEDTLS_SSL_CONF_SEND) && \
     !defined(MBEDTLS_SSL_CONF_RECV_TIMEOUT)
-    if( opt.nbio == 2 )
-        mbedtls_ssl_set_bio( &ssl, &server_fd, my_send, my_recv, NULL );
-    else
-        mbedtls_ssl_set_bio( &ssl, &server_fd,
-                             mbedtls_net_send, mbedtls_net_recv,
-                             opt.nbio == 0 ? mbedtls_net_recv_timeout : NULL );
+    io_ctx.ssl = &ssl;
+    io_ctx.net = &server_fd;
+    mbedtls_ssl_set_bio( &ssl, &io_ctx, send_cb, recv_cb,
+                         opt.nbio == 0 ? recv_timeout_cb : NULL );
 #else
      mbedtls_ssl_set_bio_ctx( &ssl, &server_fd );
 #endif
@@ -2550,13 +2681,8 @@ send_request:
                 goto exit;
             }
 
-            if( opt.nbio == 2 )
-                mbedtls_ssl_set_bio( &ssl, &server_fd, my_send, my_recv,
-                                     NULL );
-            else
-                mbedtls_ssl_set_bio( &ssl, &server_fd, mbedtls_net_send,
-                            mbedtls_net_recv,
-                            opt.nbio == 0 ? mbedtls_net_recv_timeout : NULL );
+            mbedtls_ssl_set_bio( &ssl, &io_ctx, send_cb, recv_cb,
+                                 opt.nbio == 0 ? recv_timeout_cb : NULL );
 
 #if defined(MBEDTLS_TIMING_C)
 #if !defined(MBEDTLS_SSL_CONF_SET_TIMER) && \
diff --git a/programs/ssl/ssl_server2.c b/programs/ssl/ssl_server2.c
index 747523b5f..2cd00fa39 100644
--- a/programs/ssl/ssl_server2.c
+++ b/programs/ssl/ssl_server2.c
@@ -621,7 +621,7 @@ static void my_debug( void *ctx, int level,
  * Test recv/send functions that make sure each try returns
  * WANT_READ/WANT_WRITE at least once before sucesseding
  */
-static int my_recv( void *ctx, unsigned char *buf, size_t len )
+static int delayed_recv( void *ctx, unsigned char *buf, size_t len )
 {
     static int first_try = 1;
     int ret;
@@ -638,7 +638,7 @@ static int my_recv( void *ctx, unsigned char *buf, size_t len )
     return( ret );
 }
 
-static int my_send( void *ctx, const unsigned char *buf, size_t len )
+static int delayed_send( void *ctx, const unsigned char *buf, size_t len )
 {
     static int first_try = 1;
     int ret;
@@ -658,6 +658,139 @@ static int my_send( void *ctx, const unsigned char *buf, size_t len )
           MBEDTLS_SSL_CONF_SEND &&
           MBEDTLS_SSL_CONF_RECV_TIMEOUT */
 
+typedef struct
+{
+    mbedtls_ssl_context *ssl;
+    mbedtls_net_context *net;
+} io_ctx_t;
+
+#if defined(MBEDTLS_SSL_RECORD_CHECKING)
+static int ssl_check_record( mbedtls_ssl_context const *ssl,
+                             unsigned char const *buf, size_t len )
+{
+    int ret;
+    unsigned char *tmp_buf;
+
+    /* Record checking may modify the input buffer,
+     * so make a copy. */
+    tmp_buf = mbedtls_calloc( 1, len );
+    if( tmp_buf == NULL )
+        return( MBEDTLS_ERR_SSL_ALLOC_FAILED );
+    memcpy( tmp_buf, buf, len );
+
+    ret = mbedtls_ssl_check_record( ssl, tmp_buf, len );
+    if( ret != MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE )
+    {
+        int ret_repeated;
+
+        /* Test-only: Make sure that mbedtls_ssl_check_record()
+         *            doesn't alter state. */
+        memcpy( tmp_buf, buf, len ); /* Restore buffer */
+        ret_repeated = mbedtls_ssl_check_record( ssl, tmp_buf, len );
+        if( ret != ret_repeated )
+        {
+            mbedtls_printf( "mbedtls_ssl_check_record() returned inconsistent results.\n" );
+            return( -1 );
+        }
+
+        switch( ret )
+        {
+            case 0:
+                break;
+
+            case MBEDTLS_ERR_SSL_INVALID_RECORD:
+                if( opt.debug_level > 1 )
+                    mbedtls_printf( "mbedtls_ssl_check_record() detected invalid record.\n" );
+                break;
+
+            case MBEDTLS_ERR_SSL_INVALID_MAC:
+                if( opt.debug_level > 1 )
+                    mbedtls_printf( "mbedtls_ssl_check_record() detected unauthentic record.\n" );
+                break;
+
+            case MBEDTLS_ERR_SSL_UNEXPECTED_RECORD:
+                if( opt.debug_level > 1 )
+                    mbedtls_printf( "mbedtls_ssl_check_record() detected unexpected record.\n" );
+                break;
+
+            default:
+                mbedtls_printf( "mbedtls_ssl_check_record() failed fatally with -%#04x.\n", -ret );
+                return( -1 );
+        }
+
+        /* Regardless of the outcome, forward the record to the stack. */
+    }
+
+    mbedtls_free( tmp_buf );
+
+    return( 0 );
+}
+#endif /* MBEDTLS_SSL_RECORD_CHECKING */
+
+static int recv_cb( void *ctx, unsigned char *buf, size_t len )
+{
+    io_ctx_t *io_ctx = (io_ctx_t*) ctx;
+    size_t recv_len;
+    int ret;
+
+    if( opt.nbio == 2 )
+        ret = delayed_recv( io_ctx->net, buf, len );
+    else
+        ret = mbedtls_net_recv( io_ctx->net, buf, len );
+    if( ret < 0 )
+        return( ret );
+    recv_len = (size_t) ret;
+
+    if( opt.transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM )
+    {
+        /* Here's the place to do any datagram/record checking
+         * in between receiving the packet from the underlying
+         * transport and passing it on to the TLS stack. */
+#if defined(MBEDTLS_SSL_RECORD_CHECKING)
+        if( ssl_check_record( io_ctx->ssl, buf, recv_len ) != 0 )
+            return( -1 );
+#endif /* MBEDTLS_SSL_RECORD_CHECKING */
+    }
+
+    return( (int) recv_len );
+}
+
+static int recv_timeout_cb( void *ctx, unsigned char *buf, size_t len,
+                            uint32_t timeout )
+{
+    io_ctx_t *io_ctx = (io_ctx_t*) ctx;
+    int ret;
+    size_t recv_len;
+
+    ret = mbedtls_net_recv_timeout( io_ctx->net, buf, len, timeout );
+    if( ret < 0 )
+        return( ret );
+    recv_len = (size_t) ret;
+
+    if( opt.transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM )
+    {
+        /* Here's the place to do any datagram/record checking
+         * in between receiving the packet from the underlying
+         * transport and passing it on to the TLS stack. */
+#if defined(MBEDTLS_SSL_RECORD_CHECKING)
+        if( ssl_check_record( io_ctx->ssl, buf, recv_len ) != 0 )
+            return( -1 );
+#endif /* MBEDTLS_SSL_RECORD_CHECKING */
+    }
+
+    return( (int) recv_len );
+}
+
+static int send_cb( void *ctx, unsigned char const *buf, size_t len )
+{
+    io_ctx_t *io_ctx = (io_ctx_t*) ctx;
+
+    if( opt.nbio == 2 )
+        return( delayed_send( io_ctx->net, buf, len ) );
+
+    return( mbedtls_net_send( io_ctx->net, buf, len ) );
+}
+
 #if !defined(MBEDTLS_SSL_CONF_AUTHMODE)
 /*
  * Return authmode from string, or -1 on error
@@ -1376,6 +1509,7 @@ int main( int argc, char *argv[] )
 {
     int ret = 0, len, written, frags, exchanges_left;
     int version_suites[4][2];
+    io_ctx_t io_ctx;
     unsigned char* buf = 0;
 #if defined(MBEDTLS_KEY_EXCHANGE__SOME__PSK_ENABLED)
     unsigned char psk[MBEDTLS_PSK_MAX_LEN];
@@ -2918,12 +3052,10 @@ int main( int argc, char *argv[] )
 #if !defined(MBEDTLS_SSL_CONF_RECV) && \
     !defined(MBEDTLS_SSL_CONF_SEND) && \
     !defined(MBEDTLS_SSL_CONF_RECV_TIMEOUT)
-    if( opt.nbio == 2 )
-        mbedtls_ssl_set_bio( &ssl, &client_fd, my_send, my_recv, NULL );
-    else
-        mbedtls_ssl_set_bio( &ssl, &client_fd,
-                             mbedtls_net_send, mbedtls_net_recv,
-                             opt.nbio == 0 ? mbedtls_net_recv_timeout : NULL );
+    io_ctx.ssl = &ssl;
+    io_ctx.net = &client_fd;
+    mbedtls_ssl_set_bio( &ssl, &io_ctx, send_cb, recv_cb,
+                         opt.nbio == 0 ? recv_timeout_cb : NULL );
 #else
      mbedtls_ssl_set_bio_ctx( &ssl, &client_fd );
 #endif
@@ -3586,13 +3718,8 @@ data_exchange:
              * if you want to share your set up code between the case of
              * establishing a new connection and this case.
              */
-            if( opt.nbio == 2 )
-                mbedtls_ssl_set_bio( &ssl, &client_fd, my_send, my_recv,
-                                     NULL );
-            else
-                mbedtls_ssl_set_bio( &ssl, &client_fd, mbedtls_net_send,
-                            mbedtls_net_recv,
-                            opt.nbio == 0 ? mbedtls_net_recv_timeout : NULL );
+            mbedtls_ssl_set_bio( &ssl, &io_ctx, send_cb, recv_cb,
+                                 opt.nbio == 0 ? recv_timeout_cb : NULL );
 
 #if defined(MBEDTLS_TIMING_C)
 #if !defined(MBEDTLS_SSL_CONF_SET_TIMER) && \