in ignoreeof mode socat also blocked data transfer in the other direction

This commit is contained in:
Gerhard Rieger 2008-09-19 09:03:59 +02:00
parent f8496421f9
commit 0e1eb7e4b4
4 changed files with 111 additions and 47 deletions

View file

@ -22,6 +22,10 @@ corrections:
additional empty arguments (thanks to Olivier Hervieu for reporting additional empty arguments (thanks to Olivier Hervieu for reporting
this bug) this bug)
in ignoreeof polling mode socat also blocked data transfer in the other
direction during the 1s wait intervalls (thanks to Jorgen Cederlof for
reporting this bug)
corrected alphabetical order of options (proxy-auth) corrected alphabetical order of options (proxy-auth)
some minor corrections some minor corrections

View file

@ -1 +1 @@
"1.6.0.1+ip4bind+recvfromfork+x64+execstderr+execspaces+cosmetics+poll+udplistencont" "1.6.0.1+ip4bind+recvfromfork+x64+execstderr+execspaces+cosmetics+poll+udplistencont+ignoreeofunblock"

51
socat.c
View file

@ -760,10 +760,6 @@ int _socat(void) {
/* for ignoreeof */ /* for ignoreeof */
if (polling) { if (polling) {
if (!wasaction) { if (!wasaction) {
/* yes we could do it with poll but I like readable trace output */
if (socat_opts.pollintv.tv_sec) Sleep(socat_opts.pollintv.tv_sec);
if (socat_opts.pollintv.tv_usec) Usleep(socat_opts.pollintv.tv_usec);
if (socat_opts.total_timeout.tv_sec != 0 || if (socat_opts.total_timeout.tv_sec != 0 ||
socat_opts.total_timeout.tv_usec != 0) { socat_opts.total_timeout.tv_usec != 0) {
if (total_timeout.tv_usec < socat_opts.pollintv.tv_usec) { if (total_timeout.tv_usec < socat_opts.pollintv.tv_usec) {
@ -804,6 +800,7 @@ int _socat(void) {
closing = 2; closing = 2;
} }
/* frame 1: set the poll parameters and loop over poll() EINTR) */
do { /* loop over poll() EINTR */ do { /* loop over poll() EINTR */
int _errno; int _errno;
@ -817,11 +814,20 @@ int _socat(void) {
closing = 2; closing = 2;
} }
/* now the fds are assigned */ /* use the ignoreeof timeout if appropriate */
if (polling) {
int ptimeout = 1000*socat_opts.pollintv.tv_sec +
socat_opts.pollintv.tv_usec/1000;
if (closing == 0 || ptimeout < timeout) {
timeout = ptimeout;
}
}
/* now the fds will be assigned */
if (XIO_READABLE(sock1) && if (XIO_READABLE(sock1) &&
!(XIO_RDSTREAM(sock1)->eof > 1 && !XIO_RDSTREAM(sock1)->ignoreeof) && !(XIO_RDSTREAM(sock1)->eof > 1 && !XIO_RDSTREAM(sock1)->ignoreeof) &&
!socat_opts.righttoleft) { !socat_opts.righttoleft) {
if (!mayrd1) { if (!mayrd1 && !(XIO_RDSTREAM(sock1)->eof > 1)) {
fd1in->fd = XIO_GETRDFD(sock1); fd1in->fd = XIO_GETRDFD(sock1);
fd1in->events = POLLIN; fd1in->events = POLLIN;
} else { } else {
@ -840,7 +846,7 @@ int _socat(void) {
if (XIO_READABLE(sock2) && if (XIO_READABLE(sock2) &&
!(XIO_RDSTREAM(sock2)->eof > 1 && !XIO_RDSTREAM(sock2)->ignoreeof) && !(XIO_RDSTREAM(sock2)->eof > 1 && !XIO_RDSTREAM(sock2)->ignoreeof) &&
!socat_opts.lefttoright) { !socat_opts.lefttoright) {
if (!mayrd2) { if (!mayrd2 && !(XIO_RDSTREAM(sock2)->eof > 1)) {
fd2in->fd = XIO_GETRDFD(sock2); fd2in->fd = XIO_GETRDFD(sock2);
fd2in->events = POLLIN; fd2in->events = POLLIN;
} else { } else {
@ -856,13 +862,15 @@ int _socat(void) {
fd1out->fd = -1; fd1out->fd = -1;
fd2in->fd = -1; fd2in->fd = -1;
} }
/* frame 0: innermost part of the transfer loop: check FD status */
retval = xiopoll(fds, 4, timeout); retval = xiopoll(fds, 4, timeout);
_errno = errno; if (retval >= 0 || errno != EINTR) {
if (retval < 0 && errno == EINTR) { break;
Info1("poll(): %s", strerror(errno));
} }
_errno = errno;
Info1("poll(): %s", strerror(errno));
errno = _errno; errno = _errno;
} while (retval < 0 && errno == EINTR); } while (true);
/* attention: /* attention:
when an exec'd process sends data and terminates, it is unpredictable when an exec'd process sends data and terminates, it is unpredictable
@ -881,7 +889,13 @@ int _socat(void) {
closing>=1?socat_opts.closwait.tv_usec:socat_opts.total_timeout.tv_usec); closing>=1?socat_opts.closwait.tv_usec:socat_opts.total_timeout.tv_usec);
if (polling && !wasaction) { if (polling && !wasaction) {
/* there was a ignoreeof poll timeout, use it */ /* there was a ignoreeof poll timeout, use it */
; polling = 0; /*%%%*/
if (XIO_RDSTREAM(sock1)->ignoreeof) {
mayrd1 = 0;
}
} else if (polling && wasaction) {
wasaction = 0;
} else if (socat_opts.total_timeout.tv_sec != 0 || } else if (socat_opts.total_timeout.tv_sec != 0 ||
socat_opts.total_timeout.tv_usec != 0) { socat_opts.total_timeout.tv_usec != 0) {
/* there was a total inactivity timeout */ /* there was a total inactivity timeout */
@ -970,11 +984,15 @@ int _socat(void) {
/* NOW handle EOFs */ /* NOW handle EOFs */
/*0 Debug4("bytes1=F_Zd, XIO_RDSTREAM(sock1)->eof=%d, XIO_RDSTREAM(sock1)->ignoreeof=%d, closing=%d",
bytes1, XIO_RDSTREAM(sock1)->eof, XIO_RDSTREAM(sock1)->ignoreeof,
closing);*/
if (bytes1 == 0 || XIO_RDSTREAM(sock1)->eof >= 2) { if (bytes1 == 0 || XIO_RDSTREAM(sock1)->eof >= 2) {
if (XIO_RDSTREAM(sock1)->ignoreeof && !closing) { if (XIO_RDSTREAM(sock1)->ignoreeof && !closing) {
Debug1("socket 1 (fd %d) is at EOF, ignoring", Debug1("socket 1 (fd %d) is at EOF, ignoring",
XIO_RDSTREAM(sock1)->fd); /*! */ XIO_RDSTREAM(sock1)->fd); /*! */
polling = 1; mayrd1 = true;
polling = 1; /* do not hook this eof fd to poll for pollintv*/
} else { } else {
Notice1("socket 1 (fd %d) is at EOF", XIO_GETRDFD(sock1)); Notice1("socket 1 (fd %d) is at EOF", XIO_GETRDFD(sock1));
xioshutdown(sock2, SHUT_WR); xioshutdown(sock2, SHUT_WR);
@ -982,13 +1000,16 @@ int _socat(void) {
break; break;
} }
} }
} else if (polling && XIO_RDSTREAM(sock1)->ignoreeof) {
polling = 0;
} }
if (bytes2 == 0 || XIO_RDSTREAM(sock2)->eof >= 2) { if (bytes2 == 0 || XIO_RDSTREAM(sock2)->eof >= 2) {
if (XIO_RDSTREAM(sock2)->ignoreeof && !closing) { if (XIO_RDSTREAM(sock2)->ignoreeof && !closing) {
Debug1("socket 2 (fd %d) is at EOF, ignoring", Debug1("socket 2 (fd %d) is at EOF, ignoring",
XIO_RDSTREAM(sock2)->fd); XIO_RDSTREAM(sock2)->fd);
polling = 1; mayrd2 = true;
polling = 1; /* do not hook this eof fd to poll for pollintv*/
} else { } else {
Notice1("socket 2 (fd %d) is at EOF", XIO_GETRDFD(sock2)); Notice1("socket 2 (fd %d) is at EOF", XIO_GETRDFD(sock2));
xioshutdown(sock1, SHUT_WR); xioshutdown(sock1, SHUT_WR);
@ -996,6 +1017,8 @@ int _socat(void) {
break; break;
} }
} }
} else if (polling && XIO_RDSTREAM(sock2)->ignoreeof) {
polling = 0;
} }
} }

101
test.sh
View file

@ -3218,19 +3218,13 @@ printf "test $F_n $TEST... " $N
touch "$ti" touch "$ti"
$CMD >"$tf" 2>"$te" & $CMD >"$tf" 2>"$te" &
bg=$! bg=$!
sleep 1 usleep 500000
echo "$da" >>"$ti" echo "$da" >>"$ti"
sleep 1 sleep 1
kill $bg 2>/dev/null kill $bg 2>/dev/null
if ! echo "$da" |diff - "$tf" >"$tdiff"; then if ! echo "$da" |diff - "$tf" >"$tdiff"; then
if [ -s "$te" ]; then $PRINTF "$FAILED: diff:\n"
$PRINTF "$FAILED: $SOCAT:\n" cat "$tdiff"
echo "$CMD"
cat "$te"
else
$PRINTF "$FAILED: diff:\n"
cat "$tdiff"
fi
numFAIL=$((numFAIL+1)) numFAIL=$((numFAIL+1))
else else
$PRINTF "$OK\n" $PRINTF "$OK\n"
@ -3240,7 +3234,7 @@ fi
wait wait
esac esac
N=$((N+1)) N=$((N+1))
#set +vx set +vx
NAME=EXECIGNOREEOF NAME=EXECIGNOREEOF
@ -8152,7 +8146,7 @@ sleep 1
l="$(childprocess $pid1)" l="$(childprocess $pid1)"
rcc=$? rcc=$?
kill $pid1 2>/dev/null; wait kill $pid1 2>/dev/null; wait
if [ $rc2 -ne 0 -o $rcc -ne 0 ]; then if [ $rc2 -ne 0 ]; then
$PRINTF "$NO_RESULT\n" # already handled in test UDP4STREAM $PRINTF "$NO_RESULT\n" # already handled in test UDP4STREAM
numCANT=$((numCANT+1)) numCANT=$((numCANT+1))
elif ! echo "$da" |diff - "$tf" >"$tdiff"; then elif ! echo "$da" |diff - "$tf" >"$tdiff"; then
@ -8203,7 +8197,7 @@ sleep 1
l="$(childprocess $pid1)" l="$(childprocess $pid1)"
rcc=$? rcc=$?
kill $pid1 2>/dev/null; wait kill $pid1 2>/dev/null; wait
if [ $rc2 -ne 0 -o $rcc -ne 0 ]; then if [ $rc2 -ne 0 ]; then
$PRINTF "$NO_RESULT\n" # already handled in test UDP4DGRAM $PRINTF "$NO_RESULT\n" # already handled in test UDP4DGRAM
numCANT=$((numCANT+1)) numCANT=$((numCANT+1))
elif ! echo "$da" |diff - "$tf" >"$tdiff"; then elif ! echo "$da" |diff - "$tf" >"$tdiff"; then
@ -8415,6 +8409,44 @@ PORT=$((PORT+1))
N=$((N+1)) N=$((N+1))
# during wait for next poll time option ignoreeof blocked the data transfer in
# the reverse direction
NAME=IGNOREEOFNOBLOCK
case "$TESTS" in
*%functions%*|*%socket%*|*%ignoreeof%*|*%$NAME%*)
TEST="$NAME: ignoreeof does not block other direction"
# have socat poll in ignoreeof mode. while it waits one second for next check,
# we send data in the reverse direction and then the total timeout fires.
# it the data has passed, the test succeeded.
tf="$td/test$N.stout"
te="$td/test$N.stderr"
tdiff="$td/test$N.diff"
da="test$N $(date) $RANDOM"
CMD0="$SOCAT $opts /dev/null,ignoreeof!!- -!!/dev/null"
printf "test $F_n $TEST... " $N
(usleep 333333; echo "$da") |$CMD0 >"$tf" 2>"${te}0"
rc0=$?
if [ $rc0 != 0 ]; then
$PRINTF "$FAILED\n"
echo "$CMD0 &"
echo "$CMD1"
cat "${te}0"
cat "${te}1"
numFAIL=$((numFAIL+1))
elif echo "$da" |diff - "$tf" >/dev/null; then
$PRINTF "$OK\n"
numOK=$((numOK+1))
else
$PRINTF "$FAILED\n"
echo "$CMD0 &"
echo "$CMD1"
cat "${te}0"
numFAIL=$((numFAIL+1))
fi
esac
N=$((N+1))
echo "summary: $((N-1)) tests; $numOK ok, $numFAIL failed, $numCANT could not be performed" echo "summary: $((N-1)) tests; $numOK ok, $numFAIL failed, $numCANT could not be performed"
if [ "$numFAIL" -gt 0 ]; then if [ "$numFAIL" -gt 0 ]; then
@ -8437,32 +8469,37 @@ wait
exit exit
# template # test template
NAME=!!!
# give a description of what is tested (a bugfix, a new feature...)
NAME=SHORT_UNIQUE_TESTNAME
case "$TESTS" in case "$TESTS" in
*%functions%*|*%$NAME%*) *%functions%*|*%bugs%*|*%socket%*|*%$NAME%*)
TEST="$NAME: !!!" TEST="$NAME: give a one line description of test"
# describe how the test is performed, and what's the success criteria
tf="$td/test$N.stout"
te="$td/test$N.stderr"
tdiff="$td/test$N.diff"
da="test$N $(date) $RANDOM"
CMD0="$SOCAT $opts server-address PIPE"
CMD1="$SOCAT - client-address"
printf "test $F_n $TEST... " $N printf "test $F_n $TEST... " $N
!!! $CMD0 >/dev/null 2>"${te}0" &
pid0=$!
wait<something>port $xy 1
echo "$da" |$CMD1 >"${tf}1" 2>"${te}1"
rc1=$?
kill $pid0 2>/dev/null; wait
if [ !!! ]; then if [ !!! ]; then
$PRINTF "$OK\n" $PRINTF "$OK\n"
numOK=$((numOK+1))
else else
$PRINTF "$FAILED\n" $PRINTF "$FAILED\n"
cat "$te" echo "$CMD0 &"
echo "$CMD1"
cat "${te}0"
cat "${te}1"
numFAIL=$((numFAIL+1))
fi fi
esac esac
N=$((N+1)) N=$((N+1))
TEST="$NAME: transferring from one file to another with echo"
tf1="$td/file$N.input"
tf2="$td/file$N.output"
testecho "$N" "$TEST" "" "echo" "$opts"
# MANUAL TESTS
# ZOMBIES
# have httpd on PORT/tcp
# nice -20 $SOCAT -d tcp-l:24080,fork tcp:$LOCALHOST:PORT
# i=0; while [ $i -lt 100 ]; do $ECHO 'GET / HTTP/1.0\n' |$SOCAT -t -,ignoreeof tcp:$LOCALHOST:24080 >/dev/null& i=$((i+1)); done