mirror of https://github.com/wolfSSL/wolfssh.git
Add reverse forwarding phases to fwd.test
- fwd.test only exercised portfwd's local forwarding mode - Factor the setup and line exchange into procs, run once per phase - Add a reverse phase, and a second one using -f 0, where the peer picks the port and only the tcpip-forward reply names it - Each phase uses its own forwarding ports, so it skips the previous phase's TIME_WAIT wait - Take the SSH port from echoserver -R, which is written once its listener is bound, so the client cannot race ahead of the listen() - Wait for a port in the ready files rather than their existence - Verified the reverse phase fails without the echoserver listener fix Issue: ZD-21867pull/1088/head
parent
4caf4558f4
commit
12411659dc
|
|
@ -2,24 +2,33 @@
|
||||||
#
|
#
|
||||||
# SSH Tunnel Test Script
|
# SSH Tunnel Test Script
|
||||||
#
|
#
|
||||||
# Tests an SSH tunnel using wolfSSH and netcat (nc).
|
# Tests SSH tunnels using wolfSSH and netcat (nc), one phase per forwarding
|
||||||
|
# direction. In both, the nc client sends each line of a Lorem Ipsum paragraph
|
||||||
|
# through the tunnel and the nc server echoes it back; both sides verify every
|
||||||
|
# line.
|
||||||
#
|
#
|
||||||
# Architecture:
|
# Phase 1, local forwarding (portfwd listens on 12345):
|
||||||
#
|
#
|
||||||
# [nc client] --plain--> :12345 [wolfssh client]
|
# [nc client] --plain--> :12345 [wolfssh client]
|
||||||
# |
|
# |
|
||||||
# SSH
|
# SSH
|
||||||
# |
|
# |
|
||||||
# [wolfssh server] :ephem --plain--> :11111 [nc server]
|
# [wolfssh server] --plain--> :11111 [nc server]
|
||||||
#
|
#
|
||||||
# The nc client sends each line of a Lorem Ipsum paragraph through the tunnel
|
# Phase 2, reverse forwarding (portfwd -r, the server listens on 12346):
|
||||||
# to the nc server one at a time. The server echoes each line back. Both sides
|
|
||||||
# verify receipt of every line.
|
|
||||||
#
|
#
|
||||||
# Ports used:
|
# [nc client] --plain--> :12346 [wolfssh server]
|
||||||
# 11111 - nc server (plain text backend)
|
# |
|
||||||
# 12345 - wolfssh client listener (plain text, nc connects here)
|
# SSH
|
||||||
# 22222 - wolfSSH rendezvous (SSH, internal use only)
|
# |
|
||||||
|
# [wolfssh client] --plain--> :11112 [nc server]
|
||||||
|
#
|
||||||
|
# Phase 3 repeats phase 2 with "-f 0", where the server picks the listener port
|
||||||
|
# and reports it in the tcpip-forward reply. Only that reply names the entry
|
||||||
|
# port, so the phase covers the port-0 path: request, reply parse, and cancel.
|
||||||
|
#
|
||||||
|
# The SSH port is OS-allocated (echoserver -R) and each phase uses its own
|
||||||
|
# forwarding ports, so no phase waits out the previous one's TIME_WAIT.
|
||||||
#
|
#
|
||||||
# Requirements: nc (netcat), expect
|
# Requirements: nc (netcat), expect
|
||||||
|
|
||||||
|
|
@ -31,9 +40,10 @@ set timeout 30
|
||||||
# its select() loop before it ever sends the channel-open. Redirect to
|
# its select() loop before it ever sends the channel-open. Redirect to
|
||||||
# regular files instead, and use portfwd's -R ready file for sync.
|
# regular files instead, and use portfwd's -R ready file for sync.
|
||||||
set srv_log "/tmp/fwd.test.echoserver.[pid].log"
|
set srv_log "/tmp/fwd.test.echoserver.[pid].log"
|
||||||
|
set srv_ready "/tmp/fwd.test.echoserver.[pid].ready"
|
||||||
set clt_log "/tmp/fwd.test.portfwd.[pid].log"
|
set clt_log "/tmp/fwd.test.portfwd.[pid].log"
|
||||||
set clt_ready "/tmp/fwd.test.portfwd.[pid].ready"
|
set clt_ready "/tmp/fwd.test.portfwd.[pid].ready"
|
||||||
file delete -force $srv_log $clt_log $clt_ready
|
file delete -force $srv_log $srv_ready $clt_log $clt_ready
|
||||||
|
|
||||||
set lorem_lines {
|
set lorem_lines {
|
||||||
{Lorem ipsum dolor sit amet, consectetur adipiscing elit,}
|
{Lorem ipsum dolor sit amet, consectetur adipiscing elit,}
|
||||||
|
|
@ -55,7 +65,7 @@ set nc_client_pid ""
|
||||||
# --- Cleanup -----------------------------------------------------------------
|
# --- Cleanup -----------------------------------------------------------------
|
||||||
proc cleanup {} {
|
proc cleanup {} {
|
||||||
global nc_client_pid wolfssh_clt_pid wolfssh_srv_pid nc_server_pid
|
global nc_client_pid wolfssh_clt_pid wolfssh_srv_pid nc_server_pid
|
||||||
global srv_log clt_log clt_ready
|
global srv_log srv_ready clt_log clt_ready
|
||||||
|
|
||||||
puts "\n--- Cleaning up ---"
|
puts "\n--- Cleaning up ---"
|
||||||
foreach pid [list $nc_client_pid $wolfssh_clt_pid $wolfssh_srv_pid $nc_server_pid] {
|
foreach pid [list $nc_client_pid $wolfssh_clt_pid $wolfssh_srv_pid $nc_server_pid] {
|
||||||
|
|
@ -63,17 +73,175 @@ proc cleanup {} {
|
||||||
catch {exec kill $pid}
|
catch {exec kill $pid}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
file delete -force $srv_log $clt_log $clt_ready
|
set nc_client_pid ""
|
||||||
|
set wolfssh_clt_pid ""
|
||||||
|
set wolfssh_srv_pid ""
|
||||||
|
set nc_server_pid ""
|
||||||
|
file delete -force $srv_log $srv_ready $clt_log $clt_ready
|
||||||
puts "Done."
|
puts "Done."
|
||||||
}
|
}
|
||||||
|
|
||||||
# --- Fail helper -------------------------------------------------------------
|
# --- Fail helper -------------------------------------------------------------
|
||||||
|
# Dump the logs before cleanup deletes them; they usually name the real cause,
|
||||||
|
# where the failure here is often just the resulting timeout.
|
||||||
|
proc dump_log {label path} {
|
||||||
|
if {![file exists $path]} {
|
||||||
|
puts " \[$label\] no log at $path"
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if {[catch {open $path r} f]} {
|
||||||
|
puts " \[$label\] could not open $path"
|
||||||
|
return
|
||||||
|
}
|
||||||
|
set data [read $f]
|
||||||
|
close $f
|
||||||
|
set lines [split [string trimright $data] "\n"]
|
||||||
|
set n [llength $lines]
|
||||||
|
if {$n > 20} {
|
||||||
|
set lines [lrange $lines end-19 end]
|
||||||
|
}
|
||||||
|
puts " --- $label (last [llength $lines] of $n lines) ---"
|
||||||
|
foreach line $lines {
|
||||||
|
puts " $line"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
proc fail {msg} {
|
proc fail {msg} {
|
||||||
|
global srv_log clt_log
|
||||||
|
|
||||||
puts "\n\[FAIL\] $msg"
|
puts "\n\[FAIL\] $msg"
|
||||||
|
dump_log "server" $srv_log
|
||||||
|
dump_log "client" $clt_log
|
||||||
cleanup
|
cleanup
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# --- Wait for a ready file, and return the port it names ---------------------
|
||||||
|
# The file is created before it is written, so wait for content, not existence.
|
||||||
|
# Returns 0 on timeout.
|
||||||
|
proc wait_for_port {path} {
|
||||||
|
global timeout
|
||||||
|
|
||||||
|
for {set elapsed 0} {$elapsed < $timeout} {incr elapsed} {
|
||||||
|
if {[file exists $path]} {
|
||||||
|
set f [open $path r]
|
||||||
|
set data [string trim [read $f]]
|
||||||
|
close $f
|
||||||
|
if {[string is integer -strict $data] && $data > 0} {
|
||||||
|
return $data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sleep 1
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Send every line in both directions, verifying each hop ------------------
|
||||||
|
proc exchange_lines {} {
|
||||||
|
global lorem_lines nc_client_id nc_server_id timeout
|
||||||
|
|
||||||
|
set n [llength $lorem_lines]
|
||||||
|
set i 0
|
||||||
|
foreach line $lorem_lines {
|
||||||
|
incr i
|
||||||
|
puts "\n \[$i/$n\] Client sending: \"$line\""
|
||||||
|
send -i $nc_client_id "$line\n"
|
||||||
|
|
||||||
|
expect {
|
||||||
|
-i $nc_server_id
|
||||||
|
-ex $line {
|
||||||
|
puts " \[PASS\] Server received line $i."
|
||||||
|
}
|
||||||
|
timeout {
|
||||||
|
fail "Server did not receive line $i within ${timeout}s"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
send -i $nc_server_id "$line\n"
|
||||||
|
|
||||||
|
expect {
|
||||||
|
-i $nc_client_id
|
||||||
|
-ex $line {
|
||||||
|
puts " \[PASS\] Client received echo of line $i."
|
||||||
|
}
|
||||||
|
timeout {
|
||||||
|
fail "Client did not receive echo of line $i within ${timeout}s"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Run one forwarding phase ------------------------------------------------
|
||||||
|
# Whoever listens on entry_port, the tunnel ends at target_port: reverse == 0
|
||||||
|
# means portfwd listens, reverse == 1 means it asks the SSH server to. An
|
||||||
|
# entry_port of 0 (reverse only) lets the peer pick, reported via ready file.
|
||||||
|
proc run_phase {label reverse entry_port target_port} {
|
||||||
|
global srv_log srv_ready clt_log clt_ready
|
||||||
|
global nc_server_id nc_server_pid nc_client_id nc_client_pid
|
||||||
|
global wolfssh_srv_id wolfssh_srv_pid wolfssh_clt_id wolfssh_clt_pid
|
||||||
|
|
||||||
|
puts "\n=== $label ==="
|
||||||
|
file delete -force $srv_log $srv_ready $clt_log $clt_ready
|
||||||
|
|
||||||
|
puts "\n\[1\] Starting nc server: nc -l $target_port"
|
||||||
|
spawn nc -l $target_port
|
||||||
|
set nc_server_id $spawn_id
|
||||||
|
set nc_server_pid [exp_pid]
|
||||||
|
puts " PID $nc_server_pid - waiting for a connection..."
|
||||||
|
|
||||||
|
puts "\n\[2\] Starting wolfssh server..."
|
||||||
|
spawn sh -c "exec ./examples/echoserver/echoserver -1 -f -R $srv_ready >$srv_log 2>&1"
|
||||||
|
set wolfssh_srv_id $spawn_id
|
||||||
|
set wolfssh_srv_pid [exp_pid]
|
||||||
|
|
||||||
|
# -R implies a dynamic SSH port, written only once the listener is bound.
|
||||||
|
# Waiting for it hands us the port and keeps the client from racing the
|
||||||
|
# listen().
|
||||||
|
set ssh_port [wait_for_port $srv_ready]
|
||||||
|
if {$ssh_port == 0} {
|
||||||
|
fail "$label: timed out waiting for wolfssh server (no port in $srv_ready)"
|
||||||
|
}
|
||||||
|
puts " PID $wolfssh_srv_pid listening on port $ssh_port."
|
||||||
|
|
||||||
|
if {$reverse} {
|
||||||
|
puts "\n\[3\] Starting wolfssh client (reverse, peer:$entry_port -> $target_port)..."
|
||||||
|
set reverse_flag "-r"
|
||||||
|
} else {
|
||||||
|
puts "\n\[3\] Starting wolfssh client (plain:$entry_port -> $target_port)..."
|
||||||
|
set reverse_flag ""
|
||||||
|
}
|
||||||
|
spawn sh -c "exec ./examples/portfwd/portfwd -u jill -P upthehill -p $ssh_port \
|
||||||
|
$reverse_flag -f $entry_port -t $target_port -R $clt_ready >$clt_log 2>&1"
|
||||||
|
set wolfssh_clt_id $spawn_id
|
||||||
|
set wolfssh_clt_pid [exp_pid]
|
||||||
|
|
||||||
|
# portfwd writes the entry port once the tunnel can accept connections --
|
||||||
|
# in reverse mode, only after the peer has replied.
|
||||||
|
set ready_port [wait_for_port $clt_ready]
|
||||||
|
if {$ready_port == 0} {
|
||||||
|
fail "$label: timed out waiting for wolfssh client (no port in $clt_ready)"
|
||||||
|
}
|
||||||
|
if {$entry_port != 0 && $ready_port != $entry_port} {
|
||||||
|
fail "$label: asked for entry port $entry_port, client reported $ready_port"
|
||||||
|
}
|
||||||
|
puts " wolfssh client ready on port $ready_port (PID $wolfssh_clt_pid)."
|
||||||
|
|
||||||
|
puts "\n\[4\] Starting nc client: nc localhost $ready_port"
|
||||||
|
spawn nc localhost $ready_port
|
||||||
|
set nc_client_id $spawn_id
|
||||||
|
set nc_client_pid [exp_pid]
|
||||||
|
puts " PID $nc_client_pid"
|
||||||
|
|
||||||
|
# Allow the TCP handshake and SSH negotiation to complete
|
||||||
|
sleep 1
|
||||||
|
|
||||||
|
puts "\n\[5\] Exchanging lines..."
|
||||||
|
exchange_lines
|
||||||
|
|
||||||
|
puts "\n\[6\] $label complete."
|
||||||
|
cleanup
|
||||||
|
}
|
||||||
|
|
||||||
# --- Check prerequisites -----------------------------------------------------
|
# --- Check prerequisites -----------------------------------------------------
|
||||||
foreach tool {nc} {
|
foreach tool {nc} {
|
||||||
if {[auto_execok $tool] eq ""} {
|
if {[auto_execok $tool] eq ""} {
|
||||||
|
|
@ -82,80 +250,11 @@ foreach tool {nc} {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# --- [1] Start nc server -----------------------------------------------------
|
run_phase "Phase 1: local forwarding" 0 12345 11111
|
||||||
puts "\n\[1\] Starting nc server: nc -l 11111"
|
run_phase "Phase 2: remote (reverse) forwarding" 1 12346 11112
|
||||||
spawn nc -l 11111
|
run_phase "Phase 3: reverse forwarding, peer-allocated port" 1 0 11113
|
||||||
set nc_server_id $spawn_id
|
|
||||||
set nc_server_pid [exp_pid]
|
|
||||||
puts " PID $nc_server_pid - waiting for a connection..."
|
|
||||||
|
|
||||||
# --- [2] Start wolfssh server ------------------------------------------------
|
|
||||||
puts "\n\[2\] Starting wolfssh server..."
|
|
||||||
spawn sh -c "exec ./examples/echoserver/echoserver -1 -f >$srv_log 2>&1"
|
|
||||||
set wolfssh_srv_id $spawn_id
|
|
||||||
set wolfssh_srv_pid [exp_pid]
|
|
||||||
puts " PID $wolfssh_srv_pid - waiting for a connection..."
|
|
||||||
|
|
||||||
# --- [3] Start wolfssh client ------------------------------------------------
|
|
||||||
puts "\n\[3\] Starting wolfssh client (plain:12345 -> 11111)..."
|
|
||||||
spawn sh -c "exec ./examples/portfwd/portfwd -u jill -P upthehill -f 12345 -t 11111 -R $clt_ready >$clt_log 2>&1"
|
|
||||||
set wolfssh_clt_id $spawn_id
|
|
||||||
set wolfssh_clt_pid [exp_pid]
|
|
||||||
|
|
||||||
# portfwd writes the listening port to $clt_ready once SSH is up.
|
|
||||||
set elapsed 0
|
|
||||||
while {![file exists $clt_ready] && $elapsed < $timeout} {
|
|
||||||
sleep 1
|
|
||||||
incr elapsed
|
|
||||||
}
|
|
||||||
if {![file exists $clt_ready]} {
|
|
||||||
fail "Timed out waiting for wolfssh client to start (no $clt_ready)"
|
|
||||||
}
|
|
||||||
puts " wolfssh client ready (PID $wolfssh_clt_pid)."
|
|
||||||
|
|
||||||
# --- [4] Start nc client -----------------------------------------------------
|
|
||||||
puts "\n\[4\] Starting nc client: nc localhost 12345"
|
|
||||||
spawn nc localhost 12345
|
|
||||||
set nc_client_id $spawn_id
|
|
||||||
set nc_client_pid [exp_pid]
|
|
||||||
puts " PID $nc_client_pid"
|
|
||||||
|
|
||||||
# Allow the TCP handshake and SSH negotiation to complete
|
|
||||||
sleep 1
|
|
||||||
|
|
||||||
# --- [5] Send each line, verify receipt, echo back, verify echo --------------
|
|
||||||
set n [llength $lorem_lines]
|
|
||||||
set i 0
|
|
||||||
foreach line $lorem_lines {
|
|
||||||
incr i
|
|
||||||
puts "\n\[5.$i/$n\] Client sending: \"$line\""
|
|
||||||
send -i $nc_client_id "$line\n"
|
|
||||||
|
|
||||||
expect {
|
|
||||||
-i $nc_server_id
|
|
||||||
-ex $line {
|
|
||||||
puts " \[PASS\] Server received line $i."
|
|
||||||
}
|
|
||||||
timeout {
|
|
||||||
fail "Server did not receive line $i within ${timeout}s"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
send -i $nc_server_id "$line\n"
|
|
||||||
|
|
||||||
expect {
|
|
||||||
-i $nc_client_id
|
|
||||||
-ex $line {
|
|
||||||
puts " \[PASS\] Client received echo of line $i."
|
|
||||||
}
|
|
||||||
timeout {
|
|
||||||
fail "Client did not receive echo of line $i within ${timeout}s"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# --- Done --------------------------------------------------------------------
|
# --- Done --------------------------------------------------------------------
|
||||||
puts "\n=== TEST PASSED ===\n"
|
puts "\n=== TEST PASSED ===\n"
|
||||||
|
|
||||||
cleanup
|
|
||||||
exit 0
|
exit 0
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue