aboutsummaryrefslogtreecommitdiff
path: root/v_windows/v/vlib/net/openssl/ssl_connection.v
diff options
context:
space:
mode:
Diffstat (limited to 'v_windows/v/vlib/net/openssl/ssl_connection.v')
-rw-r--r--v_windows/v/vlib/net/openssl/ssl_connection.v268
1 files changed, 268 insertions, 0 deletions
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)
+}