@@ -4,10 +4,16 @@ use crate::network_poller::Interest;
4
4
use crate :: process:: ProcessPointer ;
5
5
use crate :: result:: { error_to_int, Result } ;
6
6
use crate :: scheduler:: timeouts:: Timeout ;
7
- use crate :: socket:: Socket ;
7
+ use crate :: socket:: { read_from , Socket } ;
8
8
use crate :: state:: State ;
9
- use std:: io:: { self , Write } ;
9
+ use rustls:: pki_types:: ServerName ;
10
+ use rustls:: { ClientConfig , ClientConnection , RootCertStore , Stream } ;
11
+ use rustls_pemfile:: certs;
12
+ use rustls_platform_verifier:: tls_config;
13
+ use std:: fs:: File ;
14
+ use std:: io:: { self , BufReader , Write } ;
10
15
use std:: ptr:: { drop_in_place, write} ;
16
+ use std:: sync:: Arc ;
11
17
12
18
#[ repr( C ) ]
13
19
pub struct RawAddress {
@@ -24,19 +30,13 @@ impl RawAddress {
24
30
}
25
31
}
26
32
27
- fn blocking < T > (
33
+ fn poll (
28
34
state : & State ,
29
35
mut process : ProcessPointer ,
30
36
socket : & mut Socket ,
31
37
interest : Interest ,
32
38
deadline : i64 ,
33
- mut func : impl FnMut ( & mut Socket ) -> io:: Result < T > ,
34
- ) -> io:: Result < T > {
35
- match func ( socket) {
36
- Err ( err) if err. kind ( ) == io:: ErrorKind :: WouldBlock => { }
37
- val => return val,
38
- }
39
-
39
+ ) -> io:: Result < ( ) > {
40
40
let poll_id = unsafe { process. thread ( ) } . network_poller ;
41
41
42
42
// We must keep the process' state lock open until everything is registered,
@@ -72,7 +72,24 @@ fn blocking<T>(
72
72
return Err ( io:: Error :: from ( io:: ErrorKind :: TimedOut ) ) ;
73
73
}
74
74
75
- func ( socket)
75
+ Ok ( ( ) )
76
+ }
77
+
78
+ fn blocking < T > (
79
+ state : & State ,
80
+ process : ProcessPointer ,
81
+ socket : & mut Socket ,
82
+ interest : Interest ,
83
+ deadline : i64 ,
84
+ mut func : impl FnMut ( & mut Socket ) -> io:: Result < T > ,
85
+ ) -> io:: Result < T > {
86
+ match func ( socket) {
87
+ Err ( err) if err. kind ( ) == io:: ErrorKind :: WouldBlock => {
88
+ poll ( state, process, socket, interest, deadline)
89
+ . and_then ( |_| func ( socket) )
90
+ }
91
+ val => val,
92
+ }
76
93
}
77
94
78
95
#[ no_mangle]
@@ -127,7 +144,7 @@ pub unsafe extern "system" fn inko_socket_read(
127
144
let state = & * state;
128
145
129
146
blocking ( state, process, & mut * socket, Interest :: Read , deadline, |sock| {
130
- sock . read ( & mut ( * buffer) . value , amount as usize )
147
+ read_from ( sock , & mut ( * buffer) . value , amount as usize )
131
148
} )
132
149
. map ( |size| Result :: ok ( size as _ ) )
133
150
. unwrap_or_else ( Result :: io_error)
@@ -349,3 +366,161 @@ pub unsafe extern "system" fn inko_socket_try_clone(
349
366
pub unsafe extern "system" fn inko_socket_drop ( socket : * mut Socket ) {
350
367
drop_in_place ( socket) ;
351
368
}
369
+
370
+ #[ no_mangle]
371
+ pub unsafe extern "system" fn inko_tls_client_config_new ( ) -> Result {
372
+ Result :: ok ( Arc :: into_raw ( Arc :: new ( tls_config ( ) ) ) as * mut _ )
373
+ }
374
+
375
+ #[ no_mangle]
376
+ pub unsafe extern "system" fn inko_tls_client_config_with_certificate (
377
+ path : * const InkoString ,
378
+ ) -> Result {
379
+ let mut store = RootCertStore :: empty ( ) ;
380
+ let mut reader = match File :: open ( InkoString :: read ( path) ) {
381
+ Ok ( f) => BufReader :: new ( f) ,
382
+ Err ( e) => return Result :: io_error ( e) ,
383
+ } ;
384
+
385
+ for res in certs ( & mut reader) {
386
+ match res {
387
+ // We don't want to expose a bunch of error messages/cases for the
388
+ // different reasons for a certificate being invalid, as it's not
389
+ // clear users actually care about that. As such, at least for the
390
+ // time being we just use a single opaque error for invalid
391
+ // certificates.
392
+ Ok ( cert) => {
393
+ if store. add ( cert) . is_err ( ) {
394
+ return Result :: none ( ) ;
395
+ }
396
+ }
397
+ Err ( e) => return Result :: io_error ( e) ,
398
+ }
399
+ }
400
+
401
+ let conf = Arc :: new (
402
+ ClientConfig :: builder ( )
403
+ . with_root_certificates ( store)
404
+ . with_no_client_auth ( ) ,
405
+ ) ;
406
+
407
+ Result :: ok ( Arc :: into_raw ( conf) as * mut _ )
408
+ }
409
+
410
+ #[ no_mangle]
411
+ pub unsafe extern "system" fn inko_tls_client_config_clone (
412
+ config : * const ClientConfig ,
413
+ ) -> * const ClientConfig {
414
+ Arc :: increment_strong_count ( config) ;
415
+ config
416
+ }
417
+
418
+ #[ no_mangle]
419
+ pub unsafe extern "system" fn inko_tls_client_config_drop (
420
+ config : * const ClientConfig ,
421
+ ) {
422
+ drop ( Arc :: from_raw ( config) ) ;
423
+ }
424
+
425
+ #[ no_mangle]
426
+ pub unsafe extern "system" fn inko_tls_client_connection_new (
427
+ config : * const ClientConfig ,
428
+ server : * const InkoString ,
429
+ ) -> Result {
430
+ let name = match ServerName :: try_from ( InkoString :: read ( server) ) {
431
+ Ok ( v) => v,
432
+ Err ( _) => return Result :: error ( 0 as _ ) ,
433
+ } ;
434
+
435
+ Arc :: increment_strong_count ( config) ;
436
+
437
+ // ClientConnection::new() is fallible, but from the documentation and
438
+ // source code it's not at all clear under what circumstances it produces an
439
+ // error, and if such an error can be handled in a meaningful way. As such,
440
+ // we panic in the event of an error.
441
+ let con = ClientConnection :: new ( Arc :: from_raw ( config) , name)
442
+ . expect ( "failed to set up the client connection" ) ;
443
+
444
+ Result :: ok_boxed ( con)
445
+ }
446
+
447
+ #[ no_mangle]
448
+ pub unsafe extern "system" fn inko_tls_client_connection_drop (
449
+ connection : * mut ClientConnection ,
450
+ ) {
451
+ drop ( Box :: from_raw ( connection) ) ;
452
+ }
453
+
454
+ #[ no_mangle]
455
+ pub unsafe extern "system" fn inko_tls_socket_write (
456
+ state : * const State ,
457
+ process : ProcessPointer ,
458
+ socket : * mut Socket ,
459
+ connection : * mut ClientConnection ,
460
+ data : * mut u8 ,
461
+ size : i64 ,
462
+ deadline : i64 ,
463
+ ) -> Result {
464
+ let state = & * state;
465
+ let slice = std:: slice:: from_raw_parts ( data, size as _ ) ;
466
+ let mut stream = Stream :: new ( & mut * connection, & mut * socket) ;
467
+
468
+ loop {
469
+ match stream. write ( slice) {
470
+ Err ( err) if err. kind ( ) == io:: ErrorKind :: WouldBlock => {
471
+ let interest = Interest :: new (
472
+ stream. conn . wants_read ( ) ,
473
+ stream. conn . wants_write ( ) ,
474
+ ) ;
475
+
476
+ if let Err ( e) =
477
+ poll ( state, process, stream. sock , interest, deadline)
478
+ {
479
+ return Result :: io_error ( e) ;
480
+ }
481
+ }
482
+ val => {
483
+ return val
484
+ . map ( |v| Result :: ok ( v as _ ) )
485
+ . unwrap_or_else ( Result :: io_error) ;
486
+ }
487
+ }
488
+ }
489
+ }
490
+
491
+ #[ no_mangle]
492
+ pub unsafe extern "system" fn inko_tls_socket_read (
493
+ state : * const State ,
494
+ process : ProcessPointer ,
495
+ socket : * mut Socket ,
496
+ connection : * mut ClientConnection ,
497
+ buffer : * mut ByteArray ,
498
+ amount : i64 ,
499
+ deadline : i64 ,
500
+ ) -> Result {
501
+ let state = & * state;
502
+ let buf = & mut ( * buffer) . value ;
503
+ let mut stream = Stream :: new ( & mut * connection, & mut * socket) ;
504
+
505
+ loop {
506
+ match read_from ( & mut stream, buf, amount as usize ) {
507
+ Err ( err) if err. kind ( ) == io:: ErrorKind :: WouldBlock => {
508
+ let interest = Interest :: new (
509
+ stream. conn . wants_read ( ) ,
510
+ stream. conn . wants_write ( ) ,
511
+ ) ;
512
+
513
+ if let Err ( e) =
514
+ poll ( state, process, stream. sock , interest, deadline)
515
+ {
516
+ return Result :: io_error ( e) ;
517
+ }
518
+ }
519
+ val => {
520
+ return val
521
+ . map ( |v| Result :: ok ( v as _ ) )
522
+ . unwrap_or_else ( Result :: io_error) ;
523
+ }
524
+ } ;
525
+ }
526
+ }
0 commit comments