1
0
mirror of https://github.com/vlang/v.git synced 2023-08-10 21:13:21 +03:00

net.openssl, net.mbedtls: add support for -d trace_ssl, for easier tracing of binary protocol problems to https servers

This commit is contained in:
Delyan Angelov 2023-01-15 15:49:31 +02:00
parent 7f3531077d
commit bfb47005c8
No known key found for this signature in database
GPG Key ID: 66886C0F12D595ED
4 changed files with 136 additions and 15 deletions

View File

@ -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
)

View File

@ -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
}

View File

@ -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)

View File

@ -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
}