v2 / vlib / net / mbedtls / ssl_connection.c.v
843 lines · 759 sloc · 24.61 KB · 8e35f4d9848f7ad35d857a187dddbfd2eca5e19d
Raw
1module mbedtls
2
3import io
4import net
5import time
6
7const mbedtls_client_read_timeout_ms = $d('mbedtls_client_read_timeout_ms', 10_000)
8const mbedtls_server_read_timeout_ms = $d('mbedtls_server_read_timeout_ms', 41_000)
9const default_mbedtls_client_read_timeout = mbedtls_client_read_timeout_ms * time.millisecond
10
11fn init_rng(mut ctr_drbg C.mbedtls_ctr_drbg_context, mut entropy C.mbedtls_entropy_context) ! {
12 $if trace_ssl ? {
13 eprintln(@METHOD)
14 }
15 C.mbedtls_ctr_drbg_init(&ctr_drbg)
16 C.mbedtls_entropy_init(&entropy)
17 ret := C.mbedtls_ctr_drbg_seed(&ctr_drbg, C.mbedtls_entropy_func, &entropy, 0, 0)
18 if ret != 0 {
19 C.mbedtls_ctr_drbg_free(&ctr_drbg)
20 C.mbedtls_entropy_free(&entropy)
21 return error_with_code('net.mbedtls init_rng, failed to seed ssl context: ${ret}', ret)
22 }
23 // C.mbedtls_debug_set_threshold(5)
24}
25
26fn free_rng(mut ctr_drbg C.mbedtls_ctr_drbg_context, mut entropy C.mbedtls_entropy_context) {
27 C.mbedtls_ctr_drbg_free(&ctr_drbg)
28 C.mbedtls_entropy_free(&entropy)
29}
30
31// SSLCerts represents a pair of CA and client certificates + key
32pub struct SSLCerts {
33pub mut:
34 cacert C.mbedtls_x509_crt
35 client_cert C.mbedtls_x509_crt
36 client_key C.mbedtls_pk_context
37}
38
39// new_sslcerts initializes and returns a pair of SSL certificates and key
40pub fn new_sslcerts() &SSLCerts {
41 mut certs := SSLCerts{}
42 C.mbedtls_x509_crt_init(&certs.cacert)
43 C.mbedtls_x509_crt_init(&certs.client_cert)
44 C.mbedtls_pk_init(&certs.client_key)
45 return &certs
46}
47
48// new_sslcerts_in_memory creates a pair of SSL certificates, given their contents (not paths).
49pub fn new_sslcerts_in_memory(verify string, cert string, cert_key string) !&SSLCerts {
50 mut ctr_drbg := C.mbedtls_ctr_drbg_context{}
51 mut entropy := C.mbedtls_entropy_context{}
52 init_rng(mut ctr_drbg, mut entropy)!
53 defer {
54 free_rng(mut ctr_drbg, mut entropy)
55 }
56 return new_sslcerts_in_memory_with_rng(verify, cert, cert_key, &ctr_drbg)
57}
58
59fn new_sslcerts_in_memory_with_rng(verify string, cert string, cert_key string, rng &C.mbedtls_ctr_drbg_context) !&SSLCerts {
60 mut certs := new_sslcerts()
61 if verify != '' {
62 ret := C.mbedtls_x509_crt_parse(&certs.cacert, verify.str, verify.len + 1)
63 if ret != 0 {
64 return error_with_code('net.mbedtls new_sslcerts_in_memory, mbedtls_x509_crt_parse error 1 ret: ${ret}',
65 ret)
66 }
67 }
68 if cert != '' {
69 ret := C.mbedtls_x509_crt_parse(&certs.client_cert, cert.str, cert.len + 1)
70 if ret != 0 {
71 return error_with_code('net.mbedtls new_sslcerts_in_memory, mbedtls_x509_crt_parse error 2 ret: ${ret}',
72 ret)
73 }
74 }
75 if cert_key != '' {
76 unsafe {
77 ret := C.mbedtls_pk_parse_key(&certs.client_key, cert_key.str, cert_key.len + 1, 0, 0,
78 C.mbedtls_ctr_drbg_random, rng)
79 if ret != 0 {
80 return error_with_code('net.mbedtls new_sslcerts_in_memory, mbedtls_pk_parse_key error ret: ${ret}',
81 ret)
82 }
83 }
84 }
85 return certs
86}
87
88// new_sslcerts_from_file creates a new pair of SSL certificates, given their paths on the filesystem.
89pub fn new_sslcerts_from_file(verify string, cert string, cert_key string) !&SSLCerts {
90 mut ctr_drbg := C.mbedtls_ctr_drbg_context{}
91 mut entropy := C.mbedtls_entropy_context{}
92 init_rng(mut ctr_drbg, mut entropy)!
93 defer {
94 free_rng(mut ctr_drbg, mut entropy)
95 }
96 return new_sslcerts_from_file_with_rng(verify, cert, cert_key, &ctr_drbg)
97}
98
99fn new_sslcerts_from_file_with_rng(verify string, cert string, cert_key string, rng &C.mbedtls_ctr_drbg_context) !&SSLCerts {
100 mut certs := new_sslcerts()
101 if verify != '' {
102 ret := C.mbedtls_x509_crt_parse_file(&certs.cacert, &char(verify.str))
103 if ret != 0 {
104 return error_with_code('net.mbedtls new_sslcerts_from_file, mbedtls_x509_crt_parse_file error 1 ret: ${ret}',
105 ret)
106 }
107 }
108 if cert != '' {
109 ret := C.mbedtls_x509_crt_parse_file(&certs.client_cert, &char(cert.str))
110 if ret != 0 {
111 return error_with_code('net.mbedtls new_sslcerts_from_file, mbedtls_x509_crt_parse_file error 2 ret: ${ret}',
112 ret)
113 }
114 }
115 if cert_key != '' {
116 unsafe {
117 ret := C.mbedtls_pk_parse_keyfile(&certs.client_key, &char(cert_key.str), 0,
118 C.mbedtls_ctr_drbg_random, rng)
119 if ret != 0 {
120 return error_with_code('net.mbedtls new_sslcerts_from_file, mbedtls_pk_parse_keyfile error ret: ${ret}',
121 ret)
122 }
123 }
124 }
125 return certs
126}
127
128// cleanup frees the SSL certificates
129pub fn (mut c SSLCerts) cleanup() {
130 C.mbedtls_x509_crt_free(&c.cacert)
131 C.mbedtls_x509_crt_free(&c.client_cert)
132 C.mbedtls_pk_free(&c.client_key)
133}
134
135// SSLConn is the current connection
136pub struct SSLConn {
137pub:
138 config SSLConnectConfig
139pub mut:
140 server_fd C.mbedtls_net_context
141 ssl C.mbedtls_ssl_context
142 conf C.mbedtls_ssl_config
143 certs &SSLCerts = unsafe { nil }
144 ctr_drbg C.mbedtls_ctr_drbg_context
145 entropy C.mbedtls_entropy_context
146 handle int
147 duration time.Duration
148 opened bool
149 ip string
150 read_timeout time.Duration
151
152 owns_socket bool
153}
154
155// SSLListener listens on a TCP port and accepts connection secured with TLS
156pub struct SSLListener {
157 saddr string
158 config SSLConnectConfig
159mut:
160 server_fd C.mbedtls_net_context
161 ssl C.mbedtls_ssl_context
162 conf C.mbedtls_ssl_config
163 certs &SSLCerts = unsafe { nil }
164 ctr_drbg C.mbedtls_ctr_drbg_context
165 entropy C.mbedtls_entropy_context
166 opened bool
167 // handle int
168 // duration time.Duration
169}
170
171// create a new SSLListener binding to `saddr`
172pub fn new_ssl_listener(saddr string, config SSLConnectConfig) !&SSLListener {
173 mut listener := &SSLListener{
174 saddr: saddr
175 config: config
176 }
177 listener.init()!
178 listener.opened = true
179 return listener
180}
181
182// finish the listener and clean up resources
183pub fn (mut l SSLListener) shutdown() ! {
184 $if trace_ssl ? {
185 eprintln(@METHOD)
186 }
187 if unsafe { l.certs != nil } {
188 l.certs.cleanup()
189 }
190 C.mbedtls_ssl_free(&l.ssl)
191 C.mbedtls_ssl_config_free(&l.conf)
192 free_rng(mut l.ctr_drbg, mut l.entropy)
193 if l.opened {
194 C.mbedtls_net_free(&l.server_fd)
195 }
196}
197
198// internal function to init and bind the listener
199fn (mut l SSLListener) init() ! {
200 $if trace_ssl ? {
201 eprintln(@METHOD)
202 }
203
204 lhost, lport := net.split_address(l.saddr)!
205 if l.config.cert == '' || l.config.cert_key == '' {
206 return error('net.mbedtls SSLListener.init, no certificate or key provided')
207 }
208 if l.config.validate && l.config.verify == '' {
209 return error('net.mbedtls SSLListener.init, no root CA provided')
210 }
211 C.mbedtls_net_init(&l.server_fd)
212 C.mbedtls_ssl_init(&l.ssl)
213 C.mbedtls_ssl_config_init(&l.conf)
214 init_rng(mut l.ctr_drbg, mut l.entropy)!
215 $if trace_mbedtls_timeouts ? {
216 dump(mbedtls_server_read_timeout_ms)
217 }
218 C.mbedtls_ssl_conf_read_timeout(&l.conf, mbedtls_server_read_timeout_ms)
219 l.certs = &SSLCerts{}
220 C.mbedtls_x509_crt_init(&l.certs.client_cert)
221 C.mbedtls_pk_init(&l.certs.client_key)
222
223 unsafe {
224 C.mbedtls_ssl_conf_rng(&l.conf, C.mbedtls_ctr_drbg_random, &l.ctr_drbg)
225 }
226
227 mut ret := 0
228
229 if l.config.in_memory_verification {
230 l.certs = new_sslcerts_in_memory_with_rng(l.config.verify, l.config.cert,
231 l.config.cert_key, &l.ctr_drbg) or {
232 return error('net.mbedtls SSLListener.init, cert failure 1, err: ${err}')
233 }
234 } else {
235 l.certs = new_sslcerts_from_file_with_rng(l.config.verify, l.config.cert,
236 l.config.cert_key, &l.ctr_drbg) or {
237 return error('net.mbedtls SSLListener.init, cert failure 2, err: ${err}')
238 }
239 }
240
241 if l.config.validate {
242 C.mbedtls_ssl_conf_authmode(&l.conf, C.MBEDTLS_SSL_VERIFY_REQUIRED)
243 }
244
245 mut bind_ip := unsafe { nil }
246 if lhost != '' {
247 bind_ip = voidptr(lhost.str)
248 }
249 bind_port := lport.str()
250
251 ret = C.mbedtls_net_bind(&l.server_fd, bind_ip, voidptr(bind_port.str), C.MBEDTLS_NET_PROTO_TCP)
252
253 if ret != 0 {
254 return error_with_code("net.mbedtls SSLListener.init, mbedtls_net_bind can't bind to ${l.saddr} error ret: ${ret}",
255 ret)
256 }
257
258 ret = C.mbedtls_ssl_config_defaults(&l.conf, C.MBEDTLS_SSL_IS_SERVER,
259 C.MBEDTLS_SSL_TRANSPORT_STREAM, C.MBEDTLS_SSL_PRESET_DEFAULT)
260 if ret != 0 {
261 return error_with_code("net.mbedtls SSLListener.init, mbedtls_ssl_config_defaults can't set config defaults ret: ${ret}",
262 ret)
263 }
264
265 C.mbedtls_ssl_conf_ca_chain(&l.conf, &l.certs.cacert, unsafe { nil })
266 ret = C.mbedtls_ssl_conf_own_cert(&l.conf, &l.certs.client_cert, &l.certs.client_key)
267 if ret != 0 {
268 return error_with_code("net.mbedtls SSLListener.init, mbedtls_ssl_conf_own_cert can't load certificate ret: ${ret}",
269 ret)
270 }
271
272 ret = C.mbedtls_ssl_setup(&l.ssl, &l.conf)
273 if ret != 0 {
274 return error_with_code("net.mbedtls SSLListener.init, mbedtls_ssl_setup can't setup ssl ret: ${ret}",
275 ret)
276 }
277
278 if get_cert_callback := l.config.get_certificate {
279 l.init_sni(get_cert_callback)
280 }
281}
282
283// setup SNI callback
284fn (mut l SSLListener) init_sni(get_cert_callback fn (mut SSLListener, string) !&SSLCerts) {
285 $if trace_ssl ? {
286 eprintln(@METHOD)
287 }
288 C.mbedtls_ssl_conf_sni(&l.conf, fn [get_cert_callback, mut l] (p_info voidptr, ssl &C.mbedtls_ssl_context, name &char, lng int) int {
289 host := unsafe { name.vstring_literal_with_len(lng) }
290 if certs := get_cert_callback(mut l, host) {
291 return C.mbedtls_ssl_set_hs_own_cert(ssl, &certs.client_cert, &certs.client_key)
292 } else {
293 return -1
294 }
295 }, &l.conf)
296}
297
298// accepts a new connection and returns a SSLConn of the connected client
299pub fn (mut l SSLListener) accept() !&SSLConn {
300 mut conn := &SSLConn{
301 config: l.config
302 opened: true
303 }
304
305 ip := [16]u8{}
306 iplen := usize(0)
307
308 mut ret := C.mbedtls_net_accept(&l.server_fd, &conn.server_fd, &ip, 16, &iplen)
309 if ret != 0 {
310 return error_with_code("net.mbedtls SSLListener.accept, mbedtls_net_accept can't accept connection ret: ${ret}",
311 ret)
312 }
313 conn.handle = conn.server_fd.fd
314 conn.owns_socket = true
315 if iplen == 4 {
316 conn.ip = '${ip[0]}.${ip[1]}.${ip[2]}.${ip[3]}'
317 }
318
319 C.mbedtls_ssl_init(&conn.ssl)
320 C.mbedtls_ssl_config_init(&conn.conf)
321 ret = C.mbedtls_ssl_setup(&conn.ssl, &l.conf)
322 if ret != 0 {
323 return error_with_code('net.mbedtls SSLListener.accept, mbedtls_ssl_setup SSL setup failed ret: ${ret}',
324 ret)
325 }
326
327 C.mbedtls_ssl_set_bio(&conn.ssl, &conn.server_fd, C.mbedtls_net_send, C.mbedtls_net_recv,
328 C.mbedtls_net_recv_timeout)
329
330 ret = C.mbedtls_ssl_handshake(&conn.ssl)
331 for ret != 0 {
332 if ret != C.MBEDTLS_ERR_SSL_WANT_READ && ret != C.MBEDTLS_ERR_SSL_WANT_WRITE {
333 conn.shutdown() or {
334 $if trace_ssl ? {
335 eprintln('${@METHOD} shutdown ---> res: ${err}')
336 }
337 }
338 return error_with_code('net.mbedtls SSLListener.accept, mbedtls_ssl_handshake failed 1; handshake ret: ${ret}',
339 ret)
340 }
341 ret = C.mbedtls_ssl_handshake(&conn.ssl)
342 }
343
344 return conn
345}
346
347@[params]
348pub struct SSLConnectConfig {
349pub:
350 verify string // the path to a rootca.pem file, containing trusted CA certificate(s)
351 cert string // the path to a cert.pem file, containing client certificate(s) for the request
352 cert_key string // the path to a key.pem file, containing private keys for the client certificate(s)
353 validate bool // set this to true, if you want to stop requests, when their certificates are found to be invalid
354
355 in_memory_verification bool // if true, verify, cert, and cert_key are read from memory, not from a file
356
357 get_certificate ?fn (mut SSLListener, string) !&SSLCerts
358
359 read_timeout time.Duration = default_mbedtls_client_read_timeout // the SSL client read timeout
360}
361
362fn ssl_read_timeout_ms(timeout time.Duration) u32 {
363 if timeout <= 0 || timeout == net.infinite_timeout {
364 return 0
365 }
366 timeout_ms := timeout.milliseconds()
367 if timeout_ms > i64(max_u32) {
368 return max_u32
369 }
370 return u32(timeout_ms)
371}
372
373fn ssl_timeout_deadline(timeout time.Duration) time.Time {
374 if timeout <= 0 || timeout == net.infinite_timeout {
375 return time.unix(0)
376 }
377 return time.now().add(timeout)
378}
379
380fn ssl_remaining_timeout(deadline time.Time) time.Duration {
381 if deadline.unix() == 0 {
382 return net.infinite_timeout
383 }
384 return deadline - time.now()
385}
386
387// read_timeout returns the current SSL read timeout.
388pub fn (s &SSLConn) read_timeout() time.Duration {
389 return s.read_timeout
390}
391
392// set_read_timeout sets the SSL read timeout for subsequent operations.
393pub fn (mut s SSLConn) set_read_timeout(timeout time.Duration) {
394 s.read_timeout = timeout
395 s.duration = timeout
396 C.mbedtls_ssl_conf_read_timeout(&s.conf, ssl_read_timeout_ms(timeout))
397}
398
399// new_ssl_conn returns a new SSLConn with the given config.
400pub fn new_ssl_conn(config SSLConnectConfig) !&SSLConn {
401 $if trace_ssl ? {
402 eprintln(@METHOD)
403 }
404 mut conn := &SSLConn{
405 config: config
406 duration: config.read_timeout
407 read_timeout: config.read_timeout
408 }
409 conn.init()!
410 return conn
411}
412
413// Select operation
414enum Select {
415 read
416 write
417 except
418}
419
420// close terminates the ssl connection and does cleanup
421pub fn (mut s SSLConn) close() ! {
422 s.shutdown()!
423}
424
425// shutdown terminates the ssl connection and does cleanup
426pub fn (mut s SSLConn) shutdown() ! {
427 $if trace_ssl ? {
428 eprintln(@METHOD)
429 }
430 if !s.opened {
431 return error('net.mbedtls SSLConn.shutdown, connection was not open')
432 }
433 if unsafe { s.certs != nil } {
434 C.mbedtls_x509_crt_free(&s.certs.cacert)
435 C.mbedtls_x509_crt_free(&s.certs.client_cert)
436 C.mbedtls_pk_free(&s.certs.client_key)
437 }
438 C.mbedtls_ssl_free(&s.ssl)
439 C.mbedtls_ssl_config_free(&s.conf)
440 free_rng(mut s.ctr_drbg, mut s.entropy)
441 if s.owns_socket {
442 net.shutdown(s.handle)
443 net.close(s.handle)!
444 }
445}
446
447// connect to server using mbedtls
448fn (mut s SSLConn) init() ! {
449 $if trace_ssl ? {
450 eprintln(@METHOD)
451 }
452 C.mbedtls_net_init(&s.server_fd)
453 C.mbedtls_ssl_init(&s.ssl)
454 C.mbedtls_ssl_config_init(&s.conf)
455 init_rng(mut s.ctr_drbg, mut s.entropy)!
456 mut ret := 0
457 ret = C.mbedtls_ssl_config_defaults(&s.conf, C.MBEDTLS_SSL_IS_CLIENT,
458 C.MBEDTLS_SSL_TRANSPORT_STREAM, C.MBEDTLS_SSL_PRESET_DEFAULT)
459 if ret != 0 {
460 return error_with_code('net.mbedtls SSLConn.init, mbedtls_ssl_config_defaults failed to set SSL configuration ret: ${ret}',
461 ret)
462 }
463 $if trace_mbedtls_timeouts ? {
464 dump(s.read_timeout)
465 }
466 s.set_read_timeout(s.read_timeout)
467
468 unsafe {
469 C.mbedtls_ssl_conf_rng(&s.conf, C.mbedtls_ctr_drbg_random, &s.ctr_drbg)
470
471 // Enable ALPN for HTTP/1.1 (Required by strict servers like Rustls/Pijul)
472 // We allocate a small C array of strings: ["http/1.1", NULL]
473 // This memory must persist while the SSL config is active.
474 /*
475 alpn_list := &&char(C.malloc(2 * sizeof(voidptr)))
476 if alpn_list != 0 {
477 alpn_list[0] = c'http/1.1'
478 alpn_list[1] = &char(0)
479 C.mbedtls_ssl_conf_alpn_protocols(&s.conf, alpn_list)
480 }
481 TODO free alpn_list
482 */
483 }
484 if s.config.verify != '' || s.config.cert != '' || s.config.cert_key != '' {
485 s.certs = &SSLCerts{}
486 C.mbedtls_x509_crt_init(&s.certs.cacert)
487 C.mbedtls_x509_crt_init(&s.certs.client_cert)
488 C.mbedtls_pk_init(&s.certs.client_key)
489 }
490
491 if s.config.in_memory_verification {
492 if s.config.verify != '' {
493 ret = C.mbedtls_x509_crt_parse(&s.certs.cacert, s.config.verify.str,
494
495 s.config.verify.len + 1)
496 }
497 if s.config.cert != '' {
498 ret = C.mbedtls_x509_crt_parse(&s.certs.client_cert, s.config.cert.str,
499
500 s.config.cert.len + 1)
501 }
502 if s.config.cert_key != '' {
503 unsafe {
504 ret = C.mbedtls_pk_parse_key(&s.certs.client_key, s.config.cert_key.str,
505
506 s.config.cert_key.len + 1, 0, 0, C.mbedtls_ctr_drbg_random, &s.ctr_drbg)
507 }
508 }
509 } else {
510 if s.config.verify != '' {
511 ret = C.mbedtls_x509_crt_parse_file(&s.certs.cacert, &char(s.config.verify.str))
512 }
513 if s.config.cert != '' {
514 ret = C.mbedtls_x509_crt_parse_file(&s.certs.client_cert, &char(s.config.cert.str))
515 }
516 if s.config.cert_key != '' {
517 unsafe {
518 ret = C.mbedtls_pk_parse_keyfile(&s.certs.client_key, &char(s.config.cert_key.str),
519 0, C.mbedtls_ctr_drbg_random, &s.ctr_drbg)
520 }
521 }
522 }
523 if ret < 0 {
524 return error_with_code('net.mbedtls SSLConn.init, failed to set certificates, ret: ${ret}',
525 ret)
526 }
527
528 if unsafe { s.certs != nil } {
529 C.mbedtls_ssl_conf_ca_chain(&s.conf, &s.certs.cacert, 0)
530 C.mbedtls_ssl_conf_own_cert(&s.conf, &s.certs.client_cert, &s.certs.client_key)
531 }
532
533 if s.config.validate {
534 C.mbedtls_ssl_conf_authmode(&s.conf, C.MBEDTLS_SSL_VERIFY_REQUIRED)
535 } else {
536 C.mbedtls_ssl_conf_authmode(&s.conf, C.MBEDTLS_SSL_VERIFY_OPTIONAL)
537 }
538
539 ret = C.mbedtls_ssl_setup(&s.ssl, &s.conf)
540 if ret != 0 {
541 return error_with_code('net.mbedtls SSLConn.init, mbedtls_ssl_setup failed to setup SSL connection ret: ${ret}',
542 ret)
543 }
544}
545
546// connect sets up an ssl connection on an existing TCP connection
547pub fn (mut s SSLConn) connect(mut tcp_conn net.TcpConn, hostname string) ! {
548 $if trace_ssl ? {
549 eprintln('${@METHOD} hostname: ${hostname}')
550 }
551 if s.opened {
552 return error('net.mbedtls SSLConn.connect, ssl connection was already open')
553 }
554 s.handle = tcp_conn.sock.handle
555 s.set_read_timeout(tcp_conn.read_timeout())
556 mut ret := C.mbedtls_ssl_set_hostname(&s.ssl, &char(hostname.str))
557 if ret != 0 {
558 return error_with_code('net.mbedtls SSLConn.connect, mbedtls_ssl_set_hostname failed to set hostname',
559 ret)
560 }
561 s.server_fd.fd = s.handle
562 C.mbedtls_ssl_set_bio(&s.ssl, &s.server_fd, C.mbedtls_net_send, C.mbedtls_net_recv,
563 C.mbedtls_net_recv_timeout)
564 ret = C.mbedtls_ssl_handshake(&s.ssl)
565 if ret != 0 {
566 return error_with_code('net.mbedtls SSLConn.connect, mbedtls_ssl_handshake failed 2; ret: ${ret}',
567 ret)
568 }
569 s.opened = true
570}
571
572// dial opens an ssl connection on hostname:port
573pub fn (mut s SSLConn) dial(hostname string, port int) ! {
574 $if trace_ssl ? {
575 eprintln('${@METHOD} hostname: ${hostname} | port: ${port}')
576 }
577 if s.opened {
578 return error('net.mbedtls SSLConn.dial, the ssl connection was already open')
579 }
580 mut connected := false
581 defer {
582 if !connected {
583 if unsafe { s.certs != nil } {
584 C.mbedtls_x509_crt_free(&s.certs.cacert)
585 C.mbedtls_x509_crt_free(&s.certs.client_cert)
586 C.mbedtls_pk_free(&s.certs.client_key)
587 s.certs = unsafe { nil }
588 }
589 C.mbedtls_net_free(&s.server_fd)
590 C.mbedtls_ssl_free(&s.ssl)
591 C.mbedtls_ssl_config_free(&s.conf)
592 free_rng(mut s.ctr_drbg, mut s.entropy)
593 s.handle = 0
594 s.owns_socket = false
595 }
596 }
597 s.owns_socket = true
598
599 mut ret := C.mbedtls_ssl_set_hostname(&s.ssl, &char(hostname.str))
600 if ret != 0 {
601 return error_with_code('net.mbedtls SSLConn.dial, failed to set hostname', ret)
602 }
603
604 port_str := port.str()
605 ret = C.mbedtls_net_connect(&s.server_fd, &char(hostname.str), &char(port_str.str),
606 C.MBEDTLS_NET_PROTO_TCP)
607 if ret != 0 {
608 return error_with_code('net.mbedtls SSLConn.dial, failed to connect to host', ret)
609 }
610 C.mbedtls_ssl_set_bio(&s.ssl, &s.server_fd, C.mbedtls_net_send, C.mbedtls_net_recv,
611 C.mbedtls_net_recv_timeout)
612 s.handle = s.server_fd.fd
613 ret = C.mbedtls_ssl_handshake(&s.ssl)
614 if ret != 0 {
615 return error_with_code('net.mbedtls SSLConn.dial, mbedtls_ssl_handshake failed 3; ret: ${ret}',
616 ret)
617 }
618 s.opened = true
619 connected = true
620}
621
622// addr retrieves the local ip address and port number for this connection
623pub fn (s &SSLConn) addr() !net.Addr {
624 return net.addr_from_socket_handle(s.handle)
625}
626
627// peer_addr retrieves the ip address and port number used by the peer
628pub fn (s &SSLConn) peer_addr() !net.Addr {
629 return net.peer_addr_from_socket_handle(s.handle)
630}
631
632// socket_read_into_ptr reads `len` bytes into `buf`
633pub fn (mut s SSLConn) socket_read_into_ptr(buf_ptr &u8, len int) !int {
634 mut res := 0
635 $if trace_ssl ? {
636 defer(fn) {
637 if len > 0 {
638 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) }}`')
639 }
640 }
641 }
642
643 deadline := ssl_timeout_deadline(s.duration)
644 // s.wait_for_read(deadline - time.now())!
645 for {
646 res = C.mbedtls_ssl_read(&s.ssl, buf_ptr, len)
647 if res > 0 {
648 return res
649 } else if res == 0 {
650 $if trace_ssl ? {
651 eprintln('${@METHOD} ---> res: io.Eof')
652 }
653 return io.Eof{}
654 } else {
655 match res {
656 C.MBEDTLS_ERR_SSL_WANT_READ {
657 s.wait_for_read(ssl_remaining_timeout(deadline)) or {
658 $if trace_ssl ? {
659 eprintln('${@METHOD} ---> res: ${err}, C.MBEDTLS_ERR_SSL_WANT_READ')
660 }
661 return err
662 }
663 }
664 C.MBEDTLS_ERR_SSL_WANT_WRITE {
665 s.wait_for_write(ssl_remaining_timeout(deadline)) or {
666 $if trace_ssl ? {
667 eprintln('${@METHOD} ---> res: ${err}, C.MBEDTLS_ERR_SSL_WANT_WRITE')
668 }
669 return err
670 }
671 }
672 C.MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET {
673 // TLS 1.3 servers can deliver tickets asynchronously while the
674 // connection is otherwise healthy. Keep reading application data.
675 continue
676 }
677 C.MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY {
678 $if trace_ssl ? {
679 eprintln('${@METHOD} ---> res: 0 C.MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY')
680 }
681 return 0
682 }
683 C.MBEDTLS_ERR_SSL_TIMEOUT {
684 $if trace_ssl ? {
685 eprintln('${@METHOD} ---> res: C.MBEDTLS_ERR_SSL_TIMEOUT')
686 }
687 return error_with_code('net.mbedtls SSLConn.socket_read_into_ptr, did not receive any data within ${s.read_timeout.milliseconds()}ms. Use conn.set_read_timeout(...) to increase the timeout',
688 res)
689 }
690 else {
691 $if trace_ssl ? {
692 eprintln('${@METHOD} ---> res: could not read using SSL')
693 }
694 return error_with_code('net.mbedtls SSLConn.socket_read_into_ptr, could not read using SSL',
695 res)
696 }
697 }
698 }
699 }
700 // Dead code, just to satisfy the compiler:
701 return error('net.mbedtls SSLConn.socket_read_into_ptr, unknown error')
702}
703
704// read reads data from the ssl connection into `buffer`
705pub fn (mut s SSLConn) read(mut buffer []u8) !int {
706 $if trace_ssl ? {
707 eprintln('${@METHOD} buffer.len: ${buffer.len}')
708 }
709 return s.socket_read_into_ptr(&u8(buffer.data), buffer.len)
710}
711
712// write_ptr writes `len` bytes from `bytes` to the ssl connection
713pub fn (mut s SSLConn) write_ptr(bytes &u8, len int) !int {
714 mut total_sent := 0
715 $if trace_ssl ? {
716 defer(fn) {
717 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-=-=-=-')
718 }
719 }
720
721 deadline := ssl_timeout_deadline(s.duration)
722 unsafe {
723 mut ptr_base := bytes
724 for total_sent < len {
725 ptr := ptr_base + total_sent
726 remaining := len - total_sent
727 mut sent := C.mbedtls_ssl_write(&s.ssl, ptr, remaining)
728 if sent <= 0 {
729 match sent {
730 C.MBEDTLS_ERR_SSL_WANT_READ {
731 s.wait_for_read(ssl_remaining_timeout(deadline))!
732 continue
733 }
734 C.MBEDTLS_ERR_SSL_WANT_WRITE {
735 s.wait_for_write(ssl_remaining_timeout(deadline))!
736 continue
737 }
738 else {
739 $if trace_ssl ? {
740 eprintln('${@METHOD} ---> res: could not write SSL, sent: ${sent}')
741 }
742 return error_with_code('net.mbedtls SSLConn.write_ptr, could not write using SSL',
743 sent)
744 }
745 }
746 }
747 total_sent += sent
748 }
749 }
750 return total_sent
751}
752
753// write writes data from `bytes` to the ssl connection
754pub fn (mut s SSLConn) write(bytes []u8) !int {
755 return s.write_ptr(&u8(bytes.data), bytes.len)
756}
757
758// write_string writes a string to the ssl connection
759pub fn (mut s SSLConn) write_string(str string) !int {
760 $if trace_ssl ? {
761 eprintln('${@METHOD} str: ${str}')
762 }
763 return s.write_ptr(str.str, str.len)
764}
765
766// Select waits for an io operation (specified by parameter `test`) to be available
767fn select(handle int, test Select, timeout time.Duration) !bool {
768 $if trace_ssl ? {
769 eprintln('${@METHOD} handle: ${handle}, timeout: ${timeout}')
770 }
771 set := C.fd_set{}
772 C.FD_ZERO(&set)
773 C.FD_SET(handle, &set)
774
775 is_infinite := timeout <= 0 || timeout == net.infinite_timeout
776 deadline := ssl_timeout_deadline(timeout)
777 mut remaining_time := if is_infinite { i64(0) } else { timeout.milliseconds() }
778 for is_infinite || remaining_time > 0 {
779 seconds := remaining_time / 1000
780 microseconds := (remaining_time % 1000) * 1000
781
782 tt := C.timeval{
783 tv_sec: u64(seconds)
784 tv_usec: u64(microseconds)
785 }
786 timeval_timeout := if is_infinite { &C.timeval(unsafe { nil }) } else { &tt }
787
788 mut res := -1
789 match test {
790 .read {
791 res = net.socket_error(C.select(handle + 1, &set, C.NULL, C.NULL, timeval_timeout))!
792 }
793 .write {
794 res = net.socket_error(C.select(handle + 1, C.NULL, &set, C.NULL, timeval_timeout))!
795 }
796 .except {
797 res = net.socket_error(C.select(handle + 1, C.NULL, C.NULL, &set, timeval_timeout))!
798 }
799 }
800
801 if res < 0 {
802 if C.errno == C.EINTR {
803 // errno is 4, Spurious wakeup from signal, keep waiting
804 if !is_infinite {
805 remaining_time = ssl_remaining_timeout(deadline).milliseconds()
806 }
807 continue
808 }
809 cerr := C.errno
810 return error_with_code('net.mbedtls select, failed, res: ${res}', cerr)
811 } else if res == 0 {
812 return net.err_timed_out
813 }
814
815 res = C.FD_ISSET(handle, &set)
816 $if trace_ssl ? {
817 eprintln('${@METHOD} ---> res: ${res}')
818 }
819 return res != 0
820 }
821
822 return net.err_timed_out
823}
824
825// wait_for wraps the common wait code
826fn wait_for(handle int, what Select, timeout time.Duration) ! {
827 ready := select(handle, what, timeout)!
828 if ready {
829 return
830 }
831
832 return net.err_timed_out
833}
834
835// wait_for_write waits for a write io operation to be available
836fn (mut s SSLConn) wait_for_write(timeout time.Duration) ! {
837 return wait_for(s.handle, .write, timeout)
838}
839
840// wait_for_read waits for a read io operation to be available
841fn (mut s SSLConn) wait_for_read(timeout time.Duration) ! {
842 return wait_for(s.handle, .read, timeout)
843}
844