From bfb47005c82bb89898f6e10e75eb8df12b0a4a90 Mon Sep 17 00:00:00 2001 From: Delyan Angelov Date: Sun, 15 Jan 2023 15:49:31 +0200 Subject: [PATCH] net.openssl, net.mbedtls: add support for `-d trace_ssl`, for easier tracing of binary protocol problems to https servers --- vlib/net/mbedtls/c.v | 6 +-- vlib/net/mbedtls/ssl_connection.v | 67 ++++++++++++++++++++++++--- vlib/net/openssl/openssl.v | 3 ++ vlib/net/openssl/ssl_connection.v | 75 ++++++++++++++++++++++++++++--- 4 files changed, 136 insertions(+), 15 deletions(-) diff --git a/vlib/net/mbedtls/c.v b/vlib/net/mbedtls/c.v index 169b90c08a..7d8b6699bd 100644 --- a/vlib/net/mbedtls/c.v +++ b/vlib/net/mbedtls/c.v @@ -1,5 +1,7 @@ module mbedtls +pub const is_used = 1 + #flag -I @VEXEROOT/thirdparty/mbedtls/library #flag -I @VEXEROOT/thirdparty/mbedtls/include // #flag -D _FILE_OFFSET_BITS=64 @@ -195,7 +197,3 @@ fn C.mbedtls_high_level_strerr(int) &char // C.OPENSSL_init_ssl(C.OPENSSL_INIT_LOAD_SSL_STRINGS, 0) // } // } - -pub const ( - is_used = 1 -) diff --git a/vlib/net/mbedtls/ssl_connection.v b/vlib/net/mbedtls/ssl_connection.v index cca0455e2e..d7c357b6cc 100644 --- a/vlib/net/mbedtls/ssl_connection.v +++ b/vlib/net/mbedtls/ssl_connection.v @@ -9,6 +9,9 @@ const ctr_drbg = C.mbedtls_ctr_drbg_context{} const entropy = C.mbedtls_entropy_context{} fn init() { + $if trace_ssl ? { + eprintln(@METHOD) + } C.mbedtls_ctr_drbg_init(&mbedtls.ctr_drbg) C.mbedtls_entropy_init(&mbedtls.entropy) @@ -53,6 +56,9 @@ pub struct SSLConnectConfig { // new_ssl_conn returns a new SSLConn with the given config. pub fn new_ssl_conn(config SSLConnectConfig) !&SSLConn { + $if trace_ssl ? { + eprintln(@METHOD) + } mut conn := &SSLConn{ config: config } @@ -69,6 +75,9 @@ enum Select { // shutdown terminates the ssl connection and does cleanup pub fn (mut s SSLConn) shutdown() ! { + $if trace_ssl ? { + eprintln(@METHOD) + } if !s.opened { return error('ssl connection not open') } @@ -92,6 +101,9 @@ pub fn (mut s SSLConn) shutdown() ! { // connect to server using mbedtls fn (mut s SSLConn) init() ! { + $if trace_ssl ? { + eprintln(@METHOD) + } C.mbedtls_net_init(&s.server_fd) C.mbedtls_ssl_init(&s.ssl) C.mbedtls_ssl_config_init(&s.conf) @@ -158,6 +170,9 @@ fn (mut s SSLConn) init() ! { // connect sets up an ssl connection on an existing TCP connection pub fn (mut s SSLConn) connect(mut tcp_conn net.TcpConn, hostname string) ! { + $if trace_ssl ? { + eprintln('${@METHOD} hostname: ${hostname}') + } if s.opened { return error('ssl connection already open') } @@ -184,6 +199,9 @@ pub fn (mut s SSLConn) connect(mut tcp_conn net.TcpConn, hostname string) ! { // dial opens an ssl connection on hostname:port pub fn (mut s SSLConn) dial(hostname string, port int) ! { + $if trace_ssl ? { + eprintln('${@METHOD} hostname: ${hostname} | port: ${port}') + } s.owns_socket = true if s.opened { return error('ssl connection already open') @@ -218,23 +236,39 @@ pub fn (mut s SSLConn) dial(hostname string, port int) ! { // socket_read_into_ptr reads `len` bytes into `buf` pub fn (mut s SSLConn) socket_read_into_ptr(buf_ptr &u8, len int) !int { mut res := 0 + $if trace_ssl ? { + defer { + if len > 0 { + eprintln('${@METHOD} res: ${res}: buf_ptr: ${voidptr(buf_ptr):x}, len: ${len}, hex: ${unsafe { buf_ptr.vbytes(len).hex() }} data: `${unsafe { buf_ptr.vstring_with_len(len) }}`') + } + } + } for { res = C.mbedtls_ssl_read(&s.ssl, buf_ptr, len) if res > 0 { return res } else if res == 0 { + $if trace_ssl ? { + eprintln('${@METHOD} ---> res: io.Eof') + } return io.Eof{} } else { match res { C.MBEDTLS_ERR_SSL_WANT_READ { ready := @select(s.handle, .read, s.duration)! if !ready { + $if trace_ssl ? { + eprintln('${@METHOD} ---> res: net.err_timed_out, C.MBEDTLS_ERR_SSL_WANT_READ') + } return net.err_timed_out } } C.MBEDTLS_ERR_SSL_WANT_WRITE { ready := @select(s.handle, .write, s.duration)! if !ready { + $if trace_ssl ? { + eprintln('${@METHOD} ---> res: net.err_timed_out, C.MBEDTLS_ERR_SSL_WANT_WRITE') + } return net.err_timed_out } } @@ -242,6 +276,9 @@ pub fn (mut s SSLConn) socket_read_into_ptr(buf_ptr &u8, len int) !int { break } else { + $if trace_ssl ? { + eprintln('${@METHOD} ---> res: could not read using SSL') + } return error_with_code('Could not read using SSL', res) } } @@ -252,15 +289,22 @@ pub fn (mut s SSLConn) socket_read_into_ptr(buf_ptr &u8, len int) !int { // read reads data from the ssl connection into `buffer` pub fn (mut s SSLConn) read(mut buffer []u8) !int { - res := s.socket_read_into_ptr(&u8(buffer.data), buffer.len) or { return err } - return res + $if trace_ssl ? { + eprintln('${@METHOD} buffer.len: ${buffer.len}') + } + return s.socket_read_into_ptr(&u8(buffer.data), buffer.len) } // write_ptr writes `len` bytes from `bytes` to the ssl connection pub fn (mut s SSLConn) write_ptr(bytes &u8, len int) !int { + mut total_sent := 0 + $if trace_ssl ? { + defer { + eprintln('${@METHOD} total_sent: ${total_sent}, bytes: ${voidptr(bytes):x}, len: ${len}, hex: ${unsafe { bytes.vbytes(len).hex() }}, data:-=-=-=-\n${unsafe { bytes.vstring_with_len(len) }}\n-=-=-=-') + } + } unsafe { mut ptr_base := bytes - mut total_sent := 0 for total_sent < len { ptr := ptr_base + total_sent remaining := len - total_sent @@ -286,14 +330,17 @@ pub fn (mut s SSLConn) write_ptr(bytes &u8, len int) !int { continue } else { + $if trace_ssl ? { + eprintln('${@METHOD} ---> res: could not write SSL, sent: ${sent}') + } return error_with_code('Could not write using SSL', sent) } } } total_sent += sent } - return total_sent } + return total_sent } // write writes data from `bytes` to the ssl connection @@ -303,6 +350,9 @@ pub fn (mut s SSLConn) write(bytes []u8) !int { // write_string writes a string to the ssl connection pub fn (mut s SSLConn) write_string(str string) !int { + $if trace_ssl ? { + eprintln('${@METHOD} str: ${str}') + } return s.write_ptr(str.str, str.len) } @@ -314,6 +364,9 @@ This is basically a copy of Emily socket implementation of select. // Select waits for an io operation (specified by parameter `test`) to be available fn @select(handle int, test Select, timeout time.Duration) !bool { + $if trace_ssl ? { + eprintln('${@METHOD} handle: ${handle}, timeout: ${timeout}') + } set := C.fd_set{} C.FD_ZERO(&set) @@ -346,5 +399,9 @@ fn @select(handle int, test Select, timeout time.Duration) !bool { } } - return C.FD_ISSET(handle, &set) + res := C.FD_ISSET(handle, &set) + $if trace_ssl ? { + eprintln('${@METHOD} ---> res: ${res}') + } + return res } diff --git a/vlib/net/openssl/openssl.v b/vlib/net/openssl/openssl.v index af5f15a159..ae4751a4c6 100644 --- a/vlib/net/openssl/openssl.v +++ b/vlib/net/openssl/openssl.v @@ -3,6 +3,9 @@ module openssl // ssl_error returns non error ssl code or error if unrecoverable and we should panic fn ssl_error(ret int, ssl voidptr) !SSLError { res := C.SSL_get_error(ssl, ret) + $if trace_ssl ? { + eprintln('${@METHOD} ret: ${ret} | ssl: ${ssl:x} | res: ${res}') + } match unsafe { SSLError(res) } { .ssl_error_syscall { return error_with_code('unrecoverable syscall (${res})', res) diff --git a/vlib/net/openssl/ssl_connection.v b/vlib/net/openssl/ssl_connection.v index 7c9e291355..5b2ff6891a 100644 --- a/vlib/net/openssl/ssl_connection.v +++ b/vlib/net/openssl/ssl_connection.v @@ -29,6 +29,9 @@ pub struct SSLConnectConfig { // new_ssl_conn instance an new SSLCon struct pub fn new_ssl_conn(config SSLConnectConfig) !&SSLConn { + $if trace_ssl ? { + eprintln(@METHOD) + } mut conn := &SSLConn{ config: config sslctx: 0 @@ -48,6 +51,9 @@ enum Select { // shutdown closes the ssl connection and does cleanup pub fn (mut s SSLConn) shutdown() ! { + $if trace_ssl ? { + eprintln(@METHOD) + } if s.ssl != 0 { mut res := 0 for { @@ -109,6 +115,9 @@ pub fn (mut s SSLConn) shutdown() ! { } fn (mut s SSLConn) init() ! { + $if trace_ssl ? { + eprintln(@METHOD) + } s.sslctx = unsafe { C.SSL_CTX_new(C.SSLv23_client_method()) } if s.sslctx == 0 { return error("Couldn't get ssl context") @@ -176,6 +185,9 @@ fn (mut s SSLConn) init() ! { // connect to server using OpenSSL pub fn (mut s SSLConn) connect(mut tcp_conn net.TcpConn, hostname string) ! { + $if trace_ssl ? { + eprintln('${@METHOD} hostname: ${hostname}') + } s.handle = tcp_conn.sock.handle s.duration = tcp_conn.read_timeout() @@ -193,6 +205,9 @@ pub fn (mut s SSLConn) connect(mut tcp_conn net.TcpConn, hostname string) ! { // dial opens an ssl connection on hostname:port pub fn (mut s SSLConn) dial(hostname string, port int) ! { + $if trace_ssl ? { + eprintln('${@METHOD} hostname: ${hostname} | port: ${port}') + } s.owns_socket = true mut tcp_conn := net.dial_tcp('${hostname}:${port}') or { return err } $if macos { @@ -202,6 +217,9 @@ pub fn (mut s SSLConn) dial(hostname string, port int) ! { } fn (mut s SSLConn) complete_connect() ! { + $if trace_ssl ? { + eprintln(@METHOD) + } for { mut res := C.SSL_connect(voidptr(s.ssl)) if res != 1 { @@ -269,11 +287,21 @@ fn (mut s SSLConn) complete_connect() ! { pub fn (mut s SSLConn) socket_read_into_ptr(buf_ptr &u8, len int) !int { mut res := 0 + $if trace_ssl ? { + defer { + if len > 0 { + eprintln('${@METHOD} res: ${res}: buf_ptr: ${voidptr(buf_ptr):x}, len: ${len}, hex: ${unsafe { buf_ptr.vbytes(len).hex() }} data: `${unsafe { buf_ptr.vstring_with_len(len) }}`') + } + } + } for { res = C.SSL_read(voidptr(s.ssl), buf_ptr, len) if res > 0 { return res } else if res == 0 { + $if trace_ssl ? { + eprintln('${@METHOD} ---> res: io.Eof') + } return io.Eof{} } else { err_res := ssl_error(res, s.ssl)! @@ -281,19 +309,31 @@ pub fn (mut s SSLConn) socket_read_into_ptr(buf_ptr &u8, len int) !int { .ssl_error_want_read { ready := @select(s.handle, .read, s.duration)! if !ready { + $if trace_ssl ? { + eprintln('${@METHOD} ---> res: net.err_timed_out .ssl_error_want_read') + } return net.err_timed_out } } .ssl_error_want_write { ready := @select(s.handle, .write, s.duration)! if !ready { + $if trace_ssl ? { + eprintln('${@METHOD} ---> res: net.err_timed_out .ssl_error_want_write') + } return net.err_timed_out } } .ssl_error_zero_return { + $if trace_ssl ? { + eprintln('${@METHOD} ---> res: 0 .ssl_error_zero_return') + } return 0 } else { + $if trace_ssl ? { + eprintln('${@METHOD} ---> res: could not read, err_res: ${err_res}') + } return error('Could not read using SSL. (${err_res})') } } @@ -303,15 +343,22 @@ pub fn (mut s SSLConn) socket_read_into_ptr(buf_ptr &u8, len int) !int { } pub fn (mut s SSLConn) read(mut buffer []u8) !int { - res := s.socket_read_into_ptr(&u8(buffer.data), buffer.len) or { return err } - return res + $if trace_ssl ? { + eprintln('${@METHOD} buffer.len: ${buffer.len}') + } + return s.socket_read_into_ptr(&u8(buffer.data), buffer.len) } // write_ptr writes `len` bytes from `bytes` to the ssl connection pub fn (mut s SSLConn) write_ptr(bytes &u8, len int) !int { + mut total_sent := 0 + $if trace_ssl ? { + defer { + eprintln('${@METHOD} total_sent: ${total_sent}, bytes: ${voidptr(bytes):x}, len: ${len}, hex: ${unsafe { bytes.vbytes(len).hex() }}, data:-=-=-=-\n${unsafe { bytes.vstring_with_len(len) }}\n-=-=-=-') + } + } unsafe { mut ptr_base := bytes - mut total_sent := 0 for total_sent < len { ptr := ptr_base + total_sent remaining := len - total_sent @@ -334,23 +381,32 @@ pub fn (mut s SSLConn) write_ptr(bytes &u8, len int) !int { } continue } else if err_res == .ssl_error_zero_return { + $if trace_ssl ? { + eprintln('${@METHOD} ---> res: ssl write on closed connection .ssl_error_zero_return') + } return error('ssl write on closed connection') // Todo error_with_code close } + $if trace_ssl ? { + eprintln('${@METHOD} ---> res: could not write SSL, err_res: ${err_res}') + } return error_with_code('Could not write SSL. (${err_res}),err', int(err_res)) } total_sent += sent } - return total_sent } + return total_sent } // write writes data from `bytes` to the ssl connection pub fn (mut s SSLConn) write(bytes []u8) !int { - return s.write_ptr(&u8(bytes.data), bytes.len) + return s.write_ptr(&u8(bytes.data), bytes.len)! } // write_string writes a string to the ssl connection pub fn (mut s SSLConn) write_string(str string) !int { + $if trace_ssl ? { + eprintln('${@METHOD} str: ${str}') + } return s.write_ptr(str.str, str.len) } @@ -365,6 +421,9 @@ This is basically a copy of Emily socket implementation of select. // Select waits for an io operation (specified by parameter `test`) to be available fn @select(handle int, test Select, timeout time.Duration) !bool { + $if trace_ssl ? { + eprintln('${@METHOD} handle: ${handle}, timeout: ${timeout}') + } set := C.fd_set{} C.FD_ZERO(&set) @@ -397,5 +456,9 @@ fn @select(handle int, test Select, timeout time.Duration) !bool { } } - return C.FD_ISSET(handle, &set) + res := C.FD_ISSET(handle, &set) + $if trace_ssl ? { + eprintln('${@METHOD} ---> res: ${res}') + } + return res }