Add support for event-driven IO in ssl_client2 and ssl_server2
diff --git a/programs/ssl/ssl_client2.c b/programs/ssl/ssl_client2.c
index 5032a9f..e82adaa 100644
--- a/programs/ssl/ssl_client2.c
+++ b/programs/ssl/ssl_client2.c
@@ -70,6 +70,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 ""
@@ -243,23 +244,25 @@
" server_port=%%d default: 4433\n" \
" request_page=%%s default: \".\"\n" \
" request_size=%%d default: about 34 (basic request)\n" \
- " (minimum: 0, max: 16384)\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" \
+ " (minimum: 0, max: 16384)\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" \
@@ -299,7 +302,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 */
@@ -433,6 +437,78 @@
};
#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)
+void idle( mbedtls_ssl_context *ssl,
+ mbedtls_net_context *fd,
+ int idle_reason )
+{
+#else
+void idle( mbedtls_ssl_context *ssl,
+ mbedtls_net_context *fd,
+ mbedtls_timing_delay_context *timer,
+ int idle_reason )
+{
+#if defined(MBEDTLS_DEBUG_C)
+ struct mbedtls_timing_hr_time tm;
+ unsigned long time_elapsed;
+#endif
+#endif
+
+ 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
+ {
+ MBEDTLS_SSL_DEBUG_MSG( 1, ( "WARNING: No reason for idling given" ) );
+ return;
+ }
+#endif
+
+ /* One should not idle on the underlying transport
+ * if data is still pending to be processed. */
+ if( mbedtls_ssl_check_pending( ssl ) != 0 )
+ {
+ MBEDTLS_SSL_DEBUG_MSG( 1, ( "WARNING: Data still pending, "
+ "but idling requested!" ) );
+ }
+ MBEDTLS_SSL_DEBUG_MSG( 3, ( "idle, waiting for event... " ) );
+
+#if defined(MBEDTLS_TIMING_C) && defined(MBEDTLS_DEBUG_C)
+ mbedtls_timing_get_timer( &tm, 1 /* restart */ );
+#endif
+
+ while( 1 )
+ {
+#if defined(MBEDTLS_TIMING_C)
+#if defined(MBEDTLS_DEBUG_C)
+ time_elapsed = mbedtls_timing_get_timer( &tm, 0 );
+#endif
+ if( mbedtls_timing_get_delay( timer ) == 2 )
+ {
+ MBEDTLS_SSL_DEBUG_MSG( 3, ( "[%lu ms] timer expired - continue",
+ time_elapsed ) );
+ break;
+ }
+#endif
+
+ if( poll_type != 0 &&
+ mbedtls_net_poll( fd, poll_type, 0 ) == poll_type )
+ {
+ MBEDTLS_SSL_DEBUG_MSG( 3, ( "[%lu ms] net_context signals data - "
+ "continue", time_elapsed ) );
+ break;
+ }
+ }
+}
+
int main( int argc, char *argv[] )
{
int ret = 0, len, tail_len, i, written, frags, retry_left;
@@ -516,6 +592,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;
@@ -589,6 +666,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 )
@@ -858,6 +941,16 @@
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
@@ -1092,7 +1185,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;
}
@@ -1115,7 +1209,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
{
@@ -1125,7 +1220,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;
}
@@ -1138,7 +1234,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
{
@@ -1148,7 +1245,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;
}
@@ -1166,11 +1264,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;
}
@@ -1180,7 +1280,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;
}
@@ -1197,7 +1298,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;
}
@@ -1220,13 +1322,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
@@ -1249,8 +1353,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)
@@ -1262,7 +1366,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
@@ -1301,7 +1406,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;
}
}
@@ -1320,16 +1426,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 )
@@ -1338,14 +1447,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
@@ -1357,7 +1468,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;
}
}
@@ -1366,7 +1478,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)
@@ -1384,9 +1497,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. "
@@ -1398,10 +1513,21 @@
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)
+ idle( &ssl, &server_fd, &timer, ret );
+#else
+ idle( &ssl, &server_fd, ret );
+#endif
+ }
}
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 );
@@ -1429,7 +1555,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;
}
@@ -1448,7 +1575,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 );
}
@@ -1478,9 +1606,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( &ssl, &server_fd, &timer, ret );
+#else
+ idle( &ssl, &server_fd, ret );
+#endif
+ }
+
}
mbedtls_printf( " ok\n" );
}
@@ -1524,27 +1664,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( &ssl, &server_fd, &timer, ret );
+#else
+ idle( &ssl, &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( &ssl, &server_fd, &timer, ret );
+#else
+ idle( &ssl, &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;
}
@@ -1553,7 +1720,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
@@ -1574,7 +1742,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( &ssl, &server_fd, &timer, ret );
+#else
+ idle( &ssl, &server_fd, ret );
+#endif
+ }
continue;
+ }
if( ret <= 0 )
{
@@ -1616,9 +1795,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( &ssl, &server_fd, &timer, ret );
+#else
+ idle( &ssl, &server_fd, ret );
+#endif
+ }
+ }
if( ret <= 0 )
{
@@ -1671,6 +1865,16 @@
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( &ssl, &server_fd, &timer, ret );
+#else
+ idle( &ssl, &server_fd, ret );
+#endif
+ }
}
mbedtls_printf( " ok\n" );