From 16970d29127332e109f928fbe9efecc0a118c8dc Mon Sep 17 00:00:00 2001 From: Hanno Becker Date: Tue, 10 Oct 2017 15:56:37 +0100 Subject: [PATCH] Add support for event-driven IO in ssl_client2 and ssl_server2 --- programs/ssl/ssl_client2.c | 302 +++++++++++++++++++++++++++++++------ programs/ssl/ssl_server2.c | 185 +++++++++++++++++++++-- 2 files changed, 429 insertions(+), 58 deletions(-) diff --git a/programs/ssl/ssl_client2.c b/programs/ssl/ssl_client2.c index 5032a9f3d..e82adaa7b 100644 --- a/programs/ssl/ssl_client2.c +++ b/programs/ssl/ssl_client2.c @@ -70,6 +70,7 @@ int main( void ) #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 @@ int main( void ) " 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 @@ struct options 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 @@ static int ssl_sig_hashes_for_test[] = { }; #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 @@ int main( int argc, char *argv[] ) 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 @@ int main( int argc, char *argv[] ) 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 @@ int main( int argc, char *argv[] ) 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 @@ int main( int argc, char *argv[] ) #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 @@ int main( int argc, char *argv[] ) 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 @@ int main( int argc, char *argv[] ) #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 @@ int main( int argc, char *argv[] ) 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 @@ int main( int argc, char *argv[] ) #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 @@ int main( int argc, char *argv[] ) 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 @@ int main( int argc, char *argv[] ) 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 @@ int main( int argc, char *argv[] ) 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 @@ int main( int argc, char *argv[] ) #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 @@ int main( int argc, char *argv[] ) #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 @@ int main( int argc, char *argv[] ) 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 @@ int main( int argc, char *argv[] ) { 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 @@ int main( int argc, char *argv[] ) (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 @@ int main( int argc, char *argv[] ) 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 @@ int main( int argc, char *argv[] ) (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 @@ int main( int argc, char *argv[] ) 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 @@ int main( int argc, char *argv[] ) 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 @@ int main( int argc, char *argv[] ) 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 @@ int main( int argc, char *argv[] ) 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 @@ int main( int argc, char *argv[] ) 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 @@ int main( int argc, char *argv[] ) 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 @@ send_request: { 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 @@ send_request: } 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 @@ send_request: 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 @@ send_request: 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 @@ send_request: 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" ); diff --git a/programs/ssl/ssl_server2.c b/programs/ssl/ssl_server2.c index a25886824..b317bcca3 100644 --- a/programs/ssl/ssl_server2.c +++ b/programs/ssl/ssl_server2.c @@ -101,6 +101,7 @@ int main( void ) #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 @@ int main( void ) " 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 @@ struct options 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,78 @@ static int ssl_sig_hashes_for_test[] = { }; #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, written, frags, exchanges_left; @@ -969,6 +1045,7 @@ int main( int argc, char *argv[] ) 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 +1124,12 @@ int main( int argc, char *argv[] ) 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 ) @@ -1328,6 +1411,16 @@ int main( int argc, char *argv[] ) 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 @@ -2113,9 +2206,22 @@ handshake: 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) + idle( &ssl, &client_fd, &timer, ret ); +#else + idle( &ssl, &client_fd, ret ); +#endif + } + } if( ret == MBEDTLS_ERR_SSL_HELLO_VERIFY_REQUIRED ) { @@ -2221,7 +2327,18 @@ data_exchange: 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( &ssl, &client_fd, &timer, ret ); +#else + idle( &ssl, &client_fd, ret ); +#endif + } + continue; + } if( ret <= 0 ) { @@ -2309,9 +2426,24 @@ data_exchange: 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, &client_fd, &timer, ret ); +#else + idle( &ssl, &client_fd, ret ); +#endif + } + } if( ret <= 0 ) { @@ -2352,6 +2484,16 @@ data_exchange: 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( &ssl, &client_fd, &timer, ret ); +#else + idle( &ssl, &client_fd, ret ); +#endif + } } mbedtls_printf( " ok\n" ); @@ -2386,14 +2528,39 @@ data_exchange: 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( &ssl, &client_fd, &timer, ret ); +#else + idle( &ssl, &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( &ssl, &client_fd, &timer, ret ); +#else + idle( &ssl, &client_fd, ret ); +#endif + } + } if( ret < 0 ) {