vxx2 / vlib / net / openssl / openssl.c.v
227 lines · 172 sloc · 7.23 KB · 0e30f8d2f2cf6ed81ee9cebe949190dc76b548c9
Raw
1module openssl
2
3#define OPENSSL_API_COMPAT 0x30000000L
4
5// On Linux, prefer a locally built openssl, because it is
6// much more likely for it to be newer, than the system
7// openssl from libssl-dev. If there is no local openssl,
8// the next #pkgconfig flag is harmless, since it will still
9// use the (older) system openssl.
10#flag linux -I/usr/local/include/openssl
11#flag linux -L/usr/local/lib64
12// On FreeBSD, prefer openssl from the ports collection, because
13// it is much more likely for it to be newer, than the system
14// openssl.
15#flag freebsd -I/usr/local/include
16#flag freebsd -L/usr/local/lib
17
18// Installed through choco:
19#flag windows -IC:/Program Files/OpenSSL-Win64/include
20#flag windows -LC:/Program Files/OpenSSL-Win64/lib/VC/x64/MD
21
22// Installed on the CI:
23#flag windows -IC:/Program Files/OpenSSL/include
24#flag windows -LC:/Program Files/OpenSSL/lib/VC/x64/MD
25
26$if $pkgconfig('openssl') {
27 #pkgconfig --cflags --libs openssl
28} $else {
29 #flag windows -l libssl -l libcrypto
30 $if !windows {
31 #flag -lssl -lcrypto
32 }
33 #flag linux -ldl -lpthread
34 // Prefer a single matching macOS OpenSSL prefix to avoid mixing Intel and arm64 installs.
35 $if arm64 {
36 #flag darwin -I$when_first_existing('/opt/local/include','/opt/homebrew/opt/openssl/include','/opt/homebrew/opt/openssl@3/include','/opt/procursus/include','/usr/local/opt/openssl/include','/usr/local/opt/openssl@3/include')
37 #flag darwin -L$when_first_existing('/opt/local/lib','/opt/homebrew/opt/openssl/lib','/opt/homebrew/opt/openssl@3/lib','/opt/procursus/lib','/usr/local/opt/openssl/lib','/usr/local/opt/openssl@3/lib')
38 } $else {
39 #flag darwin -I$when_first_existing('/opt/local/include','/usr/local/opt/openssl/include','/usr/local/opt/openssl@3/include','/opt/procursus/include','/opt/homebrew/opt/openssl/include','/opt/homebrew/opt/openssl@3/include')
40 #flag darwin -L$when_first_existing('/opt/local/lib','/usr/local/opt/openssl/lib','/usr/local/opt/openssl@3/lib','/opt/procursus/lib','/opt/homebrew/opt/openssl/lib','/opt/homebrew/opt/openssl@3/lib')
41 }
42}
43
44#include <openssl/rand.h> # Please install OpenSSL development headers
45#include <openssl/ssl.h>
46#include <openssl/err.h>
47#insert "@VEXEROOT/vlib/net/openssl/openssl_compat.h"
48
49@[typedef]
50pub struct C.SSL {
51}
52
53@[typedef]
54pub struct C.BIO {
55}
56
57@[typedef]
58pub struct C.SSL_METHOD {
59}
60
61@[typedef]
62pub struct C.X509 {
63}
64
65@[typedef]
66pub struct C.SSL_CTX {
67}
68
69// The above C structs, have incomplete declarations in the OpenSSL headers.
70// For this reason, we have to prevent the automatic str() generation for them,
71// by adding manual implementations of their .str() methods, that are defined on
72// pointers to them:
73fn (s &C.SSL) str() string {
74 return 'C.SSL(0x${voidptr(s)})'
75}
76
77fn (c &C.SSL_CTX) str() string {
78 return 'C.SSL_CTX(0x${voidptr(c)})'
79}
80
81fn C.BIO_new_ssl_connect(ctx &C.SSL_CTX) &C.BIO
82
83fn C.BIO_set_conn_hostname(b &C.BIO, name &char) i32
84
85// there are actually 2 macros for BIO_get_ssl
86// fn C.BIO_get_ssl(bp &C.BIO, ssl charptr, c int)
87// fn C.BIO_get_ssl(bp &C.BIO, sslp charptr)
88fn C.BIO_get_ssl(bp &C.BIO, vargs ...voidptr)
89
90fn C.BIO_do_connect(b &C.BIO) i32
91
92fn C.BIO_do_handshake(b &C.BIO) i32
93
94fn C.BIO_puts(b &C.BIO, buf &char)
95
96fn C.BIO_read(b &C.BIO, buf voidptr, len i32) i32
97
98fn C.BIO_free_all(a &C.BIO)
99
100fn C.SSL_CTX_new(method &C.SSL_METHOD) &C.SSL_CTX
101
102fn C.SSL_CTX_set_options(ctx &C.SSL_CTX, options i32)
103
104fn C.SSL_CTX_set_verify_depth(s &C.SSL_CTX, depth i32)
105
106fn C.SSL_CTX_load_verify_locations(ctx &C.SSL_CTX, const_file &char, const_ca_path &char) i32
107
108fn C.SSL_CTX_free(ctx &C.SSL_CTX)
109
110fn C.SSL_CTX_use_certificate_file(ctx &C.SSL_CTX, const_file &char, file_type i32) i32
111
112fn C.SSL_CTX_use_PrivateKey_file(ctx &C.SSL_CTX, const_file &char, file_type i32) i32
113
114fn C.SSL_new(&C.SSL_CTX) &C.SSL
115
116fn C.SSL_set_fd(ssl &C.SSL, fd i32) i32
117
118fn C.SSL_connect(&C.SSL) i32
119
120fn C.SSL_do_handshake(&C.SSL) i32
121
122fn C.SSL_set_cipher_list(ctx &C.SSL, str &char) i32
123
124fn C.v_net_openssl_get1_peer_certificate(ssl &C.SSL) &C.X509
125
126fn C.X509_free(const_cert &C.X509)
127
128fn C.ERR_clear_error()
129
130fn C.ERR_get_error() u64
131
132fn C.ERR_error_string_n(e u64, buf &char, len usize)
133
134fn C.SSL_get_error(ssl &C.SSL, ret i32) i32
135
136fn C.SSL_get_verify_result(ssl &C.SSL) i32
137
138fn C.SSL_set_tlsext_host_name(s &C.SSL, name &char) i32
139
140// The ALPN calls go through small version-guarded shims in openssl_compat.h,
141// so the module still links against OpenSSL versions older than 1.0.2 that
142// predate ALPN. v_net_openssl_set_alpn_protos returns 0 on success (like the
143// underlying SSL_set_alpn_protos) and non-zero when ALPN is unavailable.
144// `data` is `const unsigned char **`; declared as voidptr so V emits a clean
145// `(void*)` cast and avoids -cstrict nested-pointer const warnings.
146fn C.v_net_openssl_set_alpn_protos(ssl &C.SSL, protos &u8, protos_len u32) i32
147
148fn C.v_net_openssl_get0_alpn_selected(ssl &C.SSL, data voidptr, len &u32)
149
150fn C.SSL_shutdown(&C.SSL) i32
151
152fn C.SSL_free(&C.SSL)
153
154fn C.SSL_write(ssl &C.SSL, buf voidptr, buflen i32) i32
155
156fn C.SSL_read(ssl &C.SSL, buf voidptr, buflen i32) i32
157
158fn C.SSLv23_client_method() &C.SSL_METHOD
159
160fn C.TLS_method() voidptr
161
162fn C.TLSv1_2_method() voidptr
163
164fn C.v_net_openssl_init_ssl() i32
165
166fn init() {
167 C.v_net_openssl_init_ssl()
168}
169
170// ssl_get_error_queue drains the current thread's OpenSSL error queue and
171// returns a human-readable, semicolon-separated description of every queued
172// entry (oldest first), or an empty string if the queue is empty. It always
173// leaves the queue empty afterwards: this both turns the otherwise opaque
174// `SSL_ERROR_SSL`/`SSL_ERROR_SYSCALL` codes into actionable messages, and
175// prevents a leftover entry from making a later SSL_get_error() misreport the
176// status of the next, unrelated I/O operation.
177fn ssl_get_error_queue() string {
178 mut reasons := []string{}
179 for {
180 code := C.ERR_get_error()
181 if code == 0 {
182 break
183 }
184 mut buf := [256]char{}
185 C.ERR_error_string_n(code, &buf[0], usize(buf.len))
186 reasons << unsafe { cstring_to_vstring(&buf[0]) }
187 }
188 return reasons.join('; ')
189}
190
191// ssl_error returns non error ssl code or error if unrecoverable and we should panic
192fn ssl_error(ret int, ssl voidptr) !SSLError {
193 res := C.SSL_get_error(ssl, ret)
194 $if trace_ssl ? {
195 eprintln('${@METHOD} ret: ${ret} | ssl: ${ssl:x} | res: ${res}')
196 }
197 match unsafe { SSLError(res) } {
198 .ssl_error_syscall {
199 details := ssl_get_error_queue()
200 suffix := if details == '' { '' } else { ': ${details}' }
201 return error_with_code('net.openssl unrecoverable syscall (${res})${suffix}', res)
202 }
203 .ssl_error_ssl {
204 details := ssl_get_error_queue()
205 suffix := if details == '' { '' } else { ': ${details}' }
206 return error_with_code('net.openssl unrecoverable ssl protocol error (${res})${suffix}',
207 res)
208 }
209 else {
210 return unsafe { SSLError(res) }
211 }
212 }
213}
214
215enum SSLError {
216 ssl_error_none = C.SSL_ERROR_NONE
217 ssl_error_ssl = C.SSL_ERROR_SSL
218 ssl_error_want_read = C.SSL_ERROR_WANT_READ
219 ssl_error_want_write = C.SSL_ERROR_WANT_WRITE
220 ssl_error_want_x509_lookup = C.SSL_ERROR_WANT_X509_LOOKUP
221 ssl_error_syscall = C.SSL_ERROR_SYSCALL
222 ssl_error_zero_return = C.SSL_ERROR_ZERO_RETURN
223 ssl_error_want_connect = C.SSL_ERROR_WANT_CONNECT
224 ssl_error_want_accept = C.SSL_ERROR_WANT_ACCEPT
225 ssl_error_want_async = C.SSL_ERROR_WANT_ASYNC
226 ssl_error_want_async_job = C.SSL_ERROR_WANT_ASYNC_JOB
227}
228