Skip to content

Commit 973d13e

Browse files
committed
Refactor: Move to new files
Signed-off-by: Changlei Li <changlei.li@cloud.com>
1 parent 866a23a commit 973d13e

7 files changed

Lines changed: 344 additions & 183 deletions

File tree

ocaml/libs/stunnel/stunnel.ml

Lines changed: 51 additions & 161 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,12 @@
1313
*)
1414
(* Copyright (C) 2007 XenSource Inc *)
1515

16-
module D = Debug.Make (struct let name = "stunnel" end)
16+
(* module D = Debug.Make (struct let name = "stunnel" end) *)
17+
module D = struct
18+
let debug fmt = Printf.ksprintf (fun s -> Printf.printf "%s\n" s) fmt
19+
20+
let error fmt = Printf.ksprintf (fun s -> Printf.printf "%s\n" s) fmt
21+
end
1722

1823
open Printf
1924
open Xapi_stdext_unix
@@ -135,11 +140,6 @@ type t = {
135140
; verified: verification_config option
136141
}
137142
138-
type stunnel_error =
139-
| Certificate_verify of string list
140-
| Stunnel of string
141-
| Unknown of string
142-
143143
let appliance =
144144
{
145145
sni= None
@@ -501,134 +501,21 @@ let with_client_proxy_systemd_service ~verify_cert ~remote_host ~remote_port
501501
)
502502
(fun () -> Unixext.unlink_safe conf_path)
503503
504-
type log_line_status = Continue | LineFound | LineError of stunnel_error
505-
506-
type log_scan_result =
507-
| End of int
508-
| ScanError of stunnel_error * int
509-
| ScanFound of int
510-
511-
let try_iter f lst =
512-
let rec aux = function
513-
| [] ->
514-
Continue
515-
| x :: xs -> (
516-
match f x with Continue -> aux xs | other -> other
517-
)
518-
in
519-
aux lst
520-
521-
(** Monadic bind for log_line_status composition *)
522-
let ( >>= ) check1 check2 line =
523-
match check1 line with Continue -> check2 line | other -> other
524-
525-
let check_stunnel_error line =
526-
[
527-
"Configuration failed"
528-
; "Connection refused"
529-
; "No host resolved"
530-
; "No route to host"
531-
; "Invalid argument"
532-
; "Address already in use"
533-
]
534-
|> try_iter (fun s ->
535-
if Astring.String.is_infix ~affix:s line then
536-
LineError (Stunnel s)
537-
else
538-
Continue
539-
)
540-
541-
let check_verify_error cert_errors line =
542-
(* When verified with a mismatched certificate, one line of log from stunnel
543-
* would look like:
544-
SSL_connect: ssl/statem/statem_clnt.c:1889: error:0A000086:SSL routines::certificate verify failed
545-
* in this case, Stunnel_verify_error can be raised with detailed error as
546-
* reason if it can found in the log *)
547-
if Astring.String.is_infix ~affix:"certificate verify failed" line then
548-
LineError (Certificate_verify cert_errors)
549-
else
550-
Continue
551-
552-
(** Stream through lines from a specific position, applying function to each.
553-
Returns new_position. Stops early on Error. *)
554-
let stream_from_position (filepath : string) (start_pos : int)
555-
(f : string -> log_line_status) : log_scan_result =
556-
try
557-
let fd = Unix.openfile filepath [Unix.O_RDONLY] 0 in
558-
let finally = Xapi_stdext_pervasives.Pervasiveext.finally in
559-
finally
560-
(fun () ->
561-
let _ = Unix.lseek fd start_pos Unix.SEEK_SET in
562-
let ic = Unix.in_channel_of_descr fd in
563-
let rec loop () =
564-
match input_line ic with
565-
| exception End_of_file ->
566-
let end_pos = (Unix.fstat fd).Unix.st_size in
567-
End end_pos
568-
| line -> (
569-
match f line with
570-
| Continue ->
571-
loop ()
572-
| LineError e ->
573-
let pos = Unix.lseek fd 0 Unix.SEEK_CUR in
574-
ScanError (e, pos)
575-
| LineFound ->
576-
let pos = Unix.lseek fd 0 Unix.SEEK_CUR in
577-
ScanFound pos
578-
)
579-
in
580-
loop ()
581-
)
582-
(fun () -> Unix.close fd)
583-
with
584-
| Unix.Unix_error (err, fn, arg) ->
585-
ScanError
586-
( Stunnel (Printf.sprintf "%s: %s(%s)" (Unix.error_message err) fn arg)
587-
, start_pos )
588-
| e ->
589-
ScanError (Stunnel (Printexc.to_string e), start_pos)
590-
591-
let check_stunnel_logfile_from_position logfile start_pos =
592-
let cert_errors = ref [] in
593-
let check_line line =
594-
!stunnel_logger line ;
595-
Astring.String.cut ~rev:true ~sep:"CERT: " line
596-
|> Option.iter (fun (_, cert_error) ->
597-
cert_errors := cert_error :: !cert_errors
598-
) ;
599-
match check_verify_error !cert_errors line with
600-
| Continue ->
601-
check_stunnel_error line
602-
| other ->
603-
other
604-
in
605-
stream_from_position logfile start_pos check_line
606-
607504
let diagnose_failure st_proc =
608-
match check_stunnel_logfile_from_position st_proc.logfile 0 with
505+
let open Stunnel_log_scanner in
506+
match
507+
check_stunnel_logfile_from_position
508+
(fun s -> !stunnel_logger s)
509+
st_proc.logfile 0
510+
with
609511
| End _ | ScanFound _ ->
610512
()
611-
| ScanError (Certificate_verify reasons, _pos) ->
513+
| ScanError (Stunnel_error.Certificate_verify reasons, _pos) ->
612514
raise (Stunnel_verify_error reasons)
613-
| ScanError (Stunnel reason, _pos) | ScanError (Unknown reason, _pos) ->
515+
| ScanError (Stunnel_error.Stunnel reason, _pos)
516+
| ScanError (Stunnel_error.Unknown reason, _pos) ->
614517
raise (Stunnel_error reason)
615518
616-
(* check stunnel log, until *)
617-
let check_stunnel_log_until logfile check_line interval count start_pos =
618-
let rec check ~max_retries cnt start_pos =
619-
Thread.delay interval ;
620-
match stream_from_position logfile start_pos check_line with
621-
| End new_pos when cnt <= max_retries ->
622-
check ~max_retries (cnt + 1) new_pos
623-
| ScanError (e, _pos) ->
624-
Error e
625-
| ScanFound new_pos ->
626-
Ok new_pos
627-
| End _ ->
628-
Error (Stunnel "Timed out waiting for stunnel condition")
629-
in
630-
check ~max_retries:count 0 start_pos
631-
632519
module UnixSocketProxy = struct
633520
(** Handle for a long-running stunnel proxy *)
634521
type t = {
@@ -653,7 +540,7 @@ module UnixSocketProxy = struct
653540
let diagnose handle =
654541
let start_pos = handle.last_checked_position in
655542
let current_size = (Unix.stat handle.proxy_logfile).Unix.st_size in
656-
543+
let open Stunnel_log_scanner in
657544
if current_size <= start_pos then (
658545
D.debug "%s: no new log entries (position %d)" __FUNCTION__ start_pos ;
659546
Ok ()
@@ -675,7 +562,9 @@ module UnixSocketProxy = struct
675562
)
676563
(fun () -> Unix.close fd) ;
677564
match
678-
check_stunnel_logfile_from_position handle.proxy_logfile start_pos
565+
check_stunnel_logfile_from_position
566+
(fun s -> !stunnel_logger s)
567+
handle.proxy_logfile start_pos
679568
with
680569
| End pos | ScanFound pos ->
681570
handle.last_checked_position <- pos ;
@@ -686,24 +575,18 @@ module UnixSocketProxy = struct
686575
)
687576
688577
let wait_for_init_done logfile =
689-
let check_init_success line =
690-
if Astring.String.is_infix ~affix:"Configuration successful" line then
691-
LineFound
692-
else
693-
Continue
694-
in
695-
let check_line = check_init_success >>= check_stunnel_error in
578+
let open Stunnel_log_scanner in
579+
let check_line = check_configuration_success >>= check_stunnel_error in
696580
check_stunnel_log_until logfile check_line 1.0 3 0
697581
698582
let wait_for_connection_done logfile start_pos =
699-
let certs = ref [] in
700-
let connected line =
701-
if Astring.String.is_infix ~affix:"connected remote server from" line then
702-
LineFound
703-
else
704-
Continue
583+
let open Stunnel_log_scanner in
584+
let check_verify_error = make_check_verify_error () in
585+
let check_line =
586+
check_connection_established
587+
>>= check_verify_error
588+
>>= check_stunnel_error
705589
in
706-
let check_line = connected >>= check_verify_error !certs in
707590
check_stunnel_log_until logfile check_line 1.0 10 start_pos
708591
709592
(** Start a long-running stunnel proxy listening on a UNIX socket.
@@ -719,8 +602,9 @@ module UnixSocketProxy = struct
719602
is not started. If successful, subsequent connections by stubs will also
720603
be verified automatically by stunnel. *)
721604
let start ~verify_cert ~remote_host ~remote_port ?unix_socket_path
722-
?socket_mode () =
605+
?socket_mode ?(test_connection = true) () =
723606
let ( let* ) = Result.bind in
607+
let open Stunnel_error in
724608
let unix_socket_path =
725609
match unix_socket_path with
726610
| Some path ->
@@ -781,22 +665,28 @@ module UnixSocketProxy = struct
781665
D.debug "%s: started stunnel proxy (pid:%d):%s -> %s:%d log: %s"
782666
__FUNCTION__ (getpid pid) unix_socket_path remote_host remote_port logfile ;
783667
784-
(* Make initial connection to verify certificate *)
785-
let sock = Unix.socket Unix.PF_UNIX Unix.SOCK_STREAM 0 in
786-
let finally = Xapi_stdext_pervasives.Pervasiveext.finally in
668+
(* Optionally make initial connection to verify certificate *)
787669
let* pos =
788-
finally
789-
(fun () ->
790-
Unix.connect sock (Unix.ADDR_UNIX unix_socket_path) ;
791-
(* Wait for TLS handshake and certificate verification *)
792-
wait_for_connection_done logfile pos
793-
|> Result.map_error (fun e ->
794-
D.error "%s: stunnel connection failed" __FUNCTION__ ;
795-
clean_up () ;
796-
e
670+
if test_connection then (
671+
D.debug "%s: performing initial connection test" __FUNCTION__ ;
672+
let sock = Unix.socket Unix.PF_UNIX Unix.SOCK_STREAM 0 in
673+
let finally = Xapi_stdext_pervasives.Pervasiveext.finally in
674+
finally
675+
(fun () ->
676+
Unix.connect sock (Unix.ADDR_UNIX unix_socket_path) ;
677+
(* Wait for TLS handshake and certificate verification *)
678+
wait_for_connection_done logfile pos
679+
|> Result.map_error (fun e ->
680+
D.error "%s: stunnel connection failed" __FUNCTION__ ;
681+
clean_up () ;
682+
e
683+
)
797684
)
798-
)
799-
(fun () -> Unix.close sock)
685+
(fun () -> Unix.close sock)
686+
) else (
687+
D.debug "%s: skipping initial connection test" __FUNCTION__ ;
688+
Ok pos
689+
)
800690
in
801691
802692
let handle =
@@ -829,10 +719,10 @@ module UnixSocketProxy = struct
829719
If [socket_mode] is provided, stunnel will set the socket file permissions.
830720
This is the preferred way to use the proxy for most use cases. *)
831721
let with_proxy ~verify_cert ~remote_host ~remote_port ?unix_socket_path
832-
?socket_mode f =
722+
?socket_mode ?test_connection f =
833723
match
834724
start ~verify_cert ~remote_host ~remote_port ?unix_socket_path
835-
?socket_mode ()
725+
?socket_mode ?test_connection ()
836726
with
837727
| Error _ as e ->
838728
e

ocaml/libs/stunnel/stunnel.mli

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -55,11 +55,6 @@ type t = {
5555
; verified: verification_config option
5656
}
5757

58-
type stunnel_error =
59-
| Certificate_verify of string list
60-
| Stunnel of string
61-
| Unknown of string
62-
6358
module UnixSocketProxy : sig
6459
(** Handle for a long-running stunnel proxy that exposes TLS connection
6560
via a UNIX socket file *)
@@ -76,20 +71,25 @@ module UnixSocketProxy : sig
7671
-> remote_port:int
7772
-> ?unix_socket_path:string
7873
-> ?socket_mode:int
74+
-> ?test_connection:bool
7975
-> unit
80-
-> (t, stunnel_error) result
76+
-> (t, Stunnel_error.t) result
8177
(** Start a long-running stunnel proxy listening on a UNIX socket.
8278
83-
This function starts the proxy and immediately performs certificate
84-
verification by making an initial test connection. If certificate
85-
verification fails, the proxy is stopped and cleaned up automatically.
79+
Returns [Ok handle] if stunnel starts successfully. If [test_connection]
80+
is [true] (the default), performs an immediate certificate verification
81+
test connection. If certificate verification fails, the proxy is stopped
82+
and cleaned up automatically.
83+
84+
Set [~test_connection:false] to skip the initial connection test and
85+
only verify that stunnel initializes successfully. Certificate validation
86+
will then happen on the first actual connection.
8687
87-
Returns [Ok handle] if stunnel starts successfully and the certificate
88-
is valid. Returns [Error] if stunnel fails to start, initialize, or
89-
if certificate verification fails.
88+
Returns [Error] if stunnel fails to start, initialize, or (when
89+
[test_connection] is [true]) if certificate verification fails.
9090
9191
If [unix_socket_path] is not provided, a unique path will be generated
92-
automatically in /var/run with the format:
92+
automatically in /tmp with the format:
9393
stunnel-proxy-{host}-{port}-{uuid}.sock
9494
9595
If [socket_mode] is provided (e.g., [~socket_mode:0o666]), the socket
@@ -99,7 +99,7 @@ module UnixSocketProxy : sig
9999
(** Stop a running stunnel proxy and clean up resources.
100100
This kills the stunnel process and removes the socket and log files. *)
101101

102-
val diagnose : t -> (unit, stunnel_error) result
102+
val diagnose : t -> (unit, Stunnel_error.t) result
103103
(** Diagnose the status of a running stunnel proxy by checking its logfile.
104104
105105
Only checks NEW log entries since the last call to [diagnose] (or since
@@ -114,12 +114,14 @@ module UnixSocketProxy : sig
114114
-> remote_port:int
115115
-> ?unix_socket_path:string
116116
-> ?socket_mode:int
117-
-> (t -> ('a, stunnel_error) result)
118-
-> ('a, stunnel_error) result
117+
-> ?test_connection:bool
118+
-> (t -> ('a, Stunnel_error.t) result)
119+
-> ('a, Stunnel_error.t) result
119120
(** Start a proxy, execute a function with it, and automatically stop it.
120121
The proxy is guaranteed to be stopped even if the function raises an exception.
121122
If [unix_socket_path] is not provided, a unique path will be generated.
122123
If [socket_mode] is provided, stunnel will set the socket file permissions.
124+
If [test_connection] is [true] (default), performs initial certificate verification.
123125
This is the preferred way to use the proxy for most use cases. *)
124126
end
125127

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
(*
2+
* Copyright (c) Cloud Software Group, Inc.
3+
*
4+
* This program is free software; you can redistribute it and/or modify
5+
* it under the terms of the GNU Lesser General Public License as published
6+
* by the Free Software Foundation; version 2.1 only. with the special
7+
* exception on linking described in file LICENSE.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU Lesser General Public License for more details.
13+
*)
14+
15+
(** Stunnel error types shared between modules *)
16+
17+
type t =
18+
| Certificate_verify of string list
19+
| Stunnel of string
20+
| Unknown of string

0 commit comments

Comments
 (0)