aboutsummaryrefslogtreecommitdiff
path: root/v_windows/v/vlib/net/openssl
diff options
context:
space:
mode:
authorIndrajith K L2022-12-03 17:00:20 +0530
committerIndrajith K L2022-12-03 17:00:20 +0530
commitf5c4671bfbad96bf346bd7e9a21fc4317b4959df (patch)
tree2764fc62da58f2ba8da7ed341643fc359873142f /v_windows/v/vlib/net/openssl
downloadcli-tools-windows-master.tar.gz
cli-tools-windows-master.tar.bz2
cli-tools-windows-master.zip
Adds most of the toolsHEADmaster
Diffstat (limited to 'v_windows/v/vlib/net/openssl')
-rw-r--r--v_windows/v/vlib/net/openssl/c.v120
-rw-r--r--v_windows/v/vlib/net/openssl/openssl.v32
-rw-r--r--v_windows/v/vlib/net/openssl/ssl_connection.v268
3 files changed, 420 insertions, 0 deletions
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)
+}