From f5c4671bfbad96bf346bd7e9a21fc4317b4959df Mon Sep 17 00:00:00 2001
From: Indrajith K L
Date: Sat, 3 Dec 2022 17:00:20 +0530
Subject: Adds most of the tools

---
 v_windows/v/vlib/net/openssl/c.v              | 120 ++++++++++++
 v_windows/v/vlib/net/openssl/openssl.v        |  32 +++
 v_windows/v/vlib/net/openssl/ssl_connection.v | 268 ++++++++++++++++++++++++++
 3 files changed, 420 insertions(+)
 create mode 100644 v_windows/v/vlib/net/openssl/c.v
 create mode 100644 v_windows/v/vlib/net/openssl/openssl.v
 create mode 100644 v_windows/v/vlib/net/openssl/ssl_connection.v

(limited to 'v_windows/v/vlib/net/openssl')

diff --git a/v_windows/v/vlib/net/openssl/c.v b/v_windows/v/vlib/net/openssl/c.v
new file mode 100644
index 0000000..dedba2a
--- /dev/null
+++ b/v_windows/v/vlib/net/openssl/c.v
@@ -0,0 +1,120 @@
+module openssl
+
+// On Linux, prefer a localy built openssl, because it is
+// much more likely for it to be newer, than the system
+// openssl from libssl-dev. If there is no local openssl,
+// the next flag is harmless, since it will still use the
+// (older) system openssl.
+#flag linux -I/usr/local/include/openssl -L/usr/local/lib
+#flag windows -l libssl -l libcrypto
+#flag -lssl -lcrypto
+#flag linux -ldl -lpthread
+// MacPorts
+#flag darwin -I/opt/local/include
+#flag darwin -L/opt/local/lib
+// Brew
+#flag darwin -I/usr/local/opt/openssl/include
+#flag darwin -L/usr/local/opt/openssl/lib
+// Brew arm64
+#flag darwin -I /opt/homebrew/opt/openssl/include
+#flag darwin -L /opt/homebrew/opt/openssl/lib
+//
+#include <openssl/rand.h> # Please install OpenSSL development headers
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+
+pub struct C.SSL {
+}
+
+pub struct SSL_CTX {
+}
+
+pub struct SSL {
+}
+
+pub struct SSL_METHOD {
+}
+
+pub struct OPENSSL_INIT_SETTINGS {
+}
+
+fn C.BIO_new_ssl_connect(ctx &C.SSL_CTX) &C.BIO
+
+fn C.BIO_set_conn_hostname(b &C.BIO, name &char) int
+
+// there are actually 2 macros for BIO_get_ssl
+// fn C.BIO_get_ssl(bp &C.BIO, ssl charptr, c int)
+// fn C.BIO_get_ssl(bp &C.BIO, sslp charptr)
+fn C.BIO_get_ssl(bp &C.BIO, vargs ...voidptr)
+
+fn C.BIO_do_connect(b &C.BIO) int
+
+fn C.BIO_do_handshake(b &C.BIO) int
+
+fn C.BIO_puts(b &C.BIO, buf &char)
+
+fn C.BIO_read(b &C.BIO, buf voidptr, len int) int
+
+fn C.BIO_free_all(a &C.BIO)
+
+fn C.SSL_CTX_new(method &C.SSL_METHOD) &C.SSL_CTX
+
+fn C.SSL_CTX_set_options(ctx &C.SSL_CTX, options int)
+
+fn C.SSL_CTX_set_verify_depth(s &C.SSL_CTX, depth int)
+
+fn C.SSL_CTX_load_verify_locations(ctx &C.SSL_CTX, ca_file &char, ca_path &char) int
+
+fn C.SSL_CTX_free(ctx &C.SSL_CTX)
+
+fn C.SSL_new(&C.SSL_CTX) &C.SSL
+
+fn C.SSL_set_fd(ssl &C.SSL, fd int) int
+
+fn C.SSL_connect(&C.SSL) int
+
+fn C.SSL_set_cipher_list(ctx &SSL, str &char) int
+
+fn C.SSL_get_peer_certificate(ssl &SSL) &C.X509
+
+fn C.ERR_clear_error()
+
+fn C.SSL_get_error(ssl &C.SSL, ret int) int
+
+fn C.SSL_get_verify_result(ssl &SSL) int
+
+fn C.SSL_set_tlsext_host_name(s &SSL, name &char) int
+
+fn C.SSL_shutdown(&C.SSL) int
+
+fn C.SSL_free(&C.SSL)
+
+fn C.SSL_write(ssl &C.SSL, buf voidptr, buflen int) int
+
+fn C.SSL_read(ssl &C.SSL, buf voidptr, buflen int) int
+
+fn C.SSL_load_error_strings()
+
+fn C.SSL_library_init() int
+
+fn C.SSLv23_client_method() &C.SSL_METHOD
+
+fn C.TLS_method() voidptr
+
+fn C.TLSv1_2_method() voidptr
+
+fn C.OPENSSL_init_ssl(opts u64, settings &OPENSSL_INIT_SETTINGS) int
+
+fn init() {
+	$if ssl_pre_1_1_version ? {
+		// OPENSSL_VERSION_NUMBER < 0x10100000L
+		C.SSL_load_error_strings()
+		C.SSL_library_init()
+	} $else {
+		C.OPENSSL_init_ssl(C.OPENSSL_INIT_LOAD_SSL_STRINGS, 0)
+	}
+}
+
+pub const (
+	is_used = 1
+)
diff --git a/v_windows/v/vlib/net/openssl/openssl.v b/v_windows/v/vlib/net/openssl/openssl.v
new file mode 100644
index 0000000..ffcabf5
--- /dev/null
+++ b/v_windows/v/vlib/net/openssl/openssl.v
@@ -0,0 +1,32 @@
+module openssl
+
+// ssl_error returns non error ssl code or error if unrecoverable and we should panic
+pub fn ssl_error(ret int, ssl voidptr) ?SSLError {
+	res := C.SSL_get_error(ssl, ret)
+	match SSLError(res) {
+		.ssl_error_syscall {
+			return error_with_code('unrecoverable syscall ($res)', res)
+		}
+		.ssl_error_ssl {
+			return error_with_code('unrecoverable ssl protocol error ($res)', res)
+		}
+		else {
+			return SSLError(res)
+		}
+	}
+}
+
+pub enum SSLError {
+	ssl_error_none = 0 // SSL_ERROR_NONE
+	ssl_error_ssl = 1 // SSL_ERROR_SSL
+	ssl_error_want_read = 2 // SSL_ERROR_WANT_READ
+	ssl_error_want_write = 3 // SSL_ERROR_WANT_WRITE
+	ssl_error_want_x509_lookup = 4 // SSL_ERROR_WANT_X509_LOOKUP
+	ssl_error_syscall = 5 // SSL_ERROR_SYSCALL
+	ssl_error_zero_return = 6 // SSL_ERROR_ZERO_RETURN
+	ssl_error_want_connect = 7 // SSL_ERROR_WANT_CONNECT
+	ssl_error_want_accept = 8 // SSL_ERROR_WANT_ACCEPT
+	ssl_error_want_async = 9 // SSL_ERROR_WANT_ASYNC
+	ssl_error_want_async_job = 10 // SSL_ERROR_WANT_ASYNC_JOB
+	ssl_error_want_early = 11 // SSL_ERROR_WANT_EARLY
+}
diff --git a/v_windows/v/vlib/net/openssl/ssl_connection.v b/v_windows/v/vlib/net/openssl/ssl_connection.v
new file mode 100644
index 0000000..58f47f6
--- /dev/null
+++ b/v_windows/v/vlib/net/openssl/ssl_connection.v
@@ -0,0 +1,268 @@
+module openssl
+
+import net
+import time
+
+// SSLConn is the current connection
+pub struct SSLConn {
+mut:
+	sslctx   &C.SSL_CTX
+	ssl      &C.SSL
+	handle   int
+	duration time.Duration
+}
+
+// new_ssl_conn instance an new SSLCon struct
+pub fn new_ssl_conn() &SSLConn {
+	return &SSLConn{
+		sslctx: 0
+		ssl: 0
+		handle: 0
+	}
+}
+
+// Select operation
+enum Select {
+	read
+	write
+	except
+}
+
+// shutdown closes the ssl connection and do clean up
+pub fn (mut s SSLConn) shutdown() ? {
+	if s.ssl != 0 {
+		mut res := 0
+		for {
+			res = C.SSL_shutdown(voidptr(s.ssl))
+			if res < 0 {
+				err_res := ssl_error(res, s.ssl) or {
+					break // We break to free rest of resources
+				}
+				if err_res == .ssl_error_want_read {
+					for {
+						ready := @select(s.handle, .read, s.duration) ?
+						if ready {
+							break
+						}
+					}
+					continue
+				} else if err_res == .ssl_error_want_write {
+					for {
+						ready := @select(s.handle, .write, s.duration) ?
+						if ready {
+							break
+						}
+					}
+					continue
+				} else {
+					unsafe { C.SSL_free(voidptr(s.ssl)) }
+					if s.sslctx != 0 {
+						C.SSL_CTX_free(s.sslctx)
+					}
+					return error('unexepedted ssl error $err_res')
+				}
+				if s.ssl != 0 {
+					unsafe { C.SSL_free(voidptr(s.ssl)) }
+				}
+				if s.sslctx != 0 {
+					C.SSL_CTX_free(s.sslctx)
+				}
+				return error('Could not connect using SSL. ($err_res),err')
+			} else if res == 0 {
+				continue
+			} else if res == 1 {
+				break
+			}
+		}
+		C.SSL_free(voidptr(s.ssl))
+	}
+	if s.sslctx != 0 {
+		C.SSL_CTX_free(s.sslctx)
+	}
+}
+
+// connect to server using open ssl
+pub fn (mut s SSLConn) connect(mut tcp_conn net.TcpConn, hostname string) ? {
+	s.handle = tcp_conn.sock.handle
+	s.duration = tcp_conn.read_timeout()
+
+	s.sslctx = unsafe { C.SSL_CTX_new(C.SSLv23_client_method()) }
+	if s.sslctx == 0 {
+		return error("Couldn't get ssl context")
+	}
+
+	// TODO: Fix option to enable/disable checks for valid
+	//		 certificates to allow both secure and self signed
+	//		 for now the checks are not done at all to comply
+	// 		 to current autobahn tests
+
+	// C.SSL_CTX_set_verify_depth(s.sslctx, 4)
+	// flags := C.SSL_OP_NO_SSLv2 | C.SSL_OP_NO_SSLv3 | C.SSL_OP_NO_COMPRESSION
+	// C.SSL_CTX_set_options(s.sslctx, flags)
+	// mut res := C.SSL_CTX_load_verify_locations(s.sslctx, 'random-org-chain.pem', 0)
+
+	s.ssl = unsafe { &C.SSL(C.SSL_new(s.sslctx)) }
+	if s.ssl == 0 {
+		return error("Couldn't create OpenSSL instance.")
+	}
+
+	// preferred_ciphers := 'HIGH:!aNULL:!kRSA:!PSK:!SRP:!MD5:!RC4'
+	// mut res := C.SSL_set_cipher_list(s.ssl, preferred_ciphers.str)
+	// if res != 1 {
+	// 	println('http: openssl: cipher failed')
+	// }
+
+	mut res := C.SSL_set_tlsext_host_name(voidptr(s.ssl), voidptr(hostname.str))
+	if res != 1 {
+		return error('cannot set host name')
+	}
+
+	if C.SSL_set_fd(voidptr(s.ssl), tcp_conn.sock.handle) != 1 {
+		return error("Couldn't assign ssl to socket.")
+	}
+	for {
+		res = C.SSL_connect(voidptr(s.ssl))
+		if res != 1 {
+			err_res := ssl_error(res, s.ssl) ?
+			if err_res == .ssl_error_want_read {
+				for {
+					ready := @select(s.handle, .read, s.duration) ?
+					if ready {
+						break
+					}
+				}
+				continue
+			} else if err_res == .ssl_error_want_write {
+				for {
+					ready := @select(s.handle, .write, s.duration) ?
+					if ready {
+						break
+					}
+				}
+				continue
+			}
+			return error('Could not connect using SSL. ($err_res),err')
+		}
+		break
+	}
+}
+
+pub fn (mut s SSLConn) socket_read_into_ptr(buf_ptr &byte, len int) ?int {
+	mut res := 0
+	for {
+		res = C.SSL_read(voidptr(s.ssl), buf_ptr, len)
+		if res < 0 {
+			err_res := ssl_error(res, s.ssl) ?
+			if err_res == .ssl_error_want_read {
+				for {
+					ready := @select(s.handle, .read, s.duration) ?
+					if ready {
+						break
+					}
+				}
+				continue
+			} else if err_res == .ssl_error_want_write {
+				for {
+					ready := @select(s.handle, .write, s.duration) ?
+					if ready {
+						break
+					}
+				}
+				continue
+			} else if err_res == .ssl_error_zero_return {
+				return 0
+			}
+			return error('Could not read using SSL. ($err_res)')
+		}
+		break
+	}
+	return res
+}
+
+pub fn (mut s SSLConn) read_into(mut buffer []byte) ?int {
+	res := s.socket_read_into_ptr(&byte(buffer.data), buffer.len) ?
+	return res
+}
+
+// write number of bytes to SSL connection
+pub fn (mut s SSLConn) write(bytes []byte) ?int {
+	unsafe {
+		mut ptr_base := &byte(bytes.data)
+		mut total_sent := 0
+		for total_sent < bytes.len {
+			ptr := ptr_base + total_sent
+			remaining := bytes.len - total_sent
+			mut sent := C.SSL_write(voidptr(s.ssl), ptr, remaining)
+			if sent <= 0 {
+				err_res := ssl_error(sent, s.ssl) ?
+				if err_res == .ssl_error_want_read {
+					for {
+						ready := @select(s.handle, .read, s.duration) ?
+						if ready {
+							break
+						}
+					}
+				} else if err_res == .ssl_error_want_write {
+					for {
+						ready := @select(s.handle, .write, s.duration) ?
+						if ready {
+							break
+						}
+					}
+					continue
+				} else if err_res == .ssl_error_zero_return {
+					return error('ssl write on closed connection') // Todo error_with_code close
+				}
+				return error_with_code('Could not write SSL. ($err_res),err', int(err_res))
+			}
+			total_sent += sent
+		}
+		return total_sent
+	}
+}
+
+/*
+This is basically a copy of Emily socket implementation of select.
+	This have to be consolidated into common net lib features
+	when merging this to V
+*/
+// [typedef]
+// pub struct C.fd_set {
+// }
+
+// Select waits for an io operation (specified by parameter `test`) to be available
+fn @select(handle int, test Select, timeout time.Duration) ?bool {
+	set := C.fd_set{}
+
+	C.FD_ZERO(&set)
+	C.FD_SET(handle, &set)
+
+	seconds := timeout.milliseconds() / 1000
+	microseconds := timeout - (seconds * time.second)
+	mut tt := C.timeval{
+		tv_sec: u64(seconds)
+		tv_usec: u64(microseconds)
+	}
+
+	mut timeval_timeout := &tt
+
+	// infinite timeout is signaled by passing null as the timeout to
+	// select
+	if timeout == net.infinite_timeout {
+		timeval_timeout = &C.timeval(0)
+	}
+
+	match test {
+		.read {
+			net.socket_error(C.@select(handle + 1, &set, C.NULL, C.NULL, timeval_timeout)) ?
+		}
+		.write {
+			net.socket_error(C.@select(handle + 1, C.NULL, &set, C.NULL, timeval_timeout)) ?
+		}
+		.except {
+			net.socket_error(C.@select(handle + 1, C.NULL, C.NULL, &set, timeval_timeout)) ?
+		}
+	}
+
+	return C.FD_ISSET(handle, &set)
+}
-- 
cgit v1.2.3