@@ -538,8 +538,35 @@ let check_stunnel_logfile logfile =
538538
539539let diagnose_failure st_proc = check_stunnel_logfile st_proc.logfile
540540
541- let check_stunnel_status logfile =
542- match check_stunnel_logfile logfile with
541+ (* * Check only new log entries since the given position *)
542+ let check_stunnel_logfile_from_position logfile start_pos =
543+ let cert_errors = ref [] in
544+ let fd = Unix. openfile logfile [Unix. O_RDONLY ] 0 in
545+ let finally = Xapi_stdext_pervasives.Pervasiveext. finally in
546+ finally
547+ (fun () ->
548+ let _ = Unix. lseek fd start_pos Unix. SEEK_SET in
549+ let ic = Unix. in_channel_of_descr fd in
550+ try
551+ while true do
552+ let line = input_line ic in
553+ ! stunnel_logger line ;
554+ Astring.String. cut ~rev: true ~sep: " CERT: " line
555+ |> Option. iter (fun (_ , cert_error ) ->
556+ cert_errors := cert_error :: ! cert_errors
557+ ) ;
558+ check_verify_error ! cert_errors line ;
559+ check_error " Connection refused" line ;
560+ check_error " No host resolved" line ;
561+ check_error " No route to host" line ;
562+ check_error " Invalid argument" line
563+ done
564+ with End_of_file -> ()
565+ )
566+ (fun () -> Unix. close fd)
567+
568+ let check_stunnel_status_from_position logfile start_pos =
569+ match check_stunnel_logfile_from_position logfile start_pos with
543570 | () ->
544571 Ok ()
545572 | exception Stunnel_verify_error reasons ->
@@ -600,7 +627,12 @@ let wait_for_connection_done logfile =
600627
601628module UnixSocketProxy = struct
602629 (* * Handle for a long-running stunnel proxy *)
603- type t = {proxy_pid : pid ; proxy_socket_path : string ; proxy_logfile : string }
630+ type t = {
631+ proxy_pid : pid
632+ ; proxy_socket_path : string
633+ ; proxy_logfile : string
634+ ; mutable last_checked_position : int
635+ }
604636
605637 let socket_path handle = handle.proxy_socket_path
606638
@@ -610,13 +642,55 @@ module UnixSocketProxy = struct
610642 Printf. sprintf " /var/run/stunnel-proxy-%s-%d-%s.sock" remote_host
611643 remote_port uuid
612644
645+ (* * Diagnose the status of a running stunnel proxy by checking its logfile.
646+ Only checks new log entries since the last call to diagnose.
647+ Updates the last_checked_position after checking.
648+ Returns Ok () if no errors found, Error with details otherwise. *)
649+ let diagnose handle =
650+ let start_pos = handle.last_checked_position in
651+ let current_size = (Unix. stat handle.proxy_logfile).Unix. st_size in
652+
653+ if current_size < = start_pos then (
654+ D. debug " %s: no new log entries (position %d)" __FUNCTION__ start_pos ;
655+ Ok ()
656+ ) else (
657+ D. debug " %s: checking log from position %d to %d" __FUNCTION__ start_pos
658+ current_size ;
659+ (* Print new log content for debugging *)
660+ let fd = Unix. openfile handle.proxy_logfile [Unix. O_RDONLY ] 0 in
661+ let finally = Xapi_stdext_pervasives.Pervasiveext. finally in
662+ finally
663+ (fun () ->
664+ let _ = Unix. lseek fd start_pos Unix. SEEK_SET in
665+ let len = current_size - start_pos in
666+ let buf = Bytes. create len in
667+ let n = Unix. read fd buf 0 len in
668+ if n > 0 then
669+ D. debug " %s: new log content:\n %s" __FUNCTION__
670+ (Bytes. sub_string buf 0 n)
671+ )
672+ (fun () -> Unix. close fd) ;
673+ match
674+ check_stunnel_status_from_position handle.proxy_logfile start_pos
675+ with
676+ | Ok () ->
677+ handle.last_checked_position < - current_size ;
678+ Ok ()
679+ | Error _ as e ->
680+ handle.last_checked_position < - current_size ;
681+ e
682+ )
683+
613684 (* * Start a long-running stunnel proxy listening on a UNIX socket.
614685 Returns Ok handle that must be explicitly stopped with [stop].
615686 The stunnel process will continue running until stopped, allowing
616687 multiple clients to connect to the UNIX socket over time.
617688 If [unix_socket_path] is not provided, a unique path will be generated.
618- Note: This only starts the proxy - it does NOT verify the certificate.
619- Use [check_cert] after starting to verify the remote server's certificate. *)
689+
690+ This function performs initial certificate verification by making a test
691+ connection. If certificate verification fails, returns Error and the proxy
692+ is not started. If successful, subsequent connections by stubs will also
693+ be verified automatically by stunnel. *)
620694 let start ~verify_cert ~remote_host ~remote_port ?unix_socket_path () =
621695 try
622696 let unix_socket_path =
@@ -638,12 +712,39 @@ module UnixSocketProxy = struct
638712 D. debug " %s: started stunnel proxy (pid:%d):%s -> %s:%d log: %s"
639713 __FUNCTION__ (getpid pid) unix_socket_path remote_host remote_port
640714 logfile ;
641- Ok
715+
716+ let handle =
642717 {
643718 proxy_pid= pid
644719 ; proxy_socket_path= unix_socket_path
645720 ; proxy_logfile= logfile
721+ ; last_checked_position= 0
646722 }
723+ in
724+
725+ (* Make initial connection to verify certificate *)
726+ let sock = Unix. socket Unix. PF_UNIX Unix. SOCK_STREAM 0 in
727+ let finally = Xapi_stdext_pervasives.Pervasiveext. finally in
728+ finally
729+ (fun () ->
730+ Unix. connect sock (Unix. ADDR_UNIX unix_socket_path) ;
731+ (* Wait for TLS handshake and certificate verification *)
732+ wait_for_connection_done logfile
733+ )
734+ (fun () -> Unix. close sock) ;
735+
736+ (* Check for certificate verification errors using diagnose *)
737+ match diagnose handle with
738+ | Ok () ->
739+ D. debug " %s: initial certificate verification passed" __FUNCTION__ ;
740+ Ok handle
741+ | Error e ->
742+ (* Certificate verification failed, clean up *)
743+ D. debug " %s: initial certificate verification failed" __FUNCTION__ ;
744+ disconnect_with_pid ~wait: false ~force: true pid ;
745+ Unixext. unlink_safe unix_socket_path ;
746+ Unixext. unlink_safe logfile ;
747+ Error e
647748 with
648749 | Stunnel_error reason ->
649750 Error (Stunnel reason)
@@ -664,12 +765,6 @@ module UnixSocketProxy = struct
664765 D. debug " %s: stopped stunnel proxy (pid:%d):%s" __FUNCTION__
665766 (getpid handle.proxy_pid) handle.proxy_socket_path
666767
667- (* * Diagnose the status of a running stunnel proxy by checking its logfile.
668- Returns Ok () if no errors found, Error with details otherwise. *)
669- let diagnose handle =
670- print_log handle ;
671- check_stunnel_status handle.proxy_logfile
672-
673768 (* * Start a proxy, execute a function with it, and automatically stop it.
674769 The proxy is guaranteed to be stopped even if the function raises an exception.
675770 If [unix_socket_path] is not provided, a unique path will be generated.
@@ -716,28 +811,6 @@ let fetch_server_cert ~remote_host ~remote_port =
716811 None
717812 with _ -> None
718813
719- (* * Check certificate verification using a temporary stunnel connection.
720- This creates an isolated stunnel connection solely for certificate verification.
721- Returns Ok () if certificate is valid, Error with details otherwise. *)
722- let check_cert ~verify_cert ~remote_host ~remote_port =
723- UnixSocketProxy. with_proxy ~verify_cert ~remote_host ~remote_port
724- (fun handle ->
725- (* Make one connection to trigger certificate verification *)
726- let sock = Unix. socket Unix. PF_UNIX Unix. SOCK_STREAM 0 in
727- let finally = Xapi_stdext_pervasives.Pervasiveext. finally in
728- finally
729- (fun () ->
730- (* Connecting to the socket triggers stunnel to establish TLS connection
731- and perform certificate verification *)
732- Unix. connect sock (Unix. ADDR_UNIX handle.proxy_socket_path) ;
733- (* Wait for the connection attempt to complete and be logged *)
734- wait_for_connection_done handle.proxy_logfile
735- )
736- (fun () -> Unix. close sock) ;
737- (* Check the logfile for certificate verification result *)
738- UnixSocketProxy. diagnose handle
739- )
740-
741814(* If we reach here the whole stunnel log should have been gone through
742815 (possibly printed/logged somewhere. No necessity to raise an exception,
743816 since when this function being called, there is usually some exception
0 commit comments