diff --git a/CHANGES b/CHANGES index 6c264e4..69f091b 100644 --- a/CHANGES +++ b/CHANGES @@ -60,10 +60,24 @@ Corrections: now handled properly. Test: UNIX_L_BIND - Removed unused bytes variable from gettimestamp(), corrected #elsif and - socks4 record length. + Removed unused bytes variable from gettimestamp(), corrected #elsif, + and socks4 record length. Thanks to clang-18 and gcc-13. + Address TCP-CONNECT, when target address resolves to both IPv4 and + IPv6, now tries to take into account bind address for protocol + selection. + + Reworked and harmonized ipapp client addresses. + Tests: TCP_CONNECT_RETRY SCTP_CONNECT_RETRY DCCP_CONNECT_RETRY + OPENSSL_CONNECT_RETRY SOCKS4_RETRY SOCKS5_CONNECT_RETRY + PROXY_CONNECT_RETRY + + Socks and proxy clients now also support option max-children. + Tests: TCP_CONNECT_MAXCHILDREN SCTP_CONNECT_MAXCHILDREN + DCCP_CONNECT_MAXCHILDREN OPENSSL_CONNECT_MAXCHILDREN + SOCKS4_MAXCHILDREN SOCKS5_CONNECT_MAXCHILDREN PROXY_CONNECT_MAXCHILDREN + Features: POSIXMQ-RECV now takes option o-nonblock; this, in combination with -T, makes it possible to terminate Socat in case the queue is empty. diff --git a/socks4echo.sh b/socks4echo.sh index a577b20..44c631a 100755 --- a/socks4echo.sh +++ b/socks4echo.sh @@ -9,6 +9,7 @@ # it is required for test.sh # for TCP, use this script as: # socat tcp-l:1080,reuseaddr,crlf system:"socks4echo.sh" +# Then connect with a socks4 request for 32.98.76.54:32109 and user nobody # older bash and ksh do not have -n option to read command; we try dd then #if echo a |read -n 1 null >/dev/null 2>&1; then diff --git a/test.sh b/test.sh index 0ed9584..5829e63 100755 --- a/test.sh +++ b/test.sh @@ -39,7 +39,7 @@ usage() { $ECHO "Usage: $0 <options> [<test-spec> ...]" $ECHO "options:" $ECHO "\t-h \t\tShow this help" - $ECHO "\t-t <sec> \tBase for timeouts in seconds, default: 0.1" + $ECHO "\t-t <sec> \tBase for timeouts in seconds, default is automatically determined" $ECHO "\t-v \t\tBe more verbose, show failed commands" $ECHO "\t-n <num> \tOnly perform test with given number" $ECHO "\t-N <num> \tOnly perform tests starting with given number" @@ -123,6 +123,13 @@ divide_uint_by_1000000 () { fi } + +# output the value in seconds for n * val_t +relsecs () { + local n="$1" + divide_uint_by_1000000 $((n*MICROS)) +} + _MICROS=$((MICROS+999999)); SECONDs="${_MICROS%??????}" [ -z "$SECONDs" ] && SECONDs=0 [ "$DEFS" ] && echo "SECONDs=\"$SECONDs\"" >&2 @@ -145,26 +152,20 @@ if [ -z "$FILAN" ]; then if test -x ./filan; then FILAN="./filan"; elif ! type f if ! sleep 0.1 2>/dev/null; then sleep () { - $SOCAT -T $0 PIPE PIPE + $SOCAT -T "$1" PIPE PIPE } fi if [ -z "$val_t" ]; then - # Determine the time Socat needs for an empty run + # Estimate the time Socat needs for an empty run + sleep 0.5 # immediately after build the first runs are extremely fast $SOCAT /dev/null /dev/null # populate caches - MILLIs=$(bash -c 'time $SOCAT $opts /dev/null /dev/null' 2>&1 |grep ^real |sed 's/.*m\(.*\)s.*/\1/' |tr -d ,.) + MILLIs=$(bash -c "time for _ in {1..3}; do $SOCAT -d0 $opts /dev/null /dev/null; done" 2>&1 |grep ^real |sed 's/.*m\(.*\)s.*/\1/' |tr -d ,.) while [ "${MILLIs:0:1}" = '0' ]; do MILLIs=${MILLIs##0}; done # strip leading '0' to avoid octal [ -z "$MILLIs" ] && MILLIs=1 - [ "$DEFS" ] && echo "MILLIs=\"$MILLIs\" (1)" >&2 - - # On my idle development computer this value flaps from 0.001 to 0.004 - # 0.001 lets many tests fail, so we triple the result - #MILLIs=$((10*MILLIs)) - MILLIs=$((3*MILLIs)) - [ "$DEFS" ] && echo "MILLIs=\"$MILLIs\" (2)" >&2 + [ "$DEFS" ] && echo "MILLIs=\"$MILLIs\"" >&2 MICROS=${MILLIs}000 - #set -vx case $MICROS in ???????*) val_t=${MICROS%??????}.${MICROS: -6} ;; *) x=000000$MICROS; val_t=0.${x: -6} ;; @@ -212,7 +213,13 @@ PATH=.:$PATH # for relsleep MISCDELAY=1 OPTS="$opt_t $OPTS" -[ "$EXPERIMENTAL" ] && OPTS="--experimental $OPTS" + +if [ "$EXPERIMENTAL" ]; then + if $SOCAT -h |grep -e --experimental >/dev/null; then + OPTS="$OPTS --experimental" + fi +fi + opts="$OPTS" [ "$DEFS" ] && echo "opts=\"$opts\"" >&2 @@ -1009,7 +1016,7 @@ runsip4 () { CYGWIN*) l=$(ipconfig |grep IPv4);; *) l=$($IFCONFIG -a |grep ' ::1[^:0-9A-Fa-f]') ;; esac - [ -z "$l" ] && return 1 + [ -z "$l" ] && return 1 # existence of interface might not suffice, check for routeability: case "$UNAME" in Darwin) ping -c 1 127.0.0.1 >/dev/null 2>&1; l="$?" ;; @@ -1043,7 +1050,7 @@ runsip6 () { CYGWIN*) l=$(ipconfig |grep IPv6);; *) l=$($IFCONFIG -a |grep ' ::1[^:0-9A-Fa-f]') ;; esac - [ -z "$l" ] && return 1 + [ -z "$l" ] && return 1 # existence of interface might not suffice, check for routeability: case "$UNAME" in Darwin) $PING6 -c 1 ::1 >/dev/null 2>&1; l="$?" ;; @@ -1484,6 +1491,24 @@ checktcpport () { return 1 } +waittcpport () { + local port="$1" + local logic="$2" # 0..wait until free; 1..wait until listening (default) + local timeout="$3" + while true; do +#echo "timeout=\"$timeout\"" >&2 + if [ "$logic" = 0 ]; then + if checktcpport $1; then break; fi + else + if ! checktcpport $1; then break; fi + fi + if [ $timeout -le 0 ]; then return 1; fi + sleep 1 + let --timeout; + done + return 0; +} + checktcp4port () { checktcpport $1 } @@ -2289,7 +2314,7 @@ esac N=$((N+1)) -# test: send EOF to exec'ed sub process, let it finish its operation, and +# test: send EOF to exec'ed sub process, let it finish its operation, and # check if the sub process returns its data before terminating. NAME=EXECSOCKETPAIRFLUSH # idea: have socat exec'ing od; send data and EOF, and check if the od'ed data @@ -2455,8 +2480,9 @@ esac N=$((N+1)) newport() { - _PORT=$((_PORT+1)) + _PORT=$((_PORT+1)) while eval wait${1}port $_PORT 1 0 2>/dev/null; do _PORT=$((_PORT+1)); done + #while ! eval check${1}port $_PORT 2>/dev/null; do sleep 1; _PORT=$((_PORT+1)); done #echo "PORT=$_PORT" >&2 PORT=$_PORT } @@ -3565,7 +3591,7 @@ printf "test $F_n $TEST... " $N touch "$ti" $CMD >"$tf" 2>"$te" & bg=$! -# Up to 1.8.0.1 this sleep was 0.1 and thus the test said OK despite the bug +# Up to 1.8.0.1 this sleep was 0.1 and thus the test said OK despite the bug sleep 1.1 echo "$da" >>"$ti" sleep 1 @@ -4028,8 +4054,6 @@ esac N=$((N+1)) -newport $RUNS # in case it has not yet been invoked - while read NAMEKEYW FEAT RUNS TESTTMPL PEERTMPL WAITTMPL; do if [ -z "$NAMEKEYW" ] || [[ "$NAMEKEYW" == \#* ]]; then continue; fi @@ -4514,35 +4538,47 @@ elif ! testfeats listen tcp ip4 >/dev/null || ! runsip4 >/dev/null; then $PRINTF "test $F_n $TEST... ${YELLOW}TCP/IPv4 not available${NORMAL}\n" $N cant else -ts="$td/test$N.sh" -tf="$td/test$N.stdout" -te="$td/test$N.stderr" -tdiff="$td/test$N.diff" -da="test$N $(date) $RANDOM"; da="$da$($ECHO '\r')" -newport tcp4 # provide free port number in $PORT -#CMD2="$TRACE $SOCAT tcp4-l:$PORT,crlf SYSTEM:\"read; read; $ECHO \\\"HTTP/1.0 200 OK\n\\\"; cat\"" -CMD2="$TRACE $SOCAT $opts TCP4-L:$PORT,$REUSEADDR,crlf EXEC:\"/usr/bin/env bash proxyecho.sh\"" -CMD="$TRACE $SOCAT $opts - PROXY:$LOCALHOST:127.0.0.1:1000,pf=ip4,proxyport=$PORT" -printf "test $F_n $TEST... " $N -eval "$CMD2 2>\"${te}2\" &" -pid=$! # background process id -waittcp4port $PORT 1 -echo "$da" |$CMD >"$tf" 2>"${te}1" -if ! echo "$da" |diff - "$tf" >"$tdiff"; then - $PRINTF "$FAILED: $TRACE $SOCAT:\n" - echo "$CMD2 &" - echo "$CMD" - cat "${te}1" - cat "${te}2" - cat "$tdiff" - failed -else - $PRINTF "$OK\n" - if [ -n "$debug" ]; then cat "${te}1" "${te}2"; fi - ok -fi -kill $pid 2>/dev/null -wait + ts="$td/test$N.sh" + tf="$td/test$N.stdout" + te="$td/test$N.stderr" + tdiff="$td/test$N.diff" + da="test$N $(date) $RANDOM"; da="$da$($ECHO '\r')" + newport tcp4 # provide free port number in $PORT + #CMD0="$TRACE $SOCAT tcp4-l:$PORT,crlf SYSTEM:\"read; read; $ECHO \\\"HTTP/1.0 200 OK\n\\\"; cat\"" + CMD0="$TRACE $SOCAT $opts TCP4-L:$PORT,$REUSEADDR,crlf EXEC:\"/usr/bin/env bash proxyecho.sh\"" + CMD1="$TRACE $SOCAT $opts - PROXY:$LOCALHOST:127.0.0.1:1000,pf=ip4,proxyport=$PORT" + printf "test $F_n $TEST... " $N + eval "$CMD0 2>\"${te}0\" &" + pid=$! # background process id + waittcp4port $PORT 1 + echo "$da" |$CMD1 >"$tf" 2>"${te}1" + rc1=$? + if [ "$rc1" -ne 0 ]; then + $PRINTF "$FAILED (rc1=$rc1)\n" + echo "$CMD0 &" + cat "${te}0" >&2 + echo "$CMD1" + cat "${te}1" >&2 + fail + elif ! echo "$da" |diff - "$tf" >"$tdiff"; then + $PRINTF "$FAILED (diff)\n" + echo "$CMD0 &" + cat "${te}0" >&2 + echo "$CMD1" + cat "${te}1" >&2 + echo "// diff:" >&2 + cat "$tdiff" >&2 + fail + else + $PRINTF "$OK\n" + if [ "$VERBOSE" ]; then echo "$CMD0 &"; fi + if [ "$DEBUG" ]; then cat "${te}0" >&2; fi + if [ "$VERBOSE" ]; then echo "$CMD1"; fi + if [ "$DEBUG" ]; then cat "${te}1" >&2; fi + ok + fi + kill $pid 2>/dev/null + wait fi ;; # NUMCOND, feats esac N=$((N+1)) @@ -4559,35 +4595,47 @@ elif ! testfeats listen tcp ip6 >/dev/null || ! runsip6 >/dev/null; then $PRINTF "test $F_n $TEST... ${YELLOW}TCP/IPv6 not available${NORMAL}\n" $N cant else -ts="$td/test$N.sh" -tf="$td/test$N.stdout" -te="$td/test$N.stderr" -tdiff="$td/test$N.diff" -da="test$N $(date) $RANDOM"; da="$da$($ECHO '\r')" -newport tcp6 # provide free port number in $PORT -#CMD2="$TRACE $SOCAT $opts TCP6-L:$PORT,crlf SYSTEM:\"read; read; $ECHO \\\"HTTP/1.0 200 OK\n\\\"; cat\"" -CMD2="$TRACE $SOCAT $opts TCP6-L:$PORT,$REUSEADDR,crlf EXEC:\"/usr/bin/env bash proxyecho.sh\"" -CMD="$TRACE $SOCAT $opts - PROXY:$LOCALHOST6:127.0.0.1:1000,proxyport=$PORT" -printf "test $F_n $TEST... " $N -eval "$CMD2 2>\"${te}2\" &" -pid=$! # background process id -waittcp6port $PORT 1 -echo "$da" |$CMD >"$tf" 2>"${te}1" -if ! echo "$da" |diff - "$tf" >"$tdiff"; then - $PRINTF "$FAILED: $TRACE $SOCAT:\n" - echo "$CMD2 &" - echo "$CMD" - cat "${te}1" - cat "${te}2" - cat "$tdiff" - failed -else - $PRINTF "$OK\n" - if [ -n "$debug" ]; then cat "${te}1" "${te}2"; fi - ok -fi -kill $pid 2>/dev/null -wait + ts="$td/test$N.sh" + tf="$td/test$N.stdout" + te="$td/test$N.stderr" + tdiff="$td/test$N.diff" + da="test$N $(date) $RANDOM"; da="$da$($ECHO '\r')" + newport tcp6 # provide free port number in $PORT + #CMD0="$TRACE $SOCAT $opts TCP6-L:$PORT,crlf SYSTEM:\"read; read; $ECHO \\\"HTTP/1.0 200 OK\n\\\"; cat\"" + CMD0="$TRACE $SOCAT $opts TCP6-L:$PORT,$REUSEADDR,crlf EXEC:\"/usr/bin/env bash proxyecho.sh\"" + CMD1="$TRACE $SOCAT $opts - PROXY:$LOCALHOST6:127.0.0.1:1000,proxyport=$PORT" + printf "test $F_n $TEST... " $N + eval "$CMD0 2>\"${te}0\" &" + pid=$! # background process id + waittcp6port $PORT 1 + echo "$da" |$CMD1 >"$tf" 2>"${te}1" + rc1=$? + if [ "$rc1" -ne 0 ]; then + $PRINTF "$FAILED (rc1=$rc1)\n" + echo "$CMD0 &" + cat "${te}0" >&2 + echo "$CMD1" + cat "${te}1" >&2 + failed + elif ! echo "$da" |diff - "$tf" >"$tdiff"; then + $PRINTF "$FAILED (diff)\n" + echo "$CMD0 &" + cat "${te}0" >&2 + echo "$CMD1" + cat "${te}1" >&2 + echo "// diff: " >&2 + cat "$tdiff" >&2 + failed + else + $PRINTF "$OK\n" + if [ "$VERBOSE" ]; then echo "$CMD0 &"; fi + if [ "$DEBUG" ]; then cat "${te}0" >&2; fi + if [ "$VERBOSE" ]; then echo "$CMD1"; fi + if [ "$DEBUG" ]; then cat "${te}1" >&2; fi + ok + fi + kill $pid 2>/dev/null + wait fi ;; # NUMCOND, feats esac N=$((N+1)) @@ -5033,7 +5081,7 @@ N=$((N+1)) NAME=GENDERCHANGER case "$TESTS" in -*%$N%*|*%functions%*|*%listen%*|*%$NAME%*) +*%$N%*|*%functions%*|*%listen%*|*%retry%*|*%$NAME%*) TEST="$NAME: TCP4 \"gender changer\"" if ! eval $NUMCOND; then :; else tf="$td/test$N.stdout" @@ -5096,7 +5144,7 @@ N=$((N+1)) NAME=OUTBOUNDIN case "$TESTS" in -*%$N%*|*%functions%*|*%openssl%*|*%proxy%*|*%fork%*|*%listen%*|*%$NAME%*) +*%$N%*|*%functions%*|*%openssl%*|*%proxy%*|*%fork%*|*%listen%*|*%retry%*|*%$NAME%*) TEST="$NAME: gender changer via SSL through HTTP proxy, oneshot" if ! eval $NUMCOND; then :; elif ! feat=$(testfeats openssl proxy); then @@ -5185,7 +5233,7 @@ N=$((N+1)) #! NAME=INTRANETRIPPER case "$TESTS" in -*%$N%*|*%functions%*|*%openssl%*|*%proxy%*|*%listen%*|*%fork%*|*%$NAME%*) +*%$N%*|*%functions%*|*%openssl%*|*%proxy%*|*%listen%*|*%fork%*|*%retry%*|*%$NAME%*) TEST="$NAME: gender changer via SSL through HTTP proxy, daemons" if ! eval $NUMCOND; then :; elif ! feat=$(testfeats openssl proxy); then @@ -5460,7 +5508,7 @@ testserversec () { NAME=TCP4RANGEBITS case "$TESTS" in -*%$N%*|*%functions%*|*%security%*|*%tcp%*|*%tcp4%*|*%ip4%*|*%range%*|*%listen%*|*%fork%*|*%$NAME%*) +*%$N%*|*%functions%*|*%security%*|*%tcp%*|*%tcp4%*|*%ip4%*|*%range%*|*%listen%*|*%fork%*|*%retry%*|*%$NAME%*) TEST="$NAME: security of TCP4-L with RANGE option" if ! eval $NUMCOND; then :; elif [ -z "$SECONDADDR" ]; then @@ -5476,7 +5524,7 @@ N=$((N+1)) NAME=TCP4RANGEMASK case "$TESTS" in -*%$N%*|*%functions%*|*%security%*|*%tcp%*|*%tcp4%*|*%ip4%*|*%range%*|*%listen%*|*%fork%*|*%$NAME%*) +*%$N%*|*%functions%*|*%security%*|*%tcp%*|*%tcp4%*|*%ip4%*|*%range%*|*%listen%*|*%fork%*|*%retry%*|*%$NAME%*) TEST="$NAME: security of TCP4-L with RANGE option" if ! eval $NUMCOND; then :; elif [ -z "$SECONDADDR" ]; then @@ -5493,7 +5541,7 @@ N=$((N+1)) # like TCP4RANGEMASK, but the "bad" address is within the same class A network NAME=TCP4RANGEMASKHAIRY case "$TESTS" in -*%$N%*|*%functions%*|*%security%*|*%tcp%*|*%tcp4%*|*%ip4%*|*%range%*|*%listen%*|*%fork%*|*%$NAME%*) +*%$N%*|*%functions%*|*%security%*|*%tcp%*|*%tcp4%*|*%ip4%*|*%range%*|*%listen%*|*%fork%*|*%retry%*|*%$NAME%*) TEST="$NAME: security of TCP4-L with RANGE option" if ! eval $NUMCOND; then :; else newport tcp4 # provide free port number in $PORT @@ -5505,7 +5553,7 @@ N=$((N+1)) NAME=TCP4SOURCEPORT case "$TESTS" in -*%$N%*|*%functions%*|*%security%*|*%tcp%*|*%tcp4%*|*%ip4%*|*%sourceport%*|*%listen%*|*%fork%*|*%$NAME%*) +*%$N%*|*%functions%*|*%security%*|*%tcp%*|*%tcp4%*|*%ip4%*|*%sourceport%*|*%listen%*|*%fork%*|*%retry%*|*%$NAME%*) TEST="$NAME: security of TCP4-L with SOURCEPORT option" if ! eval $NUMCOND; then :; else newport tcp4 # provide free port number in $PORT @@ -5516,7 +5564,7 @@ N=$((N+1)) NAME=TCP4LOWPORT case "$TESTS" in -*%$N%*|*%functions%*|*%security%*|*%tcp%*|*%tcp4%*|*%ip4%*|*%lowport%*|*%listen%*|*%fork%*|*%$NAME%*) +*%$N%*|*%functions%*|*%security%*|*%tcp%*|*%tcp4%*|*%ip4%*|*%lowport%*|*%listen%*|*%fork%*|*%retry%*|*%$NAME%*) TEST="$NAME: security of TCP4-L with LOWPORT option" if ! eval $NUMCOND; then :; else newport tcp4 # provide free port number in $PORT @@ -5527,7 +5575,7 @@ N=$((N+1)) NAME=TCP4WRAPPERS_ADDR case "$TESTS" in -*%$N%*|*%functions%*|*%security%*|*%tcp%*|*%tcp4%*|*%ip4%*|*%tcpwrap%*|*%listen%*|*%fork%*|*%$NAME%*) +*%$N%*|*%functions%*|*%security%*|*%tcp%*|*%tcp4%*|*%ip4%*|*%tcpwrap%*|*%listen%*|*%fork%*|*%retry%*|*%$NAME%*) TEST="$NAME: security of TCP4-L with TCPWRAP option" if ! eval $NUMCOND; then :; elif ! feat=$(testfeats tcp ip4 libwrap) || ! runsip4 >/dev/null; then @@ -5546,7 +5594,7 @@ N=$((N+1)) NAME=TCP4WRAPPERS_NAME case "$TESTS" in -*%$N%*|*%functions%*|*%security%*|*%tcp%*|*%tcp4%*|*%ip4%*|*%tcpwrap%*|*%listen%*|*%fork%*|*%$NAME%*) +*%$N%*|*%functions%*|*%security%*|*%tcp%*|*%tcp4%*|*%ip4%*|*%tcpwrap%*|*%listen%*|*%fork%*|*%retry%*|*%$NAME%*) TEST="$NAME: security of TCP4-L with TCPWRAP option" if ! eval $NUMCOND; then :; elif ! feat=$(testfeats tcp ip4 libwrap) || ! runsip4 >/dev/null; then @@ -5566,7 +5614,7 @@ N=$((N+1)) NAME=TCP6RANGE case "$TESTS" in -*%$N%*|*%functions%*|*%security%*|*%tcp%*|*%tcp6%*|*%ip6%*|*%range%*|*%listen%*|*%fork%*|*%$NAME%*) +*%$N%*|*%functions%*|*%security%*|*%tcp%*|*%tcp6%*|*%ip6%*|*%range%*|*%listen%*|*%fork%*|*%retry%*|*%$NAME%*) TEST="$NAME: security of TCP6-L with RANGE option" if ! eval $NUMCOND; then :; elif ! feat=$(testfeats tcp ip6) || ! runsip6 >/dev/null; then @@ -5581,7 +5629,7 @@ N=$((N+1)) NAME=TCP6SOURCEPORT case "$TESTS" in -*%$N%*|*%functions%*|*%security%*|*%tcp%*|*%tcp6%*|*%ip6%*|*%sourceport%*|*%listen%|*%fork%**|*%$NAME%*) +*%$N%*|*%functions%*|*%security%*|*%tcp%*|*%tcp6%*|*%ip6%*|*%sourceport%*|*%listen%|*%fork%*|*%retry%*|*%$NAME%*) TEST="$NAME: security of TCP6-L with SOURCEPORT option" if ! eval $NUMCOND; then :; elif ! feat=$(testfeats tcp ip6) || ! runsip6 >/dev/null; then @@ -5596,7 +5644,7 @@ N=$((N+1)) NAME=TCP6LOWPORT case "$TESTS" in -*%$N%*|*%functions%*|*%security%*|*%tcp%*|*%tcp6%*|*%ip6%*|*%lowport%*|*%listen%*|*%fork%*|*%$NAME%*) +*%$N%*|*%functions%*|*%security%*|*%tcp%*|*%tcp6%*|*%ip6%*|*%lowport%*|*%listen%*|*%fork%*|*%retry%*|*%$NAME%*) TEST="$NAME: security of TCP6-L with LOWPORT option" if ! eval $NUMCOND; then :; elif ! feat=$(testfeats tcp ip6) || ! runsip6 >/dev/null; then @@ -5611,7 +5659,7 @@ N=$((N+1)) NAME=TCP6TCPWRAP case "$TESTS" in -*%$N%*|*%functions%*|*%security%*|*%tcp%*|*%tcp6%*|*%ip6%*|*%tcpwrap%*|*%listen%*|*%fork%*|*%$NAME%*) +*%$N%*|*%functions%*|*%security%*|*%tcp%*|*%tcp6%*|*%ip6%*|*%tcpwrap%*|*%listen%*|*%fork%*|*%retry%*|*%$NAME%*) TEST="$NAME: security of TCP6-L with TCPWRAP option" if ! eval $NUMCOND; then :; elif ! feat=$(testfeats tcp ip6 libwrap && runstcp6); then @@ -5750,7 +5798,7 @@ N=$((N+1)) NAME=OPENSSLTCP4_RANGE case "$TESTS" in -*%$N%*|*%functions%*|*%security%*|*%tcp%*|*%tcp4%*|*%ip4%*|*%openssl%*|*%range%*|*%listen%*|*%fork%*|*%$NAME%*) +*%$N%*|*%functions%*|*%security%*|*%tcp%*|*%tcp4%*|*%ip4%*|*%openssl%*|*%range%*|*%listen%*|*%fork%*|*%retry%*|*%$NAME%*) TEST="$NAME: security of SSL-L over TCP/IPv4 with RANGE option" if ! eval $NUMCOND; then :; elif ! testfeats openssl >/dev/null; then @@ -5766,7 +5814,7 @@ N=$((N+1)) NAME=OPENSSLTCP4_SOURCEPORT case "$TESTS" in -*%$N%*|*%functions%*|*%security%*|*%tcp%*|*%tcp4%*|*%ip4%*|*%openssl%*|*%sourceport%*|*%listen%*|*%fork%*|*%$NAME%*) +*%$N%*|*%functions%*|*%security%*|*%tcp%*|*%tcp4%*|*%ip4%*|*%openssl%*|*%sourceport%*|*%listen%*|*%fork%*|*%retry%*|*%$NAME%*) TEST="$NAME: security of SSL-L with SOURCEPORT option" if ! eval $NUMCOND; then :; elif ! testfeats openssl >/dev/null; then @@ -5782,7 +5830,7 @@ N=$((N+1)) NAME=OPENSSLTCP4_LOWPORT case "$TESTS" in -*%$N%*|*%functions%*|*%security%*|*%tcp%*|*%tcp4%*|*%ip4%*|*%openssl%*|*%lowport%*|*%listen%*|*%fork%*|*%$NAME%*) +*%$N%*|*%functions%*|*%security%*|*%tcp%*|*%tcp4%*|*%ip4%*|*%openssl%*|*%lowport%*|*%listen%*|*%fork%*|*%retry%*|*%$NAME%*) TEST="$NAME: security of SSL-L with LOWPORT option" if ! eval $NUMCOND; then :; elif ! testfeats openssl >/dev/null; then @@ -5798,7 +5846,7 @@ N=$((N+1)) NAME=OPENSSLTCP4_TCPWRAP case "$TESTS" in -*%$N%*|*%functions%*|*%security%*|*%tcp%*|*%tcp4%*|*%ip4%*|*%openssl%*|*%tcpwrap%*|*%listen%*|*%fork%*|*%$NAME%*) +*%$N%*|*%functions%*|*%security%*|*%tcp%*|*%tcp4%*|*%ip4%*|*%openssl%*|*%tcpwrap%*|*%listen%*|*%fork%*|*%retry%*|*%$NAME%*) TEST="$NAME: security of SSL-L with TCPWRAP option" if ! eval $NUMCOND; then :; elif ! feat=$(testfeats ip4 tcp libwrap openssl); then @@ -5818,7 +5866,7 @@ N=$((N+1)) NAME=OPENSSLCERTSERVER case "$TESTS" in -*%$N%*|*%functions%*|*%security%*|*%tcp%*|*%tcp4%*|*%ip4%*|*%openssl%*|*%listen%*|*%fork%*|*%$NAME%*) +*%$N%*|*%functions%*|*%security%*|*%tcp%*|*%tcp4%*|*%ip4%*|*%openssl%*|*%listen%*|*%fork%*|*%retry%*|*%$NAME%*) TEST="$NAME: security of SSL-L with client certificate" if ! eval $NUMCOND; then :; elif ! testfeats openssl >/dev/null; then @@ -5835,7 +5883,7 @@ N=$((N+1)) NAME=OPENSSLCERTCLIENT case "$TESTS" in -*%$N%*|*%functions%*|*%security%*|*%tcp%*|*%tcp4%*|*%ip4%*|*%openssl%*|*%listen%*|*%fork%*|*%$NAME%*) +*%$N%*|*%functions%*|*%security%*|*%tcp%*|*%tcp4%*|*%ip4%*|*%openssl%*|*%listen%*|*%fork%*|*%retry%*|*%$NAME%*) TEST="$NAME: security of SSL with server certificate" if ! eval $NUMCOND; then :; elif ! testfeats openssl >/dev/null; then @@ -5853,7 +5901,7 @@ N=$((N+1)) NAME=OPENSSLTCP6_RANGE case "$TESTS" in -*%$N%*|*%functions%*|*%security%*|*%tcp%*|*%tcp6%*|*%ip6%*|*%openssl%*|*%range%*|*%listen%*|*%fork%*|*%$NAME%*) +*%$N%*|*%functions%*|*%security%*|*%tcp%*|*%tcp6%*|*%ip6%*|*%openssl%*|*%range%*|*%listen%*|*%fork%*|*%retry%*|*%$NAME%*) TEST="$NAME: security of SSL-L over TCP/IPv6 with RANGE option" if ! eval $NUMCOND; then :; elif ! testfeats openssl >/dev/null; then @@ -5872,7 +5920,7 @@ N=$((N+1)) NAME=OPENSSLTCP6_SOURCEPORT case "$TESTS" in -*%$N%*|*%functions%*|*%security%*|*%tcp%*|*%tcp6%*|*%ip6%*|*%openssl%*|*%sourceport%*|*%listen%*|*%fork%*|*%$NAME%*) +*%$N%*|*%functions%*|*%security%*|*%tcp%*|*%tcp6%*|*%ip6%*|*%openssl%*|*%sourceport%*|*%listen%*|*%fork%*|*%retry%*|*%$NAME%*) TEST="$NAME: security of SSL-L over TCP/IPv6 with SOURCEPORT option" if ! eval $NUMCOND; then :; elif ! testfeats openssl >/dev/null; then @@ -5891,7 +5939,7 @@ N=$((N+1)) NAME=OPENSSLTCP6_LOWPORT case "$TESTS" in -*%$N%*|*%functions%*|*%security%*|*%tcp%*|*%tcp6%*|*%ip6%*|*%openssl%*|*%lowport%*|*%listen%*|*%fork%*|*%$NAME%*) +*%$N%*|*%functions%*|*%security%*|*%tcp%*|*%tcp6%*|*%ip6%*|*%openssl%*|*%lowport%*|*%listen%*|*%fork%*|*%retry%*|*%$NAME%*) TEST="$NAME: security of SSL-L over TCP/IPv6 with LOWPORT option" if ! eval $NUMCOND; then :; elif ! testfeats openssl >/dev/null; then @@ -5910,7 +5958,7 @@ N=$((N+1)) NAME=OPENSSLTCP6_TCPWRAP case "$TESTS" in -*%$N%*|*%functions%*|*%security%*|*%tcp%*|*%tcp6%*|*%ip6%*|*%openssl%*|*%tcpwrap%*|*%listen%*|*%fork%*|*%$NAME%*) +*%$N%*|*%functions%*|*%security%*|*%tcp%*|*%tcp6%*|*%ip6%*|*%openssl%*|*%tcpwrap%*|*%listen%*|*%fork%*|*%retry%*|*%$NAME%*) TEST="$NAME: security of SSL-L over TCP/IPv6 with TCPWRAP option" if ! eval $NUMCOND; then :; elif ! feat=$(testfeats ip6 tcp libwrap openssl && runsip6); then @@ -5932,7 +5980,7 @@ N=$((N+1)) # test security with the openssl-commonname option on client side NAME=OPENSSL_CN_CLIENT_SECURITY case "$TESTS" in -*%$N%*|*%functions%*|*%security%*|*%openssl%*|*%tcp%*|*%tcp4%*|*%ip4%*|*%listen%*|*%fork%*|*%$NAME%*) +*%$N%*|*%functions%*|*%security%*|*%openssl%*|*%tcp%*|*%tcp4%*|*%ip4%*|*%listen%*|*%fork%*|*%retry%*|*%$NAME%*) TEST="$NAME: security of client openssl-commonname option" # connect using non matching server name/address with commonname # options, this should succeed. Then without this option, should fail @@ -5979,7 +6027,7 @@ N=$((N+1)) NAME=OPENSSL_FIPS_SECURITY case "$TESTS" in -*%$N%*|*%functions%*|*%security%*|*%openssl%*|*%fips%*|*%tcp%*|*%tcp4%*|*%ip4%*|*%listen%*|*%fork%*|*%$NAME%*) +*%$N%*|*%functions%*|*%security%*|*%openssl%*|*%fips%*|*%tcp%*|*%tcp4%*|*%ip4%*|*%listen%*|*%fork%*|*%retry%*|*%$NAME%*) TEST="$NAME: OpenSSL restrictions by FIPS" if ! eval $NUMCOND; then :; elif ! testfeats openssl >/dev/null; then @@ -6556,7 +6604,7 @@ case "$MAJADDR" in tca="$ts" waitproto="file" waitfor="$ts" ;; -esac +esac case "$MINADDR" in "PORT") newport $protov # provide free port number in $PORT @@ -6564,7 +6612,7 @@ case "$MINADDR" in tca="$MAJADDR:$PORT" waitproto="${protov}port" waitfor="$PORT" ;; -esac +esac #set -xv echo -e "$da1a\n$da2\n$da1b" >"$tref" # establish a listening and forking listen socket in background @@ -10662,7 +10710,7 @@ N=$((N+1)) NAME=SOCKETRANGEMASK case "$TESTS" in -*%$N%*|*%functions%*|*%security%*|*%generic%*|*%tcp%*|*%tcp4%*|*%ip4%*|*%socket%*|*%range%*|*%listen%*|*%fork%*|*%$NAME%*) +*%$N%*|*%functions%*|*%security%*|*%generic%*|*%tcp%*|*%tcp4%*|*%ip4%*|*%socket%*|*%range%*|*%listen%*|*%fork%*|*%retry%*|*%$NAME%*) TEST="$NAME: security of generic socket-listen with RANGE option" if ! eval $NUMCOND; then :; elif [ -z "$SECONDADDR" ]; then @@ -11483,10 +11531,10 @@ PROTO=$KEYW proto="$(tolower "$PROTO")" feat="$(tolower "$FEAT")" # test the max-children option on really connection oriented sockets -NAME=${KEYW}MAXCHILDREN +NAME=${KEYW}_L_MAXCHILDREN case "$TESTS" in *%$N%*|*%functions%*|*%fork%*|*%maxchildren%*|*%$feat%*|*%$proto%*|*%socket%*|*%listen%*|*%$NAME%*) -TEST="$NAME: max-children option" +TEST="$NAME: max-children option with $PROTO-LISTEN" # start a listen process with max-children=1; connect with a client, let it # sleep some time before sending data; connect with second client that sends # data immediately. If max-children is working correctly the first data should @@ -11566,10 +11614,10 @@ RUNS=$(tolower $KEYW) PROTO=$KEYW proto="$(tolower "$PROTO")" # test the max-children option on pseudo connected sockets -NAME=${KEYW}MAXCHILDREN +NAME=${KEYW}_L_MAXCHILDREN case "$TESTS" in *%$N%*|*%functions%*|*%fork%*|*%maxchildren%*|*%$SEL%*|*%socket%*|*%listen%*|*%$NAME%*) -TEST="$NAME: max-children option" +TEST="$NAME: max-children option with $PROTO-LISTEN" # start a listen process with max-children=1; connect with a client, let it # send data and then sleep; connect with second client that wants to send # data immediately, but keep first client active until server terminates. @@ -11931,7 +11979,7 @@ N=$((N+1)) # had a bug that converted a bit mask of 0 internally to 0xffffffff NAME=TCP4RANGE_0BITS case "$TESTS" in -*%$N%*|*%functions%*|*%fork%*|*%tcp%*|*%tcp4%*|*%ip4%*|*%range%*|*%listen%*|*%$NAME%*) +*%$N%*|*%functions%*|*%fork%*|*%tcp%*|*%tcp4%*|*%ip4%*|*%range%*|*%listen%*|*%retry%*|*%$NAME%*) TEST="$NAME: correct evaluation of range mask 0" if ! eval $NUMCOND; then :; elif [ -z "$SECONDADDR" ]; then @@ -14617,7 +14665,6 @@ fi fi # NUMCOND ;; esac -PORT=$((PORT+1)) N=$((N+1)) # Socats access to different types of file system entries using various kinds @@ -14752,7 +14799,7 @@ orphaned gopen # Up to 1.7.4.3 this terminated immediately on connection refused NAME=TCP_TIMEOUT_RETRY case "$TESTS" in -*%$N%*|*%functions%*|*%bugs%*|*%tcp%*|*%socket%*|*%listen%*|*%$NAME%*) +*%$N%*|*%functions%*|*%bugs%*|*%tcp%*|*%socket%*|*%listen%*|*%retry%*|*%$NAME%*) TEST="$NAME: TCP with options connect-timeout and retry" # In background run a delayed echo server # In foreground start TCP with connect-timeout and retry. On first attempt the @@ -14831,7 +14878,6 @@ fi fi # NUMCOND ;; esac -PORT=$((PORT+1)) N=$((N+1)) # Up to 1.7.4.3 there was a bug with the lowport option: @@ -14880,7 +14926,6 @@ fi fi # NUMCOND ;; esac -PORT=$((PORT+1)) N=$((N+1)) # Test if trailing garbage in integer type options gives error @@ -15160,7 +15205,6 @@ fi fi # NUMCOND ;; esac -PORT=$((PORT+1)) N=$((N+1)) @@ -15215,7 +15259,6 @@ fi fi # NUMCOND ;; esac -PORT=$((PORT+1)) N=$((N+1)) @@ -15258,7 +15301,6 @@ fi fi # NUMCOND ;; esac -PORT=$((PORT+1)) N=$((N+1)) # Test if Socat makes the sniffing file descriptos (-r, -R) CLOEXEC to not leak @@ -15301,7 +15343,6 @@ fi fi # NUMCOND ;; esac -PORT=$((PORT+1)) N=$((N+1)) @@ -15544,7 +15585,6 @@ kill $pid 2>/dev/null wait fi ;; # NUMCOND, feats esac -PORT=$((PORT+1)) N=$((N+1)) @@ -15857,7 +15897,7 @@ waitunixport $ts 1 rc1=$? kill $pid0 2>/dev/null; wait relsleep 1 # child process might need more time -if grep -q " W connect" ${te}0; then +if ! grep -q " E " ${te}0; then $PRINTF "$OK\n" if [ "$VERBOSE" ]; then echo "$CMD0 &"; fi if [ "$DEBUG" ]; then cat "${te}0" >&2; fi @@ -15960,7 +16000,6 @@ else fi fi ;; # NUMCOND, feats esac -PORT=$((PORT+1)) N=$((N+1)) @@ -16614,8 +16653,8 @@ else tf="$td/test$N.stdout" te="$td/test$N.stderr" tdiff="$td/test$N.diff" -ts1p=$PORT; PORT=$((PORT+1)) -ts2p=$PORT; PORT=$((PORT+1)) +newport udp; ts1p=$PORT +newport udp; ts2p=$PORT da="test$N $(date) $RANDOM" CMD1="$TRACE $SOCAT $opts -T 0.2 UDP4-DATAGRAM:$LOCALHOST:$ts2p,bind=$LOCALHOST:$ts1p SOCKETPAIR,socktype=$SOCK_DGRAM" CMD2="$TRACE $SOCAT $opts -b 24 -t 0.2 -T 0.3 - UDP4-DATAGRAM:$LOCALHOST:$ts1p,bind=$LOCALHOST:$ts2p" @@ -16655,7 +16694,6 @@ fi fi # NUMCOND ;; esac -PORT=$((PORT+1)) N=$((N+1)) @@ -16856,13 +16894,14 @@ case "$TESTS" in *%$N%*|*%functions%*|*%fork%*|*%maxchildren%*|*%socket%*|*%posixmq%*|*%$NAME%*) TEST="$NAME: POSIX-MQ RECV with fork,max-children" # Start a POSIX-MQ receiver with fork that creates a POSIX-MQ and stores its -# output via sub processes that sleeps after writing. +# output via sub processes that sleep after writing. # Run a client/sender that sends message 1; -# run a client/sender that sends message 3, has to wait; -# write message 2 directly into output file; +# run a client/sender that sends message 2; +# run a client/sender that sends message 4, has to wait; +# write message 3 directly into output file; # Check if the messages are stored in order of their numbers if ! eval $NUMCOND; then :; -elif ! cond=$(checkconds "" "" "" "POSIXMQ STDIO SYSTEM" "POSIXMQ-SEND POSIXMQ-RECEIVE STDIO SYSTEM" "fork max-children unlink-early unlink-close"); then +elif ! cond=$(checkconds "" "" "" "POSIXMQ STDIO SHELL" "POSIXMQ-SEND POSIXMQ-RECEIVE STDIO SHELL" "fork max-children unlink-early unlink-close"); then $PRINTF "test $F_n $TEST... ${YELLOW}$cond${NORMAL}\n" $N cant else @@ -16871,7 +16910,7 @@ te="$td/test$N.stderr" tdiff="$td/test$N.diff" da="test$N $(date) $RANDOM" tq=/test$N -CMD0="$TRACE $SOCAT $opts -u POSIXMQ-RECV:$tq,unlink-early,fork,max-children=1 SYSTEM:\"cat\ >>${tf}0;\ sleep\ 1\"" +CMD0="$TRACE $SOCAT $opts -u POSIXMQ-RECV:$tq,unlink-early,fork,max-children=2 SHELL:\"cat\ >>${tf}0;\ relsleep\ 5\"" CMD1a="$TRACE $SOCAT $opts -u STDIO POSIXMQ-SEND:$tq" CMD1b="$TRACE $SOCAT $opts -u STDIO POSIXMQ-SEND:$tq,unlink-close" printf "test $F_n $TEST... " $N @@ -16880,23 +16919,28 @@ pid0=$! relsleep 1 echo "$da 1" |$CMD1a >/dev/null 2>"${te}1a" rc1a=$? -echo "$da 3" |$CMD1b >/dev/null 2>"${te}1b" +echo "$da 2" |$CMD1a >/dev/null 2>"${te}1b" rc1b=$? -sleep 0.5 -echo "$da 2" >>"${tf}0" -sleep 1 # as in SYSTEM +echo "$da 4" |$CMD1b >/dev/null 2>"${te}1c" +rc1c=$? +#sleep 0.5 +relsleep 2 +echo "$da 3" >>"${tf}0" +relsleep 5 # as in SHELL kill $(childpids $pid0) $pid0 2>/dev/null wait 2>/dev/null if [ $rc1a -ne 0 -o $rc1b -ne 0 ]; then - $PRINTF "$FAILED (rc1a=$rc1a, rc1b=$rc1b)\n" + $PRINTF "$FAILED (rc1a=$rc1a, rc1b=$rc1b, rc1c=$rc1c)\n" echo "$CMD0" cat "${te}0" >&2 echo "$CMD1a" cat "${te}1a" >&2 echo "$CMD1b" cat "${te}1b" >&2 + echo "$CMD1c" + cat "${te}1c" >&2 failed -elif $ECHO "$da 1\n$da 2\n$da 3" |diff - ${tf}0 >${tdiff}0; then +elif $ECHO "$da 1\n$da 2\n$da 3\n$da 4" |diff - ${tf}0 >${tdiff}0; then $PRINTF "$OK\n" if [ "$VERBOSE" ]; then echo "$CMD0"; fi if [ "$DEBUG" ]; then cat "${te}0" >&2; fi @@ -16904,6 +16948,8 @@ elif $ECHO "$da 1\n$da 2\n$da 3" |diff - ${tf}0 >${tdiff}0; then if [ "$DEBUG" ]; then cat "${te}1a" >&2; fi if [ "$VERBOSE" ]; then echo "$CMD1b"; fi if [ "$DEBUG" ]; then cat "${te}1b" >&2; fi + if [ "$VERBOSE" ]; then echo "$CMD1c"; fi + if [ "$DEBUG" ]; then cat "${te}1c" >&2; fi ok else $PRINTF "$FAILED (diff)\n" @@ -16913,7 +16959,9 @@ else cat "${te}1a" >&2 echo "$CMD1b" cat "${te}1b" >&2 - echo "difference:" >&2 + echo "$CMD1c" + cat "${te}1c" >&2 + echo "// diff:" >&2 cat ${tdiff}0 >&2 failed fi @@ -16927,44 +16975,49 @@ NAME=POSIXMQ_SEND_MAXCHILDREN case "$TESTS" in *%$N%*|*%functions%*|*%fork%*|*%maxchildren%*|*%socket%*|*%posixmq%*|*%$NAME%*) TEST="$NAME: POSIX-MQ SEND with fork,max-children" -# Start a POSIX-MQ receiver that creates a POSIX-MQ and transfers data from it +# Start a POSIX-MQ receiver that creates the MQ and transfers data from it # to an output file -# Run a POSIX-MQ sender that two times forks and invokes a data generator -# for messages 1 and 3 in a shell process with some trailing sleep. -# Afterwards write message 2 directly into output file; message 3 should be +# Run a POSIX-MQ sender that forks two child shell processes that get data from +# a file queue with messages 1, 2, and 4, transfer it to the receiver and sleep +# afterwards to delay the third child by option max-children=2 +# Afterwards write message 3 directly into output file; message 4 should be # delayed due to max-children option # Check if the messages are stored in order of their numbers. -# The data generator is implemented as a receiver from an MQ with "1", "3" +# The data generator is implemented with just a directory containing files +# "1", "2", "4" if ! eval $NUMCOND; then :; -elif ! cond=$(checkconds "" "" "" "POSIXMQ STDIO SYSTEM" "POSIXMQ-SEND POSIXMQ-READ STDIO SYSTEM" "fork max-children mq-prio unlink-early unlink-close"); then +elif ! cond=$(checkconds "" "" "" "POSIXMQ STDIO SHELL" "POSIXMQ-SEND POSIXMQ-READ STDIO SHELL" "fork max-children mq-prio unlink-early unlink-close"); then $PRINTF "test $F_n $TEST... ${YELLOW}$cond${NORMAL}\n" $N cant else tf="$td/test$N.stdout" te="$td/test$N.stderr" tdiff="$td/test$N.diff" +tq="/test$N" +tQ="$td/test$N.q" da="test$N $(date) $RANDOM" -tq=/test$N -CMD1="$TRACE $SOCAT $opts -u POSIXMQ-READ:$tq,unlink-early STDIO" -CMD2="$TRACE $SOCAT $opts -U POSIXMQ-SEND:$tq,fork,max-children=1,interval=0.1 SYSTEM:\"./socat\ -u\ POSIXMQ-RECV\:$tq-data\ -;\ sleep\ 1\"" +CMD1="$TRACE $SOCAT $opts -lp reader -u POSIXMQ-READ:$tq,unlink-early STDIO" +#CMD2="$TRACE $SOCAT $opts -lp worker -U POSIXMQ-SEND:$tq,fork,max-children=2,interval=$(relsecs 2) SHELL:'f=\$(ls -1 $tQ|head -n 1);\ test\ -f\ "$tQ/\$f"\ ||\ exit\ 1;\ {\ cat\ $tQ/\$f;\ rm\ $tQ/\$f;\ };\ sleep\ $(relsecs 5)'" +CMD2="$TRACE $SOCAT $opts -lp worker -U POSIXMQ-SEND:$tq,fork,max-children=2,interval=$(relsecs 2) SHELL:'shopt\ -s\ nullglob;\ f=\$(ls -1 $tQ|head -n 1);\ test\ -z\ "\$f"\ &&\ exit;\ {\ cat\ $tQ/\$f;\ rm\ $tQ/\$f;\ };\ sleep\ $(relsecs 5)'" printf "test $F_n $TEST... " $N # create data for the generator -echo "$da 1" |$SOCAT -u - POSIXMQ-SEND:$tq-data,unlink-early 2>"${te}0a" -echo "$da 3" |$SOCAT -u - POSIXMQ-SEND:$tq-data 2>"${te}0b" +mkdir -p $tQ +echo "$da 1" >$tQ/01 +echo "$da 2" >$tQ/02 +echo "$da 4" >$tQ/04 eval $CMD1 2>"${te}1" >>"${tf}1" & pid1=$! relsleep 1 eval $CMD2 2>"${te}2" & pid2=$! -sleep 0.5 -echo "$da 2" >>"${tf}1" -sleep 1 # as in SYSTEM -kill $pid1 $(childpids $pid1) $pid2 $(childpids $pid2) 2>/dev/null +relsleep 4 +echo "$da 3" >>"${tf}1" +relsleep 10 +kill $(childpids -r $pid1) $pid1 $(childpids -r $pid2) $pid2 2>/dev/null wait 2>/dev/null -# remove the queues -$SOCAT -u /dev/null POSIXMQ-SEND:$tq-data,unlink-close 2>"${te}3a" +# remove the MQ $SOCAT -u /dev/null POSIXMQ-SEND:$tq,unlink-close 2>"${te}3b" -if $ECHO "$da 1\n$da 2\n$da 3" |diff - ${tf}1 >${tdiff}1; then +if $ECHO "$da 1\n$da 2\n$da 3\n$da 4" |diff - ${tf}1 >${tdiff}1; then $PRINTF "$OK\n" if [ "$VERBOSE" ]; then echo "$CMD1"; fi if [ "$DEBUG" ]; then cat "${te}1" >&2; fi @@ -16977,7 +17030,7 @@ else cat "${te}1" >&2 echo "$CMD2" cat "${te}2" >&2 - echo "difference:" >&2 + echo "// diff:" >&2 cat ${tdiff}1 >&2 failed fi @@ -16985,6 +17038,7 @@ fi # NUMCOND ;; esac N=$((N+1)) +date "+%Y/%m/%d %H:%M:%S.%N" # Test the sigint option with SHELL address @@ -17184,7 +17238,7 @@ if grep -q " W waitpid..: child .* exited with status 130" "${te}0"; then if [ "$DEBUG" ]; then cat "${te}0" >&2; fi ok else - $PRINTF "${YELLOW}FAILED (shell does not propagate SIGINT?${NORMAL}\n" + $PRINTF "${YELLOW}FAILED (shell does not propagate SIGINT?)${NORMAL}\n" echo "$CMD0 &" cat "${te}0" >&2 cant @@ -17664,7 +17718,6 @@ fi fi # NUMCOND ;; esac -PORT=$((PORT+1)) N=$((N+1)) done <<<" @@ -17740,7 +17793,6 @@ fi fi # NUMCOND ;; esac -PORT=$((PORT+1)) N=$((N+1)) @@ -17769,7 +17821,7 @@ tf="$td/test$N.stdout" te="$td/test$N.stderr" tdiff="$td/test$N.diff" da="test$N $(date) $RANDOM" -newport tcp4 # or whatever proto, or drop this line +newport tcp4 # Find the default pipe size PIPESZ="$(echo |$FILAN -n 0 |grep "0:" |head -n 1 |sed 's/.*F_GETPIPE_SZ=\([0-9][0-9]*\).*/\1/')" PIPESZ2=$((2*PIPESZ)) @@ -17968,7 +18020,6 @@ else fi fi ;; # NUMCOND esac -PORT=$((PORT+1)) N=$((N+1)) @@ -18029,7 +18080,6 @@ else fi fi ;; # NUMCOND esac -PORT=$((PORT+1)) N=$((N+1)) @@ -18090,7 +18140,6 @@ else fi fi ;; # NUMCOND esac -PORT=$((PORT+1)) N=$((N+1)) @@ -18201,10 +18250,10 @@ RUNS=$(tolower $KEYW) PROTO=$KEYW proto="$(tolower "$PROTO")" # test the max-children option on pseudo connected sockets -NAME=${KEYW}MAXCHILDREN +NAME=${KEYW}_L_MAXCHILDREN case "$TESTS" in *%$N%*|*%functions%*|*%fork%*|*%maxchildren%*|*%$SEL%*|*%socket%*|*%listen%*|*%$NAME%*) -TEST="$NAME: max-children option" +TEST="$NAME: max-children option with $PROTO-LISTEN" # start a listen process with max-children=1; connect with a client, let it # send data and then sleep; connect with second client that wants to send # data immediately, but keep first client active until server terminates. @@ -19005,7 +19054,6 @@ fi # NUMCOND esac N=$((N+1)) - BIN_TIMEOUT= if type timeout >/dev/null 2>&1; then BIN_TIMEOUT=timeout @@ -19164,7 +19212,6 @@ else fi # NUMCOND ;; esac -PORT=$((PORT+1)) N=$((N+1)) done <<<" UDP-SENDTO udp4 PORT @@ -19236,7 +19283,6 @@ else fi # NUMCOND ;; esac -PORT=$((PORT+1)) N=$((N+1)) done <<<" TCP-CONNECT tcp4 PORT @@ -19249,7 +19295,9 @@ DCCP-CONNECT dccp4 PORT #ROXY::127.0.0.1 tcp4 PORT " + # Above tests introduced before or with 1.8.0.1 +#============================================================================== # Below test introduced with 1.8.0.2 # Test the readline.sh file overwrite vulnerability @@ -19310,10 +19358,9 @@ N=$((N+1)) # Above test introduced with 1.8.0.2 +#============================================================================== # Below tests introduced with 1.8.0.3 (or later) - - # Test the SOCKS5-CONNECT and SOCKS5-LISTEN addresses with IPv4 for SUFFIX in CONNECT LISTEN; do @@ -19430,7 +19477,7 @@ else cat "${te}0" >&2 echo "$CMD1" cat "${te}1" >&2 - fail + failed elif ! echo "$da" |diff - "${tf}1" >$tdiff; then $PRINTF "$FAILED (diff)\n" echo "$CMD0 &" @@ -19439,7 +19486,7 @@ else cat "${te}1" >&2 echo "// diff:" >&2 cat "$tdiff" >&2 - fail + failed else $PRINTF "$OK\n" if [ "$VERBOSE" ]; then echo "$CMD0 &"; fi @@ -19505,6 +19552,535 @@ esac N=$((N+1)) +while read ADDR proto CSUFF CPARMS COPTS SADDR SOPTS PIPE; do +if [ -z "$ADDR" ] || [[ "$ADDR" == \#* ]]; then continue; fi + +if [ "X$CSUFF" != "X-" ]; then + CADDR=$ADDR-$CSUFF +else + CADDR=$ADDR +fi +CNAME=$(echo $CADDR |tr - _) +PROTO=$(toupper $proto) +FEAT=$ADDR +runs=$proto +case "$CPARMS" in + PORT) newport $proto; CPARMS=$PORT ;; + *'$PORT'*) newport $proto; CPARMS=$(eval echo "$CPARMS") ;; +esac +#echo "PORT=$PORT CPARMS=$CPARMS" >&2 +case "X$COPTS" in + X-) COPTS= ;; + *'$PORT'*) newport $proto; COPTS=$(eval echo "$COPTS") ;; +esac +case "X$SOPTS" in + X-) SOPTS= ;; +esac + +# Test if bind on *-CONNECT selects the matching IP version +NAME=${CNAME}_BIND_6_4 +case "$TESTS" in +*%$N%*|*%functions%*|*%$proto%*|*%${proto}4%*|*%${proto}6%*|*%ip4%*|*%ip6%*|*%listen%*|*%bind%*|*%socket%*|*%$NAME%*) +TEST="$NAME: $ADDR bind chooses matching IPv" +# Have an IPv4 listener +# Host name localhost-4-6.dest-unreach.net resolves to both 127.0.0.1 and [::1], +# consequently; with option -6 we have Socat try IPv6 first, and on failure try +# IPv4 +# Start Socat TCP-CONNECT with -6 and binding and connecting to this host name; +# Up to version 1.8.0.0 Socat only tries IPv6 and fails +# With version 1.8.0.1 Socat first connects using IPv6, and due to ECONNREFUSED +# tries to connect using IPv4 but still binds to IPv6 which fails with +# EAFNOSUPPORT "Address family not supported by protocol"; +# With 1.8.0.3 the connection attempt with IPv4 correctly binds to IPv4 and +# succeeds +if ! eval $NUMCOND; then : +elif ! cond=$(checkconds \ + "" \ + "" \ + "" \ + "$FEAT IP4 IP6 TCP LISTEN STDIO PIPE" \ + "$CADDR $SADDR STDIO PIPE" \ + "bind pf" \ + "${runs}4 ${runs}6" ); then + $PRINTF "test $F_n $TEST... ${YELLOW}$cond${NORMAL}\n" $N + cant +elif ! SOCAT_MAIN_WAIT= $SOCAT -h |grep -e '[[:space:]]-6[[:space:]]' >/dev/null; then + $PRINTF "test $F_n $TEST... ${YELLOW}no option -0${NORMAL}\n" $N + cant +else + tf="$td/test$N.stdout" + te="$td/test$N.stderr" + tdiff="$td/test$N.diff" + da="test$N $(date) $RANDOM" + CMD0="$TRACE $SOCAT $opts $SADDR:$PORT,$SOPTS,pf=2 $PIPE" + CMD1="$TRACE $SOCAT $opts -6 STDIO $CADDR:localhost-4-6.dest-unreach.net:$CPARMS,bind=localhost-4-6.dest-unreach.net,$COPTS" + printf "test $F_n $TEST... " $N + $CMD0 >/dev/null 2>"${te}0" & + pid0=$! + wait${proto}4port $PORT 1 + { echo "$da"; relsleep 10; } |$CMD1 >"${tf}1" 2>"${te}1" + rc1=$? + kill $pid0 2>/dev/null; wait + if echo "$da" |diff - "${tf}1" >$tdiff; then + $PRINTF "$OK\n" + if [ "$VERBOSE" ]; then echo "$CMD0 &"; fi + if [ "$DEBUG" ]; then cat "${te}0" >&2; fi + if [ "$VERBOSE" ]; then echo "$CMD1"; fi + if [ "$DEBUG" ]; then cat "${te}1" >&2; fi + ok + elif [ "$rc1" -ne 0 ] && grep "Address family not supported by protocol" "${te}1" >/dev/null; then + $PRINTF "$FAILED (EAFNOSUPPORT)\n" + echo "$CMD0 &" + cat "${te}0" >&2 + echo "$CMD1" + cat "${te}1" >&2 + failed + elif [ "$rc1" -ne 0 ]; then + $PRINTF "$CANT (unexpected error)\n" + echo "$CMD0 &" + cat "${te}0" >&2 + echo "$CMD1" + cat "${te}1" >&2 + cant + elif ! echo "$da" |diff - "${tf}1" >$tdiff; then + $PRINTF "$FAILED (diff)\n" + echo "$CMD0 &" + cat "${te}0" >&2 + echo "$CMD1" + cat "${te}1" >&2 + echo "// diff:" >&2 + cat "$tdiff" >&2 + failed + else + $PRINTF "$CANT (unexpected problem)\n" + if [ "$VERBOSE" ]; then echo "$CMD0 &"; fi + if [ "$DEBUG" ]; then cat "${te}0" >&2; fi + if [ "$VERBOSE" ]; then echo "$CMD1"; fi + if [ "$DEBUG" ]; then cat "${te}1" >&2; fi + cant + fi +fi # NUMCOND + ;; +esac +N=$((N+1)) + +done <<<" +TCP tcp CONNECT PORT - TCP-L - PIPE +SCTP sctp CONNECT PORT - SCTP-L - PIPE +DCCP dccp CONNECT PORT - DCCP-L - PIPE +OPENSSL tcp CONNECT PORT cafile=testsrv.pem,verify=0 SSL-L cert=testsrv.pem,key=testsrv.key,verify=0 PIPE +SOCKS4 tcp - 32.98.76.54:32109 socksport=\$PORT,socksuser=nobody TCP-L - EXEC:./socks4echo.sh +SOCKS5 tcp CONNECT \$PORT:127.0.0.1:80 - TCP-L - EXEC:./socks5server-echo.sh +PROXY tcp CONNECT 127.0.0.1:80 proxyport=\$PORT,crlf TCP-L crlf EXEC:./proxyecho.sh +" + + +# Test if TCP-CONNECT with host name resolving to IPv6 first and IPv4 second +# (due to option -6) chooses IPv4 when bind option is specific. +# This works only since version 1.8.0.3 +NAME=TCP_BIND_4 +case "$TESTS" in +*%$N%*|*%functions%*|*%internet%*|*%tcp4%*|*%tcp6%*|*%ip4%*|*%ip6%*|*%listen%*|*%socket%*|*%$NAME%*) +TEST="$NAME: TCP-CONNECT chooses IPv4 from bind" +# Start a TCP4 listener with echo function +# Start Socat TCP-CONNECT with host name resolving to IPv6 first and IPv4 +# second, and bind to IPv4 explicitly. +# When connection and data transfer work the test succeeded. +if ! eval $NUMCOND; then : +elif ! cond=$(checkconds \ + "" \ + "" \ + "" \ + "IP4 IP6 TCP LISTEN STDIO PIPE" \ + "TCP-CONNECT TCP4-LISTEN STDIO PIPE" \ + "" \ + "tcp4 tcp6" ); then + $PRINTF "test $F_n $TEST... ${YELLOW}$cond${NORMAL}\n" $N + cant +elif ! SOCAT_MAIN_WAIT= $SOCAT -h |grep -e '[[:space:]]-6[[:space:]]' >/dev/null; then + $PRINTF "test $F_n $TEST... ${YELLOW}no option -0${NORMAL}\n" $N + cant +else + tf="$td/test$N.stdout" + te="$td/test$N.stderr" + tdiff="$td/test$N.diff" + da="test$N $(date) $RANDOM" + newport tcp4 + CMD0="$TRACE $SOCAT $opts TCP4-LISTEN:$PORT PIPE" + CMD1="$TRACE $SOCAT $opts -6 - TCP-CONNECT:localhost-4-6.dest-unreach.net:$PORT,bind=127.0.0.1" + printf "test $F_n $TEST... " $N + $CMD0 >/dev/null 2>"${te}0" & + pid0=$! + waittcp4port $PORT 1 + echo "$da" |$CMD1 >"${tf}1" 2>"${te}1" + rc1=$? + kill $pid0 2>/dev/null; wait + if echo "$da" |diff - "${tf}1" >$tdiff; then + $PRINTF "$OK\n" + if [ "$VERBOSE" ]; then echo "$CMD0 &"; fi + if [ "$DEBUG" ]; then cat "${te}0" >&2; fi + if [ "$VERBOSE" ]; then echo "$CMD1"; fi + if [ "$DEBUG" ]; then cat "${te}1" >&2; fi + ok + elif [ "$rc1" -ne 0 ] && grep "Address family for hostname not supported" "${te}1" >/dev/null; then + $PRINTF "$FAILED (EAFNOSUPPORT)\n" + echo "$CMD0 &" + cat "${te}0" >&2 + echo "$CMD1" + cat "${te}1" >&2 + failed + elif [ "$rc1" -ne 0 ]; then + $PRINTF "$CANT (unexpected error)\n" + echo "$CMD0 &" + cat "${te}0" >&2 + echo "$CMD1" + cat "${te}1" >&2 + cant + elif ! echo "$da" |diff - "${tf}1" >$tdiff; then + $PRINTF "$FAILED (diff)\n" + echo "$CMD0 &" + cat "${te}0" >&2 + echo "$CMD1" + cat "${te}1" >&2 + echo "// diff:" >&2 + cat "$tdiff" >&2 + failed + else + $PRINTF "$CANT (unexpected problem)\n" + if [ "$VERBOSE" ]; then echo "$CMD0 &"; fi + if [ "$DEBUG" ]; then cat "${te}0" >&2; fi + if [ "$VERBOSE" ]; then echo "$CMD1"; fi + if [ "$DEBUG" ]; then cat "${te}1" >&2; fi + cant + fi +fi # NUMCOND + ;; +esac +N=$((N+1)) + +# Test if TCP-CONNECT with host name resolving to IPv4 first and IPv6 second +# (due to option -4) chooses IPv6 when bind option is specific. +# This works only since version 1.8.0.3 +NAME=TCP_BIND_6 +case "$TESTS" in +*%$N%*|*%functions%*|*%internet%*|*%tcp4%*|*%tcp6%*|*%ip4%*|*%ip6%*|*%listen%*|*%socket%*|*%$NAME%*) +TEST="$NAME: TCP-CONNECT chooses IPv6 from bind" +# Start a TCP6 listener with echo function +# Start Socat TCP-CONNECT with host name resolving to IPv4 first and IPv6 +# second, and bind to IPv6 explicitly. +# When connection and data transfer work the test succeeded. +if ! eval $NUMCOND; then : +elif ! cond=$(checkconds \ + "" \ + "" \ + "" \ + "IP4 IP6 TCP LISTEN STDIO PIPE" \ + "TCP-CONNECT TCP4-LISTEN STDIO PIPE" \ + "" \ + "tcp4 tcp6" ); then + $PRINTF "test $F_n $TEST... ${YELLOW}$cond${NORMAL}\n" $N + cant +elif ! SOCAT_MAIN_WAIT= $SOCAT -h |grep -e '[[:space:]]-4[[:space:]]' >/dev/null; then + $PRINTF "test $F_n $TEST... ${YELLOW}no option -0${NORMAL}\n" $N + cant +else + tf="$td/test$N.stdout" + te="$td/test$N.stderr" + tdiff="$td/test$N.diff" + da="test$N $(date) $RANDOM" + newport tcp4 + CMD0="$TRACE $SOCAT $opts TCP6-LISTEN:$PORT PIPE" + CMD1="$TRACE $SOCAT $opt -4 - TCP-CONNECT:localhost-4-6.dest-unreach.net:$PORT,bind=[::1]" + printf "test $F_n $TEST... " $N + $CMD0 >/dev/null 2>"${te}0" & + pid0=$! + waittcp4port $PORT 1 + echo "$da" |$CMD1 >"${tf}1" 2>"${te}1" + rc1=$? + kill $pid0 2>/dev/null; wait + if echo "$da" |diff - "${tf}1" >$tdiff; then + $PRINTF "$OK\n" + if [ "$VERBOSE" ]; then echo "$CMD0 &"; fi + if [ "$DEBUG" ]; then cat "${te}0" >&2; fi + if [ "$VERBOSE" ]; then echo "$CMD1"; fi + if [ "$DEBUG" ]; then cat "${te}1" >&2; fi + ok + elif [ "$rc1" -ne 0 ] && grep "Address family for hostname not supported" "${te}1" >/dev/null; then + $PRINTF "$FAILED (EAFNOSUPPORT)\n" + echo "$CMD0 &" + cat "${te}0" >&2 + echo "$CMD1" + cat "${te}1" >&2 + failed + elif [ "$rc1" -ne 0 ]; then + $PRINTF "$CANT (unexpected error)\n" + echo "$CMD0 &" + cat "${te}0" >&2 + echo "$CMD1" + cat "${te}1" >&2 + cant + elif ! echo "$da" |diff - "${tf}1" >$tdiff; then + $PRINTF "$FAILED (diff)\n" + echo "$CMD0 &" + cat "${te}0" >&2 + echo "$CMD1" + cat "${te}1" >&2 + echo "// diff:" >&2 + cat "$tdiff" >&2 + failed + else + $PRINTF "$CANT (unexpected problem)\n" + if [ "$VERBOSE" ]; then echo "$CMD0 &"; fi + if [ "$DEBUG" ]; then cat "${te}0" >&2; fi + if [ "$VERBOSE" ]; then echo "$CMD1"; fi + if [ "$DEBUG" ]; then cat "${te}1" >&2; fi + cant + fi +fi # NUMCOND + ;; +esac +N=$((N+1)) + + +while read ADDR proto CSUFF CPARMS COPTS SADDR SOPTS PIPE; do +if [ -z "$ADDR" ] || [[ "$ADDR" == \#* ]]; then continue; fi + +if [ "X$CSUFF" != "X-" ]; then + CADDR=$ADDR-$CSUFF +else + CADDR=$ADDR +fi +CNAME=$(echo $CADDR |tr - _) +PROTO=$(toupper $proto) +FEAT=$ADDR +runs=$proto +case "$CPARMS" in + PORT) newport $proto; CPARMS=$PORT ;; + *'$PORT'*) newport $proto; CPARMS=$(eval echo "$CPARMS") ;; +esac +#echo "PORT=$PORT CPARMS=$CPARMS" >&2 +case "X$COPTS" in + X-) COPTS= ;; + *'$PORT'*) newport $proto; COPTS=$(eval echo "$COPTS") ;; +esac +case "X$SOPTS" in + X-) SOPTS= ;; +esac + +# Test the retry option with *-CONNECT addresses +NAME=${CNAME}_RETRY +case "$TESTS" in +*%$N%*|*%functions%*|*%$proto%*|*%${proto}4%*|*%ip4%*|*%listen%*|*%socket%*|*%retry%*|*%$NAME%*) +TEST="$NAME: $ADDR can retry" +# Have an IPv4 listener with delay +# Start a connector whose first attempt must fail; check if the second attempt +# succeeds. +if ! eval $NUMCOND; then : +elif ! cond=$(checkconds \ + "" \ + "" \ + "" \ + "$FEAT IP4 TCP LISTEN STDIO" \ + "$CADDR $SADDR STDIO PIPE" \ + "pf retry interval" \ + "${runs}4 ${runs}6" ); then + $PRINTF "test $F_n $TEST... ${YELLOW}$cond${NORMAL}\n" $N + cant +elif ! SOCAT_MAIN_WAIT= $SOCAT -h |grep -e '[[:space:]]-6[[:space:]]' >/dev/null; then + $PRINTF "test $F_n $TEST... ${YELLOW}no option -0${NORMAL}\n" $N + cant +else +# newport $proto +#echo "PORT=$PORT CPARMS=$CPARMS" >&2 + tf="$td/test$N.stdout" + te="$td/test$N.stderr" + tdiff="$td/test$N.diff" + da="test$N $(date) $RANDOM" + CMD0="relsleep 5; $TRACE $SOCAT $opts $SADDR:$PORT,$SOPTS,pf=2 $PIPE" + CMD1="$TRACE $SOCAT $opts -4 STDIO $CADDR:$LOCALHOST:$CPARMS,retry=1,interval=$(relsecs 10),$COPTS" + printf "test $F_n $TEST... " $N +#date +%Y/%m/%d" "%H:%M:%S.%N + eval "$CMD0" >/dev/null 2>"${te}0" & + pid0=$! + { echo "$da"; relsleep 15; } |$CMD1 >"${tf}1" 2>"${te}1" + rc1=$? + kill $pid0 2>/dev/null; wait + if echo "$da" |diff - "${tf}1" >$tdiff; then + $PRINTF "$OK\n" + if [ "$VERBOSE" ]; then echo "$CMD0 &"; fi + if [ "$DEBUG" ]; then cat "${te}0" >&2; fi + if [ "$VERBOSE" ]; then echo "$CMD1"; fi + if [ "$DEBUG" ]; then cat "${te}1" >&2; fi + ok + elif [ "$rc1" -ne 0 ] && grep "Address family not supported by protocol" "${te}1" >/dev/null; then + $PRINTF "$FAILED (EAFNOSUPPORT)\n" + echo "$CMD0 &" + cat "${te}0" >&2 + echo "$CMD1" + cat "${te}1" >&2 + failed + elif [ "$rc1" -ne 0 ]; then + $PRINTF "$CANT (unexpected error)\n" + echo "$CMD0 &" + cat "${te}0" >&2 + echo "$CMD1" + cat "${te}1" >&2 + cant + elif ! echo "$da" |diff - "${tf}1" >$tdiff; then + $PRINTF "$FAILED (diff)\n" + echo "$CMD0 &" + cat "${te}0" >&2 + echo "$CMD1" + cat "${te}1" >&2 + echo "// diff:" >&2 + cat "$tdiff" >&2 + failed + else + $PRINTF "$CANT (unexpected problem)\n" + if [ "$VERBOSE" ]; then echo "$CMD0 &"; fi + if [ "$DEBUG" ]; then cat "${te}0" >&2; fi + if [ "$VERBOSE" ]; then echo "$CMD1"; fi + if [ "$DEBUG" ]; then cat "${te}1" >&2; fi + cant + fi +fi # NUMCOND + ;; +esac +N=$((N+1)) + +done <<<" +TCP tcp CONNECT PORT - TCP-L - PIPE +SCTP sctp CONNECT PORT - SCTP-L - PIPE +DCCP dccp CONNECT PORT - DCCP-L - PIPE +OPENSSL tcp CONNECT PORT cafile=testsrv.pem,verify=0 SSL-L cert=testsrv.pem,key=testsrv.key,verify=0 PIPE +SOCKS4 tcp - 32.98.76.54:32109 socksport=\$PORT,socksuser=nobody TCP-L - EXEC:./socks4echo.sh +SOCKS5 tcp CONNECT \$PORT:127.0.0.1:80 - TCP-L - EXEC:./socks5server-echo.sh +PROXY tcp CONNECT 127.0.0.1:80 proxyport=\$PORT,crlf TCP-L crlf EXEC:./proxyecho.sh +" + +#------------------------------------------------------------------------------ + +while read ADDR proto CSUFF CPARMS COPTS SADDR SOPTS PIPE SLOW; do +if [ -z "$ADDR" ] || [[ "$ADDR" == \#* ]]; then continue; fi + +if [ "X$CSUFF" != "X-" ]; then + CADDR=$ADDR-$CSUFF +else + CADDR=$ADDR +fi +CNAME=$(echo $CADDR |tr - _) +PROTO=$(toupper $proto) +FEAT=$ADDR +runs=$proto +case "$CPARMS" in + PORT) newport $proto; CPARMS=$PORT ;; + *'$PORT'*) newport $proto; CPARMS=$(eval echo "$CPARMS") ;; +esac +#echo "PORT=$PORT CPARMS=$CPARMS" >&2 +case "X$COPTS" in + X-) COPTS= ;; + *'$PORT'*) newport $proto; COPTS=$(eval echo "$COPTS") ;; +esac +case "X$SOPTS" in + X-) SOPTS= ;; +esac + +# Test the fork and max-children options with CONNECT addresses +NAME=${CNAME}_MAXCHILDREN +case "$TESTS" in +*%$N%*|*%functions%*|*%$proto%*|*%${proto}4%*|*%ip4%*|*%listen%*|*%socket%*|*%fork%*|*%maxchildren%*|*%$NAME%*) +TEST="$NAME: $ADDR with fork,max-children" +# Start a reader process that transfers received data to an output file; +# run a sending client that forks at most 2 parallel child processes that +# transfer data from a simple directory queue to the reader but afterwards +# hang some time to prevent more child process. +# After the first two transfers write the third record directly to the file; +# a little later the Socat mechanism puts a 4th record. +# When the 4 records in the output file have the expected order the test +# succeeded. +if ! eval $NUMCOND; then : +elif ! cond=$(checkconds \ + "" \ + "" \ + "" \ + "$FEAT IP4 TCP LISTEN STDIO PIPE" \ + "$CADDR $SADDR STDIO PIPE" \ + "pf" \ + "${runs}4" ); then + $PRINTF "test $F_n $TEST... ${YELLOW}$cond${NORMAL}\n" $N + cant +else +# newport $proto +#echo "PORT=$PORT CPARMS=$CPARMS" >&2 + tf="$td/test$N.stdout" + te="$td/test$N.stderr" + tdiff="$td/test$N.diff" + tQ="$td/test$N.q" + ext=q.y.f. # some unusual extension to prevent from deleting wrong file + da="test$N $(date) $RANDOM" + CMD0="$TRACE $SOCAT $opts -lp reader $SADDR:$PORT,$SOPTS,pf=2,reuseaddr,fork $PIPE" + CMD1="$TRACE $SOCAT $opts -4 $CADDR:$LOCALHOST:$CPARMS,fork,max-children=2,interval=$(relsecs $((2*SLOW))),$COPTS SHELL:'shopt\ -s\ nullglob;\ F=\$(ls -1 $tQ|grep .$ext\\\$|head -n 1);\ test\ \"\$F\"\ ||\ exit;\ cat\ $tQ/\$F;\ mv\ -i\ $tQ/\$F\ $tQ/.\$F;\ sleep\ $(relsecs $((5*SLOW)) )'!!-" + printf "test $F_n $TEST... " $N + # create data for the generator + mkdir -p $tQ + echo "$da 1" >$tQ/01.$ext + echo "$da 2" >$tQ/02.$ext + echo "$da 4" >$tQ/04.$ext + eval "$CMD0" 2>"${te}0" & + pid0=$! + relsleep $((1*SLOW)) + eval $CMD1 2>"${te}1" >>"${tf}0" & + pid1=$! + relsleep $((4*SLOW)) +#date +%Y/%m/%d" "%H:%M:%S.%N + echo "$da 3" >>"${tf}0" + relsleep $((4*SLOW)) + kill $(childpids -r $pid0) $pid0 $(childpids -r $pid1) $pid1 2>/dev/null + wait 2>/dev/null + if ! test -e "${tf}0" || ! test -s "${tf}0"; then + $PRINTF "$FAILED\n" + echo "$CMD0 &" + cat "${te}0" >&2 + echo "$CMD1" + cat "${te}1" >&2 + failed + elif $ECHO "$da 1\n$da 2\n$da 3\n$da 4" |diff - ${tf}0 >$tdiff; then + $PRINTF "$OK\n" + if [ "$VERBOSE" ]; then echo "$CMD0 &"; fi + if [ "$DEBUG" ]; then cat "${te}0" >&2; fi + if [ "$VERBOSE" ]; then echo "$CMD1"; fi + if [ "$DEBUG" ]; then cat "${te}1" >&2; fi + ok + else + $PRINTF "$FAILED (diff)\n" + echo "$CMD0 &" + cat "${te}0" >&2 + echo "$CMD1" + cat "${te}1" >&2 + echo "// diff:" >&2 + cat "$tdiff" >&2 + failed + fi +fi # NUMCOND + ;; +esac +N=$((N+1)) + +done <<<" +TCP tcp CONNECT PORT - TCP-L - PIPE 1 +SCTP sctp CONNECT PORT - SCTP-L - PIPE 1 +DCCP dccp CONNECT PORT - DCCP-L - PIPE 1 +OPENSSL tcp CONNECT PORT cafile=testsrv.pem,verify=0 SSL-L cert=testsrv.pem,key=testsrv.key,verify=0 PIPE 6 +SOCKS4 tcp - 32.98.76.54:32109 socksport=\$PORT,socksuser=nobody TCP-L - EXEC:./socks4echo.sh 6 +SOCKS5 tcp CONNECT \$PORT:127.0.0.1:80 - TCP-L - EXEC:./socks5server-echo.sh 5 +PROXY tcp CONNECT 127.0.0.1:80 proxyport=\$PORT,crlf TCP-L crlf EXEC:./proxyecho.sh 4 +" +# detto IP6 + + +# test combined 4_6/6_4 with retry and fork + + # end of common tests ################################################################################## @@ -19519,7 +20095,7 @@ case "$TESTS" in TEST="$NAME: pty with group-late works on pty" # up to socat 1.7.1.1 address pty changed the ownership of /dev/ptmx instead of # the pty with options user-late, group-late, or perm-late. -# here we check for correct behaviour. +# here we check for correct behaviour. # ATTENTION: in case of failure of this test the # group of /dev/ptmx might be changed! if ! eval $NUMCOND; then :; else @@ -19626,7 +20202,7 @@ rm -f testsrv.* testcli.* testsrvdsa* testsrvfips* testclifips* # end # too dangerous - run as root and having a shell problem, it might purge your -# file systems +# file systems #rm -r "$td" # sometimes subprocesses hang; we want to see this diff --git a/xio-ip.c b/xio-ip.c index a0c12c9..7b1c56e 100644 --- a/xio-ip.c +++ b/xio-ip.c @@ -161,6 +161,79 @@ int Res_init(void) { #endif /* HAVE_RESOLV_H */ +/* Looks for a bind option and, if found, passes it to resolver; + for IP (v4, v6) and raw (PF_UNSPEC); + returns list of addrinfo results; + returns STAT_OK if option exists and could be resolved, + STAT_NORETRY if option exists but had error, + or STAT_NOACTION if it does not exist */ +int retropt_bind_ip( + struct opt *opts, + int af, + int socktype, + int ipproto, + struct addrinfo ***bindlist, + int feats, /* TCP etc: 1..address allowed, + 3..address and port allowed + */ + const int ai_flags[2]) +{ + const char portsep[] = ":"; + const char *ends[] = { portsep, NULL }; + const char *nests[] = { "[", "]", NULL }; + bool portallowed; + char *bindname, *bindp; + char hostname[512], *hostp = hostname, *portp = NULL; + size_t hostlen = sizeof(hostname)-1; + int parsres; + int ai_flags2[2]; + int result; + + if (retropt_string(opts, OPT_BIND, &bindname) < 0) { + return STAT_NOACTION; + } + bindp = bindname; + + portallowed = (feats>=2); + parsres = + nestlex((const char **)&bindp, &hostp, &hostlen, ends, NULL, NULL, nests, + true, false, false); + if (parsres < 0) { + Error1("option too long: \"%s\"", bindp); + return STAT_NORETRY; + } else if (parsres > 0) { + Error1("syntax error in \"%s\"", bindp); + return STAT_NORETRY; + } + *hostp++ = '\0'; + if (!strncmp(bindp, portsep, strlen(portsep))) { + if (!portallowed) { + Error("port specification not allowed in this bind option"); + return STAT_NORETRY; + } else { + portp = bindp + strlen(portsep); + } + } + + /* Set AI_PASSIVE, except when it is explicitely disabled */ + ai_flags2[0] = ai_flags[0]; + ai_flags2[1] = ai_flags[1]; + if (!(ai_flags2[1] & AI_PASSIVE)) + ai_flags2[0] |= AI_PASSIVE; + + if ((result = + xiogetaddrinfo(hostname[0]!='\0'?hostname:NULL, portp, + af, socktype, ipproto, + bindlist, ai_flags2)) + != STAT_OK) { + Error2("error resolving bind option \"%s\" with af=%d", bindname, af); + return STAT_NORETRY; + } + + return STAT_OK; +} + + #if WITH_DEVTESTS /* Have a couple of hard coded sockaddr records, to be copied and adapted when @@ -516,11 +589,11 @@ int _xiogetaddrinfo(const char *node, const char *service, continue; } if ((error_num = Getaddrinfo(node, service, &hints, res)) != 0) { - Warn7("getaddrinfo(\"%s\", \"%s\", {0x%02x,%d,%d,%d}, {}): %d", + Warn7("getaddrinfo(\"%s\", \"%s\", {0x%02x,%d,%d,%d}, {}): %s", node?node:"NULL", service?service:"NULL", hints.ai_flags, hints.ai_family, hints.ai_socktype, hints.ai_protocol, - error_num); + gai_strerror(error_num)); if (numnode) free(numnode); @@ -760,6 +833,9 @@ void xiofreeaddrinfo(struct addrinfo **ai_sorted) { int ain; struct addrinfo *res; + if (ai_sorted == NULL) + return; + /* Find the original *res from getaddrinfo past NULL */ ain = 0; while (ai_sorted[ain] != NULL) diff --git a/xio-ip.h b/xio-ip.h index ec94e16..c6c0355 100644 --- a/xio-ip.h +++ b/xio-ip.h @@ -49,6 +49,7 @@ extern const struct optdesc opt_res_nsaddr; extern int xioinit_ip(int *pf, char ipv); +extern int retropt_bind_ip(struct opt *opts, int af, int socktype, int ipproto, struct addrinfo ***bindlist, int feats, const int ai_flags[2]); extern int xiogetaddrinfo(const char *node, const char *service, int family, int socktype, int protocol, struct addrinfo ***ai_sorted, const int ai_flags[2]); extern void xiofreeaddrinfo(struct addrinfo **ai_sorted); extern int _xio_sort_ip_addresses(struct addrinfo *themlist, struct addrinfo **ai_sorted); diff --git a/xio-ipapp.c b/xio-ipapp.c index fcc0d33..53c505d 100644 --- a/xio-ipapp.c +++ b/xio-ipapp.c @@ -19,6 +19,7 @@ const struct optdesc opt_sourceport = { "sourceport", "sp", OPT_SOURCEPORT /*const struct optdesc opt_port = { "port", NULL, OPT_PORT, GROUP_IPAPP, PH_BIND, TYPE_USHORT, OFUNC_SPEC };*/ const struct optdesc opt_lowport = { "lowport", NULL, OPT_LOWPORT, GROUP_IPAPP, PH_LATE, TYPE_BOOL, OFUNC_SPEC }; + #if _WITH_IP4 || _WITH_IP6 /* we expect the form "host:port" */ int xioopen_ipapp_connect( @@ -31,20 +32,18 @@ int xioopen_ipapp_connect( { struct single *sfd = &xxfd->stream; struct opt *opts0 = NULL; + const char *hostname = argv[1], *portname = argv[2]; + int pf = addrdesc->arg3; int socktype = addrdesc->arg1; int ipproto = addrdesc->arg2; - int pf = addrdesc->arg3; - const char *hostname = argv[1], *portname = argv[2]; bool dofork = false; int maxchildren = 0; - union sockaddr_union us_sa, *us = &us_sa; - socklen_t uslen = sizeof(us_sa); - struct addrinfo **themarr, *themp; - char infobuff[256]; + struct addrinfo **bindarr = NULL; + struct addrinfo **themarr = NULL; + uint16_t bindport = 0; bool needbind = false; bool lowport = false; - int level; - int i; + int level = E_ERROR; int result; if (argc != 3) { @@ -52,97 +51,84 @@ int xioopen_ipapp_connect( return STAT_NORETRY; } - if (sfd->howtoend == END_UNSPEC) - sfd->howtoend = END_SHUTDOWN; + /* Apply and retrieve some options */ + result = _xioopen_ipapp_init(sfd, xioflags, opts, + &dofork, &maxchildren, + &pf, &socktype, &ipproto); + if (result != STAT_OK) + return result; - if (applyopts_single(sfd, opts, PH_INIT) < 0) - return -1; - applyopts(sfd, -1, opts, PH_INIT); + opts0 = opts; /* save remaining options for each loop */ + opts = NULL; - retropt_bool(opts, OPT_FORK, &dofork); - if (dofork) { - if (!(xioflags & XIO_MAYFORK)) { - Error("option fork not allowed here"); - return STAT_NORETRY; - } - sfd->flags |= XIO_DOESFORK; - } + Notice2("opening connection to %s:%s", hostname, portname); - retropt_int(opts, OPT_MAX_CHILDREN, &maxchildren); - - if (! dofork && maxchildren) { - Error("option max-children not allowed without option fork"); - return STAT_NORETRY; - } - - if (_xioopen_ipapp_prepare(opts, &opts0, hostname, portname, &pf, ipproto, - sfd->para.socket.ip.ai_flags, - &themarr, us, &uslen, &needbind, &lowport, - socktype) != STAT_OK) { - return STAT_NORETRY; - } - - if (dofork) { - xiosetchilddied(); /* set SIGCHLD handler */ - } - - if (xioparms.logopt == 'm') { - Info("starting connect loop, switching to syslog"); - diag_set('y', xioparms.syslogfac); xioparms.logopt = 'y'; - } else { - Info("starting connect loop"); - } - - do { /* loop over retries, and forks */ - - /* Loop over themarr (which had been "ai_sorted") */ - result = STAT_RETRYLATER; - i = 0; - themp = themarr[i++]; - while (themp != NULL) { - Notice1("opening connection to %s", - sockaddr_info(themp->ai_addr, themp->ai_addrlen, - infobuff, sizeof(infobuff))); + do { /* loop over retries and/or forks */ + int _errno; #if WITH_RETRY - if (sfd->forever || sfd->retry) { - level = E_INFO; - } else if (themarr[i] != NULL) { - level = E_WARN; - } else + if (sfd->forever || sfd->retry) { + level = E_NOTICE; + } else #endif /* WITH_RETRY */ - level = E_ERROR; + level = E_WARN; - result = - _xioopen_connect(sfd, - needbind?us:NULL, uslen, - themp->ai_addr, themp->ai_addrlen, - opts, pf?pf:themp->ai_family, socktype, ipproto, - lowport, level); - if (result == STAT_OK) - break; - themp = themarr[i++]; - if (themp == NULL) { - result = STAT_RETRYLATER; - } - } + opts = copyopts(opts0, GROUP_ALL); + + result = + _xioopen_ipapp_prepare(&opts, opts0, hostname, portname, + pf, socktype, ipproto, + sfd->para.socket.ip.ai_flags, + &themarr, &bindarr, &bindport, &needbind, &lowport); switch (result) { case STAT_OK: break; #if WITH_RETRY case STAT_RETRYLATER: case STAT_RETRYNOW: - if (sfd->forever || sfd->retry) { - --sfd->retry; - if (result == STAT_RETRYLATER) { + if (sfd->forever || sfd->retry--) { + if (result == STAT_RETRYLATER) Nanosleep(&sfd->intervall, NULL); - } - dropopts(opts, PH_ALL); free(opts); opts = copyopts(opts0, GROUP_ALL); + if (bindarr != NULL) xiofreeaddrinfo(bindarr); + xiofreeaddrinfo(themarr); + freeopts(opts); continue; } #endif /* WITH_RETRY */ - default: + /* FALLTHROUGH */ + case STAT_NORETRY: + if (bindarr != NULL) xiofreeaddrinfo(bindarr); xiofreeaddrinfo(themarr); - free(opts0);free(opts); + freeopts(opts); + freeopts(opts0); + return result; + } + + result = + _xioopen_ipapp_connect(sfd, hostname, opts, themarr, + needbind, bindarr, bindport, lowport, level); + _errno = errno; + if (bindarr != NULL) + xiofreeaddrinfo(bindarr); + xiofreeaddrinfo(themarr); + switch (result) { + case STAT_OK: break; +#if WITH_RETRY + case STAT_RETRYLATER: + case STAT_RETRYNOW: + if (sfd->forever || sfd->retry--) { + if (result == STAT_RETRYLATER) { + Nanosleep(&sfd->intervall, NULL); + } + freeopts(opts); + continue; + } +#endif /* WITH_RETRY */ + /* FALLTHROUGH */ + default: + Error4("%s:%s:%s: %s", argv[0], argv[1], argv[2], + _errno?strerror(_errno):"(See above)"); + freeopts(opts); + freeopts(opts0); return result; } @@ -155,16 +141,18 @@ int xioopen_ipapp_connect( so Notice is too weak */ } while ((pid = xio_fork(false, level, sfd->shutup)) < 0) { - if (sfd->forever || --sfd->retry) { - Nanosleep(&sfd->intervall, NULL); continue; + if (sfd->forever || sfd->retry--) { + Nanosleep(&sfd->intervall, NULL); + continue; } - xiofreeaddrinfo(themarr); - free(opts0); + freeopts(opts); + freeopts(opts0); return STAT_RETRYLATER; } if (pid == 0) { /* child process */ - sfd->forever = false; sfd->retry = 0; + sfd->forever = false; + sfd->retry = 0; break; } @@ -176,109 +164,266 @@ int xioopen_ipapp_connect( Info1("all %d allowed children are active, waiting", maxchildren); Nanosleep(&sfd->intervall, NULL); } - dropopts(opts, PH_ALL); free(opts); opts = copyopts(opts0, GROUP_ALL); + freeopts(opts); continue; /* with next socket() bind() connect() */ } else #endif /* WITH_RETRY */ { break; } - } while (true); + } while (true); /* end of loop over retries and/or forks */ /* only "active" process breaks (master without fork, or child) */ - xiofreeaddrinfo(themarr); - if ((result = _xio_openlate(sfd, opts)) < 0) { - free(opts0);free(opts); - return result; - } - free(opts0); free(opts); - return 0; + Notice2("successfully connected to %s:%s", hostname, portname); + + result = _xio_openlate(sfd, opts); + freeopts(opts); + freeopts(opts0); + return result; } -/* returns STAT_OK on success or some other value on failure +/* This function performs static initializations for addresses like TCP-CONNECT + before start of the outer loop: + it retrieves some options + returns STAT_OK on success or some other value on failure; + applies and consumes the following options: + PH_INIT, OPT_FORK, OPT_MAX_CHILDREN, OPT_PROTOCOL_FAMILY, OPT_SO_TYPE, + OPT_SO_PROTOTYPE +*/ +int _xioopen_ipapp_init( + struct single *sfd, + int xioflags, + struct opt *opts, + bool *dofork, + int *maxchildren, + int *pf, + int *socktype, + int *ipproto) +{ + if (sfd->howtoend == END_UNSPEC) + sfd->howtoend = END_SHUTDOWN; + + if (applyopts_single(sfd, opts, PH_INIT) < 0) + return -1; + if (applyopts(sfd, -1, opts, PH_INIT) < 0) + return -1; + + retropt_bool(opts, OPT_FORK, dofork); + if (dofork) { + if (!(xioflags & XIO_MAYFORK)) { + Error1("%s: option fork not allowed here", sfd->addr->defname); + return STAT_NORETRY; + } + sfd->flags |= XIO_DOESFORK; + } + + retropt_int(opts, OPT_MAX_CHILDREN, maxchildren); + if (! dofork && maxchildren) { + Error1("%s: option max-children not allowed without option fork", sfd->addr->defname); + return STAT_NORETRY; + } + + retropt_socket_pf(opts, pf); + retropt_int(opts, OPT_SO_TYPE, socktype); + retropt_int(opts, OPT_SO_PROTOTYPE, ipproto); + + if (dofork) { + xiosetchilddied(); /* set SIGCHLD handler */ + } + + if (xioparms.logopt == 'm') { + Info("starting connect loop, switching to syslog"); + diag_set('y', xioparms.syslogfac); + xioparms.logopt = 'y'; + } else { + Info("starting connect loop"); + } + + return STAT_OK; +} + + +/* This function performs preparations for addresses like TCP-CONNECT + at the beginning of the outer (retry/fork) loop: + it evaluates some options and performs name resolution of both server + (target, "them") address and bind ("us") address. + It is intended to be invoked before the connect loop starts; + returns STAT_OK on success or some other value on failure; applies and consumes the following options: PH_EARLY - OPT_PROTOCOL_FAMILY, OPT_BIND, OPT_SOURCEPORT, OPT_LOWPORT - */ -int - _xioopen_ipapp_prepare( - struct opt *opts, - struct opt **opts0, - const char *hostname, - const char *portname, - int *pf, - int protocol, - const int ai_flags[2], - struct addrinfo ***themarr, - union sockaddr_union *us, - socklen_t *uslen, - bool *needbind, - bool *lowport, - int socktype) { + OPT_BIND, OPT_SOURCEPORT, OPT_LOWPORT + returns STAT_OK, STAT_RETRYLATER, or STAT_NORETRY (+errno) +*/ +int _xioopen_ipapp_prepare( + struct opt **opts, + struct opt *opts0, + const char *hostname, + const char *portname, + int pf, + int socktype, + int protocol, + const int ai_flags[2], + struct addrinfo ***themarr, /* always from getaddrinfo(); xiofreeaddrinfo()! */ + struct addrinfo ***bindarr, /* on bind from getaddrinfo(); xiofreeaddrinfo()! */ + uint16_t *bindport, /* for bind without address */ + bool *needbind, + bool *lowport) +{ uint16_t port; int rc; - retropt_socket_pf(opts, pf); + *opts = copyopts(opts0, GROUP_ALL); if (hostname != NULL || portname != NULL) { - rc = xiogetaddrinfo(hostname, portname, *pf, socktype, protocol, + rc = xiogetaddrinfo(hostname, portname, pf, socktype, protocol, themarr, ai_flags); if (rc == EAI_AGAIN) { Warn4("_xioopen_ipapp_prepare(node=\"%s\", service=\"%s\", pf=%d, ...): %s", hostname?hostname:"NULL", portname?portname:"NULL", - *pf, gai_strerror(rc)); + pf, gai_strerror(rc)); + errno = EAGAIN; return STAT_RETRYLATER; } else if (rc != 0) { Error4("_xioopen_ipapp_prepare(node=\"%s\", service=\"%s\", pf=%d, ...): %s", hostname?hostname:"NULL", portname?portname:"NULL", - *pf, (rc == EAI_SYSTEM)?strerror(errno):gai_strerror(rc)); + pf, (rc == EAI_SYSTEM)?strerror(errno):gai_strerror(rc)); + errno = 0; /* unspecified */ return STAT_NORETRY; /*! STAT_RETRYLATER? */ } } - applyopts(NULL, -1, opts, PH_EARLY); + applyopts(NULL, -1, *opts, PH_EARLY); /* 3 means: IP address AND port accepted */ - if (retropt_bind(opts, (*pf!=PF_UNSPEC)?*pf:(**themarr)->ai_family, - socktype, protocol, (struct sockaddr *)us, uslen, 3, - ai_flags) + if (retropt_bind_ip(*opts, pf, socktype, protocol, bindarr, 3, ai_flags) != STAT_NOACTION) { *needbind = true; - } else { - switch ((*pf!=PF_UNSPEC)?*pf:(**themarr)->ai_family) { -#if WITH_IP4 - case PF_INET: socket_in_init(&us->ip4); *uslen = sizeof(us->ip4); break; -#endif /* WITH_IP4 */ -#if WITH_IP6 - case PF_INET6: socket_in6_init(&us->ip6); *uslen = sizeof(us->ip6); break; -#endif /* WITH_IP6 */ - default: Error("unsupported protocol family"); - } } - - if (retropt_2bytes(opts, OPT_SOURCEPORT, &port) >= 0) { - switch ((*pf!=PF_UNSPEC)?*pf:(**themarr)->ai_family) { + if (retropt_2bytes(*opts, OPT_SOURCEPORT, &port) >= 0) { + if (*bindarr) { + struct addrinfo **bindp; + bindp = *bindarr; + switch ((*bindp)->ai_family) { #if WITH_IP4 - case PF_INET: us->ip4.sin_port = htons(port); break; + case PF_INET: ((struct sockaddr_in *)(*bindp)->ai_addr)->sin_port = htons(port); break; #endif /* WITH_IP4 */ #if WITH_IP6 - case PF_INET6: us->ip6.sin6_port = htons(port); break; + case PF_INET6: ((struct sockaddr_in6 *)(*bindp)->ai_addr)->sin6_port = htons(port); break; #endif /* WITH_IP6 */ - default: Error("unsupported protocol family"); + default: + Error("unsupported protocol family"); + errno = EPROTONOSUPPORT; + return STAT_NORETRY; + } + } else { + *bindport = port; } *needbind = true; } - retropt_bool(opts, OPT_LOWPORT, lowport); - - *opts0 = copyopts(opts, GROUP_ALL); + retropt_bool(*opts, OPT_LOWPORT, lowport); return STAT_OK; } #endif /* _WITH_IP4 || _WITH_IP6 */ +/* Tries to connect to the addresses in themarr, for each one it tries to bind + to the addresses in bindarr. + Ends on success or when all attempts failed. + Returns STAT_OK on success, or STAT_RETRYLATER (+errno) on failure. */ +int _xioopen_ipapp_connect(struct single *sfd, + const char *hostname, + struct opt *opts, + struct addrinfo **themarr, + bool needbind, + struct addrinfo **bindarr, + uint16_t bindport, + bool lowport, + int level) +{ + struct addrinfo **themp; + struct addrinfo **bindp; + union sockaddr_union bindaddr = {0}; + union sockaddr_union *bindaddrp = NULL; + socklen_t bindlen = 0; + char infobuff[256]; + int _errno; + int result = STAT_OK; + + --level; + + /* Loop over server addresses (themarr) */ + themp = themarr; + while (*themp != NULL) { + Notice1("opening connection to %s", + sockaddr_info((*themp)->ai_addr, (*themp)->ai_addrlen, + infobuff, sizeof(infobuff))); + + if (*(themp+1) == NULL) { + ++level; /* last attempt */ + } + + /* Loop over array (list) of bind addresses */ + if (needbind && bindarr != NULL) { + /* Bind by hostname, use resolvers results list */ + bindp = bindarr; + while (*bindp != NULL) { + if ((*bindp)->ai_family == (*themp)->ai_family) + break; + ++bindp; + } + if (*bindp == NULL) { + Warn3("%s: No bind address with matching address family (%d) of %s available", + sfd->addr->defname, (*themp)->ai_family, hostname); + ++themp; + if ((*themp) == NULL) { + result = STAT_RETRYLATER; + } + _errno = ENOPROTOOPT; + continue; + } + bindaddrp = (union sockaddr_union *)(*bindp)->ai_addr; + bindlen = (*bindp)->ai_addrlen; + } else if (needbind && bindport) { + /* Bind by sourceport option */ + switch ((*themp)->ai_family) { + case PF_INET: + bindaddr.ip4.sin_family = (*themp)->ai_family; + bindaddr.ip4.sin_port = htons(bindport); + bindaddrp = &bindaddr; + bindlen = sizeof(bindaddr.ip4); + break; + case PF_INET6: + bindaddr.ip6.sin6_family = (*themp)->ai_family; + bindaddr.ip6.sin6_port = htons(bindport); + bindaddrp = &bindaddr; + bindlen = sizeof(bindaddr.ip6); + break; + } + } + + result = + _xioopen_connect(sfd, + bindaddrp, bindlen, + (*themp)->ai_addr, (*themp)->ai_addrlen, + opts, /*pf?pf:*/(*themp)->ai_family, (*themp)->ai_socktype, (*themp)->ai_protocol, + lowport, level); + if (result == STAT_OK) + break; + _errno = errno; + ++themp; + if (*themp == NULL) + result = STAT_RETRYLATER; + } /* end of loop over target addresses */ + + if (result != STAT_OK) + errno = _errno; + return result; +} + + #if WITH_TCP && WITH_LISTEN /* applies and consumes the following options: diff --git a/xio-ipapp.h b/xio-ipapp.h index af9e7b4..e190e36 100644 --- a/xio-ipapp.h +++ b/xio-ipapp.h @@ -15,11 +15,9 @@ extern const struct optdesc opt_sourceport; extern const struct optdesc opt_lowport; extern int xioopen_ipapp_connect(int argc, const char *argv[], struct opt *opts, int xioflags, xiofile_t *fd, const struct addrdesc *addrdesc); -extern int _xioopen_ipapp_prepare(struct opt *opts, struct opt **opts0, const char *hostname, const char *portname, int *pf, int protocol, const int ai_flags[2], struct addrinfo ***themlist, union sockaddr_union *us, socklen_t *uslen, bool *needbind, bool *lowport, int socktype); -extern int _xioopen_ip4app_connect(const char *hostname, const char *portname, - struct single *xfd, - int socktype, int ipproto, void *protname, - struct opt *opts); +extern int _xioopen_ipapp_init(struct single *sfd, int xioflags, struct opt *opts, bool *dofork, int *maxchildren, int *pf, int *socktype, int *protocol); +extern int _xioopen_ipapp_prepare(struct opt **opts, struct opt *opts0, const char *hostname, const char *portname, int pf, int socktype, int protocol, const int ai_flags[2], struct addrinfo ***themarr, struct addrinfo ***bindarr, uint16_t *bindport, bool *needbind, bool *lowport); +extern int _xioopen_ipapp_connect(struct single *sfd, const char *hostname, struct opt *opts, struct addrinfo **themarr, bool needbind, struct addrinfo **bindarr, uint16_t bindport, bool lowport, int level); extern int xioopen_ipapp_listen(int argc, const char *argv[], struct opt *opts, int xioflags, xiofile_t *fd, const struct addrdesc *addrdesc); extern int _xioopen_ipapp_listen_prepare(struct opt *opts, struct opt **opts0, const char *portname, int *pf, int ipproto, const int ai_flags[2], union sockaddr_union *us, socklen_t *uslen, int socktype); diff --git a/xio-openssl.c b/xio-openssl.c index c333ca3..21a571a 100644 --- a/xio-openssl.c +++ b/xio-openssl.c @@ -238,13 +238,13 @@ static int xioopen_openssl_connect( int socktype = SOCK_STREAM; int ipproto = IPPROTO_TCP; bool dofork = false; - union sockaddr_union us_sa, *us = &us_sa; - socklen_t uslen = sizeof(us_sa); - struct addrinfo **themarr, *themp; + int maxchildren = 0; + struct addrinfo **bindarr = NULL; + struct addrinfo **themarr = NULL; + uint16_t bindport = 0; bool needbind = false; bool lowport = false; int level = E_ERROR; - int i; SSL_CTX* ctx; bool opt_ver = true; /* verify peer certificate */ char *opt_cert = NULL; /* file name of client certificate */ @@ -254,7 +254,7 @@ static int xioopen_openssl_connect( int result; if (!(xioflags & XIO_MAYCONVERT)) { - Error("address with data processing not allowed here"); + Error1("%s: address with data processing not allowed here", argv[0]); return STAT_NORETRY; } sfd->flags |= XIO_DOESCONVERT; @@ -272,14 +272,6 @@ static int xioopen_openssl_connect( return STAT_NORETRY; } - if (sfd->howtoend == END_UNSPEC) - sfd->howtoend = END_SHUTDOWN; - if (applyopts_single(sfd, opts, PH_INIT) < 0) - return -1; - applyopts(sfd, -1, opts, PH_INIT); - - retropt_bool(opts, OPT_FORK, &dofork); - retropt_string(opts, OPT_OPENSSL_CERTIFICATE, &opt_cert); retropt_string(opts, OPT_OPENSSL_COMMONNAME, (char **)&opt_commonname); #if defined(HAVE_SSL_set_tlsext_host_name) || defined(SSL_set_tlsext_host_name) @@ -315,73 +307,83 @@ static int xioopen_openssl_connect( socktype = SOCK_DGRAM; ipproto = IPPROTO_UDP; } - retropt_int(opts, OPT_SO_TYPE, &socktype); - retropt_int(opts, OPT_SO_PROTOTYPE, &ipproto); - result = - _xioopen_ipapp_prepare(opts, &opts0, hostname, portname, &pf, ipproto, - sfd->para.socket.ip.ai_flags, - &themarr, us, &uslen, - &needbind, &lowport, socktype); - if (result != STAT_OK) return STAT_NORETRY; + /* Apply and retrieve some options */ + result = _xioopen_ipapp_init(sfd, xioflags, opts, + &dofork, &maxchildren, + &pf, &socktype, &ipproto); + if (result != STAT_OK) + return result; - if (xioparms.logopt == 'm') { - Info("starting connect loop, switching to syslog"); - diag_set('y', xioparms.syslogfac); xioparms.logopt = 'y'; - } else { - Info("starting connect loop"); - } + opts0 = opts; /* save remaining options for each loop */ + opts = NULL; - do { /* loop over failed connect and SSL handshake attempts */ + Notice2("opening OpenSSL connection to %s:%s", hostname, portname); - /* Loop over themarr (which had been "ai_sorted") */ - i = 0; - themp = themarr[i++]; - while (themp != NULL) { + do { /* loop over retries (failed connect and SSL handshake attempts) and/or forks */ + int _errno; #if WITH_RETRY - if (sfd->forever || sfd->retry || themarr[i] != NULL) { - level = E_INFO; - } else + if (sfd->forever || sfd->retry) { + level = E_NOTICE; + } else #endif /* WITH_RETRY */ - level = E_ERROR; + level = E_WARN; - /* This cannot fork because we retrieved fork option above */ - result = - _xioopen_connect(sfd, - needbind?us:NULL, uslen, - themp->ai_addr, themp->ai_addrlen, - opts, pf?pf:themp->ai_addr->sa_family, socktype, ipproto, lowport, level); - if (result == STAT_OK) - break; - themp = themarr[i++]; - if (themp == NULL) { - result = STAT_RETRYLATER; - } - } + opts = copyopts(opts0, GROUP_ALL); + + result = + _xioopen_ipapp_prepare(&opts, opts0, hostname, portname, + pf, socktype, ipproto, + sfd->para.socket.ip.ai_flags, + &themarr, &bindarr, &bindport, &needbind, &lowport); switch (result) { case STAT_OK: break; #if WITH_RETRY case STAT_RETRYLATER: case STAT_RETRYNOW: - if (sfd->forever || sfd->retry) { - dropopts(opts, PH_ALL); opts = copyopts(opts0, GROUP_ALL); + if (sfd->forever || sfd->retry--) { + if (result == STAT_RETRYLATER) + Nanosleep(&sfd->intervall, NULL); + if (bindarr != NULL) xiofreeaddrinfo(bindarr); + xiofreeaddrinfo(themarr); + freeopts(opts); + continue; + } +#endif /* WITH_RETRY */ + case STAT_NORETRY: + if (bindarr != NULL) xiofreeaddrinfo(bindarr); + xiofreeaddrinfo(themarr); + freeopts(opts); + freeopts(opts0); + return result; + } + + Notice2("opening connection to server %s:%s", hostname, portname); + result = + _xioopen_ipapp_connect(sfd, hostname, opts, themarr, + needbind, bindarr, bindport, lowport, level); + _errno = errno; + if (bindarr != NULL) xiofreeaddrinfo(bindarr); + xiofreeaddrinfo(themarr); + switch (result) { + case STAT_OK: break; +#if WITH_RETRY + case STAT_RETRYLATER: + case STAT_RETRYNOW: + if (sfd->forever || sfd->retry--) { if (result == STAT_RETRYLATER) { Nanosleep(&sfd->intervall, NULL); } - --sfd->retry; + freeopts(opts); continue; } - xiofreeaddrinfo(themarr); - return STAT_NORETRY; #endif /* WITH_RETRY */ default: - xiofreeaddrinfo(themarr); - return result; - } - /*! isn't this too early? */ - if ((result = _xio_openlate(sfd, opts)) < 0) { - xiofreeaddrinfo(themarr); + Error4("%s:%s:%s: %s", argv[0], hostname, portname, + _errno?strerror(_errno):"(See above)"); + freeopts(opts0); + freeopts(opts); return result; } @@ -392,19 +394,19 @@ static int xioopen_openssl_connect( #if WITH_RETRY case STAT_RETRYLATER: case STAT_RETRYNOW: - if (sfd->forever || sfd->retry) { - Close(sfd->fd); - dropopts(opts, PH_ALL); opts = copyopts(opts0, GROUP_ALL); + if (sfd->forever || sfd->retry--) { if (result == STAT_RETRYLATER) { Nanosleep(&sfd->intervall, NULL); } - --sfd->retry; + freeopts(opts); + Close(sfd->fd); continue; } #endif /* WITH_RETRY */ default: - xiofreeaddrinfo(themarr); - return STAT_NORETRY; + freeopts(opts); + freeopts(opts0); + return result; } if (dofork) { @@ -419,15 +421,19 @@ static int xioopen_openssl_connect( level = E_WARN; } while ((pid = xio_fork(false, level, sfd->shutup)) < 0) { - if (sfd->forever || --sfd->retry) { - Nanosleep(&sfd->intervall, NULL); continue; + if (sfd->forever || sfd->retry--) { + Nanosleep(&sfd->intervall, NULL); + freeopts(opts); + continue; } - xiofreeaddrinfo(themarr); + freeopts(opts); + freeopts(opts0); return STAT_RETRYLATER; } if (pid == 0) { /* child process */ - sfd->forever = false; sfd->retry = 0; + sfd->forever = false; + sfd->retry = 0; break; } @@ -437,21 +443,29 @@ static int xioopen_openssl_connect( sfd->para.openssl.ssl = NULL; /* with and without retry */ Nanosleep(&sfd->intervall, NULL); - dropopts(opts, PH_ALL); opts = copyopts(opts0, GROUP_ALL); + while (maxchildren > 0 && num_child >= maxchildren) { + Info1("all %d allowed children are active, waiting", maxchildren); + Nanosleep(&sfd->intervall, NULL); + } + freeopts(opts); continue; /* with next socket() bind() connect() */ } #endif /* WITH_RETRY */ break; + } while (true); /* drop out on success */ - xiofreeaddrinfo(themarr); openssl_conn_loginfo(sfd->para.openssl.ssl); free((void *)opt_commonname); free((void *)opt_snihost); - /* fill in the fd structure */ - return STAT_OK; + Notice2("successfully connected to SSL server %s:%s", hostname, portname); + + result = _xio_openlate(sfd, opts); + freeopts(opts); + freeopts(opts0); + return result; } diff --git a/xio-proxy.c b/xio-proxy.c index 0a8d4e6..ba82d5d 100644 --- a/xio-proxy.c +++ b/xio-proxy.c @@ -83,18 +83,18 @@ static int xioopen_proxy_connect( xiofile_t *xxfd, const struct addrdesc *addrdesc) { - /* we expect the form: host:host:port */ + /* we expect the form: host:host:port */ struct single *sfd = &xxfd->stream; struct opt *opts0 = NULL; struct proxyvars struct_proxyvars = { 0 }, *proxyvars = &struct_proxyvars; /* variables to be filled with address option values */ bool dofork = false; + int maxchildren = 0; /* */ int pf = PF_UNSPEC; - union sockaddr_union us_sa, *us = &us_sa; - socklen_t uslen = sizeof(us_sa); - struct addrinfo **themarr, *themp; - int i; + struct addrinfo **bindarr = NULL; + struct addrinfo **themarr = NULL; + uint16_t bindport = 0; const char *proxyname; char *proxyport = NULL; const char *targetname, *targetport; int ipproto = IPPROTO_TCP; @@ -112,90 +112,123 @@ static int xioopen_proxy_connect( targetname = argv[2]; targetport = argv[3]; - if (sfd->howtoend == END_UNSPEC) - sfd->howtoend = END_SHUTDOWN; - if (applyopts_single(sfd, opts, PH_INIT) < 0) - return -1; - applyopts(sfd, 1, opts, PH_INIT); - - retropt_int(opts, OPT_SO_TYPE, &socktype); - - retropt_bool(opts, OPT_FORK, &dofork); - if (retropt_string(opts, OPT_PROXYPORT, &proxyport) < 0) { if ((proxyport = strdup(PROXYPORT)) == NULL) { errno = ENOMEM; return -1; } } - result = _xioopen_proxy_prepare(proxyvars, opts, targetname, targetport, - sfd->para.socket.ip.ai_flags); - if (result != STAT_OK) - return result; - - Notice4("opening connection to %s:%u via proxy %s:%s", - proxyvars->targetaddr, proxyvars->targetport, proxyname, proxyport); - - i = 0; - do { /* loop over retries (failed connect and proxy-request attempts) */ - - level = E_INFO; - - result = - _xioopen_ipapp_prepare(opts, &opts0, proxyname, proxyport, - &pf, ipproto, - sfd->para.socket.ip.ai_flags, - &themarr, us, &uslen, - &needbind, &lowport, socktype); + result = + _xioopen_ipapp_init(sfd, xioflags, opts, + &dofork, &maxchildren, + &pf, &socktype, &ipproto); if (result != STAT_OK) return result; - /* Loop over themlist */ - i = 0; - themp = themarr[i++]; - while (themp != NULL) { - Notice4("opening connection to %s:%u via proxy %s:%s", - proxyvars->targetaddr, proxyvars->targetport, proxyname, proxyport); -#if WITH_RETRY - if (sfd->forever || sfd->retry || themarr[i] != NULL) { - level = E_INFO; - } else -#endif /* WITH_RETRY */ - level = E_ERROR; + result = _xioopen_proxy_init(proxyvars, opts, targetname, targetport); + if (result != STAT_OK) + return result; - result = - _xioopen_connect(sfd, - needbind?us:NULL, uslen, - themp->ai_addr, themp->ai_addrlen, - opts, pf?pf:themp->ai_family, socktype, IPPROTO_TCP, lowport, level); - if (result == STAT_OK) - break; - themp = themarr[i++]; - if (themp == NULL) { - result = STAT_RETRYLATER; - } + opts0 = opts; /* save remaining options for each loop */ + opts = NULL; + + Notice4("opening connection to %s:%s via proxy %s:%s", + targetname, targetport, proxyname, proxyport); + + do { /* loop over retries (failed connect and proxy-request attempts) + and/or forks */ + int _errno; + +#if WITH_RETRY + if (sfd->forever || sfd->retry) { + level = E_NOTICE; + } else +#endif /* WITH_RETRY */ + level = E_WARN; + + opts = copyopts(opts0, GROUP_ALL); + + result = + _xioopen_ipapp_prepare(&opts, opts0, proxyname, proxyport, + pf, socktype, ipproto, + sfd->para.socket.ip.ai_flags, + &themarr, &bindarr, &bindport, &needbind, &lowport); switch (result) { case STAT_OK: break; #if WITH_RETRY case STAT_RETRYLATER: case STAT_RETRYNOW: - if (sfd->forever || sfd->retry) { - --sfd->retry; + if (sfd->forever || sfd->retry--) { if (result == STAT_RETRYLATER) Nanosleep(&sfd->intervall, NULL); + if (bindarr != NULL) xiofreeaddrinfo(bindarr); + xiofreeaddrinfo(themarr); + freeopts(opts); continue; } #endif /* WITH_RETRY */ - default: + /* FALLTHROUGH */ + case STAT_NORETRY: + if (bindarr != NULL) xiofreeaddrinfo(bindarr); xiofreeaddrinfo(themarr); + freeopts(opts); + freeopts(opts0); return result; } - } - xiofreeaddrinfo(themarr); - applyopts(sfd, -1, opts, PH_ALL); - if ((result = _xio_openlate(sfd, opts)) < 0) + result = + _xioopen_proxy_prepare(proxyvars, opts, targetname, targetport, + sfd->para.socket.ip.ai_flags); + switch (result) { + case STAT_OK: break; +#if WITH_RETRY + case STAT_RETRYLATER: + case STAT_RETRYNOW: + if (sfd->forever || sfd->retry--) { + if (result == STAT_RETRYLATER) + Nanosleep(&sfd->intervall, NULL); + if (bindarr != NULL) xiofreeaddrinfo(bindarr); + xiofreeaddrinfo(themarr); + freeopts(opts); + continue; + } +#endif /* WITH_RETRY */ + /* FALLTHROUGH */ + default: + if (bindarr != NULL) xiofreeaddrinfo(bindarr); + xiofreeaddrinfo(themarr); + freeopts(opts); + freeopts(opts0); return result; + } + + Notice2("opening connection to proxy %s:%s", proxyname, proxyport); + result = + _xioopen_ipapp_connect(sfd, proxyname, opts, themarr, + needbind, bindarr, bindport, lowport, level); + _errno = errno; + if (bindarr != NULL) xiofreeaddrinfo(bindarr); + xiofreeaddrinfo(themarr); + switch (result) { + case STAT_OK: break; +#if WITH_RETRY + case STAT_RETRYLATER: + case STAT_RETRYNOW: + if (sfd->forever || sfd->retry--) { + if (result == STAT_RETRYLATER) + Nanosleep(&sfd->intervall, NULL); + freeopts(opts); + continue; + } +#endif /* WITH_RETRY */ + /* FALLTHROUGH */ + default: + Error4("%s:%s:...,proxyport=%s: %s", argv[0], proxyname, proxyport, + _errno?strerror(_errno):"(See above)"); + freeopts(opts0); + freeopts(opts); + return result; + } result = _xioopen_proxy_connect(sfd, proxyvars, level); switch (result) { @@ -204,11 +237,16 @@ static int xioopen_proxy_connect( case STAT_RETRYLATER: case STAT_RETRYNOW: if (sfd->forever || sfd->retry--) { - if (result == STAT_RETRYLATER) Nanosleep(&sfd->intervall, NULL); + if (result == STAT_RETRYLATER) + Nanosleep(&sfd->intervall, NULL); + freeopts(opts); continue; } #endif /* WITH_RETRY */ + /* FALLTHROUGH */ default: + freeopts(opts); + freeopts(opts0); return result; } @@ -225,21 +263,32 @@ static int xioopen_proxy_connect( } while ((pid = xio_fork(false, level, sfd->shutup)) < 0) { if (sfd->forever || --sfd->retry) { - Nanosleep(&sfd->intervall, NULL); continue; + if (sfd->retry > 0) + --sfd->retry; + Nanosleep(&sfd->intervall, NULL); + freeopts(opts); + continue; } + freeopts(opts); + freeopts(opts0); return STAT_RETRYLATER; } if (pid == 0) { /* child process */ - sfd->forever = false; sfd->retry = 0; + sfd->forever = false; + sfd->retry = 0; break; } /* parent process */ Close(sfd->fd); + /* With and without retry */ Nanosleep(&sfd->intervall, NULL); - dropopts(opts, PH_ALL); - opts = copyopts(opts0, GROUP_ALL); + while (maxchildren > 0 && num_child >= maxchildren) { + Info1("all %d allowed children are active, waiting", maxchildren); + Nanosleep(&sfd->intervall, NULL); + } + freeopts(opts); continue; } else #endif /* WITH_RETRY */ @@ -248,25 +297,25 @@ static int xioopen_proxy_connect( } } while (true); /* end of complete open loop - drop out on success */ + /* Only "active" process breaks (master without fork, or child) */ Notice4("successfully connected to %s:%u via proxy %s:%s", proxyvars->targetaddr, proxyvars->targetport, proxyname, proxyport); - return 0; + result = _xio_openlate(sfd, opts); + freeopts(opts); + freeopts(opts0); + return result; } -int _xioopen_proxy_prepare( +int _xioopen_proxy_init( struct proxyvars *proxyvars, struct opt *opts, const char *targetname, - const char *targetport, - const int ai_flags[2]) { - union sockaddr_union host; - socklen_t socklen = sizeof(host); - int rc; - + const char *targetport) +{ retropt_bool(opts, OPT_IGNORECR, &proxyvars->ignorecr); retropt_bool(opts, OPT_PROXY_RESOLVE, &proxyvars->doresolve); retropt_string(opts, OPT_HTTP_VERSION, &proxyvars->version); @@ -316,6 +365,28 @@ int _xioopen_proxy_prepare( Close(authfd); } + if (!proxyvars->doresolve) { + proxyvars->targetaddr = strdup(targetname); + if (proxyvars->targetaddr == NULL) { + Error1("strdup(\"%s\"): out of memory", targetname); + return STAT_RETRYLATER; + } + } + + return STAT_OK; +} + +int _xioopen_proxy_prepare( + struct proxyvars *proxyvars, + struct opt *opts, + const char *targetname, + const char *targetport, + const int ai_flags[2]) +{ + union sockaddr_union host; + socklen_t socklen = sizeof(host); + int rc; + if (proxyvars->doresolve) { /* currently we only resolve to IPv4 addresses. This is in accordance to RFC 2396; however once it becomes clear how IPv6 addresses should be @@ -325,6 +396,10 @@ int _xioopen_proxy_prepare( &host, &socklen, ai_flags); if (rc != STAT_OK) { proxyvars->targetaddr = strdup(targetname); + if (proxyvars->targetaddr == NULL) { + Error1("strdup(\"%s\"): out of memory", targetname); + return STAT_RETRYLATER; + } } else { #define LEN 16 /* www.xxx.yyy.zzz\0 */ if ((proxyvars->targetaddr = Malloc(LEN)) == NULL) { @@ -337,8 +412,6 @@ int _xioopen_proxy_prepare( ((unsigned char *)&host.ip4.sin_addr.s_addr)[3]); #undef LEN } - } else { - proxyvars->targetaddr = strdup(targetname); } proxyvars->targetport = htons(parseport(targetport, IPPROTO_TCP)); diff --git a/xio-proxy.h b/xio-proxy.h index fee18b7..f7fb2b8 100644 --- a/xio-proxy.h +++ b/xio-proxy.h @@ -25,9 +25,8 @@ extern const struct optdesc opt_proxy_authorization_file; extern const struct addrdesc xioaddr_proxy_connect; +extern int _xioopen_proxy_init(struct proxyvars *proxyvars, struct opt *opts, const char *targetname, const char *targetport); extern int _xioopen_proxy_prepare(struct proxyvars *proxyvars, struct opt *opts, const char *targetname, const char *targetport, const int ai_flags[2]); -int _xioopen_proxy_connect(struct single *xfd, - struct proxyvars *proxyvars, - int level); +extern int _xioopen_proxy_connect(struct single *xfd, struct proxyvars *proxyvars, int level); #endif /* !defined(__xio_proxy_h_included) */ diff --git a/xio-socket.c b/xio-socket.c index c798598..96e9893 100644 --- a/xio-socket.c +++ b/xio-socket.c @@ -805,7 +805,7 @@ int xiogetpacketinfo(struct single *sfd, int fd) with IP sockets: lowport (selects randomly a free port from 640 to 1023) with UNIX and abstract sockets: uses tmpname() to find a free file system entry. - returns 0 on success. + Returns STAT_OK on success, or STAT_RETRYLATER (+errno) on failure. */ int _xioopen_connect(struct single *sfd, union sockaddr_union *us, size_t uslen, struct sockaddr *them, size_t themlen, @@ -835,7 +835,7 @@ int _xioopen_connect(struct single *sfd, union sockaddr_union *us, size_t uslen, applyopts_cloexec(sfd->fd, opts); if (xiobind(sfd, us, uslen, opts, pf, alt, level) < 0) { - return -1; + return STAT_RETRYLATER; } applyopts(sfd, -1, opts, PH_CONNECT); @@ -875,6 +875,7 @@ int _xioopen_connect(struct single *sfd, union sockaddr_union *us, size_t uslen, Msg4(level, "xiopoll({%d,POLLOUT|POLLERR},,{"F_tv_sec"."F_tv_usec"): %s", sfd->fd, timeout.tv_sec, timeout.tv_usec, strerror(errno)); Close(sfd->fd); + errno = _errno; return STAT_RETRYLATER; } if (result == 0) { @@ -882,6 +883,7 @@ int _xioopen_connect(struct single *sfd, union sockaddr_union *us, size_t uslen, sockaddr_info(them, themlen, infobuff, sizeof(infobuff)), strerror(ETIMEDOUT)); Close(sfd->fd); + errno = _errno; return STAT_RETRYLATER; } if (writefd.revents & POLLERR) { @@ -897,15 +899,19 @@ int _xioopen_connect(struct single *sfd, union sockaddr_union *us, size_t uslen, sfd->fd, sockaddr_info(them, themlen, infobuff, sizeof(infobuff)), themlen, strerror(errno)); #endif + _errno = errno; Close(sfd->fd); + errno = _errno; return STAT_RETRYLATER; } /* otherwise OK or network error */ result = Getsockopt(sfd->fd, SOL_SOCKET, SO_ERROR, &err, &errlen); if (result != 0) { + _errno = errno; Msg2(level, "getsockopt(%d, SOL_SOCKET, SO_ERROR, ...): %s", sfd->fd, strerror(err)); Close(sfd->fd); + errno = _errno; return STAT_RETRYLATER; } Debug2("getsockopt(%d, SOL_SOCKET, SO_ERROR, { %d }) -> 0", @@ -915,6 +921,7 @@ int _xioopen_connect(struct single *sfd, union sockaddr_union *us, size_t uslen, sfd->fd, sockaddr_info(them, themlen, infobuff, sizeof(infobuff)), themlen, strerror(err)); Close(sfd->fd); + errno = err; return STAT_RETRYLATER; } Fcntl_l(sfd->fd, F_SETFL, fcntl_flags); @@ -945,6 +952,7 @@ int _xioopen_connect(struct single *sfd, union sockaddr_union *us, size_t uslen, sfd->fd, sockaddr_info(them, themlen, infobuff, sizeof(infobuff)), themlen, strerror(errno)); Close(sfd->fd); + errno = _errno; return STAT_RETRYLATER; } } else { /* result >= 0 */ @@ -1679,9 +1687,7 @@ int xiocheckrange(union sockaddr_union *sa, struct xiorange *range) { int xiocheckpeer(xiosingle_t *sfd, union sockaddr_union *pa, union sockaddr_union *la) { char infobuff[256]; -#if (WITH_TCP || WITH_UDP) && WITH_LIBWRAP int result; -#endif #if WITH_IP4 || WITH_IP6 if (sfd->para.socket.dorange) { @@ -2091,9 +2097,8 @@ int xiosetsockaddrenv(const char *lr, /* retrieves options so-type and so-prototype from opts, calls socket, and ev. generates an appropriate error message. - returns 0 on success or -1 if an error occurred. */ -int -xiosocket(struct opt *opts, int pf, int socktype, int proto, int msglevel) { + returns 0 on success or -1 (and errno) if an error occurred. */ +int xiosocket(struct opt *opts, int pf, int socktype, int proto, int msglevel) { int result; retropt_int(opts, OPT_SO_TYPE, &socktype); @@ -2118,6 +2123,7 @@ xiosocket(struct opt *opts, int pf, int socktype, int proto, int msglevel) { with IP sockets: lowport (selects randomly a free port from 640 to 1023) with UNIX and abstract sockets: uses a method similar to tmpname() to find a free file system entry. + On success returns STAT_OK, otherwise errno is set. */ int xiobind( struct single *sfd, @@ -2154,18 +2160,20 @@ int xiobind( usrname = strndup(us->un.sun_path, sizeof(us->un.sun_path)); if (usrname == NULL) { int _errno = errno; - Error2("strndup(\"%s\", "F_Zu"): out of memory", + Msg2(level, "strndup(\"%s\", "F_Zu"): out of memory", us->un.sun_path, sizeof(us->un.sun_path)); errno = _errno; - return -1; + return STAT_RETRYLATER; } } do { /* loop over tempnam bind() attempts */ sockname = xio_tempnam(usrname, abstract); if (sockname == NULL) { + int _errno = errno; Error2("tempnam(\"%s\"): %s", usrname, strerror(errno)); free(usrname); + errno = _errno; return -1; } strncpy(us->un.sun_path+(abstract?1:0), sockname, sizeof(us->un.sun_path)); @@ -2179,8 +2187,10 @@ int xiobind( infobuff, sizeof(infobuff)), uslen, strerror(errno)); if (errno != EADDRINUSE) { + int _errno = errno; free(usrname); Close(sfd->fd); + errno = _errno; return STAT_RETRYLATER; } } else { @@ -2193,10 +2203,12 @@ int xiobind( if (us != NULL) { if (Bind(sfd->fd, &us->soa, uslen) < 0) { + int _errno = errno; Msg4(level, "bind(%d, {%s}, "F_Zd"): %s", sfd->fd, sockaddr_info(&us->soa, uslen, infobuff, sizeof(infobuff)), uslen, strerror(errno)); Close(sfd->fd); + errno = _errno; return STAT_RETRYLATER; } applyopts_named(us->un.sun_path, opts, PH_PREOPEN); @@ -2268,12 +2280,14 @@ int xiobind( do { /* loop over lowport bind() attempts */ *port = htons(i); if (Bind(sfd->fd, &sinp->soa, sizeof(*sinp)) < 0) { + int _errno = errno; Msg4(errno==EADDRINUSE?E_INFO:level, "bind(%d, {%s}, "F_Zd"): %s", sfd->fd, sockaddr_info(&sinp->soa, sizeof(*sinp), infobuff, sizeof(infobuff)), sizeof(*sinp), strerror(errno)); - if (errno != EADDRINUSE) { + if (_errno != EADDRINUSE) { Close(sfd->fd); + errno = _errno; return STAT_RETRYLATER; } } else { @@ -2282,8 +2296,8 @@ int xiobind( --i; if (i < XIO_IPPORT_LOWER) i = IPPORT_RESERVED-1; if (i == N) { Msg(level, "no low port available"); - /*errno = EADDRINUSE; still assigned */ Close(sfd->fd); + errno = EADDRINUSE; return STAT_RETRYLATER; } } while (i != N); @@ -2294,17 +2308,19 @@ int xiobind( if (us) { applyopts(sfd, sfd->fd, opts, PH_BIND); if (Bind(sfd->fd, &us->soa, uslen) < 0) { + int _errno = errno; Msg4(level, "bind(%d, {%s}, "F_Zd"): %s", sfd->fd, sockaddr_info(&us->soa, uslen, infobuff, sizeof(infobuff)), uslen, strerror(errno)); Close(sfd->fd); + errno = _errno; return STAT_RETRYLATER; } } } applyopts(sfd, -1, opts, PH_PASTBIND); - return 0; + return STAT_OK; } /* Handles the SO_REUSEADDR socket option for TCP LISTEN addresses depending on diff --git a/xio-socks.c b/xio-socks.c index fd064dd..ea0805c 100644 --- a/xio-socks.c +++ b/xio-socks.c @@ -54,13 +54,12 @@ static int xioopen_socks4_connect( int pf = PF_UNSPEC; int ipproto = IPPROTO_TCP; bool dofork = false; - union sockaddr_union us_sa, *us = &us_sa; - socklen_t uslen = sizeof(us_sa); - struct addrinfo **themarr, *themp; - int i; + int maxchildren = 0; + struct addrinfo **bindarr = NULL; + struct addrinfo **themarr = NULL; + uint16_t bindport = 0; bool needbind = false; bool lowport = false; - char infobuff[256]; unsigned char buff[BUFF_LEN]; struct socks4 *sockhead = (struct socks4 *)buff; size_t buflen = sizeof(buff); @@ -76,41 +75,43 @@ static int xioopen_socks4_connect( targetname = argv[2]; targetport = argv[3]; - if (sfd->howtoend == END_UNSPEC) - sfd->howtoend = END_SHUTDOWN; - if (applyopts_single(sfd, opts, PH_INIT) < 0) return -1; - applyopts(sfd, 1, opts, PH_INIT); - - retropt_int(opts, OPT_SO_TYPE, &socktype); - - retropt_bool(opts, OPT_FORK, &dofork); - - result = _xioopen_socks4_prepare(targetport, opts, &socksport, sockhead, &buflen); + /* Apply and retrieve some options */ + result = _xioopen_ipapp_init(sfd, xioflags, opts, + &dofork, &maxchildren, + &pf, &socktype, &ipproto); if (result != STAT_OK) return result; + result = _xioopen_socks4_init(targetport, opts, &socksport, sockhead, + &buflen); + if (result != STAT_OK) + return result; + + opts0 = opts; /* save remaining options for each loop */ + opts = NULL; + Notice5("opening connection to %s:%u via socks4 server %s:%s as user \"%s\"", - targetname, - ntohs(sockhead->port), + targetname, ntohs(sockhead->port), sockdname, socksport, sockhead->userid); - i = 0; - do { /* loop over retries (failed connect and socks-request attempts) */ + do { /* loop over retries (failed connect and socks-request attempts) + and/or forks */ + int _errno; - level = E_INFO; +#if WITH_RETRY + if (sfd->forever || sfd->retry) { + level = E_NOTICE; + } else +#endif /* WITH_RETRY */ + level = E_WARN; + + opts = copyopts(opts0, GROUP_ALL); result = - _xioopen_ipapp_prepare(opts, &opts0, sockdname, socksport, - &pf, ipproto, + _xioopen_ipapp_prepare(&opts, opts0, sockdname, socksport, + pf, socktype, ipproto, sfd->para.socket.ip.ai_flags, - &themarr, us, &uslen, - &needbind, &lowport, socktype); - - /* we try to resolve the target address _before_ connecting to the socks - server: this avoids unnecessary socks connects and timeouts */ - result = - _xioopen_socks4_connect0(sfd, targetname, socks4a, sockhead, - (ssize_t *)&buflen, level); + &themarr, &bindarr, &bindport, &needbind, &lowport); switch (result) { case STAT_OK: break; #if WITH_RETRY @@ -119,60 +120,78 @@ static int xioopen_socks4_connect( if (sfd->forever || sfd->retry--) { if (result == STAT_RETRYLATER) Nanosleep(&sfd->intervall, NULL); + if (bindarr != NULL) xiofreeaddrinfo(bindarr); + xiofreeaddrinfo(themarr); + freeopts(opts); continue; } #endif /* WITH_RETRY */ - default: + /* FALLTHROUGH */ + case STAT_NORETRY: + if (bindarr != NULL) xiofreeaddrinfo(bindarr); + xiofreeaddrinfo(themarr); + freeopts(opts); + freeopts(opts0); return result; } - /* loop over themarr */ - i = 0; - themp = themarr[i++]; - while (themp != NULL) { - Notice1("opening connection to %s", - sockaddr_info(themp->ai_addr, themp->ai_addrlen, - infobuff, sizeof(infobuff))); -#if WITH_RETRY - if (sfd->forever || sfd->retry || themarr[i] != NULL) { - level = E_INFO; - } else -#endif /* WITH_RETRY */ - level = E_ERROR; - - /* this cannot fork because we retrieved fork option above */ - result = - _xioopen_connect(sfd, - needbind?us:NULL, uslen, - themp->ai_addr, themp->ai_addrlen, - opts, pf?pf:themp->ai_family, socktype, IPPROTO_TCP, lowport, level); - if (result == STAT_OK) - break; - themp = themarr[i++]; - if (themp == NULL) - result = STAT_RETRYLATER; - } + /* we try to resolve the target address _before_ connecting to the socks + server: this may avoid unnecessary connects and timeouts */ + result = + _xioopen_socks4_prepare(sfd, targetname, socks4a, sockhead, + (ssize_t *)&buflen, level); switch (result) { case STAT_OK: break; #if WITH_RETRY case STAT_RETRYLATER: case STAT_RETRYNOW: - if (sfd->forever || sfd->retry) { - --sfd->retry; + if (sfd->forever || sfd->retry--) { if (result == STAT_RETRYLATER) Nanosleep(&sfd->intervall, NULL); + if (bindarr != NULL) xiofreeaddrinfo(bindarr); + xiofreeaddrinfo(themarr); + freeopts(opts); continue; } #endif /* WITH_RETRY */ + /* FALLTHROUGH */ default: + if (bindarr != NULL) xiofreeaddrinfo(bindarr); xiofreeaddrinfo(themarr); + freeopts(opts); + freeopts(opts0); return result; } - xiofreeaddrinfo(themarr); - applyopts(sfd, -1, opts, PH_ALL); - if ((result = _xio_openlate(sfd, opts)) < 0) + Notice2("opening connection to sockd %s:%s", sockdname, socksport); + result = + _xioopen_ipapp_connect(sfd, sockdname, opts, themarr, + needbind, bindarr, bindport, lowport, level); + _errno = errno; + if (bindarr != NULL) xiofreeaddrinfo(bindarr); + xiofreeaddrinfo(themarr); + switch (result) { + case STAT_OK: break; +#if WITH_RETRY + case STAT_RETRYLATER: + case STAT_RETRYNOW: + if (sfd->forever || sfd->retry--) { + if (result == STAT_RETRYLATER) { + Nanosleep(&sfd->intervall, NULL); + } + freeopts(opts); + continue; + } +#endif /* WITH_RETRY */ + /* FALLTHROUGH */ + default: + errno = _errno; + Error4("%s:%s:...,socksport=%s: %s", argv[0], sockdname, socksport, + _errno?strerror(_errno):"(See above)"); + freeopts(opts0); + freeopts(opts); return result; + } result = _xioopen_socks4_connect(sfd, sockhead, buflen, level); switch (result) { @@ -181,11 +200,16 @@ static int xioopen_socks4_connect( case STAT_RETRYLATER: case STAT_RETRYNOW: if (sfd->forever || sfd->retry--) { - if (result == STAT_RETRYLATER) Nanosleep(&sfd->intervall, NULL); + if (result == STAT_RETRYLATER) + Nanosleep(&sfd->intervall, NULL); + freeopts(opts); continue; } #endif /* WITH_RETRY */ + /* FALLTHROUGH */ default: + freeopts(opts); + freeopts(opts0); return result; } @@ -202,10 +226,13 @@ static int xioopen_socks4_connect( so Notice is too weak */ } while ((pid = xio_fork(false, level, sfd->shutup)) < 0) { - if (sfd->forever || --sfd->retry) { + if (sfd->forever || sfd->retry--) { Nanosleep(&sfd->intervall, NULL); + freeopts(opts); continue; } + freeopts(opts); + freeopts(opts0); return STAT_RETRYLATER; } @@ -217,8 +244,13 @@ static int xioopen_socks4_connect( /* parent process */ Close(sfd->fd); + /* with and without retry */ Nanosleep(&sfd->intervall, NULL); - dropopts(opts, PH_ALL); opts = copyopts(opts0, GROUP_ALL); + while (maxchildren > 0 && num_child >= maxchildren) { + Info1("all %d allowed children are active, waiting", maxchildren); + Nanosleep(&sfd->intervall, NULL); + } + freeopts(opts); continue; } else #endif /* WITH_RETRY */ @@ -227,7 +259,15 @@ static int xioopen_socks4_connect( } } while (true); /* end of complete open loop - drop out on success */ - return 0; + /* only "active" process breaks (master without fork, or child) */ + + Notice4("successfully connected to %s:%s via sockd %s:%s", + targetname, targetport, sockdname, socksport); + + result = _xio_openlate(sfd, opts); + freeopts(opts); + freeopts(opts0); + return result; } #endif /* WITH_SOCKS4 || WITH_SOCKS4A */ @@ -260,7 +300,13 @@ int _xioopen_opt_socksport( #endif /* WITH_SOCKS4 || WITH_SOCKS4A || WITH_SOCKS5 */ #if WITH_SOCKS4 || WITH_SOCKS4A -int _xioopen_socks4_prepare(const char *targetport, struct opt *opts, char **socksport, struct socks4 *sockhead, size_t *headlen) { +int _xioopen_socks4_init( + const char *targetport, + struct opt *opts, + char **socksport, + struct socks4 *sockhead, + size_t *headlen) +{ char *userid; /* generate socks header - points to final target */ @@ -286,14 +332,14 @@ int _xioopen_socks4_prepare(const char *targetport, struct opt *opts, char **soc /* called within retry/fork loop, before connect() */ -int - _xioopen_socks4_connect0(struct single *sfd, - const char *hostname, /* socks target host */ - int socks4a, - struct socks4 *sockhead, - ssize_t *headlen, /* get available space, - return used length*/ - int level) { +int _xioopen_socks4_prepare( + struct single *sfd, + const char *hostname, /* socks target host */ + int socks4a, + struct socks4 *sockhead, + ssize_t *headlen, /* get available space, return used length*/ + int level) +{ int result; if (!socks4a) { @@ -435,16 +481,7 @@ int _xioopen_socks4_connect(struct single *sfd, switch (replyhead->action) { case SOCKS_CD_GRANTED: /* Notice("socks: connect request succeeded"); */ -#if 0 - if (Getsockname(sfd->fd, (struct sockaddr *)&us, &uslen) < 0) { - Warn4("getsockname(%d, %p, {%d}): %s", - sfd->fd, &us, uslen, strerror(errno)); - } - Notice1("successfully connected from %s via socks4", - sockaddr_info((struct sockaddr *)&us, infobuff, sizeof(infobuff))); -#else Notice("successfully connected via socks4"); -#endif break; case SOCKS_CD_FAILED: diff --git a/xio-socks.h b/xio-socks.h index f9a1131..f5abfb9 100644 --- a/xio-socks.h +++ b/xio-socks.h @@ -21,15 +21,8 @@ extern const struct addrdesc xioaddr_socks4_connect; extern const struct addrdesc xioaddr_socks4a_connect; extern int _xioopen_opt_socksport(struct opt *opts, char **socksport); -extern int _xioopen_socks4_prepare(const char *targetport, struct opt *opts, char **socksport, struct socks4 *sockhead, size_t *headlen); -extern int - _xioopen_socks4_connect0(struct single *xfd, - const char *hostname, /* socks target host */ - int socks4a, - struct socks4 *sockhead, - ssize_t *headlen, /* get available space, - return used length*/ - int level); +extern int _xioopen_socks4_init(const char *targetport, struct opt *opts, char **socksport, struct socks4 *sockhead, size_t *headlen); +extern int _xioopen_socks4_prepare(struct single *xfd, const char *hostname, int socks4a, struct socks4 *sockhead, ssize_t *headlen, int level); extern int _xioopen_socks4_connect(struct single *xfd, struct socks4 *sockhead, size_t headlen, diff --git a/xio-socks5.c b/xio-socks5.c index 31aa1ca..27ae1b1 100644 --- a/xio-socks5.c +++ b/xio-socks5.c @@ -512,6 +512,7 @@ static int xioopen_socks5( { int socks_command = addrdesc->arg1; bool dofork = false; + int maxchildren = 0; int socktype = SOCK_STREAM; int pf = PF_UNSPEC; int ipproto = IPPROTO_TCP; @@ -519,12 +520,11 @@ static int xioopen_socks5( struct opt *opts0 = NULL; struct single *sfd = &xxfd->stream; const char *socks_server, *target_name, *target_port, *socks_port; - union sockaddr_union us_sa, *us = &us_sa; - socklen_t uslen = sizeof(us_sa); - struct addrinfo **themarr, *themp; + struct addrinfo **bindarr = NULL; + struct addrinfo **themarr = NULL; + uint16_t bindport = 0; bool needbind = false; bool lowport = false; - char infobuff[256]; if (argc < 4 || argc > 5) { xio_syntax(argv[0], 4, argc-1, addrdesc->syntax); @@ -542,75 +542,115 @@ static int xioopen_socks5( target_port = argv[3]; } - if (sfd->howtoend == END_UNSPEC) - sfd->howtoend = END_SHUTDOWN; - if (applyopts_single(sfd, opts, PH_INIT) < 0) return -1; - applyopts(sfd, -1, opts, PH_INIT); - - retropt_int(opts, OPT_SO_TYPE, &socktype); - retropt_bool(opts, OPT_FORK, &dofork); + /* Apply and retrieve some options */ + result = _xioopen_ipapp_init(sfd, xioflags, opts, + &dofork, &maxchildren, + &pf, &socktype, &ipproto); + if (result != STAT_OK) + return result; if (_xioopen_opt_socksport(opts, (char **)&socks_port) < 0) { return STAT_NORETRY; } /*! possible memory leak */ - result = _xioopen_ipapp_prepare(opts, &opts0, socks_server, socks_port, - &pf, ipproto, - sfd->para.socket.ip.ai_flags, - &themarr, us, &uslen, - &needbind, &lowport, socktype); + opts0 = opts; + opts = NULL; - Notice2("connecting to socks5 server %s:%s", - socks_server, socks_port); + Notice4("opening connection to %s:%s vis socks5 server %s:%s", + target_name, target_port, socks_server, socks_port); - do { + do { /* loop over retries (failed connect and socks-request attempts) + and/or forks */ + int _errno; #if WITH_RETRY if (sfd->forever || sfd->retry) { - level = E_INFO; - } else { - level = E_ERROR; - } + level = E_NOTICE; + } else #endif + level = E_WARN; - /* loop over themarr */ - themp = themarr[0]; - while (themp != NULL) { - Notice1("opening connection to %s", - sockaddr_info(themp->ai_addr, themp->ai_addrlen, - infobuff, sizeof(infobuff))); - result = _xioopen_connect(sfd, needbind?us:NULL, sizeof(*us), - themp->ai_addr, themp->ai_addrlen, - opts, pf?pf:themp->ai_family, socktype, - IPPROTO_TCP, lowport, level); - if (result == STAT_OK) - break; - themp = themp->ai_next; - if (themp == NULL) - result = STAT_RETRYLATER; + opts = copyopts(opts0, GROUP_ALL); - switch(result){ - break; + result = + _xioopen_ipapp_prepare(&opts, opts0, socks_server, socks_port, + pf, socktype, ipproto, + sfd->para.socket.ip.ai_flags, + &themarr, &bindarr, &bindport, &needbind, + &lowport); + switch (result) { + case STAT_OK: break; #if WITH_RETRY - case STAT_RETRYLATER: - case STAT_RETRYNOW: - if (sfd->forever || sfd->retry-- ) { - if (result == STAT_RETRYLATER) Nanosleep(&sfd->intervall, NULL); - continue; - } -#endif - default: + case STAT_RETRYLATER: + case STAT_RETRYNOW: + if (sfd->forever || sfd->retry--) { + if (result == STAT_RETRYLATER) + Nanosleep(&sfd->intervall, NULL); + if (bindarr != NULL) xiofreeaddrinfo(bindarr); xiofreeaddrinfo(themarr); - return result; + freeopts(opts); + continue; } - } - xiofreeaddrinfo(themarr); - applyopts(sfd, -1, opts, PH_ALL); - - if ((result = _xio_openlate(sfd, opts)) < 0) +#endif /* WITH_RETRY */ + /* FALLTHROUGH */ + case STAT_NORETRY: + if (bindarr != NULL) xiofreeaddrinfo(bindarr); + xiofreeaddrinfo(themarr); + freeopts(opts); + freeopts(opts0); return result; + } - if ((result = _xioopen_socks5_handshake(sfd, level)) != STAT_OK) { + Notice2("opening connection to socks5 server %s:%s", + socks_server, socks_port); + result = + _xioopen_ipapp_connect(sfd, socks_server, opts, themarr, + needbind, bindarr, bindport, lowport, level); + _errno = errno; + if (bindarr != NULL) xiofreeaddrinfo(bindarr); + xiofreeaddrinfo(themarr); + switch (result) { + case STAT_OK: break; +#if WITH_RETRY + case STAT_RETRYLATER: + case STAT_RETRYNOW: + if (sfd->forever || sfd->retry--) { + if (result == STAT_RETRYLATER) { + Nanosleep(&sfd->intervall, NULL); + } + freeopts(opts); + continue; + } +#endif /* WITH_RETRY */ + /* FALLTHROUGH */ + default: + Error4("%s:%s:%s:...: %s", + argv[0], socks_server, socks_port, + _errno?strerror(_errno):"(See above)"); + freeopts(opts0); + freeopts(opts); + return result; + } + + result = _xioopen_socks5_handshake(sfd, level); + switch (result) { + case STAT_OK: break; +#if WITH_RETRY + case STAT_RETRYLATER: + case STAT_RETRYNOW: + if (sfd->forever || sfd->retry--) { + if (result == STAT_RETRYLATER) { + Nanosleep(&sfd->intervall, NULL); + } + freeopts(opts); + continue; + } +#endif /* WITH_RETRY */ + /* FALLTHROUGH */ + default: + Error3("%s:%s:%s: Connection failed", argv[0], socks_server, socks_port); + freeopts(opts0); + freeopts(opts); return result; } @@ -622,11 +662,16 @@ static int xioopen_socks5( case STAT_RETRYLATER: case STAT_RETRYNOW: if ( sfd->forever || sfd->retry-- ) { - if (result == STAT_RETRYLATER) Nanosleep(&sfd->intervall, NULL); + if (result == STAT_RETRYLATER) + Nanosleep(&sfd->intervall, NULL); + freeopts(opts); continue; } -#endif +#endif /* WITH_RETRY */ + /* FALLTHROUGH */ default: + freeopts(opts); + freeopts(opts0); return result; } @@ -642,12 +687,18 @@ static int xioopen_socks5( level = E_WARN; } while ((pid = xio_fork(false, level, sfd->shutup)) < 0) { - if (sfd->forever || --sfd->retry) { + if (sfd->forever || sfd->retry) { + if (sfd->retry > 0) + --sfd->retry; Nanosleep(&sfd->intervall, NULL); + freeopts(opts); continue; } + freeopts(opts); + freeopts(opts0); return STAT_RETRYLATER; } + if ( pid == 0 ) { sfd->forever = false; sfd->retry = 0; @@ -656,17 +707,26 @@ static int xioopen_socks5( Close(sfd->fd); Nanosleep(&sfd->intervall, NULL); - dropopts(opts, PH_ALL); - opts = copyopts(opts0, GROUP_ALL); + while (maxchildren > 0 && num_child >= maxchildren) { + Info1("all %d allowed children are active, waiting", maxchildren); + Nanosleep(&sfd->intervall, NULL); + } + freeopts(opts); continue; } else -#endif +#endif /* WITH_RETRY */ { break; } } while (true); - return 0; + Notice4("successfully connected to %s:%s via socks5 server %s:%s", + target_name, target_port, socks_server, socks_port); + + result = _xio_openlate(sfd, opts); + freeopts(opts); + freeopts(opts0); + return STAT_OK; } #endif /* WITH_SOCKS5 */ diff --git a/xioopts.c b/xioopts.c index d36179c..71d5cf4 100644 --- a/xioopts.c +++ b/xioopts.c @@ -2912,7 +2912,6 @@ int showleft(const struct opt *opts) { return 0; } - /* determines the address group from mode_t */ /* does not set GROUP_FD; cannot determine GROUP_TERMIOS ! */ groups_t _groupbits(mode_t mode) { @@ -4690,3 +4689,11 @@ int dumpopts(struct opt *opts) } return 0; } + +/* Better with type specific free function */ +void freeopts( + struct opt *opts) +{ + free(opts); + return; +} diff --git a/xioopts.h b/xioopts.h index 5071cc4..d40e34d 100644 --- a/xioopts.h +++ b/xioopts.h @@ -1031,6 +1031,7 @@ extern groups_t _groupbits(mode_t mode); extern int dropopts(struct opt *opts, unsigned int phase); extern int dropopts2(struct opt *opts, unsigned int from, unsigned int to); extern int dumpopts(struct opt *opts); +extern void freeopts(struct opt *opts); #if HAVE_BASIC_UID_T==1 # define retropt_uid(o,c,r) retropt_short(o,c,r)