Merge branch 'pr_946' into development-proposed
diff --git a/ChangeLog b/ChangeLog
index f2cb932..9ee82c6 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -19,6 +19,14 @@
mbedtls_ecdh_compute_shared()) are supported for now. Contributed by
Nicholas Wilson (#348).
+API Changes
+ * Add function mbedtls_net_poll to public API allowing to wait for a
+ network context to become ready for reading or writing.
+ * Add function mbedtls_ssl_check_pending to public API allowing to check
+ if more data is pending to be processed in the internal message buffers.
+ This function is necessary to determine when it is safe to idle on the
+ underlying transport in case event-driven IO is used.
+
Bugfix
* Fix spurious uninitialized variable warning in cmac.c. Fix independently
contributed by Brian J Murray and David Brown.
@@ -39,6 +47,14 @@
the mbedtls_cipher_update() documentation. Contributed by Andy Leiserson.
* Fix overriding and ignoring return values when parsing and writing to
a file in pk_sign program. Found by kevlut in #1142.
+ * Restrict usage of error code MBEDTLS_ERR_SSL_WANT_READ to situations
+ where data needs to be fetched from the underlying transport in order
+ to make progress. Previously, this error code was also occasionally
+ returned when unexpected messages were being discarded, ignoring that
+ further messages could potentially already be pending to be processed
+ in the internal buffers; these cases lead to deadlocks in case
+ event-driven I/O was used.
+ Found and reported by Hubert Mis in #772.
Changes
* Remove some redundant code in bignum.c. Contributed by Alexey Skalozub.
diff --git a/include/mbedtls/net_sockets.h b/include/mbedtls/net_sockets.h
index 52bb8de..0f9b31e 100644
--- a/include/mbedtls/net_sockets.h
+++ b/include/mbedtls/net_sockets.h
@@ -46,12 +46,17 @@
#define MBEDTLS_ERR_NET_UNKNOWN_HOST -0x0052 /**< Failed to get an IP address for the given hostname. */
#define MBEDTLS_ERR_NET_BUFFER_TOO_SMALL -0x0043 /**< Buffer is too small to hold the data. */
#define MBEDTLS_ERR_NET_INVALID_CONTEXT -0x0045 /**< The context is invalid, eg because it was free()ed. */
+#define MBEDTLS_ERR_NET_POLL_FAILED -0x0047 /**< Polling the net context failed. */
+#define MBEDTLS_ERR_NET_BAD_INPUT_DATA -0x0049 /**< Input invalid. */
#define MBEDTLS_NET_LISTEN_BACKLOG 10 /**< The backlog that listen() should use. */
#define MBEDTLS_NET_PROTO_TCP 0 /**< The TCP transport protocol */
#define MBEDTLS_NET_PROTO_UDP 1 /**< The UDP transport protocol */
+#define MBEDTLS_NET_POLL_READ 1 /**< Used in \c mbedtls_net_poll to check for pending data */
+#define MBEDTLS_NET_POLL_WRITE 2 /**< Used in \c mbedtls_net_poll to check if write possible */
+
#ifdef __cplusplus
extern "C" {
#endif
@@ -134,6 +139,29 @@
void *client_ip, size_t buf_size, size_t *ip_len );
/**
+ * \brief Check and wait for the context to be ready for read/write
+ *
+ * \param ctx Socket to check
+ * \param rw Bitflag composed of MBEDTLS_NET_POLL_READ and
+ * MBEDTLS_NET_POLL_WRITE specifying the events
+ * to wait for:
+ * - If MBEDTLS_NET_POLL_READ is set, the function
+ * will return as soon as the net context is available
+ * for reading.
+ * - If MBEDTLS_NET_POLL_WRITE is set, the function
+ * will return as soon as the net context is available
+ * for writing.
+ * \param timeout Maximal amount of time to wait before returning,
+ * in milliseconds. If \c timeout is zero, the
+ * function returns immediately. If \c timeout is
+ * -1u, the function blocks potentially indefinitely.
+ *
+ * \return Bitmask composed of MBEDTLS_NET_POLL_READ/WRITE
+ * on success or timeout, or a negative return code otherwise.
+ */
+int mbedtls_net_poll( mbedtls_net_context *ctx, uint32_t rw, uint32_t timeout );
+
+/**
* \brief Set the socket blocking
*
* \param ctx Socket to set
diff --git a/include/mbedtls/ssl.h b/include/mbedtls/ssl.h
index e1d64b9..bb9c02d 100644
--- a/include/mbedtls/ssl.h
+++ b/include/mbedtls/ssl.h
@@ -112,13 +112,14 @@
#define MBEDTLS_ERR_SSL_HELLO_VERIFY_REQUIRED -0x6A80 /**< DTLS client must retry for hello verification */
#define MBEDTLS_ERR_SSL_BUFFER_TOO_SMALL -0x6A00 /**< A buffer is too small to receive or write a message */
#define MBEDTLS_ERR_SSL_NO_USABLE_CIPHERSUITE -0x6980 /**< None of the common ciphersuites is usable (eg, no suitable certificate, see debug messages). */
-#define MBEDTLS_ERR_SSL_WANT_READ -0x6900 /**< Connection requires a read call. */
+#define MBEDTLS_ERR_SSL_WANT_READ -0x6900 /**< No data of requested type currently available on underlying transport. */
#define MBEDTLS_ERR_SSL_WANT_WRITE -0x6880 /**< Connection requires a write call. */
#define MBEDTLS_ERR_SSL_TIMEOUT -0x6800 /**< The operation timed out. */
#define MBEDTLS_ERR_SSL_CLIENT_RECONNECT -0x6780 /**< The client initiated a reconnect from the same port. */
#define MBEDTLS_ERR_SSL_UNEXPECTED_RECORD -0x6700 /**< Record header looks valid but is not expected. */
#define MBEDTLS_ERR_SSL_NON_FATAL -0x6680 /**< The alert message received indicates a non-fatal error. */
#define MBEDTLS_ERR_SSL_INVALID_VERIFY_HASH -0x6600 /**< Couldn't set the hash for verifying CertificateVerify */
+#define MBEDTLS_ERR_SSL_CONTINUE_PROCESSING -0x6580 /**< Internal-only message signaling that further message-processing should be done */
/*
* Various constants
@@ -2301,11 +2302,59 @@
#endif /* MBEDTLS_SSL_RENEGOTIATION */
/**
- * \brief Return the number of data bytes available to read
+ * \brief Check if there is data already read from the
+ * underlying transport but not yet processed.
*
* \param ssl SSL context
*
- * \return how many bytes are available in the read buffer
+ * \return 0 if nothing's pending, 1 otherwise.
+ *
+ * \note This is different in purpose and behaviour from
+ * \c mbedtls_ssl_get_bytes_avail in that it considers
+ * any kind of unprocessed data, not only unread
+ * application data. If \c mbedtls_ssl_get_bytes
+ * returns a non-zero value, this function will
+ * also signal pending data, but the converse does
+ * not hold. For example, in DTLS there might be
+ * further records waiting to be processed from
+ * the current underlying transport's datagram.
+ *
+ * \note If this function returns 1 (data pending), this
+ * does not imply that a subsequent call to
+ * \c mbedtls_ssl_read will provide any data;
+ * e.g., the unprocessed data might turn out
+ * to be an alert or a handshake message.
+ *
+ * \note This function is useful in the following situation:
+ * If the SSL/TLS module successfully returns from an
+ * operation - e.g. a handshake or an application record
+ * read - and you're awaiting incoming data next, you
+ * must not immediately idle on the underlying transport
+ * to have data ready, but you need to check the value
+ * of this function first. The reason is that the desired
+ * data might already be read but not yet processed.
+ * If, in contrast, a previous call to the SSL/TLS module
+ * returned MBEDTLS_ERR_SSL_WANT_READ, it is not necessary
+ * to call this function, as the latter error code entails
+ * that all internal data has been processed.
+ *
+ */
+int mbedtls_ssl_check_pending( const mbedtls_ssl_context *ssl );
+
+/**
+ * \brief Return the number of application data bytes
+ * remaining to be read from the current record.
+ *
+ * \param ssl SSL context
+ *
+ * \return How many bytes are available in the application
+ * data record read buffer.
+ *
+ * \note When working over a datagram transport, this is
+ * useful to detect the current datagram's boundary
+ * in case \c mbedtls_ssl_read has written the maximal
+ * amount of data fitting into the input buffer.
+ *
*/
size_t mbedtls_ssl_get_bytes_avail( const mbedtls_ssl_context *ssl );
@@ -2420,11 +2469,25 @@
* MBEDTLS_ERR_SSL_HELLO_VERIFY_REQUIRED (see below), or
* a specific SSL error code.
*
+ * If this function returns MBEDTLS_ERR_SSL_WANT_READ, the
+ * handshake is unfinished and no further data is available
+ * from the underlying transport. In this case, you must call
+ * the function again at some later stage.
+ *
+ * \note Remarks regarding event-driven DTLS:
+ * If the function returns MBEDTLS_ERR_SSL_WANT_READ, no datagram
+ * from the underlying transport layer is currently being processed,
+ * and it is safe to idle until the timer or the underlying transport
+ * signal a new event. This is not true for a successful handshake,
+ * in which case the datagram of the underlying transport that is
+ * currently being processed might or might not contain further
+ * DTLS records.
+ *
* \note If this function returns something other than 0 or
- * MBEDTLS_ERR_SSL_WANT_READ/WRITE, then the ssl context
- * becomes unusable, and you should either free it or call
- * \c mbedtls_ssl_session_reset() on it before re-using it for
- * a new connection; the current connection must be closed.
+ * MBEDTLS_ERR_SSL_WANT_READ/WRITE, you must stop using
+ * the SSL context for reading or writing, and either free it or
+ * call \c mbedtls_ssl_session_reset() on it before re-using it
+ * for a new connection; the current connection must be closed.
*
* \note If DTLS is in use, then you may choose to handle
* MBEDTLS_ERR_SSL_HELLO_VERIFY_REQUIRED specially for logging
@@ -2441,10 +2504,10 @@
* call this function if state is MBEDTLS_SSL_HANDSHAKE_OVER.
*
* \note If this function returns something other than 0 or
- * MBEDTLS_ERR_SSL_WANT_READ/WRITE, then the ssl context
- * becomes unusable, and you should either free it or call
- * \c mbedtls_ssl_session_reset() on it before re-using it for
- * a new connection; the current connection must be closed.
+ * MBEDTLS_ERR_SSL_WANT_READ/WRITE, you must stop using
+ * the SSL context for reading or writing, and either free it or
+ * call \c mbedtls_ssl_session_reset() on it before re-using it
+ * for a new connection; the current connection must be closed.
*
* \param ssl SSL context
*
@@ -2468,10 +2531,10 @@
* value.
*
* \note If this function returns something other than 0 or
- * MBEDTLS_ERR_SSL_WANT_READ/WRITE, then the ssl context
- * becomes unusable, and you should either free it or call
- * \c mbedtls_ssl_session_reset() on it before re-using it for
- * a new connection; the current connection must be closed.
+ * MBEDTLS_ERR_SSL_WANT_READ/WRITE, you must stop using
+ * the SSL context for reading or writing, and either free it or
+ * call \c mbedtls_ssl_session_reset() on it before re-using it
+ * for a new connection; the current connection must be closed.
*/
int mbedtls_ssl_renegotiate( mbedtls_ssl_context *ssl );
#endif /* MBEDTLS_SSL_RENEGOTIATION */
@@ -2483,20 +2546,20 @@
* \param buf buffer that will hold the data
* \param len maximum number of bytes to read
*
- * \return the number of bytes read, or
- * 0 for EOF, or
- * MBEDTLS_ERR_SSL_WANT_READ or MBEDTLS_ERR_SSL_WANT_WRITE, or
- * MBEDTLS_ERR_SSL_CLIENT_RECONNECT (see below), or
- * another negative error code.
+ * \return One of the following:
+ * - 0 if the read end of the underlying transport was closed,
+ * - the (positive) number of bytes read, or
+ * - a negative error code on failure.
*
- * \note If this function returns something other than a positive
- * value or MBEDTLS_ERR_SSL_WANT_READ/WRITE or
- * MBEDTLS_ERR_SSL_CLIENT_RECONNECT, then the ssl context
- * becomes unusable, and you should either free it or call
- * \c mbedtls_ssl_session_reset() on it before re-using it for
- * a new connection; the current connection must be closed.
+ * If MBEDTLS_ERR_SSL_WANT_READ is returned, no application data
+ * is available from the underlying transport. In this case,
+ * the function needs to be called again at some later stage.
*
- * \note When this function return MBEDTLS_ERR_SSL_CLIENT_RECONNECT
+ * If MBEDTLS_ERR_SSL_WANT_WRITE is returned, a write is pending
+ * but the underlying transport isn't available for writing. In this
+ * case, the function needs to be called again at some later stage.
+ *
+ * When this function return MBEDTLS_ERR_SSL_CLIENT_RECONNECT
* (which can only happen server-side), it means that a client
* is initiating a new connection using the same source port.
* You can either treat that as a connection close and wait
@@ -2509,6 +2572,28 @@
* again. WARNING: not validating the identity of the client
* again, or not transmitting the new identity to the
* application layer, would allow authentication bypass!
+ *
+ * \note If this function returns something other than a positive value
+ * or MBEDTLS_ERR_SSL_WANT_READ/WRITE or MBEDTLS_ERR_SSL_CLIENT_RECONNECT,
+ * you must stop using the SSL context for reading or writing,
+ * and either free it or call \c mbedtls_ssl_session_reset() on it
+ * before re-using it for a new connection; the current connection
+ * must be closed.
+ *
+ * \note Remarks regarding event-driven DTLS:
+ * - If the function returns MBEDTLS_ERR_SSL_WANT_READ, no datagram
+ * from the underlying transport layer is currently being processed,
+ * and it is safe to idle until the timer or the underlying transport
+ * signal a new event.
+ * - This function may return MBEDTLS_ERR_SSL_WANT_READ even if data was
+ * initially available on the underlying transport, as this data may have
+ * been only e.g. duplicated messages or a renegotiation request.
+ * Therefore, you must be prepared to receive MBEDTLS_ERR_SSL_WANT_READ even
+ * when reacting to an incoming-data event from the underlying transport.
+ * - On success, the datagram of the underlying transport that is currently
+ * being processed may contain further DTLS records. You should call
+ * \c mbedtls_ssl_check_pending to check for remaining records.
+ *
*/
int mbedtls_ssl_read( mbedtls_ssl_context *ssl, unsigned char *buf, size_t len );
@@ -2529,11 +2614,11 @@
* or MBEDTLS_ERR_SSL_WANT_WRITE or MBEDTLS_ERR_SSL_WANT_READ,
* or another negative error code.
*
- * \note If this function returns something other than a positive
- * value or MBEDTLS_ERR_SSL_WANT_READ/WRITE, the ssl context
- * becomes unusable, and you should either free it or call
- * \c mbedtls_ssl_session_reset() on it before re-using it for
- * a new connection; the current connection must be closed.
+ * \note If this function returns something other than a positive value
+ * or MBEDTLS_ERR_SSL_WANT_READ/WRITE, you must stop using
+ * the SSL context for reading or writing, and either free it or
+ * call \c mbedtls_ssl_session_reset() on it before re-using it
+ * for a new connection; the current connection must be closed.
*
* \note When this function returns MBEDTLS_ERR_SSL_WANT_WRITE/READ,
* it must be called later with the *same* arguments,
@@ -2562,10 +2647,10 @@
* \return 0 if successful, or a specific SSL error code.
*
* \note If this function returns something other than 0 or
- * MBEDTLS_ERR_SSL_WANT_READ/WRITE, then the ssl context
- * becomes unusable, and you should either free it or call
- * \c mbedtls_ssl_session_reset() on it before re-using it for
- * a new connection; the current connection must be closed.
+ * MBEDTLS_ERR_SSL_WANT_READ/WRITE, you must stop using
+ * the SSL context for reading or writing, and either free it or
+ * call \c mbedtls_ssl_session_reset() on it before re-using it
+ * for a new connection; the current connection must be closed.
*/
int mbedtls_ssl_send_alert_message( mbedtls_ssl_context *ssl,
unsigned char level,
@@ -2578,10 +2663,10 @@
* \return 0 if successful, or a specific SSL error code.
*
* \note If this function returns something other than 0 or
- * MBEDTLS_ERR_SSL_WANT_READ/WRITE, then the ssl context
- * becomes unusable, and you should either free it or call
- * \c mbedtls_ssl_session_reset() on it before re-using it for
- * a new connection; the current connection must be closed.
+ * MBEDTLS_ERR_SSL_WANT_READ/WRITE, you must stop using
+ * the SSL context for reading or writing, and either free it or
+ * call \c mbedtls_ssl_session_reset() on it before re-using it
+ * for a new connection; the current connection must be closed.
*/
int mbedtls_ssl_close_notify( mbedtls_ssl_context *ssl );
diff --git a/library/error.c b/library/error.c
index b173c7e..96ab203 100644
--- a/library/error.c
+++ b/library/error.c
@@ -478,7 +478,7 @@
if( use_ret == -(MBEDTLS_ERR_SSL_NO_USABLE_CIPHERSUITE) )
mbedtls_snprintf( buf, buflen, "SSL - None of the common ciphersuites is usable (eg, no suitable certificate, see debug messages)" );
if( use_ret == -(MBEDTLS_ERR_SSL_WANT_READ) )
- mbedtls_snprintf( buf, buflen, "SSL - Connection requires a read call" );
+ mbedtls_snprintf( buf, buflen, "SSL - No data of requested type currently available on underlying transport" );
if( use_ret == -(MBEDTLS_ERR_SSL_WANT_WRITE) )
mbedtls_snprintf( buf, buflen, "SSL - Connection requires a write call" );
if( use_ret == -(MBEDTLS_ERR_SSL_TIMEOUT) )
@@ -491,6 +491,8 @@
mbedtls_snprintf( buf, buflen, "SSL - The alert message received indicates a non-fatal error" );
if( use_ret == -(MBEDTLS_ERR_SSL_INVALID_VERIFY_HASH) )
mbedtls_snprintf( buf, buflen, "SSL - Couldn't set the hash for verifying CertificateVerify" );
+ if( use_ret == -(MBEDTLS_ERR_SSL_CONTINUE_PROCESSING) )
+ mbedtls_snprintf( buf, buflen, "SSL - Internal-only message signaling that further message-processing should be done" );
#endif /* MBEDTLS_SSL_TLS_C */
#if defined(MBEDTLS_X509_USE_C) || defined(MBEDTLS_X509_CREATE_C)
@@ -745,6 +747,10 @@
mbedtls_snprintf( buf, buflen, "NET - Buffer is too small to hold the data" );
if( use_ret == -(MBEDTLS_ERR_NET_INVALID_CONTEXT) )
mbedtls_snprintf( buf, buflen, "NET - The context is invalid, eg because it was free()ed" );
+ if( use_ret == -(MBEDTLS_ERR_NET_POLL_FAILED) )
+ mbedtls_snprintf( buf, buflen, "NET - Polling the net context failed" );
+ if( use_ret == -(MBEDTLS_ERR_NET_BAD_INPUT_DATA) )
+ mbedtls_snprintf( buf, buflen, "NET - Input invalid" );
#endif /* MBEDTLS_NET_C */
#if defined(MBEDTLS_OID_C)
diff --git a/library/net_sockets.c b/library/net_sockets.c
index 631efad..f99d339 100644
--- a/library/net_sockets.c
+++ b/library/net_sockets.c
@@ -45,6 +45,8 @@
#if (defined(_WIN32) || defined(_WIN32_WCE)) && !defined(EFIX64) && \
!defined(EFI32)
+#define IS_EINTR( ret ) ( ( ret ) == WSAEINTR )
+
#ifdef _WIN32_WINNT
#undef _WIN32_WINNT
#endif
@@ -82,6 +84,8 @@
#include <netdb.h>
#include <errno.h>
+#define IS_EINTR( ret ) ( ( ret ) == EINTR )
+
#endif /* ( _WIN32 || _WIN32_WCE ) && !EFIX64 && !EFI32 */
/* Some MS functions want int and MSVC warns if we pass size_t,
@@ -271,7 +275,7 @@
static int net_would_block( const mbedtls_net_context *ctx )
{
int err = errno;
-
+
/*
* Never return 'WOULD BLOCK' on a non-blocking socket
*/
@@ -439,6 +443,68 @@
}
/*
+ * Check if data is available on the socket
+ */
+
+int mbedtls_net_poll( mbedtls_net_context *ctx, uint32_t rw, uint32_t timeout )
+{
+ int ret;
+ struct timeval tv;
+
+ fd_set read_fds;
+ fd_set write_fds;
+
+ int fd = ctx->fd;
+
+ if( fd < 0 )
+ return( MBEDTLS_ERR_NET_INVALID_CONTEXT );
+
+ /* Ensure that memory sanitizers consider
+ * read_fds and write_fds as initialized even
+ * if FD_ZERO is implemented in assembly. */
+ memset( &read_fds, 0, sizeof( read_fds ) );
+ memset( &write_fds, 0, sizeof( write_fds ) );
+
+ FD_ZERO( &read_fds );
+ if( rw & MBEDTLS_NET_POLL_READ )
+ {
+ rw &= ~MBEDTLS_NET_POLL_READ;
+ FD_SET( fd, &read_fds );
+ }
+
+ FD_ZERO( &write_fds );
+ if( rw & MBEDTLS_NET_POLL_WRITE )
+ {
+ rw &= ~MBEDTLS_NET_POLL_WRITE;
+ FD_SET( fd, &write_fds );
+ }
+
+ if( rw != 0 )
+ return( MBEDTLS_ERR_NET_BAD_INPUT_DATA );
+
+ tv.tv_sec = timeout / 1000;
+ tv.tv_usec = ( timeout % 1000 ) * 1000;
+
+ do
+ {
+ ret = select( fd + 1, &read_fds, &write_fds, NULL,
+ timeout == (uint32_t) -1 ? NULL : &tv );
+ }
+ while( IS_EINTR( ret ) );
+
+ if( ret < 0 )
+ return( MBEDTLS_ERR_NET_POLL_FAILED );
+
+ ret = 0;
+ if( FD_ISSET( fd, &read_fds ) )
+ ret |= MBEDTLS_NET_POLL_READ;
+ if( FD_ISSET( fd, &write_fds ) )
+ ret |= MBEDTLS_NET_POLL_WRITE;
+
+ return( ret );
+}
+
+/*
* Portable usleep helper
*/
void mbedtls_net_usleep( unsigned long usec )
@@ -497,8 +563,8 @@
/*
* Read at most 'len' characters, blocking for at most 'timeout' ms
*/
-int mbedtls_net_recv_timeout( void *ctx, unsigned char *buf, size_t len,
- uint32_t timeout )
+int mbedtls_net_recv_timeout( void *ctx, unsigned char *buf,
+ size_t len, uint32_t timeout )
{
int ret;
struct timeval tv;
diff --git a/library/ssl_srv.c b/library/ssl_srv.c
index aca4235..2c180f1 100644
--- a/library/ssl_srv.c
+++ b/library/ssl_srv.c
@@ -793,7 +793,7 @@
const mbedtls_ssl_ciphersuite_t *suite_info;
#if defined(MBEDTLS_SSL_PROTO_TLS1_2) && \
- defined(MBEDTLS_KEY_EXCHANGE__WITH_CERT__ENABLED)
+ defined(MBEDTLS_KEY_EXCHANGE__WITH_CERT__ENABLED)
mbedtls_pk_type_t sig_type;
#endif
@@ -2961,7 +2961,7 @@
return( ret );
}
-#if defined(MBEDTLS_KEY_EXCHANGE__WITH_SERVER_SIGNATURE__ENABLED)
+#if defined(MBEDTLS_KEY_EXCHANGE__WITH_SERVER_SIGNATURE__ENABLED)
dig_signed = p;
dig_signed_len = len;
#endif
@@ -3050,7 +3050,7 @@
/*
* 3.1: Choose hash algorithm:
- * A: For TLS 1.2, obey signature-hash-algorithm extension
+ * A: For TLS 1.2, obey signature-hash-algorithm extension
* to choose appropriate hash.
* B: For SSL3, TLS1.0, TLS1.1 and ECDHE_ECDSA, use SHA1
* (RFC 4492, Sec. 5.4)
@@ -3071,7 +3071,7 @@
sig_alg ) ) == MBEDTLS_MD_NONE )
{
MBEDTLS_SSL_DEBUG_MSG( 1, ( "should never happen" ) );
- /* (... because we choose a cipher suite
+ /* (... because we choose a cipher suite
* only if there is a matching hash.) */
return( MBEDTLS_ERR_SSL_INTERNAL_ERROR );
}
@@ -3750,7 +3750,10 @@
/* Read the message without adding it to the checksum */
do {
- if( ( ret = mbedtls_ssl_read_record_layer( ssl ) ) != 0 )
+ do ret = mbedtls_ssl_read_record_layer( ssl );
+ while( ret == MBEDTLS_ERR_SSL_CONTINUE_PROCESSING );
+
+ if( ret != 0 )
{
MBEDTLS_SSL_DEBUG_RET( 1, ( "mbedtls_ssl_read_record_layer" ), ret );
return( ret );
@@ -3758,7 +3761,8 @@
ret = mbedtls_ssl_handle_message_type( ssl );
- } while( MBEDTLS_ERR_SSL_NON_FATAL == ret );
+ } while( MBEDTLS_ERR_SSL_NON_FATAL == ret ||
+ MBEDTLS_ERR_SSL_CONTINUE_PROCESSING == ret );
if( 0 != ret )
{
diff --git a/library/ssl_tls.c b/library/ssl_tls.c
index 4ca74fb..e8063d2 100644
--- a/library/ssl_tls.c
+++ b/library/ssl_tls.c
@@ -2337,7 +2337,10 @@
* that will end up being dropped.
*/
if( ssl_check_timer( ssl ) != 0 )
+ {
+ MBEDTLS_SSL_DEBUG_MSG( 2, ( "timer has expired" ) );
ret = MBEDTLS_ERR_SSL_TIMEOUT;
+ }
else
{
len = MBEDTLS_SSL_BUFFER_LEN - ( ssl->in_hdr - ssl->in_buf );
@@ -3085,7 +3088,7 @@
if( ssl_bitmask_check( bitmask, msg_len ) != 0 )
{
MBEDTLS_SSL_DEBUG_MSG( 2, ( "message is not complete yet" ) );
- return( MBEDTLS_ERR_SSL_WANT_READ );
+ return( MBEDTLS_ERR_SSL_CONTINUE_PROCESSING );
}
MBEDTLS_SSL_DEBUG_MSG( 2, ( "handshake message completed" ) );
@@ -3162,9 +3165,11 @@
int ret;
unsigned int recv_msg_seq = ( ssl->in_msg[4] << 8 ) | ssl->in_msg[5];
- /* ssl->handshake is NULL when receiving ClientHello for renego */
if( ssl->handshake != NULL &&
- recv_msg_seq != ssl->handshake->in_msg_seq )
+ ( ( ssl->state != MBEDTLS_SSL_HANDSHAKE_OVER &&
+ recv_msg_seq != ssl->handshake->in_msg_seq ) ||
+ ( ssl->state == MBEDTLS_SSL_HANDSHAKE_OVER &&
+ ssl->in_msg[0] != MBEDTLS_SSL_HS_CLIENT_HELLO ) ) )
{
/* Retransmit only on last message from previous flight, to avoid
* too many retransmissions.
@@ -3191,7 +3196,7 @@
ssl->handshake->in_msg_seq ) );
}
- return( MBEDTLS_ERR_SSL_WANT_READ );
+ return( MBEDTLS_ERR_SSL_CONTINUE_PROCESSING );
}
/* Wait until message completion to increment in_msg_seq */
@@ -3594,81 +3599,23 @@
return( MBEDTLS_ERR_SSL_INVALID_RECORD );
}
- /* Check length against bounds of the current transform and version */
- if( ssl->transform_in == NULL )
- {
- if( ssl->in_msglen < 1 ||
- ssl->in_msglen > MBEDTLS_SSL_MAX_CONTENT_LEN )
- {
- MBEDTLS_SSL_DEBUG_MSG( 1, ( "bad message length" ) );
- return( MBEDTLS_ERR_SSL_INVALID_RECORD );
- }
- }
- else
- {
- if( ssl->in_msglen < ssl->transform_in->minlen )
- {
- MBEDTLS_SSL_DEBUG_MSG( 1, ( "bad message length" ) );
- return( MBEDTLS_ERR_SSL_INVALID_RECORD );
- }
-
-#if defined(MBEDTLS_SSL_PROTO_SSL3)
- if( ssl->minor_ver == MBEDTLS_SSL_MINOR_VERSION_0 &&
- ssl->in_msglen > ssl->transform_in->minlen + MBEDTLS_SSL_MAX_CONTENT_LEN )
- {
- MBEDTLS_SSL_DEBUG_MSG( 1, ( "bad message length" ) );
- return( MBEDTLS_ERR_SSL_INVALID_RECORD );
- }
-#endif
-#if defined(MBEDTLS_SSL_PROTO_TLS1) || defined(MBEDTLS_SSL_PROTO_TLS1_1) || \
- defined(MBEDTLS_SSL_PROTO_TLS1_2)
- /*
- * TLS encrypted messages can have up to 256 bytes of padding
- */
- if( ssl->minor_ver >= MBEDTLS_SSL_MINOR_VERSION_1 &&
- ssl->in_msglen > ssl->transform_in->minlen +
- MBEDTLS_SSL_MAX_CONTENT_LEN + 256 )
- {
- MBEDTLS_SSL_DEBUG_MSG( 1, ( "bad message length" ) );
- return( MBEDTLS_ERR_SSL_INVALID_RECORD );
- }
-#endif
- }
-
/*
- * DTLS-related tests done last, because most of them may result in
- * silently dropping the record (but not the whole datagram), and we only
- * want to consider that after ensuring that the "basic" fields (type,
- * version, length) are sane.
+ * DTLS-related tests.
+ * Check epoch before checking length constraint because
+ * the latter varies with the epoch. E.g., if a ChangeCipherSpec
+ * message gets duplicated before the corresponding Finished message,
+ * the second ChangeCipherSpec should be discarded because it belongs
+ * to an old epoch, but not because its length is shorter than
+ * the minimum record length for packets using the new record transform.
+ * Note that these two kinds of failures are handled differently,
+ * as an unexpected record is silently skipped but an invalid
+ * record leads to the entire datagram being dropped.
*/
#if defined(MBEDTLS_SSL_PROTO_DTLS)
if( ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM )
{
unsigned int rec_epoch = ( ssl->in_ctr[0] << 8 ) | ssl->in_ctr[1];
- /* Drop unexpected ChangeCipherSpec messages */
- if( ssl->in_msgtype == MBEDTLS_SSL_MSG_CHANGE_CIPHER_SPEC &&
- ssl->state != MBEDTLS_SSL_CLIENT_CHANGE_CIPHER_SPEC &&
- ssl->state != MBEDTLS_SSL_SERVER_CHANGE_CIPHER_SPEC )
- {
- MBEDTLS_SSL_DEBUG_MSG( 1, ( "dropping unexpected ChangeCipherSpec" ) );
- return( MBEDTLS_ERR_SSL_UNEXPECTED_RECORD );
- }
-
- /* Drop unexpected ApplicationData records,
- * except at the beginning of renegotiations */
- if( ssl->in_msgtype == MBEDTLS_SSL_MSG_APPLICATION_DATA &&
- ssl->state != MBEDTLS_SSL_HANDSHAKE_OVER
-#if defined(MBEDTLS_SSL_RENEGOTIATION)
- && ! ( ssl->renego_status == MBEDTLS_SSL_RENEGOTIATION_IN_PROGRESS &&
- ssl->state == MBEDTLS_SSL_SERVER_HELLO )
-#endif
- )
- {
- MBEDTLS_SSL_DEBUG_MSG( 1, ( "dropping unexpected ApplicationData" ) );
- return( MBEDTLS_ERR_SSL_UNEXPECTED_RECORD );
- }
-
/* Check epoch (and sequence number) with DTLS */
if( rec_epoch != ssl->in_epoch )
{
@@ -3708,9 +3655,74 @@
return( MBEDTLS_ERR_SSL_UNEXPECTED_RECORD );
}
#endif
+
+ /* Drop unexpected ChangeCipherSpec messages */
+ if( ssl->in_msgtype == MBEDTLS_SSL_MSG_CHANGE_CIPHER_SPEC &&
+ ssl->state != MBEDTLS_SSL_CLIENT_CHANGE_CIPHER_SPEC &&
+ ssl->state != MBEDTLS_SSL_SERVER_CHANGE_CIPHER_SPEC )
+ {
+ MBEDTLS_SSL_DEBUG_MSG( 1, ( "dropping unexpected ChangeCipherSpec" ) );
+ return( MBEDTLS_ERR_SSL_UNEXPECTED_RECORD );
+ }
+
+ /* Drop unexpected ApplicationData records,
+ * except at the beginning of renegotiations */
+ if( ssl->in_msgtype == MBEDTLS_SSL_MSG_APPLICATION_DATA &&
+ ssl->state != MBEDTLS_SSL_HANDSHAKE_OVER
+#if defined(MBEDTLS_SSL_RENEGOTIATION)
+ && ! ( ssl->renego_status == MBEDTLS_SSL_RENEGOTIATION_IN_PROGRESS &&
+ ssl->state == MBEDTLS_SSL_SERVER_HELLO )
+#endif
+ )
+ {
+ MBEDTLS_SSL_DEBUG_MSG( 1, ( "dropping unexpected ApplicationData" ) );
+ return( MBEDTLS_ERR_SSL_UNEXPECTED_RECORD );
+ }
}
#endif /* MBEDTLS_SSL_PROTO_DTLS */
+
+ /* Check length against bounds of the current transform and version */
+ if( ssl->transform_in == NULL )
+ {
+ if( ssl->in_msglen < 1 ||
+ ssl->in_msglen > MBEDTLS_SSL_MAX_CONTENT_LEN )
+ {
+ MBEDTLS_SSL_DEBUG_MSG( 1, ( "bad message length" ) );
+ return( MBEDTLS_ERR_SSL_INVALID_RECORD );
+ }
+ }
+ else
+ {
+ if( ssl->in_msglen < ssl->transform_in->minlen )
+ {
+ MBEDTLS_SSL_DEBUG_MSG( 1, ( "bad message length" ) );
+ return( MBEDTLS_ERR_SSL_INVALID_RECORD );
+ }
+
+#if defined(MBEDTLS_SSL_PROTO_SSL3)
+ if( ssl->minor_ver == MBEDTLS_SSL_MINOR_VERSION_0 &&
+ ssl->in_msglen > ssl->transform_in->minlen + MBEDTLS_SSL_MAX_CONTENT_LEN )
+ {
+ MBEDTLS_SSL_DEBUG_MSG( 1, ( "bad message length" ) );
+ return( MBEDTLS_ERR_SSL_INVALID_RECORD );
+ }
+#endif
+#if defined(MBEDTLS_SSL_PROTO_TLS1) || defined(MBEDTLS_SSL_PROTO_TLS1_1) || \
+ defined(MBEDTLS_SSL_PROTO_TLS1_2)
+ /*
+ * TLS encrypted messages can have up to 256 bytes of padding
+ */
+ if( ssl->minor_ver >= MBEDTLS_SSL_MINOR_VERSION_1 &&
+ ssl->in_msglen > ssl->transform_in->minlen +
+ MBEDTLS_SSL_MAX_CONTENT_LEN + 256 )
+ {
+ MBEDTLS_SSL_DEBUG_MSG( 1, ( "bad message length" ) );
+ return( MBEDTLS_ERR_SSL_INVALID_RECORD );
+ }
+#endif
+ }
+
return( 0 );
}
@@ -3799,7 +3811,10 @@
{
do {
- if( ( ret = mbedtls_ssl_read_record_layer( ssl ) ) != 0 )
+ do ret = mbedtls_ssl_read_record_layer( ssl );
+ while( ret == MBEDTLS_ERR_SSL_CONTINUE_PROCESSING );
+
+ if( ret != 0 )
{
MBEDTLS_SSL_DEBUG_RET( 1, ( "mbedtls_ssl_read_record_layer" ), ret );
return( ret );
@@ -3807,11 +3822,12 @@
ret = mbedtls_ssl_handle_message_type( ssl );
- } while( MBEDTLS_ERR_SSL_NON_FATAL == ret );
+ } while( MBEDTLS_ERR_SSL_NON_FATAL == ret ||
+ MBEDTLS_ERR_SSL_CONTINUE_PROCESSING == ret );
if( 0 != ret )
{
- MBEDTLS_SSL_DEBUG_RET( 1, ( "mbedtls_ssl_read_record_layer" ), ret );
+ MBEDTLS_SSL_DEBUG_RET( 1, ( "mbedtls_ssl_handle_message_type" ), ret );
return( ret );
}
@@ -3849,11 +3865,6 @@
* (2) Alert messages:
* Consume whole record content, in_msglen = 0.
*
- * NOTE: This needs to be fixed, since like for
- * handshake messages it is allowed to have
- * multiple alerts witin a single record.
- * Internal reference IOTSSL-1321.
- *
* (3) Change cipher spec:
* Consume whole record content, in_msglen = 0.
*
@@ -3881,12 +3892,12 @@
*/
/* Notes:
- * (1) in_hslen is *NOT* necessarily the size of the
+ * (1) in_hslen is not necessarily the size of the
* current handshake content: If DTLS handshake
* fragmentation is used, that's the fragment
* size instead. Using the total handshake message
- * size here is FAULTY and should be changed at
- * some point. Internal reference IOTSSL-1414.
+ * size here is faulty and should be changed at
+ * some point.
* (2) While it doesn't seem to cause problems, one
* has to be very careful not to assume that in_hslen
* is always <= in_msglen in a sensible communication.
@@ -3937,12 +3948,6 @@
return( 0 );
}
- /* Need to fetch a new record */
-
-#if defined(MBEDTLS_SSL_PROTO_DTLS)
-read_record_header:
-#endif
-
/* Current record either fully processed or to be discarded. */
if( ( ret = mbedtls_ssl_fetch_input( ssl, mbedtls_ssl_hdr_len( ssl ) ) ) != 0 )
@@ -3977,7 +3982,7 @@
}
/* Get next record */
- goto read_record_header;
+ return( MBEDTLS_ERR_SSL_CONTINUE_PROCESSING );
}
#endif
return( ret );
@@ -3996,7 +4001,13 @@
/* Done reading this record, get ready for the next one */
#if defined(MBEDTLS_SSL_PROTO_DTLS)
if( ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM )
+ {
ssl->next_record_offset = ssl->in_msglen + mbedtls_ssl_hdr_len( ssl );
+ if( ssl->next_record_offset < ssl->in_left )
+ {
+ MBEDTLS_SSL_DEBUG_MSG( 3, ( "more than one record within datagram" ) );
+ }
+ }
else
#endif
ssl->in_left = 0;
@@ -4043,7 +4054,7 @@
ssl->in_left = 0;
MBEDTLS_SSL_DEBUG_MSG( 1, ( "discarding invalid record (mac)" ) );
- goto read_record_header;
+ return( MBEDTLS_ERR_SSL_CONTINUE_PROCESSING );
}
return( ret );
@@ -4064,46 +4075,6 @@
}
}
- /*
- * When we sent the last flight of the handshake, we MUST respond to a
- * retransmit of the peer's previous flight with a retransmit. (In
- * practice, only the Finished message will make it, other messages
- * including CCS use the old transform so they're dropped as invalid.)
- *
- * If the record we received is not a handshake message, however, it
- * means the peer received our last flight so we can clean up
- * handshake info.
- *
- * This check needs to be done before prepare_handshake() due to an edge
- * case: if the client immediately requests renegotiation, this
- * finishes the current handshake first, avoiding the new ClientHello
- * being mistaken for an ancient message in the current handshake.
- */
-#if defined(MBEDTLS_SSL_PROTO_DTLS)
- if( ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM &&
- ssl->handshake != NULL &&
- ssl->state == MBEDTLS_SSL_HANDSHAKE_OVER )
- {
- if( ssl->in_msgtype == MBEDTLS_SSL_MSG_HANDSHAKE &&
- ssl->in_msg[0] == MBEDTLS_SSL_HS_FINISHED )
- {
- MBEDTLS_SSL_DEBUG_MSG( 2, ( "received retransmit of last flight" ) );
-
- if( ( ret = mbedtls_ssl_resend( ssl ) ) != 0 )
- {
- MBEDTLS_SSL_DEBUG_RET( 1, "mbedtls_ssl_resend", ret );
- return( ret );
- }
-
- return( MBEDTLS_ERR_SSL_WANT_READ );
- }
- else
- {
- ssl_handshake_wrapup_free_hs_transform( ssl );
- }
- }
-#endif
-
return( 0 );
}
@@ -4148,7 +4119,7 @@
if( ssl->in_msg[0] == MBEDTLS_SSL_ALERT_LEVEL_WARNING &&
ssl->in_msg[1] == MBEDTLS_SSL_ALERT_MSG_NO_RENEGOTIATION )
{
- MBEDTLS_SSL_DEBUG_MSG( 2, ( "is a SSLv3 no_cert" ) );
+ MBEDTLS_SSL_DEBUG_MSG( 2, ( "is a SSLv3 no renegotiation alert" ) );
/* Will be handled when trying to parse ServerHello */
return( 0 );
}
@@ -4170,6 +4141,15 @@
return MBEDTLS_ERR_SSL_NON_FATAL;
}
+#if defined(MBEDTLS_SSL_PROTO_DTLS)
+ if( ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM &&
+ ssl->handshake != NULL &&
+ ssl->state == MBEDTLS_SSL_HANDSHAKE_OVER )
+ {
+ ssl_handshake_wrapup_free_hs_transform( ssl );
+ }
+#endif
+
return( 0 );
}
@@ -6506,6 +6486,61 @@
return( ssl->in_offt == NULL ? 0 : ssl->in_msglen );
}
+int mbedtls_ssl_check_pending( const mbedtls_ssl_context *ssl )
+{
+ /*
+ * Case A: We're currently holding back
+ * a message for further processing.
+ */
+
+ if( ssl->keep_current_message == 1 )
+ {
+ MBEDTLS_SSL_DEBUG_MSG( 3, ( "ssl_check_pending: record held back for processing" ) );
+ return( 1 );
+ }
+
+ /*
+ * Case B: Further records are pending in the current datagram.
+ */
+
+#if defined(MBEDTLS_SSL_PROTO_DTLS)
+ if( ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM &&
+ ssl->in_left > ssl->next_record_offset )
+ {
+ MBEDTLS_SSL_DEBUG_MSG( 3, ( "ssl_check_pending: more records within current datagram" ) );
+ return( 1 );
+ }
+#endif /* MBEDTLS_SSL_PROTO_DTLS */
+
+ /*
+ * Case C: A handshake message is being processed.
+ */
+
+ if( ssl->in_hslen > 0 && ssl->in_hslen < ssl->in_msglen )
+ {
+ MBEDTLS_SSL_DEBUG_MSG( 3, ( "ssl_check_pending: more handshake messages within current record" ) );
+ return( 1 );
+ }
+
+ /*
+ * Case D: An application data message is being processed
+ */
+ if( ssl->in_offt != NULL )
+ {
+ MBEDTLS_SSL_DEBUG_MSG( 3, ( "ssl_check_pending: application data record is being processed" ) );
+ return( 1 );
+ }
+
+ /*
+ * In all other cases, the rest of the message can be dropped.
+ * As in ssl_read_record_layer, this needs to be adapted if
+ * we implement support for multiple alerts in single records.
+ */
+
+ MBEDTLS_SSL_DEBUG_MSG( 3, ( "ssl_check_pending: nothing pending" ) );
+ return( 0 );
+}
+
uint32_t mbedtls_ssl_get_verify_result( const mbedtls_ssl_context *ssl )
{
if( ssl->session != NULL )
@@ -6914,25 +6949,16 @@
}
/*
- * TODO
- *
- * The logic should be streamlined here:
- *
- * Instead of
- *
+ * The logic could be streamlined here. Instead of
* - Manually checking whether ssl->in_offt is NULL
* - Fetching a new record if yes
* - Setting ssl->in_offt if one finds an application record
* - Resetting keep_current_message after handling the application data
- *
* one should
- *
* - Adapt read_record to set ssl->in_offt automatically
* when a new application data record is processed.
* - Always call mbedtls_ssl_read_record here.
- *
* This way, the logic of ssl_read would be much clearer:
- *
* (1) Always call record layer and see what kind of record is on
* and have it ready for consumption (in particular, in_offt
* properly set for application data records).
@@ -6942,13 +6968,12 @@
* (3) If it's something different from application data,
* handle it accordingly, e.g. potentially start a
* renegotiation.
- *
* This will also remove the need to manually reset
* ssl->keep_current_message = 0 below.
- *
*/
- if( ssl->in_offt == NULL )
+ /* Loop as long as no application data record is available */
+ while( ssl->in_offt == NULL )
{
/* Start timer if not already running */
if( ssl->f_get_timer != NULL &&
@@ -7002,7 +7027,9 @@
/* With DTLS, drop the packet (probably from last handshake) */
#if defined(MBEDTLS_SSL_PROTO_DTLS)
if( ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM )
- return( MBEDTLS_ERR_SSL_WANT_READ );
+ {
+ continue;
+ }
#endif
return( MBEDTLS_ERR_SSL_UNEXPECTED_MESSAGE );
}
@@ -7017,7 +7044,9 @@
/* With DTLS, drop the packet (probably from last handshake) */
#if defined(MBEDTLS_SSL_PROTO_DTLS)
if( ssl->conf->transport == MBEDTLS_SSL_TRANSPORT_DATAGRAM )
- return( MBEDTLS_ERR_SSL_WANT_READ );
+ {
+ continue;
+ }
#endif
return( MBEDTLS_ERR_SSL_UNEXPECTED_MESSAGE );
}
@@ -7090,7 +7119,25 @@
}
}
- return( MBEDTLS_ERR_SSL_WANT_READ );
+ /* At this point, we don't know whether the renegotiation has been
+ * completed or not. The cases to consider are the following:
+ * 1) The renegotiation is complete. In this case, no new record
+ * has been read yet.
+ * 2) The renegotiation is incomplete because the client received
+ * an application data record while awaiting the ServerHello.
+ * 3) The renegotiation is incomplete because the client received
+ * a non-handshake, non-application data message while awaiting
+ * the ServerHello.
+ * In each of these case, looping will be the proper action:
+ * - For 1), the next iteration will read a new record and check
+ * if it's application data.
+ * - For 2), the loop condition isn't satisfied as application data
+ * is present, hence continue is the same as break
+ * - For 3), the loop condition is satisfied and read_record
+ * will re-deliver the message that was held back by the client
+ * when expecting the ServerHello.
+ */
+ continue;
}
#if defined(MBEDTLS_SSL_RENEGOTIATION)
else if( ssl->renego_status == MBEDTLS_SSL_RENEGOTIATION_PENDING )
diff --git a/programs/ssl/ssl_client2.c b/programs/ssl/ssl_client2.c
index 8e2feb1..5d8969d 100644
--- a/programs/ssl/ssl_client2.c
+++ b/programs/ssl/ssl_client2.c
@@ -73,6 +73,7 @@
#define DFL_REQUEST_SIZE -1
#define DFL_DEBUG_LEVEL 0
#define DFL_NBIO 0
+#define DFL_EVENT 0
#define DFL_READ_TIMEOUT 0
#define DFL_MAX_RESEND 0
#define DFL_CA_FILE ""
@@ -245,24 +246,26 @@
" server_addr=%%s default: given by name\n" \
" server_port=%%d default: 4433\n" \
" request_page=%%s default: \".\"\n" \
- " request_size=%%d default: about 34 (basic request)\n" \
+ " request_size=%%d default: about 34 (basic request)\n" \
" (minimum: 0, max: " MAX_REQUEST_SIZE_STR " )\n" \
- " debug_level=%%d default: 0 (disabled)\n" \
- " nbio=%%d default: 0 (blocking I/O)\n" \
- " options: 1 (non-blocking), 2 (added delays)\n" \
- " read_timeout=%%d default: 0 ms (no timeout)\n" \
+ " debug_level=%%d default: 0 (disabled)\n" \
+ " nbio=%%d default: 0 (blocking I/O)\n" \
+ " options: 1 (non-blocking), 2 (added delays)\n" \
+ " event=%%d default: 0 (loop)\n" \
+ " options: 1 (level-triggered, implies nbio=1),\n" \
+ " read_timeout=%%d default: 0 ms (no timeout)\n" \
" max_resend=%%d default: 0 (no resend on timeout)\n" \
"\n" \
USAGE_DTLS \
"\n" \
- " auth_mode=%%s default: (library default: none)\n" \
+ " auth_mode=%%s default: (library default: none)\n" \
" options: none, optional, required\n" \
USAGE_IO \
"\n" \
USAGE_PSK \
USAGE_ECJPAKE \
"\n" \
- " allow_legacy=%%d default: (library default: no)\n" \
+ " allow_legacy=%%d default: (library default: no)\n" \
USAGE_RENEGO \
" exchanges=%%d default: 1\n" \
" reconnect=%%d default: 0 (disabled)\n" \
@@ -302,7 +305,8 @@
const char *server_port; /* port on which the ssl service runs */
int debug_level; /* level of debugging */
int nbio; /* should I/O be blocking? */
- uint32_t read_timeout; /* timeout on mbedtls_ssl_read() in milliseconds */
+ int event; /* loop or event-driven IO? level or edge triggered? */
+ uint32_t read_timeout; /* timeout on mbedtls_ssl_read() in milliseconds */
int max_resend; /* DTLS times to resend on read timeout */
const char *request_page; /* page on server to request */
int request_size; /* pad request with header to requested size */
@@ -353,7 +357,8 @@
if( *p == '/' || *p == '\\' )
basename = p + 1;
- mbedtls_fprintf( (FILE *) ctx, "%s:%04d: |%d| %s", basename, line, level, str );
+ mbedtls_fprintf( (FILE *) ctx, "%s:%04d: |%d| %s",
+ basename, line, level, str );
fflush( (FILE *) ctx );
}
@@ -399,7 +404,8 @@
/*
* Enabled if debug_level > 1 in code below
*/
-static int my_verify( void *data, mbedtls_x509_crt *crt, int depth, uint32_t *flags )
+static int my_verify( void *data, mbedtls_x509_crt *crt,
+ int depth, uint32_t *flags )
{
char buf[1024];
((void) data);
@@ -436,6 +442,57 @@
};
#endif /* MBEDTLS_X509_CRT_PARSE_C */
+/*
+ * Wait for an event from the underlying transport or the timer
+ * (Used in event-driven IO mode).
+ */
+#if !defined(MBEDTLS_TIMING_C)
+int idle( mbedtls_net_context *fd,
+ int idle_reason )
+#else
+int idle( mbedtls_net_context *fd,
+ mbedtls_timing_delay_context *timer,
+ int idle_reason )
+#endif
+{
+
+ int ret;
+ int poll_type = 0;
+
+ if( idle_reason == MBEDTLS_ERR_SSL_WANT_WRITE )
+ poll_type = MBEDTLS_NET_POLL_WRITE;
+ else if( idle_reason == MBEDTLS_ERR_SSL_WANT_READ )
+ poll_type = MBEDTLS_NET_POLL_READ;
+#if !defined(MBEDTLS_TIMING_C)
+ else
+ return( 0 );
+#endif
+
+ while( 1 )
+ {
+ /* Check if timer has expired */
+#if defined(MBEDTLS_TIMING_C)
+ if( timer != NULL &&
+ mbedtls_timing_get_delay( timer ) == 2 )
+ {
+ break;
+ }
+#endif /* MBEDTLS_TIMING_C */
+
+ /* Check if underlying transport became available */
+ if( poll_type != 0 )
+ {
+ ret = mbedtls_net_poll( fd, poll_type, 0 );
+ if( ret < 0 )
+ return( ret );
+ if( ret == poll_type )
+ break;
+ }
+ }
+
+ return( 0 );
+}
+
int main( int argc, char *argv[] )
{
int ret = 0, len, tail_len, i, written, frags, retry_left;
@@ -521,6 +578,7 @@
opt.server_port = DFL_SERVER_PORT;
opt.debug_level = DFL_DEBUG_LEVEL;
opt.nbio = DFL_NBIO;
+ opt.event = DFL_EVENT;
opt.read_timeout = DFL_READ_TIMEOUT;
opt.max_resend = DFL_MAX_RESEND;
opt.request_page = DFL_REQUEST_PAGE;
@@ -594,6 +652,12 @@
if( opt.nbio < 0 || opt.nbio > 2 )
goto usage;
}
+ else if( strcmp( p, "event" ) == 0 )
+ {
+ opt.event = atoi( q );
+ if( opt.event < 0 || opt.event > 2 )
+ goto usage;
+ }
else if( strcmp( p, "read_timeout" ) == 0 )
opt.read_timeout = atoi( q );
else if( strcmp( p, "max_resend" ) == 0 )
@@ -638,16 +702,23 @@
}
else if( strcmp( p, "renegotiation" ) == 0 )
{
- opt.renegotiation = (atoi( q )) ? MBEDTLS_SSL_RENEGOTIATION_ENABLED :
- MBEDTLS_SSL_RENEGOTIATION_DISABLED;
+ opt.renegotiation = (atoi( q )) ?
+ MBEDTLS_SSL_RENEGOTIATION_ENABLED :
+ MBEDTLS_SSL_RENEGOTIATION_DISABLED;
}
else if( strcmp( p, "allow_legacy" ) == 0 )
{
switch( atoi( q ) )
{
- case -1: opt.allow_legacy = MBEDTLS_SSL_LEGACY_BREAK_HANDSHAKE; break;
- case 0: opt.allow_legacy = MBEDTLS_SSL_LEGACY_NO_RENEGOTIATION; break;
- case 1: opt.allow_legacy = MBEDTLS_SSL_LEGACY_ALLOW_RENEGOTIATION; break;
+ case -1:
+ opt.allow_legacy = MBEDTLS_SSL_LEGACY_BREAK_HANDSHAKE;
+ break;
+ case 0:
+ opt.allow_legacy = MBEDTLS_SSL_LEGACY_NO_RENEGOTIATION;
+ break;
+ case 1:
+ opt.allow_legacy = MBEDTLS_SSL_LEGACY_ALLOW_RENEGOTIATION;
+ break;
default: goto usage;
}
}
@@ -704,8 +775,12 @@
{
switch( atoi( q ) )
{
- case 0: opt.extended_ms = MBEDTLS_SSL_EXTENDED_MS_DISABLED; break;
- case 1: opt.extended_ms = MBEDTLS_SSL_EXTENDED_MS_ENABLED; break;
+ case 0:
+ opt.extended_ms = MBEDTLS_SSL_EXTENDED_MS_DISABLED;
+ break;
+ case 1:
+ opt.extended_ms = MBEDTLS_SSL_EXTENDED_MS_ENABLED;
+ break;
default: goto usage;
}
}
@@ -864,6 +939,15 @@
goto usage;
}
+ /* Event-driven IO is incompatible with the above custom
+ * receive and send functions, as the polling builds on
+ * refers to the underlying net_context. */
+ if( opt.event == 1 && opt.nbio != 1 )
+ {
+ mbedtls_printf( "Warning: event-driven IO mandates nbio=1 - overwrite\n" );
+ opt.nbio = 1;
+ }
+
#if defined(MBEDTLS_DEBUG_C)
mbedtls_debug_set_threshold( opt.debug_level );
#endif
@@ -871,19 +955,20 @@
if( opt.force_ciphersuite[0] > 0 )
{
const mbedtls_ssl_ciphersuite_t *ciphersuite_info;
- ciphersuite_info = mbedtls_ssl_ciphersuite_from_id( opt.force_ciphersuite[0] );
+ ciphersuite_info =
+ mbedtls_ssl_ciphersuite_from_id( opt.force_ciphersuite[0] );
if( opt.max_version != -1 &&
ciphersuite_info->min_minor_ver > opt.max_version )
{
- mbedtls_printf("forced ciphersuite not allowed with this protocol version\n");
+ mbedtls_printf( "forced ciphersuite not allowed with this protocol version\n" );
ret = 2;
goto usage;
}
if( opt.min_version != -1 &&
ciphersuite_info->max_minor_ver < opt.min_version )
{
- mbedtls_printf("forced ciphersuite not allowed with this protocol version\n");
+ mbedtls_printf( "forced ciphersuite not allowed with this protocol version\n" );
ret = 2;
goto usage;
}
@@ -909,7 +994,7 @@
{
if( opt.arc4 == MBEDTLS_SSL_ARC4_DISABLED )
{
- mbedtls_printf("forced RC4 ciphersuite with RC4 disabled\n");
+ mbedtls_printf( "forced RC4 ciphersuite with RC4 disabled\n" );
ret = 2;
goto usage;
}
@@ -929,7 +1014,7 @@
if( strlen( opt.psk ) % 2 != 0 )
{
- mbedtls_printf("pre-shared key not valid hex\n");
+ mbedtls_printf( "pre-shared key not valid hex\n" );
goto exit;
}
@@ -946,7 +1031,7 @@
c -= 'A' - 10;
else
{
- mbedtls_printf("pre-shared key not valid hex\n");
+ mbedtls_printf( "pre-shared key not valid hex\n" );
goto exit;
}
psk[ j / 2 ] = c << 4;
@@ -960,7 +1045,7 @@
c -= 'A' - 10;
else
{
- mbedtls_printf("pre-shared key not valid hex\n");
+ mbedtls_printf( "pre-shared key not valid hex\n" );
goto exit;
}
psk[ j / 2 ] |= c;
@@ -1051,11 +1136,12 @@
fflush( stdout );
mbedtls_entropy_init( &entropy );
- if( ( ret = mbedtls_ctr_drbg_seed( &ctr_drbg, mbedtls_entropy_func, &entropy,
- (const unsigned char *) pers,
- strlen( pers ) ) ) != 0 )
+ if( ( ret = mbedtls_ctr_drbg_seed( &ctr_drbg, mbedtls_entropy_func,
+ &entropy, (const unsigned char *) pers,
+ strlen( pers ) ) ) != 0 )
{
- mbedtls_printf( " failed\n ! mbedtls_ctr_drbg_seed returned -0x%x\n", -ret );
+ mbedtls_printf( " failed\n ! mbedtls_ctr_drbg_seed returned -0x%x\n",
+ -ret );
goto exit;
}
@@ -1093,12 +1179,13 @@
#else
{
ret = 1;
- mbedtls_printf("MBEDTLS_CERTS_C not defined.");
+ mbedtls_printf( "MBEDTLS_CERTS_C not defined." );
}
#endif
if( ret < 0 )
{
- mbedtls_printf( " failed\n ! mbedtls_x509_crt_parse returned -0x%x\n\n", -ret );
+ mbedtls_printf( " failed\n ! mbedtls_x509_crt_parse returned -0x%x\n\n",
+ -ret );
goto exit;
}
@@ -1121,7 +1208,8 @@
else
#endif
#if defined(MBEDTLS_CERTS_C)
- ret = mbedtls_x509_crt_parse( &clicert, (const unsigned char *) mbedtls_test_cli_crt,
+ ret = mbedtls_x509_crt_parse( &clicert,
+ (const unsigned char *) mbedtls_test_cli_crt,
mbedtls_test_cli_crt_len );
#else
{
@@ -1131,7 +1219,8 @@
#endif
if( ret != 0 )
{
- mbedtls_printf( " failed\n ! mbedtls_x509_crt_parse returned -0x%x\n\n", -ret );
+ mbedtls_printf( " failed\n ! mbedtls_x509_crt_parse returned -0x%x\n\n",
+ -ret );
goto exit;
}
@@ -1144,7 +1233,8 @@
else
#endif
#if defined(MBEDTLS_CERTS_C)
- ret = mbedtls_pk_parse_key( &pkey, (const unsigned char *) mbedtls_test_cli_key,
+ ret = mbedtls_pk_parse_key( &pkey,
+ (const unsigned char *) mbedtls_test_cli_key,
mbedtls_test_cli_key_len, NULL, 0 );
#else
{
@@ -1154,7 +1244,8 @@
#endif
if( ret != 0 )
{
- mbedtls_printf( " failed\n ! mbedtls_pk_parse_key returned -0x%x\n\n", -ret );
+ mbedtls_printf( " failed\n ! mbedtls_pk_parse_key returned -0x%x\n\n",
+ -ret );
goto exit;
}
@@ -1172,11 +1263,13 @@
opt.server_addr, opt.server_port );
fflush( stdout );
- if( ( ret = mbedtls_net_connect( &server_fd, opt.server_addr, opt.server_port,
- opt.transport == MBEDTLS_SSL_TRANSPORT_STREAM ?
- MBEDTLS_NET_PROTO_TCP : MBEDTLS_NET_PROTO_UDP ) ) != 0 )
+ if( ( ret = mbedtls_net_connect( &server_fd,
+ opt.server_addr, opt.server_port,
+ opt.transport == MBEDTLS_SSL_TRANSPORT_STREAM ?
+ MBEDTLS_NET_PROTO_TCP : MBEDTLS_NET_PROTO_UDP ) ) != 0 )
{
- mbedtls_printf( " failed\n ! mbedtls_net_connect returned -0x%x\n\n", -ret );
+ mbedtls_printf( " failed\n ! mbedtls_net_connect returned -0x%x\n\n",
+ -ret );
goto exit;
}
@@ -1186,7 +1279,8 @@
ret = mbedtls_net_set_block( &server_fd );
if( ret != 0 )
{
- mbedtls_printf( " failed\n ! net_set_(non)block() returned -0x%x\n\n", -ret );
+ mbedtls_printf( " failed\n ! net_set_(non)block() returned -0x%x\n\n",
+ -ret );
goto exit;
}
@@ -1203,7 +1297,8 @@
opt.transport,
MBEDTLS_SSL_PRESET_DEFAULT ) ) != 0 )
{
- mbedtls_printf( " failed\n ! mbedtls_ssl_config_defaults returned -0x%x\n\n", -ret );
+ mbedtls_printf( " failed\n ! mbedtls_ssl_config_defaults returned -0x%x\n\n",
+ -ret );
goto exit;
}
@@ -1226,13 +1321,15 @@
#if defined(MBEDTLS_SSL_PROTO_DTLS)
if( opt.hs_to_min != DFL_HS_TO_MIN || opt.hs_to_max != DFL_HS_TO_MAX )
- mbedtls_ssl_conf_handshake_timeout( &conf, opt.hs_to_min, opt.hs_to_max );
+ mbedtls_ssl_conf_handshake_timeout( &conf, opt.hs_to_min,
+ opt.hs_to_max );
#endif /* MBEDTLS_SSL_PROTO_DTLS */
#if defined(MBEDTLS_SSL_MAX_FRAGMENT_LENGTH)
if( ( ret = mbedtls_ssl_conf_max_frag_len( &conf, opt.mfl_code ) ) != 0 )
{
- mbedtls_printf( " failed\n ! mbedtls_ssl_conf_max_frag_len returned %d\n\n", ret );
+ mbedtls_printf( " failed\n ! mbedtls_ssl_conf_max_frag_len returned %d\n\n",
+ ret );
goto exit;
}
#endif
@@ -1255,8 +1352,8 @@
#if defined(MBEDTLS_SSL_CBC_RECORD_SPLITTING)
if( opt.recsplit != DFL_RECSPLIT )
mbedtls_ssl_conf_cbc_record_splitting( &conf, opt.recsplit
- ? MBEDTLS_SSL_CBC_RECORD_SPLITTING_ENABLED
- : MBEDTLS_SSL_CBC_RECORD_SPLITTING_DISABLED );
+ ? MBEDTLS_SSL_CBC_RECORD_SPLITTING_ENABLED
+ : MBEDTLS_SSL_CBC_RECORD_SPLITTING_DISABLED );
#endif
#if defined(MBEDTLS_DHM_C)
@@ -1268,7 +1365,8 @@
if( opt.alpn_string != NULL )
if( ( ret = mbedtls_ssl_conf_alpn_protocols( &conf, alpn_list ) ) != 0 )
{
- mbedtls_printf( " failed\n ! mbedtls_ssl_conf_alpn_protocols returned %d\n\n", ret );
+ mbedtls_printf( " failed\n ! mbedtls_ssl_conf_alpn_protocols returned %d\n\n",
+ ret );
goto exit;
}
#endif
@@ -1307,7 +1405,8 @@
{
if( ( ret = mbedtls_ssl_conf_own_cert( &conf, &clicert, &pkey ) ) != 0 )
{
- mbedtls_printf( " failed\n ! mbedtls_ssl_conf_own_cert returned %d\n\n", ret );
+ mbedtls_printf( " failed\n ! mbedtls_ssl_conf_own_cert returned %d\n\n",
+ ret );
goto exit;
}
}
@@ -1326,16 +1425,19 @@
(const unsigned char *) opt.psk_identity,
strlen( opt.psk_identity ) ) ) != 0 )
{
- mbedtls_printf( " failed\n ! mbedtls_ssl_conf_psk returned %d\n\n", ret );
+ mbedtls_printf( " failed\n ! mbedtls_ssl_conf_psk returned %d\n\n",
+ ret );
goto exit;
}
#endif
if( opt.min_version != DFL_MIN_VERSION )
- mbedtls_ssl_conf_min_version( &conf, MBEDTLS_SSL_MAJOR_VERSION_3, opt.min_version );
+ mbedtls_ssl_conf_min_version( &conf, MBEDTLS_SSL_MAJOR_VERSION_3,
+ opt.min_version );
if( opt.max_version != DFL_MAX_VERSION )
- mbedtls_ssl_conf_max_version( &conf, MBEDTLS_SSL_MAJOR_VERSION_3, opt.max_version );
+ mbedtls_ssl_conf_max_version( &conf, MBEDTLS_SSL_MAJOR_VERSION_3,
+ opt.max_version );
#if defined(MBEDTLS_SSL_FALLBACK_SCSV)
if( opt.fallback != DFL_FALLBACK )
@@ -1344,14 +1446,16 @@
if( ( ret = mbedtls_ssl_setup( &ssl, &conf ) ) != 0 )
{
- mbedtls_printf( " failed\n ! mbedtls_ssl_setup returned -0x%x\n\n", -ret );
+ mbedtls_printf( " failed\n ! mbedtls_ssl_setup returned -0x%x\n\n",
+ -ret );
goto exit;
}
#if defined(MBEDTLS_X509_CRT_PARSE_C)
if( ( ret = mbedtls_ssl_set_hostname( &ssl, opt.server_name ) ) != 0 )
{
- mbedtls_printf( " failed\n ! mbedtls_ssl_set_hostname returned %d\n\n", ret );
+ mbedtls_printf( " failed\n ! mbedtls_ssl_set_hostname returned %d\n\n",
+ ret );
goto exit;
}
#endif
@@ -1363,7 +1467,8 @@
(const unsigned char *) opt.ecjpake_pw,
strlen( opt.ecjpake_pw ) ) ) != 0 )
{
- mbedtls_printf( " failed\n ! mbedtls_ssl_set_hs_ecjpake_password returned %d\n\n", ret );
+ mbedtls_printf( " failed\n ! mbedtls_ssl_set_hs_ecjpake_password returned %d\n\n",
+ ret );
goto exit;
}
}
@@ -1372,7 +1477,8 @@
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,
+ mbedtls_ssl_set_bio( &ssl, &server_fd,
+ mbedtls_net_send, mbedtls_net_recv,
opt.nbio == 0 ? mbedtls_net_recv_timeout : NULL );
#if defined(MBEDTLS_TIMING_C)
@@ -1390,9 +1496,11 @@
while( ( ret = mbedtls_ssl_handshake( &ssl ) ) != 0 )
{
- if( ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE )
+ if( ret != MBEDTLS_ERR_SSL_WANT_READ &&
+ ret != MBEDTLS_ERR_SSL_WANT_WRITE )
{
- mbedtls_printf( " failed\n ! mbedtls_ssl_handshake returned -0x%x\n", -ret );
+ mbedtls_printf( " failed\n ! mbedtls_ssl_handshake returned -0x%x\n",
+ -ret );
if( ret == MBEDTLS_ERR_X509_CERT_VERIFY_FAILED )
mbedtls_printf(
" Unable to verify the server's certificate. "
@@ -1404,10 +1512,23 @@
mbedtls_printf( "\n" );
goto exit;
}
+
+ /* For event-driven IO, wait for socket to become available */
+ if( opt.event == 1 /* level triggered IO */ )
+ {
+#if defined(MBEDTLS_TIMING_C)
+ ret = idle( &server_fd, &timer, ret );
+#else
+ ret = idle( &server_fd, ret );
+#endif
+ if( ret != 0 )
+ goto exit;
+ }
}
mbedtls_printf( " ok\n [ Protocol is %s ]\n [ Ciphersuite is %s ]\n",
- mbedtls_ssl_get_version( &ssl ), mbedtls_ssl_get_ciphersuite( &ssl ) );
+ mbedtls_ssl_get_version( &ssl ),
+ mbedtls_ssl_get_ciphersuite( &ssl ) );
if( ( ret = mbedtls_ssl_get_record_expansion( &ssl ) ) >= 0 )
mbedtls_printf( " [ Record expansion is %d ]\n", ret );
@@ -1435,7 +1556,8 @@
if( ( ret = mbedtls_ssl_get_session( &ssl, &saved_session ) ) != 0 )
{
- mbedtls_printf( " failed\n ! mbedtls_ssl_get_session returned -0x%x\n\n", -ret );
+ mbedtls_printf( " failed\n ! mbedtls_ssl_get_session returned -0x%x\n\n",
+ -ret );
goto exit;
}
@@ -1454,7 +1576,8 @@
mbedtls_printf( " failed\n" );
- mbedtls_x509_crt_verify_info( vrfy_buf, sizeof( vrfy_buf ), " ! ", flags );
+ mbedtls_x509_crt_verify_info( vrfy_buf, sizeof( vrfy_buf ),
+ " ! ", flags );
mbedtls_printf( "%s\n", vrfy_buf );
}
@@ -1484,9 +1607,21 @@
if( ret != MBEDTLS_ERR_SSL_WANT_READ &&
ret != MBEDTLS_ERR_SSL_WANT_WRITE )
{
- mbedtls_printf( " failed\n ! mbedtls_ssl_renegotiate returned %d\n\n", ret );
+ mbedtls_printf( " failed\n ! mbedtls_ssl_renegotiate returned %d\n\n",
+ ret );
goto exit;
}
+
+ /* For event-driven IO, wait for socket to become available */
+ if( opt.event == 1 /* level triggered IO */ )
+ {
+#if defined(MBEDTLS_TIMING_C)
+ idle( &server_fd, &timer, ret );
+#else
+ idle( &server_fd, ret );
+#endif
+ }
+
}
mbedtls_printf( " ok\n" );
}
@@ -1530,27 +1665,54 @@
{
for( written = 0, frags = 0; written < len; written += ret, frags++ )
{
- while( ( ret = mbedtls_ssl_write( &ssl, buf + written, len - written ) )
- <= 0 )
+ while( ( ret = mbedtls_ssl_write( &ssl, buf + written,
+ len - written ) ) <= 0 )
{
if( ret != MBEDTLS_ERR_SSL_WANT_READ &&
ret != MBEDTLS_ERR_SSL_WANT_WRITE )
{
- mbedtls_printf( " failed\n ! mbedtls_ssl_write returned -0x%x\n\n", -ret );
+ mbedtls_printf( " failed\n ! mbedtls_ssl_write returned -0x%x\n\n",
+ -ret );
goto exit;
}
+
+ /* For event-driven IO, wait for socket to become available */
+ if( opt.event == 1 /* level triggered IO */ )
+ {
+#if defined(MBEDTLS_TIMING_C)
+ idle( &server_fd, &timer, ret );
+#else
+ idle( &server_fd, ret );
+#endif
+ }
}
}
}
else /* Not stream, so datagram */
{
- do ret = mbedtls_ssl_write( &ssl, buf, len );
- while( ret == MBEDTLS_ERR_SSL_WANT_READ ||
- ret == MBEDTLS_ERR_SSL_WANT_WRITE );
+ while( 1 )
+ {
+ ret = mbedtls_ssl_write( &ssl, buf, len );
+
+ if( ret != MBEDTLS_ERR_SSL_WANT_READ &&
+ ret != MBEDTLS_ERR_SSL_WANT_WRITE )
+ break;
+
+ /* For event-driven IO, wait for socket to become available */
+ if( opt.event == 1 /* level triggered IO */ )
+ {
+#if defined(MBEDTLS_TIMING_C)
+ idle( &server_fd, &timer, ret );
+#else
+ idle( &server_fd, ret );
+#endif
+ }
+ }
if( ret < 0 )
{
- mbedtls_printf( " failed\n ! mbedtls_ssl_write returned %d\n\n", ret );
+ mbedtls_printf( " failed\n ! mbedtls_ssl_write returned %d\n\n",
+ ret );
goto exit;
}
@@ -1565,7 +1727,8 @@
}
buf[written] = '\0';
- mbedtls_printf( " %d bytes written in %d fragments\n\n%s\n", written, frags, (char *) buf );
+ mbedtls_printf( " %d bytes written in %d fragments\n\n%s\n",
+ written, frags, (char *) buf );
/*
* 7. Read the HTTP response
@@ -1586,7 +1749,18 @@
if( ret == MBEDTLS_ERR_SSL_WANT_READ ||
ret == MBEDTLS_ERR_SSL_WANT_WRITE )
+ {
+ /* For event-driven IO, wait for socket to become available */
+ if( opt.event == 1 /* level triggered IO */ )
+ {
+#if defined(MBEDTLS_TIMING_C)
+ idle( &server_fd, &timer, ret );
+#else
+ idle( &server_fd, ret );
+#endif
+ }
continue;
+ }
if( ret <= 0 )
{
@@ -1604,7 +1778,8 @@
goto reconnect;
default:
- mbedtls_printf( " mbedtls_ssl_read returned -0x%x\n", -ret );
+ mbedtls_printf( " mbedtls_ssl_read returned -0x%x\n",
+ -ret );
goto exit;
}
}
@@ -1628,9 +1803,24 @@
len = sizeof( buf ) - 1;
memset( buf, 0, sizeof( buf ) );
- do ret = mbedtls_ssl_read( &ssl, buf, len );
- while( ret == MBEDTLS_ERR_SSL_WANT_READ ||
- ret == MBEDTLS_ERR_SSL_WANT_WRITE );
+ while( 1 )
+ {
+ ret = mbedtls_ssl_read( &ssl, buf, len );
+
+ if( ret != MBEDTLS_ERR_SSL_WANT_READ &&
+ ret != MBEDTLS_ERR_SSL_WANT_WRITE )
+ break;
+
+ /* For event-driven IO, wait for socket to become available */
+ if( opt.event == 1 /* level triggered IO */ )
+ {
+#if defined(MBEDTLS_TIMING_C)
+ idle( &server_fd, &timer, ret );
+#else
+ idle( &server_fd, ret );
+#endif
+ }
+ }
if( ret <= 0 )
{
@@ -1671,7 +1861,8 @@
if( ( ret = mbedtls_ssl_session_reset( &ssl ) ) != 0 )
{
- mbedtls_printf( " failed\n ! mbedtls_ssl_session_reset returned -0x%x\n\n", -ret );
+ mbedtls_printf( " failed\n ! mbedtls_ssl_session_reset returned -0x%x\n\n",
+ -ret );
goto exit;
}
@@ -1680,9 +1871,20 @@
if( ret != MBEDTLS_ERR_SSL_WANT_READ &&
ret != MBEDTLS_ERR_SSL_WANT_WRITE )
{
- mbedtls_printf( " failed\n ! mbedtls_ssl_handshake returned -0x%x\n\n", -ret );
+ mbedtls_printf( " failed\n ! mbedtls_ssl_handshake returned -0x%x\n\n",
+ -ret );
goto exit;
}
+
+ /* For event-driven IO, wait for socket to become available */
+ if( opt.event == 1 /* level triggered IO */ )
+ {
+#if defined(MBEDTLS_TIMING_C)
+ idle( &server_fd, &timer, ret );
+#else
+ idle( &server_fd, ret );
+#endif
+ }
}
mbedtls_printf( " ok\n" );
@@ -1729,21 +1931,25 @@
if( ( ret = mbedtls_ssl_session_reset( &ssl ) ) != 0 )
{
- mbedtls_printf( " failed\n ! mbedtls_ssl_session_reset returned -0x%x\n\n", -ret );
+ mbedtls_printf( " failed\n ! mbedtls_ssl_session_reset returned -0x%x\n\n",
+ -ret );
goto exit;
}
if( ( ret = mbedtls_ssl_set_session( &ssl, &saved_session ) ) != 0 )
{
- mbedtls_printf( " failed\n ! mbedtls_ssl_conf_session returned %d\n\n", ret );
+ mbedtls_printf( " failed\n ! mbedtls_ssl_conf_session returned %d\n\n",
+ ret );
goto exit;
}
- if( ( ret = mbedtls_net_connect( &server_fd, opt.server_addr, opt.server_port,
- opt.transport == MBEDTLS_SSL_TRANSPORT_STREAM ?
- MBEDTLS_NET_PROTO_TCP : MBEDTLS_NET_PROTO_UDP ) ) != 0 )
+ if( ( ret = mbedtls_net_connect( &server_fd,
+ opt.server_addr, opt.server_port,
+ opt.transport == MBEDTLS_SSL_TRANSPORT_STREAM ?
+ MBEDTLS_NET_PROTO_TCP : MBEDTLS_NET_PROTO_UDP ) ) != 0 )
{
- mbedtls_printf( " failed\n ! mbedtls_net_connect returned -0x%x\n\n", -ret );
+ mbedtls_printf( " failed\n ! mbedtls_net_connect returned -0x%x\n\n",
+ -ret );
goto exit;
}
@@ -1754,7 +1960,7 @@
if( ret != 0 )
{
mbedtls_printf( " failed\n ! net_set_(non)block() returned -0x%x\n\n",
- -ret );
+ -ret );
goto exit;
}
@@ -1763,7 +1969,8 @@
if( ret != MBEDTLS_ERR_SSL_WANT_READ &&
ret != MBEDTLS_ERR_SSL_WANT_WRITE )
{
- mbedtls_printf( " failed\n ! mbedtls_ssl_handshake returned -0x%x\n\n", -ret );
+ mbedtls_printf( " failed\n ! mbedtls_ssl_handshake returned -0x%x\n\n",
+ -ret );
goto exit;
}
}
diff --git a/programs/ssl/ssl_server2.c b/programs/ssl/ssl_server2.c
index cc29b49..6bfb210 100644
--- a/programs/ssl/ssl_server2.c
+++ b/programs/ssl/ssl_server2.c
@@ -101,6 +101,7 @@
#define DFL_SERVER_PORT "4433"
#define DFL_DEBUG_LEVEL 0
#define DFL_NBIO 0
+#define DFL_EVENT 0
#define DFL_READ_TIMEOUT 0
#define DFL_CA_FILE ""
#define DFL_CA_PATH ""
@@ -331,6 +332,8 @@
" debug_level=%%d default: 0 (disabled)\n" \
" nbio=%%d default: 0 (blocking I/O)\n" \
" options: 1 (non-blocking), 2 (added delays)\n" \
+ " event=%%d default: 0 (loop)\n" \
+ " options: 1 (level-triggered, implies nbio=1),\n" \
" read_timeout=%%d default: 0 ms (no timeout)\n" \
"\n" \
USAGE_DTLS \
@@ -399,6 +402,7 @@
const char *server_port; /* port on which the ssl service runs */
int debug_level; /* level of debugging */
int nbio; /* should I/O be blocking? */
+ int event; /* loop or event-driven IO? level or edge triggered? */
uint32_t read_timeout; /* timeout on mbedtls_ssl_read() in milliseconds */
const char *ca_file; /* the file with the CA certificate(s) */
const char *ca_path; /* the path with the CA certificate(s) reside */
@@ -837,6 +841,56 @@
};
#endif /* MBEDTLS_X509_CRT_PARSE_C */
+/*
+ * Wait for an event from the underlying transport or the timer
+ * (Used in event-driven IO mode).
+ */
+#if !defined(MBEDTLS_TIMING_C)
+int idle( mbedtls_net_context *fd,
+ int idle_reason )
+#else
+int idle( mbedtls_net_context *fd,
+ mbedtls_timing_delay_context *timer,
+ int idle_reason )
+#endif
+{
+ int ret;
+ int poll_type = 0;
+
+ if( idle_reason == MBEDTLS_ERR_SSL_WANT_WRITE )
+ poll_type = MBEDTLS_NET_POLL_WRITE;
+ else if( idle_reason == MBEDTLS_ERR_SSL_WANT_READ )
+ poll_type = MBEDTLS_NET_POLL_READ;
+#if !defined(MBEDTLS_TIMING_C)
+ else
+ return( 0 );
+#endif
+
+ while( 1 )
+ {
+ /* Check if timer has expired */
+#if defined(MBEDTLS_TIMING_C)
+ if( timer != NULL &&
+ mbedtls_timing_get_delay( timer ) == 2 )
+ {
+ break;
+ }
+#endif /* MBEDTLS_TIMING_C */
+
+ /* Check if underlying transport became available */
+ if( poll_type != 0 )
+ {
+ ret = mbedtls_net_poll( fd, poll_type, 0 );
+ if( ret < 0 )
+ return( ret );
+ if( ret == poll_type )
+ break;
+ }
+ }
+
+ return( 0 );
+}
+
int main( int argc, char *argv[] )
{
int ret = 0, len, written, frags, exchanges_left;
@@ -969,6 +1023,7 @@
opt.server_addr = DFL_SERVER_ADDR;
opt.server_port = DFL_SERVER_PORT;
opt.debug_level = DFL_DEBUG_LEVEL;
+ opt.event = DFL_EVENT;
opt.nbio = DFL_NBIO;
opt.read_timeout = DFL_READ_TIMEOUT;
opt.ca_file = DFL_CA_FILE;
@@ -1047,6 +1102,12 @@
if( opt.nbio < 0 || opt.nbio > 2 )
goto usage;
}
+ else if( strcmp( p, "event" ) == 0 )
+ {
+ opt.event = atoi( q );
+ if( opt.event < 0 || opt.event > 2 )
+ goto usage;
+ }
else if( strcmp( p, "read_timeout" ) == 0 )
opt.read_timeout = atoi( q );
else if( strcmp( p, "ca_file" ) == 0 )
@@ -1088,16 +1149,23 @@
opt.version_suites = q;
else if( strcmp( p, "renegotiation" ) == 0 )
{
- opt.renegotiation = (atoi( q )) ? MBEDTLS_SSL_RENEGOTIATION_ENABLED :
- MBEDTLS_SSL_RENEGOTIATION_DISABLED;
+ opt.renegotiation = (atoi( q )) ?
+ MBEDTLS_SSL_RENEGOTIATION_ENABLED :
+ MBEDTLS_SSL_RENEGOTIATION_DISABLED;
}
else if( strcmp( p, "allow_legacy" ) == 0 )
{
switch( atoi( q ) )
{
- case -1: opt.allow_legacy = MBEDTLS_SSL_LEGACY_BREAK_HANDSHAKE; break;
- case 0: opt.allow_legacy = MBEDTLS_SSL_LEGACY_NO_RENEGOTIATION; break;
- case 1: opt.allow_legacy = MBEDTLS_SSL_LEGACY_ALLOW_RENEGOTIATION; break;
+ case -1:
+ opt.allow_legacy = MBEDTLS_SSL_LEGACY_BREAK_HANDSHAKE;
+ break;
+ case 0:
+ opt.allow_legacy = MBEDTLS_SSL_LEGACY_NO_RENEGOTIATION;
+ break;
+ case 1:
+ opt.allow_legacy = MBEDTLS_SSL_LEGACY_ALLOW_RENEGOTIATION;
+ break;
default: goto usage;
}
}
@@ -1254,8 +1322,12 @@
{
switch( atoi( q ) )
{
- case 0: opt.extended_ms = MBEDTLS_SSL_EXTENDED_MS_DISABLED; break;
- case 1: opt.extended_ms = MBEDTLS_SSL_EXTENDED_MS_ENABLED; break;
+ case 0:
+ opt.extended_ms = MBEDTLS_SSL_EXTENDED_MS_DISABLED;
+ break;
+ case 1:
+ opt.extended_ms = MBEDTLS_SSL_EXTENDED_MS_ENABLED;
+ break;
default: goto usage;
}
}
@@ -1328,6 +1400,15 @@
goto usage;
}
+ /* Event-driven IO is incompatible with the above custom
+ * receive and send functions, as the polling builds on
+ * refers to the underlying net_context. */
+ if( opt.event == 1 && opt.nbio != 1 )
+ {
+ mbedtls_printf( "Warning: event-driven IO mandates nbio=1 - overwrite\n" );
+ opt.nbio = 1;
+ }
+
#if defined(MBEDTLS_DEBUG_C)
mbedtls_debug_set_threshold( opt.debug_level );
#endif
@@ -1335,19 +1416,20 @@
if( opt.force_ciphersuite[0] > 0 )
{
const mbedtls_ssl_ciphersuite_t *ciphersuite_info;
- ciphersuite_info = mbedtls_ssl_ciphersuite_from_id( opt.force_ciphersuite[0] );
+ ciphersuite_info =
+ mbedtls_ssl_ciphersuite_from_id( opt.force_ciphersuite[0] );
if( opt.max_version != -1 &&
ciphersuite_info->min_minor_ver > opt.max_version )
{
- mbedtls_printf("forced ciphersuite not allowed with this protocol version\n");
+ mbedtls_printf( "forced ciphersuite not allowed with this protocol version\n" );
ret = 2;
goto usage;
}
if( opt.min_version != -1 &&
ciphersuite_info->max_minor_ver < opt.min_version )
{
- mbedtls_printf("forced ciphersuite not allowed with this protocol version\n");
+ mbedtls_printf( "forced ciphersuite not allowed with this protocol version\n" );
ret = 2;
goto usage;
}
@@ -1526,11 +1608,12 @@
fflush( stdout );
mbedtls_entropy_init( &entropy );
- if( ( ret = mbedtls_ctr_drbg_seed( &ctr_drbg, mbedtls_entropy_func, &entropy,
- (const unsigned char *) pers,
- strlen( pers ) ) ) != 0 )
+ if( ( ret = mbedtls_ctr_drbg_seed( &ctr_drbg, mbedtls_entropy_func,
+ &entropy, (const unsigned char *) pers,
+ strlen( pers ) ) ) != 0 )
{
- mbedtls_printf( " failed\n ! mbedtls_ctr_drbg_seed returned -0x%x\n", -ret );
+ mbedtls_printf( " failed\n ! mbedtls_ctr_drbg_seed returned -0x%x\n",
+ -ret );
goto exit;
}
@@ -1627,7 +1710,7 @@
if( ( ret = mbedtls_pk_parse_keyfile( &pkey2, opt.key_file2, "" ) ) != 0 )
{
mbedtls_printf( " failed\n ! mbedtls_pk_parse_keyfile(2) returned -0x%x\n\n",
- -ret );
+ -ret );
goto exit;
}
}
@@ -1645,8 +1728,7 @@
strcmp( opt.key_file2, "none" ) != 0 )
{
#if !defined(MBEDTLS_CERTS_C)
- mbedtls_printf( "Not certificated or key provided, and \n"
- "MBEDTLS_CERTS_C not defined!\n" );
+ mbedtls_printf( "Not certificated or key provided, and \nMBEDTLS_CERTS_C not defined!\n" );
goto exit;
#else
#if defined(MBEDTLS_RSA_C)
@@ -1654,14 +1736,16 @@
(const unsigned char *) mbedtls_test_srv_crt_rsa,
mbedtls_test_srv_crt_rsa_len ) ) != 0 )
{
- mbedtls_printf( " failed\n ! mbedtls_x509_crt_parse returned -0x%x\n\n", -ret );
+ mbedtls_printf( " failed\n ! mbedtls_x509_crt_parse returned -0x%x\n\n",
+ -ret );
goto exit;
}
if( ( ret = mbedtls_pk_parse_key( &pkey,
(const unsigned char *) mbedtls_test_srv_key_rsa,
mbedtls_test_srv_key_rsa_len, NULL, 0 ) ) != 0 )
{
- mbedtls_printf( " failed\n ! mbedtls_pk_parse_key returned -0x%x\n\n", -ret );
+ mbedtls_printf( " failed\n ! mbedtls_pk_parse_key returned -0x%x\n\n",
+ -ret );
goto exit;
}
key_cert_init = 2;
@@ -1671,14 +1755,16 @@
(const unsigned char *) mbedtls_test_srv_crt_ec,
mbedtls_test_srv_crt_ec_len ) ) != 0 )
{
- mbedtls_printf( " failed\n ! x509_crt_parse2 returned -0x%x\n\n", -ret );
+ mbedtls_printf( " failed\n ! x509_crt_parse2 returned -0x%x\n\n",
+ -ret );
goto exit;
}
if( ( ret = mbedtls_pk_parse_key( &pkey2,
(const unsigned char *) mbedtls_test_srv_key_ec,
mbedtls_test_srv_key_ec_len, NULL, 0 ) ) != 0 )
{
- mbedtls_printf( " failed\n ! pk_parse_key2 returned -0x%x\n\n", -ret );
+ mbedtls_printf( " failed\n ! pk_parse_key2 returned -0x%x\n\n",
+ -ret );
goto exit;
}
key_cert_init2 = 2;
@@ -2088,8 +2174,8 @@
if( ( ret = mbedtls_ssl_set_client_transport_id( &ssl,
client_ip, cliip_len ) ) != 0 )
{
- mbedtls_printf( " failed\n ! "
- "mbedtls_ssl_set_client_transport_id() returned -0x%x\n\n", -ret );
+ mbedtls_printf( " failed\n ! mbedtls_ssl_set_client_transport_id() returned -0x%x\n\n",
+ -ret );
goto exit;
}
}
@@ -2117,9 +2203,24 @@
mbedtls_printf( " . Performing the SSL/TLS handshake..." );
fflush( stdout );
- do ret = mbedtls_ssl_handshake( &ssl );
- while( ret == MBEDTLS_ERR_SSL_WANT_READ ||
- ret == MBEDTLS_ERR_SSL_WANT_WRITE );
+ while( ( ret = mbedtls_ssl_handshake( &ssl ) ) != 0 )
+ {
+ if( ret != MBEDTLS_ERR_SSL_WANT_READ &&
+ ret != MBEDTLS_ERR_SSL_WANT_WRITE )
+ break;
+
+ /* For event-driven IO, wait for socket to become available */
+ if( opt.event == 1 /* level triggered IO */ )
+ {
+#if defined(MBEDTLS_TIMING_C)
+ ret = idle( &client_fd, &timer, ret );
+#else
+ ret = idle( &client_fd, ret );
+#endif
+ if( ret != 0 )
+ goto reset;
+ }
+ }
if( ret == MBEDTLS_ERR_SSL_HELLO_VERIFY_REQUIRED )
{
@@ -2225,7 +2326,18 @@
if( ret == MBEDTLS_ERR_SSL_WANT_READ ||
ret == MBEDTLS_ERR_SSL_WANT_WRITE )
+ {
+ if( opt.event == 1 /* level triggered IO */ )
+ {
+#if defined(MBEDTLS_TIMING_C)
+ idle( &client_fd, &timer, ret );
+#else
+ idle( &client_fd, ret );
+#endif
+ }
+
continue;
+ }
if( ret <= 0 )
{
@@ -2313,9 +2425,40 @@
len = sizeof( buf ) - 1;
memset( buf, 0, sizeof( buf ) );
- do ret = mbedtls_ssl_read( &ssl, buf, len );
- while( ret == MBEDTLS_ERR_SSL_WANT_READ ||
- ret == MBEDTLS_ERR_SSL_WANT_WRITE );
+ while( 1 )
+ {
+ /* Without the call to `mbedtls_ssl_check_pending`, it might
+ * happen that the client sends application data in the same
+ * datagram as the Finished message concluding the handshake.
+ * In this case, the application data would be ready to be
+ * processed while the underlying transport wouldn't signal
+ * any further incoming data.
+ *
+ * See the test 'Event-driven I/O: session-id resume, UDP packing'
+ * in tests/ssl-opt.sh.
+ */
+
+ /* For event-driven IO, wait for socket to become available */
+ if( mbedtls_ssl_check_pending( &ssl ) == 0 &&
+ opt.event == 1 /* level triggered IO */ )
+ {
+#if defined(MBEDTLS_TIMING_C)
+ idle( &client_fd, &timer, MBEDTLS_ERR_SSL_WANT_READ );
+#else
+ idle( &client_fd, MBEDTLS_ERR_SSL_WANT_READ );
+#endif
+ }
+
+ ret = mbedtls_ssl_read( &ssl, buf, len );
+
+ /* Note that even if `mbedtls_ssl_check_pending` returns true,
+ * it can happen that the subsequent call to `mbedtls_ssl_read`
+ * returns `MBEDTLS_ERR_SSL_WANT_READ`, because the pending messages
+ * might be discarded (e.g. because they are retransmissions). */
+ if( ret != MBEDTLS_ERR_SSL_WANT_READ &&
+ ret != MBEDTLS_ERR_SSL_WANT_WRITE )
+ break;
+ }
if( ret <= 0 )
{
@@ -2356,6 +2499,16 @@
mbedtls_printf( " failed\n ! mbedtls_ssl_renegotiate returned %d\n\n", ret );
goto reset;
}
+
+ /* For event-driven IO, wait for socket to become available */
+ if( opt.event == 1 /* level triggered IO */ )
+ {
+#if defined(MBEDTLS_TIMING_C)
+ idle( &client_fd, &timer, ret );
+#else
+ idle( &client_fd, ret );
+#endif
+ }
}
mbedtls_printf( " ok\n" );
@@ -2390,14 +2543,39 @@
mbedtls_printf( " failed\n ! mbedtls_ssl_write returned %d\n\n", ret );
goto reset;
}
+
+ /* For event-driven IO, wait for socket to become available */
+ if( opt.event == 1 /* level triggered IO */ )
+ {
+#if defined(MBEDTLS_TIMING_C)
+ idle( &client_fd, &timer, ret );
+#else
+ idle( &client_fd, ret );
+#endif
+ }
}
}
}
else /* Not stream, so datagram */
{
- do ret = mbedtls_ssl_write( &ssl, buf, len );
- while( ret == MBEDTLS_ERR_SSL_WANT_READ ||
- ret == MBEDTLS_ERR_SSL_WANT_WRITE );
+ while( 1 )
+ {
+ ret = mbedtls_ssl_write( &ssl, buf, len );
+
+ if( ret != MBEDTLS_ERR_SSL_WANT_READ &&
+ ret != MBEDTLS_ERR_SSL_WANT_WRITE )
+ break;
+
+ /* For event-driven IO, wait for socket to become available */
+ if( opt.event == 1 /* level triggered IO */ )
+ {
+#if defined(MBEDTLS_TIMING_C)
+ idle( &client_fd, &timer, ret );
+#else
+ idle( &client_fd, ret );
+#endif
+ }
+ }
if( ret < 0 )
{
diff --git a/programs/test/udp_proxy.c b/programs/test/udp_proxy.c
index 20624d2..5797f3d 100644
--- a/programs/test/udp_proxy.c
+++ b/programs/test/udp_proxy.c
@@ -53,6 +53,7 @@
#include "mbedtls/net_sockets.h"
#include "mbedtls/error.h"
#include "mbedtls/ssl.h"
+#include "mbedtls/timing.h"
#include <string.h>
@@ -74,17 +75,21 @@
#include <unistd.h>
#endif /* ( _WIN32 || _WIN32_WCE ) && !EFIX64 && !EFI32 */
-/* For gettimeofday() */
-#if !defined(_WIN32)
-#include <sys/time.h>
-#endif
-
#define MAX_MSG_SIZE 16384 + 2048 /* max record/datagram size */
#define DFL_SERVER_ADDR "localhost"
#define DFL_SERVER_PORT "4433"
#define DFL_LISTEN_ADDR "localhost"
#define DFL_LISTEN_PORT "5556"
+#define DFL_PACK 0
+
+#if defined(MBEDTLS_TIMING_C)
+#define USAGE_PACK \
+ " pack=%%d default: 0 (don't pack)\n" \
+ " options: t > 0 (pack for t milliseconds)\n"
+#else
+#define USAGE_PACK
+#endif
#define USAGE \
"\n usage: udp_proxy param=<>...\n" \
@@ -105,9 +110,10 @@
" drop packets larger than N bytes\n" \
" bad_ad=0/1 default: 0 (don't add bad ApplicationData)\n" \
" protect_hvr=0/1 default: 0 (don't protect HelloVerifyRequest)\n" \
- " protect_len=%%d default: (don't protect packets of this size)\n" \
+ " protect_len=%%d default: (don't protect packets of this size)\n" \
"\n" \
" seed=%%d default: (use current time)\n" \
+ USAGE_PACK \
"\n"
/*
@@ -128,7 +134,8 @@
int bad_ad; /* inject corrupted ApplicationData record */
int protect_hvr; /* never drop or delay HelloVerifyRequest */
int protect_len; /* never drop/delay packet of the given size*/
-
+ unsigned pack; /* merge packets into single datagram for
+ * at most \c merge milliseconds if > 0 */
unsigned int seed; /* seed for "random" events */
} opt;
@@ -152,6 +159,7 @@
opt.server_port = DFL_SERVER_PORT;
opt.listen_addr = DFL_LISTEN_ADDR;
opt.listen_port = DFL_LISTEN_PORT;
+ opt.pack = DFL_PACK;
/* Other members default to 0 */
for( i = 1; i < argc; i++ )
@@ -193,6 +201,15 @@
if( opt.drop < 0 || opt.drop > 20 || opt.drop == 1 )
exit_usage( p, q );
}
+ else if( strcmp( p, "pack" ) == 0 )
+ {
+#if defined(MBEDTLS_TIMING_C)
+ opt.pack = (unsigned) atoi( q );
+#else
+ mbedtls_printf( " option pack only defined if MBEDTLS_TIMING_C is enabled\n" );
+ exit( 1 );
+#endif
+ }
else if( strcmp( p, "mtu" ) == 0 )
{
opt.mtu = atoi( q );
@@ -267,25 +284,122 @@
}
}
+#if defined(MBEDTLS_TIMING_C)
/* Return elapsed time in milliseconds since the first call */
-static unsigned long ellapsed_time( void )
+static unsigned ellapsed_time( void )
{
-#if defined(_WIN32)
- return( 0 );
-#else
- static struct timeval ref = { 0, 0 };
- struct timeval now;
+ static int initialized = 0;
+ static struct mbedtls_timing_hr_time hires;
- if( ref.tv_sec == 0 && ref.tv_usec == 0 )
+ if( initialized == 0 )
{
- gettimeofday( &ref, NULL );
+ (void) mbedtls_timing_get_timer( &hires, 1 );
+ initialized = 1;
return( 0 );
}
- gettimeofday( &now, NULL );
- return( 1000 * ( now.tv_sec - ref.tv_sec )
- + ( now.tv_usec - ref.tv_usec ) / 1000 );
-#endif
+ return( mbedtls_timing_get_timer( &hires, 0 ) );
+}
+
+typedef struct
+{
+ mbedtls_net_context *ctx;
+
+ const char *description;
+
+ unsigned packet_lifetime;
+ unsigned num_datagrams;
+
+ unsigned char data[MAX_MSG_SIZE];
+ size_t len;
+
+} ctx_buffer;
+
+static ctx_buffer outbuf[2];
+
+static int ctx_buffer_flush( ctx_buffer *buf )
+{
+ int ret;
+
+ mbedtls_printf( " %05u flush %s: %u bytes, %u datagrams, last %u ms\n",
+ ellapsed_time(), buf->description,
+ (unsigned) buf->len, buf->num_datagrams,
+ ellapsed_time() - buf->packet_lifetime );
+
+ ret = mbedtls_net_send( buf->ctx, buf->data, buf->len );
+
+ buf->len = 0;
+ buf->num_datagrams = 0;
+
+ return( ret );
+}
+
+static unsigned ctx_buffer_time_remaining( ctx_buffer *buf )
+{
+ unsigned const cur_time = ellapsed_time();
+
+ if( buf->num_datagrams == 0 )
+ return( (unsigned) -1 );
+
+ if( cur_time - buf->packet_lifetime >= opt.pack )
+ return( 0 );
+
+ return( opt.pack - ( cur_time - buf->packet_lifetime ) );
+}
+
+static int ctx_buffer_append( ctx_buffer *buf,
+ const unsigned char * data,
+ size_t len )
+{
+ int ret;
+
+ if( len > (size_t) INT_MAX )
+ return( -1 );
+
+ if( len > sizeof( buf->data ) )
+ {
+ mbedtls_printf( " ! buffer size %u too large (max %u)\n",
+ (unsigned) len, (unsigned) sizeof( buf->data ) );
+ return( -1 );
+ }
+
+ if( sizeof( buf->data ) - buf->len < len )
+ {
+ if( ( ret = ctx_buffer_flush( buf ) ) <= 0 )
+ return( ret );
+ }
+
+ memcpy( buf->data + buf->len, data, len );
+
+ buf->len += len;
+ if( ++buf->num_datagrams == 1 )
+ buf->packet_lifetime = ellapsed_time();
+
+ return( (int) len );
+}
+#endif /* MBEDTLS_TIMING_C */
+
+static int dispatch_data( mbedtls_net_context *ctx,
+ const unsigned char * data,
+ size_t len )
+{
+#if defined(MBEDTLS_TIMING_C)
+ ctx_buffer *buf = NULL;
+ if( opt.pack > 0 )
+ {
+ if( outbuf[0].ctx == ctx )
+ buf = &outbuf[0];
+ else if( outbuf[1].ctx == ctx )
+ buf = &outbuf[1];
+
+ if( buf == NULL )
+ return( -1 );
+
+ return( ctx_buffer_append( buf, data, len ) );
+ }
+#endif /* MBEDTLS_TIMING_C */
+
+ return( mbedtls_net_send( ctx, data, len ) );
}
typedef struct
@@ -300,12 +414,22 @@
/* Print packet. Outgoing packets come with a reason (forward, dupl, etc.) */
void print_packet( const packet *p, const char *why )
{
+#if defined(MBEDTLS_TIMING_C)
if( why == NULL )
- mbedtls_printf( " %05lu %s %s (%u bytes)\n",
+ mbedtls_printf( " %05u dispatch %s %s (%u bytes)\n",
ellapsed_time(), p->way, p->type, p->len );
else
- mbedtls_printf( " %s %s (%u bytes): %s\n",
+ mbedtls_printf( " %05u dispatch %s %s (%u bytes): %s\n",
+ ellapsed_time(), p->way, p->type, p->len, why );
+#else
+ if( why == NULL )
+ mbedtls_printf( " dispatch %s %s (%u bytes)\n",
+ p->way, p->type, p->len );
+ else
+ mbedtls_printf( " dispatch %s %s (%u bytes): %s\n",
p->way, p->type, p->len, why );
+#endif
+
fflush( stdout );
}
@@ -320,20 +444,28 @@
{
unsigned char buf[MAX_MSG_SIZE];
memcpy( buf, p->buf, p->len );
- ++buf[p->len - 1];
- print_packet( p, "corrupted" );
- if( ( ret = mbedtls_net_send( dst, buf, p->len ) ) <= 0 )
+ if( p->len <= 13 )
{
- mbedtls_printf( " ! mbedtls_net_send returned %d\n", ret );
+ mbedtls_printf( " ! can't corrupt empty AD record" );
+ }
+ else
+ {
+ ++buf[13];
+ print_packet( p, "corrupted" );
+ }
+
+ if( ( ret = dispatch_data( dst, buf, p->len ) ) <= 0 )
+ {
+ mbedtls_printf( " ! dispatch returned %d\n", ret );
return( ret );
}
}
print_packet( p, why );
- if( ( ret = mbedtls_net_send( dst, p->buf, p->len ) ) <= 0 )
+ if( ( ret = dispatch_data( dst, p->buf, p->len ) ) <= 0 )
{
- mbedtls_printf( " ! mbedtls_net_send returned %d\n", ret );
+ mbedtls_printf( " ! dispatch returned %d\n", ret );
return( ret );
}
@@ -344,9 +476,9 @@
{
print_packet( p, "duplicated" );
- if( ( ret = mbedtls_net_send( dst, p->buf, p->len ) ) <= 0 )
+ if( ( ret = dispatch_data( dst, p->buf, p->len ) ) <= 0 )
{
- mbedtls_printf( " ! mbedtls_net_send returned %d\n", ret );
+ mbedtls_printf( " ! dispatch returned %d\n", ret );
return( ret );
}
}
@@ -472,6 +604,12 @@
mbedtls_net_context listen_fd, client_fd, server_fd;
+#if defined( MBEDTLS_TIMING_C )
+ struct timeval tm;
+#endif
+
+ struct timeval *tm_ptr = NULL;
+
int nb_fds;
fd_set read_fds;
@@ -560,14 +698,65 @@
nb_fds = listen_fd.fd;
++nb_fds;
+#if defined(MBEDTLS_TIMING_C)
+ if( opt.pack > 0 )
+ {
+ outbuf[0].ctx = &server_fd;
+ outbuf[0].description = "S <- C";
+ outbuf[0].num_datagrams = 0;
+ outbuf[0].len = 0;
+
+ outbuf[1].ctx = &client_fd;
+ outbuf[1].description = "S -> C";
+ outbuf[1].num_datagrams = 0;
+ outbuf[1].len = 0;
+ }
+#endif /* MBEDTLS_TIMING_C */
+
while( 1 )
{
+#if defined(MBEDTLS_TIMING_C)
+ if( opt.pack > 0 )
+ {
+ unsigned max_wait_server, max_wait_client, max_wait;
+ max_wait_server = ctx_buffer_time_remaining( &outbuf[0] );
+ max_wait_client = ctx_buffer_time_remaining( &outbuf[1] );
+
+ max_wait = (unsigned) -1;
+
+ if( max_wait_server == 0 )
+ ctx_buffer_flush( &outbuf[0] );
+ else
+ max_wait = max_wait_server;
+
+ if( max_wait_client == 0 )
+ ctx_buffer_flush( &outbuf[1] );
+ else
+ {
+ if( max_wait_client < max_wait )
+ max_wait = max_wait_client;
+ }
+
+ if( max_wait != (unsigned) -1 )
+ {
+ tm.tv_sec = max_wait / 1000;
+ tm.tv_usec = ( max_wait % 1000 ) * 1000;
+
+ tm_ptr = &tm;
+ }
+ else
+ {
+ tm_ptr = NULL;
+ }
+ }
+#endif /* MBEDTLS_TIMING_C */
+
FD_ZERO( &read_fds );
FD_SET( server_fd.fd, &read_fds );
FD_SET( client_fd.fd, &read_fds );
FD_SET( listen_fd.fd, &read_fds );
- if( ( ret = select( nb_fds, &read_fds, NULL, NULL, NULL ) ) <= 0 )
+ if( ( ret = select( nb_fds, &read_fds, NULL, NULL, tm_ptr ) ) < 0 )
{
perror( "select" );
goto exit;
@@ -589,6 +778,7 @@
&client_fd, &server_fd ) ) != 0 )
goto accept;
}
+
}
exit:
diff --git a/programs/test/udp_proxy_wrapper.sh b/programs/test/udp_proxy_wrapper.sh
new file mode 100755
index 0000000..29033d5
--- /dev/null
+++ b/programs/test/udp_proxy_wrapper.sh
@@ -0,0 +1,117 @@
+#!/bin/sh
+# -*-sh-basic-offset: 4-*-
+# Usage: udp_proxy_wrapper.sh [PROXY_PARAM...] -- [SERVER_PARAM...]
+
+set -u
+
+MBEDTLS_BASE="$(dirname -- "$0")/../.."
+TPXY_BIN="$MBEDTLS_BASE/programs/test/udp_proxy"
+SRV_BIN="$MBEDTLS_BASE/programs/ssl/ssl_server2"
+
+: ${VERBOSE:=0}
+
+stop_proxy() {
+ if [ -n "${tpxy_pid:-}" ]; then
+ echo
+ echo " * Killing proxy (pid $tpxy_pid) ..."
+ kill $tpxy_pid
+ fi
+}
+
+stop_server() {
+ if [ -n "${srv_pid:-}" ]; then
+ echo
+ echo " * Killing server (pid $srv_pid) ..."
+ kill $srv_pid >/dev/null 2>/dev/null
+ fi
+}
+
+cleanup() {
+ stop_server
+ stop_proxy
+ exit 129
+}
+
+trap cleanup INT TERM HUP
+
+# Extract the proxy parameters
+tpxy_cmd_snippet='"$TPXY_BIN"'
+while [ $# -ne 0 ] && [ "$1" != "--" ]; do
+ tail="$1" quoted=""
+ while [ -n "$tail" ]; do
+ case "$tail" in
+ *\'*) quoted="${quoted}${tail%%\'*}'\\''" tail="${tail#*\'}";;
+ *) quoted="${quoted}${tail}"; tail=; false;;
+ esac
+ done
+ tpxy_cmd_snippet="$tpxy_cmd_snippet '$quoted'"
+ shift
+done
+unset tail quoted
+if [ $# -eq 0 ]; then
+ echo " * No server arguments (must be preceded by \" -- \") - exit"
+ exit 3
+fi
+shift
+
+dtls_enabled=
+ipv6_in_use=
+server_port_orig=
+server_addr_orig=
+for param; do
+ case "$param" in
+ server_port=*) server_port_orig="${param#*=}";;
+ server_addr=*:*) server_addr_orig="${param#*=}"; ipv6_in_use=1;;
+ server_addr=*) server_addr_orig="${param#*=}";;
+ dtls=[!0]*) dtls_enabled=1;;
+ esac
+done
+
+if [ -z "$dtls_enabled" ] || [ -n "$ipv6_in_use" ]; then
+ echo >&2 "$0: Couldn't find DTLS enabling, or IPv6 is in use - immediate fallback to server application..."
+ if [ $VERBOSE -gt 0 ]; then
+ echo "[ $SRV_BIN $* ]"
+ fi
+ exec "$SRV_BIN" "$@"
+fi
+
+if [ -z "$server_port_orig" ]; then
+ server_port_orig=4433
+fi
+echo " * Server port: $server_port_orig"
+tpxy_cmd_snippet="$tpxy_cmd_snippet \"listen_port=\$server_port_orig\""
+tpxy_cmd_snippet="$tpxy_cmd_snippet \"server_port=\$server_port\""
+
+if [ -n "$server_addr_orig" ]; then
+ echo " * Server address: $server_addr_orig"
+ tpxy_cmd_snippet="$tpxy_cmd_snippet \"server_addr=\$server_addr_orig\""
+ tpxy_cmd_snippet="$tpxy_cmd_snippet \"listen_addr=\$server_addr_orig\""
+fi
+
+server_port=$(( server_port_orig + 1 ))
+set -- "$@" "server_port=$server_port"
+echo " * Intermediate port: $server_port"
+
+echo " * Start proxy in background ..."
+if [ $VERBOSE -gt 0 ]; then
+ echo "[ $tpxy_cmd_snippet ]"
+fi
+eval exec "$tpxy_cmd_snippet" >/dev/null 2>&1 &
+tpxy_pid=$!
+
+if [ $VERBOSE -gt 0 ]; then
+ echo " * Proxy ID: $TPXY_PID"
+fi
+
+echo " * Starting server ..."
+if [ $VERBOSE -gt 0 ]; then
+ echo "[ $SRV_BIN $* ]"
+fi
+
+exec "$SRV_BIN" "$@" >&2 &
+srv_pid=$!
+
+wait $srv_pid
+
+stop_proxy
+return 0
diff --git a/tests/ssl-opt.sh b/tests/ssl-opt.sh
index 9fde54a..c4a10a2 100755
--- a/tests/ssl-opt.sh
+++ b/tests/ssl-opt.sh
@@ -465,9 +465,12 @@
eval "$CLI_CMD" >> $CLI_OUT 2>&1 &
wait_client_done
+ sleep 0.05
+
# terminate the server (and the proxy)
kill $SRV_PID
wait $SRV_PID
+
if [ -n "$PXY_CMD" ]; then
kill $PXY_PID >/dev/null 2>&1
wait $PXY_PID
@@ -631,16 +634,19 @@
get_options "$@"
# sanity checks, avoid an avalanche of errors
-if [ ! -x "$P_SRV" ]; then
- echo "Command '$P_SRV' is not an executable file"
+P_SRV_BIN="${P_SRV%%[ ]*}"
+P_CLI_BIN="${P_CLI%%[ ]*}"
+P_PXY_BIN="${P_PXY%%[ ]*}"
+if [ ! -x "$P_SRV_BIN" ]; then
+ echo "Command '$P_SRV_BIN' is not an executable file"
exit 1
fi
-if [ ! -x "$P_CLI" ]; then
- echo "Command '$P_CLI' is not an executable file"
+if [ ! -x "$P_CLI_BIN" ]; then
+ echo "Command '$P_CLI_BIN' is not an executable file"
exit 1
fi
-if [ ! -x "$P_PXY" ]; then
- echo "Command '$P_PXY' is not an executable file"
+if [ ! -x "$P_PXY_BIN" ]; then
+ echo "Command '$P_PXY_BIN' is not an executable file"
exit 1
fi
if [ "$MEMCHECK" -gt 0 ]; then
@@ -2704,6 +2710,118 @@
-C "mbedtls_ssl_handshake returned" \
-c "Read from server: .* bytes read"
+# Tests for event-driven I/O: exercise a variety of handshake flows
+
+run_test "Event-driven I/O: basic handshake" \
+ "$P_SRV event=1 tickets=0 auth_mode=none" \
+ "$P_CLI event=1 tickets=0" \
+ 0 \
+ -S "mbedtls_ssl_handshake returned" \
+ -C "mbedtls_ssl_handshake returned" \
+ -c "Read from server: .* bytes read"
+
+run_test "Event-driven I/O: client auth" \
+ "$P_SRV event=1 tickets=0 auth_mode=required" \
+ "$P_CLI event=1 tickets=0" \
+ 0 \
+ -S "mbedtls_ssl_handshake returned" \
+ -C "mbedtls_ssl_handshake returned" \
+ -c "Read from server: .* bytes read"
+
+run_test "Event-driven I/O: ticket" \
+ "$P_SRV event=1 tickets=1 auth_mode=none" \
+ "$P_CLI event=1 tickets=1" \
+ 0 \
+ -S "mbedtls_ssl_handshake returned" \
+ -C "mbedtls_ssl_handshake returned" \
+ -c "Read from server: .* bytes read"
+
+run_test "Event-driven I/O: ticket + client auth" \
+ "$P_SRV event=1 tickets=1 auth_mode=required" \
+ "$P_CLI event=1 tickets=1" \
+ 0 \
+ -S "mbedtls_ssl_handshake returned" \
+ -C "mbedtls_ssl_handshake returned" \
+ -c "Read from server: .* bytes read"
+
+run_test "Event-driven I/O: ticket + client auth + resume" \
+ "$P_SRV event=1 tickets=1 auth_mode=required" \
+ "$P_CLI event=1 tickets=1 reconnect=1" \
+ 0 \
+ -S "mbedtls_ssl_handshake returned" \
+ -C "mbedtls_ssl_handshake returned" \
+ -c "Read from server: .* bytes read"
+
+run_test "Event-driven I/O: ticket + resume" \
+ "$P_SRV event=1 tickets=1 auth_mode=none" \
+ "$P_CLI event=1 tickets=1 reconnect=1" \
+ 0 \
+ -S "mbedtls_ssl_handshake returned" \
+ -C "mbedtls_ssl_handshake returned" \
+ -c "Read from server: .* bytes read"
+
+run_test "Event-driven I/O: session-id resume" \
+ "$P_SRV event=1 tickets=0 auth_mode=none" \
+ "$P_CLI event=1 tickets=0 reconnect=1" \
+ 0 \
+ -S "mbedtls_ssl_handshake returned" \
+ -C "mbedtls_ssl_handshake returned" \
+ -c "Read from server: .* bytes read"
+
+run_test "Event-driven I/O, DTLS: basic handshake" \
+ "$P_SRV dtls=1 event=1 tickets=0 auth_mode=none" \
+ "$P_CLI dtls=1 event=1 tickets=0" \
+ 0 \
+ -c "Read from server: .* bytes read"
+
+run_test "Event-driven I/O, DTLS: client auth" \
+ "$P_SRV dtls=1 event=1 tickets=0 auth_mode=required" \
+ "$P_CLI dtls=1 event=1 tickets=0" \
+ 0 \
+ -c "Read from server: .* bytes read"
+
+run_test "Event-driven I/O, DTLS: ticket" \
+ "$P_SRV dtls=1 event=1 tickets=1 auth_mode=none" \
+ "$P_CLI dtls=1 event=1 tickets=1" \
+ 0 \
+ -c "Read from server: .* bytes read"
+
+run_test "Event-driven I/O, DTLS: ticket + client auth" \
+ "$P_SRV dtls=1 event=1 tickets=1 auth_mode=required" \
+ "$P_CLI dtls=1 event=1 tickets=1" \
+ 0 \
+ -c "Read from server: .* bytes read"
+
+run_test "Event-driven I/O, DTLS: ticket + client auth + resume" \
+ "$P_SRV dtls=1 event=1 tickets=1 auth_mode=required" \
+ "$P_CLI dtls=1 event=1 tickets=1 reconnect=1" \
+ 0 \
+ -c "Read from server: .* bytes read"
+
+run_test "Event-driven I/O, DTLS: ticket + resume" \
+ "$P_SRV dtls=1 event=1 tickets=1 auth_mode=none" \
+ "$P_CLI dtls=1 event=1 tickets=1 reconnect=1" \
+ 0 \
+ -c "Read from server: .* bytes read"
+
+run_test "Event-driven I/O, DTLS: session-id resume" \
+ "$P_SRV dtls=1 event=1 tickets=0 auth_mode=none" \
+ "$P_CLI dtls=1 event=1 tickets=0 reconnect=1" \
+ 0 \
+ -c "Read from server: .* bytes read"
+
+# This test demonstrates the need for the mbedtls_ssl_check_pending function.
+# During session resumption, the client will send its ApplicationData record
+# within the same datagram as the Finished messages. In this situation, the
+# server MUST NOT idle on the underlying transport after handshake completion,
+# because the ApplicationData request has already been queued internally.
+run_test "Event-driven I/O, DTLS: session-id resume, UDP packing" \
+ -p "$P_PXY pack=50" \
+ "$P_SRV dtls=1 event=1 tickets=0 auth_mode=required" \
+ "$P_CLI dtls=1 event=1 tickets=0 reconnect=1" \
+ 0 \
+ -c "Read from server: .* bytes read"
+
# Tests for version negotiation
run_test "Version check: all -> 1.2" \
@@ -4195,8 +4313,8 @@
0 \
-c "replayed record" \
-s "replayed record" \
- -c "discarding invalid record" \
- -s "discarding invalid record" \
+ -c "record from another epoch" \
+ -s "record from another epoch" \
-S "resend" \
-s "Extra-header:" \
-c "HTTP/1.0 200 OK"
@@ -4208,13 +4326,29 @@
0 \
-c "replayed record" \
-S "replayed record" \
- -c "discarding invalid record" \
- -s "discarding invalid record" \
+ -c "record from another epoch" \
+ -s "record from another epoch" \
-c "resend" \
-s "resend" \
-s "Extra-header:" \
-c "HTTP/1.0 200 OK"
+run_test "DTLS proxy: multiple records in same datagram" \
+ -p "$P_PXY pack=50" \
+ "$P_SRV dtls=1 debug_level=2" \
+ "$P_CLI dtls=1 debug_level=2" \
+ 0 \
+ -c "next record in same datagram" \
+ -s "next record in same datagram"
+
+run_test "DTLS proxy: multiple records in same datagram, duplicate every packet" \
+ -p "$P_PXY pack=50 duplicate=1" \
+ "$P_SRV dtls=1 debug_level=2" \
+ "$P_CLI dtls=1 debug_level=2" \
+ 0 \
+ -c "next record in same datagram" \
+ -s "next record in same datagram"
+
run_test "DTLS proxy: inject invalid AD record, default badmac_limit" \
-p "$P_PXY bad_ad=1" \
"$P_SRV dtls=1 debug_level=1" \
@@ -4270,8 +4404,6 @@
0 \
-c "record from another epoch" \
-s "record from another epoch" \
- -c "discarding invalid record" \
- -s "discarding invalid record" \
-s "Extra-header:" \
-c "HTTP/1.0 200 OK"