|
| 1 | +#!/bin/sh |
| 2 | + |
| 3 | +# Bulk data through a local direct-tcpip forward. |
| 4 | +# |
| 5 | +# [nc client] --plain--> :$entry_port [portfwd] |
| 6 | +# | |
| 7 | +# SSH |
| 8 | +# | |
| 9 | +# [echoserver] --plain--> :$target_port [nc server] |
| 10 | +# |
| 11 | +# scripts/fwd.test moves a few hundred bytes, so nothing in the suite |
| 12 | +# exercises a forward past its first window. This pushes a payload several |
| 13 | +# windows long and compares the bytes that come out. |
| 14 | +# |
| 15 | +# The listening nc takes its stdin from a long sleep on purpose. Reading |
| 16 | +# end-of-input on stdin makes nc close the connection, which truncates the |
| 17 | +# transfer at whatever point the close lands and looks exactly like a |
| 18 | +# forwarding stall. |
| 19 | + |
| 20 | +no_pid=-1 |
| 21 | +hold_pid=$no_pid |
| 22 | +nc_server_pid=$no_pid |
| 23 | +server_pid=$no_pid |
| 24 | +portfwd_pid=$no_pid |
| 25 | +nc_client_pid=$no_pid |
| 26 | +work_dir="`pwd`/wolfssh_fwd_bulk$$" |
| 27 | +ready_file="$work_dir/ready" |
| 28 | +fwd_ready_file="$work_dir/fwd_ready" |
| 29 | +payload="$work_dir/payload" |
| 30 | +hold_fifo="$work_dir/hold" |
| 31 | +received="$work_dir/received" |
| 32 | +server_log="$work_dir/server.log" |
| 33 | +portfwd_log="$work_dir/portfwd.log" |
| 34 | +# Several times the 128K default window, so the transfer cannot finish |
| 35 | +# without the window being credited back at least once. The size the |
| 36 | +# receiver is held to is read back from the file dd made. |
| 37 | +payload_blocks=2000 |
| 38 | +payload_size=0 |
| 39 | +entry_port=0 |
| 40 | +target_port=0 |
| 41 | +port=0 |
| 42 | +counter=0 |
| 43 | +# Seconds to wait for the payload. Generous: the point is that a stalled |
| 44 | +# forward fails this test instead of hanging make check. |
| 45 | +transfer_limit=90 |
| 46 | +# Consecutive seconds with no new bytes before calling it stalled. |
| 47 | +stall_limit=15 |
| 48 | + |
| 49 | +[ ! -x "`command -v nc`" ] && echo "nc doesn't exist, skipping" && exit 77 |
| 50 | +[ ! -x ./examples/echoserver/echoserver ] \ |
| 51 | + && echo "echoserver doesn't exist, skipping" && exit 77 |
| 52 | +./examples/echoserver/echoserver '-?' 2>&1 | grep -q "^echoserver " \ |
| 53 | + || { echo "echoserver doesn't run, skipping"; exit 77; } |
| 54 | +[ ! -x ./examples/portfwd/portfwd ] \ |
| 55 | + && echo "portfwd doesn't exist, skipping" && exit 77 |
| 56 | +./examples/portfwd/portfwd '-?' 2>&1 | grep -q "does not exist" \ |
| 57 | + && { echo "forwarding not compiled in, skipping"; exit 77; } |
| 58 | + |
| 59 | +# A WOLFSSH_TEST_BLOCK build fails writes at random, which stalls the |
| 60 | +# echoserver regardless of what the peer does. The other echoserver scripts |
| 61 | +# skip it for the same reason. |
| 62 | +WOLFSSH_OPTIONS=`./apps/wolfssh-options` || { |
| 63 | + echo "fail: could not run ./apps/wolfssh-options" |
| 64 | + exit 1 |
| 65 | +} |
| 66 | +echo "$WOLFSSH_OPTIONS" | grep -qx "TEST_BLOCK" \ |
| 67 | + && { echo "macro WOLFSSH_TEST_BLOCK was used, skipping"; exit 77; } |
| 68 | + |
| 69 | +do_cleanup() { |
| 70 | + for pid in $nc_client_pid $portfwd_pid $server_pid $nc_server_pid \ |
| 71 | + $hold_pid |
| 72 | + do |
| 73 | + if [ "$pid" != "$no_pid" ] |
| 74 | + then |
| 75 | + kill -9 "$pid" 2>/dev/null |
| 76 | + wait "$pid" 2>/dev/null |
| 77 | + fi |
| 78 | + done |
| 79 | + rm -rf "$work_dir" |
| 80 | +} |
| 81 | + |
| 82 | +# The failure here is usually just a byte count; the logs name the cause. |
| 83 | +do_dump_logs() { |
| 84 | + for log in "$server_log" "$portfwd_log" |
| 85 | + do |
| 86 | + echo "--- `basename "$log"` ---" |
| 87 | + cat "$log" 2>/dev/null |
| 88 | + done |
| 89 | +} |
| 90 | + |
| 91 | +do_fail() { |
| 92 | + echo "$1" |
| 93 | + do_dump_logs |
| 94 | + do_cleanup |
| 95 | + exit 1 |
| 96 | +} |
| 97 | + |
| 98 | +do_trap() { |
| 99 | + echo "got trap" |
| 100 | + do_cleanup |
| 101 | + exit 1 |
| 102 | +} |
| 103 | + |
| 104 | +trap do_trap INT TERM |
| 105 | + |
| 106 | +mkdir -p "$work_dir" || { echo "couldn't make the work directory"; exit 1; } |
| 107 | + |
| 108 | +# portfwd and nc bind what they are told, so the plaintext ports have to be |
| 109 | +# picked here. Stamped with the pid so concurrent runs of the suite do not |
| 110 | +# collide, kept below the ephemeral range so an outgoing connection cannot |
| 111 | +# take one first, and clear of the ports fwd.test.expect hardcodes. |
| 112 | +entry_port=`expr 14000 + \( $$ % 1000 \)` |
| 113 | +target_port=`expr 15000 + \( $$ % 1000 \)` |
| 114 | + |
| 115 | +dd if=/dev/urandom of="$payload" bs=1000 count=$payload_blocks 2>/dev/null \ |
| 116 | + || { echo "couldn't make the payload"; do_cleanup; exit 1; } |
| 117 | +payload_size=`wc -c < "$payload" | tr -d ' '` |
| 118 | +[ "$payload_size" -gt 0 ] \ |
| 119 | + || { echo "couldn't make the payload"; do_cleanup; exit 1; } |
| 120 | + |
| 121 | +# nc closes the connection when it reads end-of-input on stdin, which |
| 122 | +# truncates the transfer. Hold stdin open with a sleep on the far side of a |
| 123 | +# fifo, so both ends have a pid this script can reap. |
| 124 | +mkfifo "$hold_fifo" || { echo "couldn't make the fifo"; do_cleanup; exit 1; } |
| 125 | +sleep 300 > "$hold_fifo" 2>/dev/null & |
| 126 | +hold_pid=$! |
| 127 | +nc -l $target_port < "$hold_fifo" > "$received" 2>/dev/null & |
| 128 | +nc_server_pid=$! |
| 129 | + |
| 130 | +# A port already in use makes nc exit at once, and the echoserver's connect |
| 131 | +# would then be refused. Catch that here, or it reports as a stall. |
| 132 | +sleep 0.2 |
| 133 | +if ! kill -0 "$nc_server_pid" 2>/dev/null |
| 134 | +then |
| 135 | + nc_server_pid=$no_pid |
| 136 | + echo "couldn't listen on port $target_port, is it in use?" |
| 137 | + do_cleanup |
| 138 | + exit 1 |
| 139 | +fi |
| 140 | + |
| 141 | +./examples/echoserver/echoserver -1 -f -R "$ready_file" \ |
| 142 | + > "$server_log" 2>&1 & |
| 143 | +server_pid=$! |
| 144 | + |
| 145 | +counter=0 |
| 146 | +while [ ! -s "$ready_file" -a "$counter" -lt 20 ]; do |
| 147 | + sleep 0.1 |
| 148 | + counter=`expr $counter + 1` |
| 149 | +done |
| 150 | +[ -s "$ready_file" ] || do_fail "no ready file, echoserver didn't start" |
| 151 | +port=`cat "$ready_file"` |
| 152 | + |
| 153 | +./examples/portfwd/portfwd -u jill -P upthehill -p "$port" \ |
| 154 | + -f $entry_port -t $target_port -R "$fwd_ready_file" \ |
| 155 | + > "$portfwd_log" 2>&1 & |
| 156 | +portfwd_pid=$! |
| 157 | + |
| 158 | +counter=0 |
| 159 | +while [ ! -s "$fwd_ready_file" -a "$counter" -lt 20 ]; do |
| 160 | + sleep 0.1 |
| 161 | + counter=`expr $counter + 1` |
| 162 | +done |
| 163 | +[ -s "$fwd_ready_file" ] || do_fail "no ready file, portfwd didn't start" |
| 164 | + |
| 165 | +nc 127.0.0.1 $entry_port < "$payload" > /dev/null 2>&1 & |
| 166 | +nc_client_pid=$! |
| 167 | + |
| 168 | +counter=0 |
| 169 | +got=0 |
| 170 | +last=0 |
| 171 | +stalled=0 |
| 172 | +while [ "$got" -lt "$payload_size" -a "$counter" -lt "$transfer_limit" ]; do |
| 173 | + sleep 1 |
| 174 | + counter=`expr $counter + 1` |
| 175 | + got=`wc -c < "$received" 2>/dev/null | tr -d ' '` |
| 176 | + [ -z "$got" ] && got=0 |
| 177 | + # Give up early once the byte count stops moving, so a stalled forward |
| 178 | + # reports in seconds instead of burning the whole limit. |
| 179 | + if [ "$got" -eq "$last" ] |
| 180 | + then |
| 181 | + stalled=`expr $stalled + 1` |
| 182 | + [ "$stalled" -ge "$stall_limit" ] && break |
| 183 | + else |
| 184 | + stalled=0 |
| 185 | + last=$got |
| 186 | + fi |
| 187 | +done |
| 188 | + |
| 189 | +[ "$got" -eq "$payload_size" ] \ |
| 190 | + || do_fail "forward stalled: sent $payload_size bytes, received $got" |
| 191 | + |
| 192 | +cmp -s "$payload" "$received" \ |
| 193 | + || do_fail "forwarded data does not match what was sent" |
| 194 | + |
| 195 | +echo "moved $payload_size bytes through a local forward" |
| 196 | +do_cleanup |
| 197 | +exit 0 |
0 commit comments