Skip to content

Commit 3d0c261

Browse files
committed
Add unit test for stunnel_log_scanner
Signed-off-by: Changlei Li <changlei.li@cloud.com>
1 parent be835ef commit 3d0c261

10 files changed

Lines changed: 264 additions & 0 deletions
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
2026.01.09 06:01:27 LOG5[ui]: stunnel 5.60 on x86_64-koji-linux-gnu platform
2+
2026.01.09 06:01:27 LOG5[ui]: Compiled/running with OpenSSL 3.0.9 30 May 2023
3+
2026.01.09 06:01:27 LOG5[ui]: Threading:PTHREAD Sockets:POLL,IPv6 TLS:ENGINE,OCSP,SNI Auth:LIBWRAP
4+
2026.01.09 06:01:27 LOG5[ui]: Reading configuration from descriptor 8
5+
2026.01.09 06:01:27 LOG5[ui]: UTF-8 byte order mark not detected
6+
2026.01.09 06:01:27 LOG5[ui]: FIPS mode disabled
7+
2026.01.09 06:01:27 LOG5[ui]: Configuration successful
8+
2026.01.09 06:01:28 LOG5[0]: Service [client-proxy] accepted connection from unnamed socket
9+
2026.01.09 06:01:28 LOG5[0]: s_connect: connected 10.79.16.92:4433
10+
2026.01.09 06:01:28 LOG5[0]: Service [client-proxy] connected remote server from 10.79.16.89:56850
11+
2026.01.09 06:01:28 LOG4[0]: CERT: Pre-verification error: certificate has expired
12+
2026.01.09 06:01:28 LOG4[0]: Rejected by CERT at depth=0: C=US, ST=California, L=San Francisco, O=MyCompany, CN=testserver
13+
2026.01.09 06:01:28 LOG3[0]: SSL_connect: ssl/statem/statem_clnt.c:1889: error:0A000086:SSL routines::certificate verify failed
14+
2026.01.09 06:01:28 LOG5[0]: Connection reset: 0 byte(s) sent to TLS, 0 byte(s) sent to socket
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
2026.01.09 06:36:43 LOG5[ui]: stunnel 5.60 on x86_64-koji-linux-gnu platform
2+
2026.01.09 06:36:43 LOG5[ui]: Compiled/running with OpenSSL 3.0.9 30 May 2023
3+
2026.01.09 06:36:43 LOG5[ui]: Threading:PTHREAD Sockets:POLL,IPv6 TLS:ENGINE,OCSP,SNI Auth:LIBWRAP
4+
2026.01.09 06:36:43 LOG5[ui]: Reading configuration from descriptor 8
5+
2026.01.09 06:36:43 LOG5[ui]: UTF-8 byte order mark not detected
6+
2026.01.09 06:36:43 LOG5[ui]: FIPS mode disabled
7+
2026.01.09 06:36:43 LOG5[ui]: Configuration successful
8+
2026.01.09 06:36:44 LOG5[0]: Service [client-proxy] accepted connection from unnamed socket
9+
2026.01.09 06:36:44 LOG5[0]: s_connect: connected 10.79.16.92:4433
10+
2026.01.09 06:36:44 LOG5[0]: Service [client-proxy] connected remote server from 10.79.16.89:56884
11+
2026.01.09 06:36:44 LOG4[0]: CERT: Pre-verification error: self-signed certificate
12+
2026.01.09 06:36:44 LOG4[0]: Rejected by CERT at depth=0: CN=testserver
13+
2026.01.09 06:36:44 LOG3[0]: SSL_connect: ssl/statem/statem_clnt.c:1889: error:0A000086:SSL routines::certificate verify failed
14+
2026.01.09 06:36:44 LOG5[0]: Connection reset: 0 byte(s) sent to TLS, 0 byte(s) sent to socket
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
[ ] Initializing inetd mode configuration
2+
[ ] Clients allowed=500
3+
[.] stunnel 5.60 on x86_64-koji-linux-gnu platform
4+
[.] Compiled/running with OpenSSL 3.0.9 30 May 2023
5+
[.] Threading:PTHREAD Sockets:POLL,IPv6 TLS:ENGINE,OCSP,SNI Auth:LIBWRAP
6+
[ ] errno: (*__errno_location ())
7+
[ ] Initializing inetd mode configuration
8+
[.] Reading configuration from descriptor 8
9+
[.] UTF-8 byte order mark not detected
10+
[.] FIPS mode disabled
11+
[ ] No PRNG seeding was required
12+
[ ] Initializing service [client-proxy]
13+
[ ] stunnel default security level set: 2
14+
[ ] Ciphers: ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256
15+
[ ] TLSv1.3 ciphersuites: TLS_AES_256_GCM_SHA384:TLS_AES_128_GCM_SHA256:TLS_CHACHA20_POLY1305_SHA256
16+
[ ] TLS options: 0x02100000 (+0x00000000, -0x00000000)
17+
[ ] Session resumption enabled
18+
[ ] No certificate or private key specified
19+
[!] error queue: crypto/x509/by_file.c:234: error:05880009:x509 certificate routines::PEM lib
20+
[!] error queue: crypto/pem/pem_info.c:169: error:0488000D:PEM routines::ASN1 lib
21+
[!] error queue: crypto/asn1/tasn_dec.c:349: error:0688010A:asn1 encoding routines::nested asn1 error
22+
[!] SSL_CTX_load_verify_locations: crypto/asn1/tasn_dec.c:1188: error:068000A8:asn1 encoding routines::wrong tag
23+
[!] Service [client-proxy]: Failed to initialize TLS context
24+
[!] Configuration failed
25+
[ ] Deallocating temporary section defaults
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
2026.01.09 05:45:23 LOG5[ui]: stunnel 5.60 on x86_64-koji-linux-gnu platform
2+
2026.01.09 05:45:23 LOG5[ui]: Compiled/running with OpenSSL 3.0.9 30 May 2023
3+
2026.01.09 05:45:23 LOG5[ui]: Threading:PTHREAD Sockets:POLL,IPv6 TLS:ENGINE,OCSP,SNI Auth:LIBWRAP
4+
2026.01.09 05:45:23 LOG5[ui]: Reading configuration from descriptor 8
5+
2026.01.09 05:45:23 LOG5[ui]: UTF-8 byte order mark not detected
6+
2026.01.09 05:45:23 LOG5[ui]: FIPS mode disabled
7+
2026.01.09 05:45:23 LOG5[ui]: Configuration successful
8+
2026.01.09 05:45:24 LOG5[0]: Service [client-proxy] accepted connection from unnamed socket
9+
2026.01.09 05:45:24 LOG5[0]: s_connect: connecting 192.168.1.100:443
10+
2026.01.09 05:45:24 LOG3[0]: s_connect: connect 192.168.1.100:443: Connection refused
11+
2026.01.09 05:45:24 LOG5[0]: Connection reset: 0 byte(s) sent to TLS, 0 byte(s) sent to socket
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
2026.01.09 05:52:10 LOG5[ui]: stunnel 5.60 on x86_64-koji-linux-gnu platform
2+
2026.01.09 05:52:10 LOG5[ui]: Compiled/running with OpenSSL 3.0.9 30 May 2023
3+
2026.01.09 05:52:10 LOG5[ui]: Threading:PTHREAD Sockets:POLL,IPv6 TLS:ENGINE,OCSP,SNI Auth:LIBWRAP
4+
2026.01.09 05:52:10 LOG5[ui]: Reading configuration from descriptor 8
5+
2026.01.09 05:52:10 LOG5[ui]: UTF-8 byte order mark not detected
6+
2026.01.09 05:52:10 LOG5[ui]: FIPS mode disabled
7+
2026.01.09 05:52:10 LOG5[ui]: Configuration successful
8+
2026.01.09 05:52:11 LOG5[0]: Service [client-proxy] accepted connection from unnamed socket
9+
2026.01.09 05:52:11 LOG5[0]: s_connect: connecting invalid.example.com:443
10+
2026.01.09 05:52:11 LOG3[0]: s_connect: No host resolved
11+
2026.01.09 05:52:11 LOG5[0]: Connection reset: 0 byte(s) sent to TLS, 0 byte(s) sent to socket
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
2026.01.09 05:47:45 LOG5[ui]: stunnel 5.60 on x86_64-koji-linux-gnu platform
2+
2026.01.09 05:47:45 LOG5[ui]: Compiled/running with OpenSSL 3.0.9 30 May 2023
3+
2026.01.09 05:47:45 LOG5[ui]: Threading:PTHREAD Sockets:POLL,IPv6 TLS:ENGINE,OCSP,SNI Auth:LIBWRAP
4+
2026.01.09 05:47:45 LOG5[ui]: Reading configuration from descriptor 8
5+
2026.01.09 05:47:45 LOG5[ui]: UTF-8 byte order mark not detected
6+
2026.01.09 05:47:45 LOG5[ui]: FIPS mode disabled
7+
2026.01.09 05:47:45 LOG5[ui]: Configuration successful
8+
2026.01.09 05:47:46 LOG5[0]: Service [client-proxy] accepted connection from unnamed socket
9+
2026.01.09 05:47:46 LOG5[0]: s_connect: connected 10.79.16.92:4433
10+
2026.01.09 05:47:46 LOG5[0]: Service [client-proxy] connected remote server from 10.79.16.89:56830
11+
2026.01.09 05:47:46 LOG4[0]: CERT: Subject checks failed
12+
2026.01.09 05:47:46 LOG4[0]: Rejected by CERT at depth=0: C=US, ST=California, L=San Francisco, O=MyCompany, CN=testserver
13+
2026.01.09 05:47:46 LOG3[0]: SSL_connect: ssl/statem/statem_clnt.c:1889: error:0A000086:SSL routines::certificate verify failed
14+
2026.01.09 05:47:46 LOG5[0]: Connection reset: 0 byte(s) sent to TLS, 0 byte(s) sent to socket
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
2026.01.09 06:17:43 LOG5[ui]: stunnel 5.60 on x86_64-koji-linux-gnu platform
2+
2026.01.09 06:17:43 LOG5[ui]: Compiled/running with OpenSSL 3.0.9 30 May 2023
3+
2026.01.09 06:17:43 LOG5[ui]: Threading:PTHREAD Sockets:POLL,IPv6 TLS:ENGINE,OCSP,SNI Auth:LIBWRAP
4+
2026.01.09 06:17:43 LOG5[ui]: Reading configuration from descriptor 8
5+
2026.01.09 06:17:43 LOG5[ui]: UTF-8 byte order mark not detected
6+
2026.01.09 06:17:43 LOG5[ui]: FIPS mode disabled
7+
2026.01.09 06:17:43 LOG5[ui]: Configuration successful
8+
2026.01.09 06:17:44 LOG5[0]: Service [client-proxy] accepted connection from unnamed socket
9+
2026.01.09 06:17:44 LOG5[0]: s_connect: connected 10.71.212.108:8083
10+
2026.01.09 06:17:44 LOG5[0]: Service [client-proxy] connected remote server from 10.79.16.89:34844
11+
2026.01.09 06:17:45 LOG5[0]: Certificate accepted at depth=0: C=US, ST=Florida, L=Fort Lauderdale, O="Citrix Systems, Inc.", CN=*.xenrt.citrite.net
12+
2026.01.09 06:17:45 LOG5[0]: Connection closed: 6 byte(s) sent to TLS, 519 byte(s) sent to socket

ocaml/libs/stunnel/test/dune

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
(test
2+
(name test_stunnel_log_scanner)
3+
(libraries
4+
alcotest
5+
astring
6+
stunnel
7+
unix
8+
xapi-stdext-pervasives
9+
xapi-stdext-unix)
10+
(deps
11+
(source_tree data)))
Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
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+
open Stunnel_log_scanner
16+
17+
(** Path to test data directory - relative to where dune runs the test *)
18+
let data_dir = "data"
19+
20+
(** Helper to build path to test log file *)
21+
let log_path filename = Filename.concat data_dir filename
22+
23+
(** Collect logged lines for verification *)
24+
let make_logger () =
25+
let lines = ref [] in
26+
let log line = lines := line :: !lines in
27+
(log, fun () -> List.rev !lines)
28+
29+
(** Calculate byte position after a line containing the target substring *)
30+
let calculate_position_of_line filename target_substring =
31+
let acc_bytes = ref 0 in
32+
Xapi_stdext_unix.Unixext.readfile_line
33+
(fun line ->
34+
let line_bytes = String.length line + 1 in
35+
(* +1 for newline *)
36+
acc_bytes := !acc_bytes + line_bytes ;
37+
if Astring.String.is_infix ~affix:target_substring line then
38+
raise Xapi_stdext_unix.Unixext.Break
39+
)
40+
filename ;
41+
!acc_bytes
42+
43+
(** Test successful connection log *)
44+
let test_successful_connection () =
45+
let logfile = log_path "successful_connection.log" in
46+
let logger, _get_lines = make_logger () in
47+
48+
match check_stunnel_logfile_from_position logger logfile 0 with
49+
| End pos ->
50+
let size = Unix.(stat logfile).st_size in
51+
Alcotest.(check int) "Should reach end of file" size pos
52+
| ScanFound _ ->
53+
Alcotest.fail "Should not get ScanFound in successful log"
54+
| ScanError (e, _) ->
55+
Alcotest.fail
56+
("Should not error on successful connection log: "
57+
^ Stunnel_error.to_string e
58+
)
59+
60+
let test_certificate_verify logfile expected_substring () =
61+
let logfile = log_path logfile in
62+
let logger, _get_lines = make_logger () in
63+
let expected_pos =
64+
calculate_position_of_line logfile "certificate verify failed"
65+
in
66+
67+
match check_stunnel_logfile_from_position logger logfile 0 with
68+
| ScanError (Stunnel_error.Certificate_verify msg, pos) ->
69+
Alcotest.(check int) "Position should match expected" expected_pos pos ;
70+
Alcotest.(check bool)
71+
"Error message should contain expected substring" true
72+
(Astring.String.is_infix ~affix:expected_substring msg)
73+
| _ ->
74+
Alcotest.fail "Should detect certificate verification failure"
75+
76+
let test_stunnel_error logfile expected_substring () =
77+
let logfile = log_path logfile in
78+
let logger, _get_lines = make_logger () in
79+
let expected_pos = calculate_position_of_line logfile expected_substring in
80+
81+
match check_stunnel_logfile_from_position logger logfile 0 with
82+
| ScanError (Stunnel_error.Stunnel msg, pos) ->
83+
Alcotest.(check int) "Position should match expected" expected_pos pos ;
84+
Alcotest.(check bool)
85+
"Error message should contain expected substring" true
86+
(Astring.String.is_infix ~affix:expected_substring msg)
87+
| _ ->
88+
Alcotest.fail "Should detect stunnel error"
89+
90+
let test_check_stunnel_log_until () =
91+
let logfile = log_path "successful_connection.log" in
92+
let check_line line =
93+
if Astring.String.is_infix ~affix:"Configuration successful" line then
94+
LineFound
95+
else
96+
Continue
97+
in
98+
match check_stunnel_log_until_found_or_error logfile check_line 0.1 2 0 with
99+
| ScanFound pos ->
100+
Alcotest.(check bool) "Should return positive pos" true (pos > 0) ;
101+
(* Verify we can read the next line from the returned position *)
102+
let fd = Unix.openfile logfile [Unix.O_RDONLY] 0 in
103+
let finally = Xapi_stdext_pervasives.Pervasiveext.finally in
104+
finally
105+
(fun () ->
106+
let _ = Unix.lseek fd pos Unix.SEEK_SET in
107+
let ic = Unix.in_channel_of_descr fd in
108+
let line = input_line ic in
109+
(* The next line after "Configuration successful" should be about service accepting connection *)
110+
Alcotest.(check bool)
111+
"Next line should be about service accepted" true
112+
(Astring.String.is_infix ~affix:"Service [client-proxy] accepted"
113+
line
114+
)
115+
)
116+
(fun () -> Unix.close fd)
117+
| ScanError (e, _pos) ->
118+
Alcotest.fail ("Should find target line: " ^ Stunnel_error.to_string e)
119+
| End _ ->
120+
Alcotest.fail "Should not reach end without finding target line"
121+
122+
let tests =
123+
[
124+
( "test_stunnel_log_scanner"
125+
, [
126+
Alcotest.test_case "successful_connection" `Quick
127+
test_successful_connection
128+
; Alcotest.test_case "certificate_self_signed" `Quick
129+
(test_certificate_verify "certificate_self_signed.log"
130+
"self-signed certificate"
131+
)
132+
; Alcotest.test_case "certificate_expired" `Quick
133+
(test_certificate_verify "certificate_expired.log"
134+
"certificate has expired"
135+
)
136+
; Alcotest.test_case "subject_checks_failed" `Quick
137+
(test_certificate_verify "subject_checks_failed.log"
138+
"Subject checks failed"
139+
)
140+
; Alcotest.test_case "connection_refused" `Quick
141+
(test_stunnel_error "connection_refused.log" "Connection refused")
142+
; Alcotest.test_case "no_host_resolved" `Quick
143+
(test_stunnel_error "no_host_resolved.log" "No host resolved")
144+
; Alcotest.test_case "configuration_failed" `Quick
145+
(test_stunnel_error "configuration_failed.log" "Configuration failed")
146+
; Alcotest.test_case "check_stunnel_log_until" `Quick
147+
test_check_stunnel_log_until
148+
]
149+
)
150+
]
151+
152+
let () = Alcotest.run "StunnelLogScanner" tests

ocaml/libs/stunnel/test/test_stunnel_log_scanner.mli

Whitespace-only changes.

0 commit comments

Comments
 (0)