diff -r -N -U 3 socat-1.6.0.0/procan.c socat-1.6.0.0+maxfds/procan.c --- procan.c.orig 2006-12-28 08:25:01.000000000 +0100 +++ procan.c 2007-04-05 12:41:26.000000000 +0200 @@ -161,6 +161,10 @@ #endif } + /* C defines */ + fprintf(outfile, "#define FD_SETSIZE %u\n", FD_SETSIZE); + fprintf(outfile, "#define FOPEN_MAX %u\n", FOPEN_MAX); + /* file descriptors */ /* what was this for?? */ diff -r -N -U 3 socat-1.6.0.0/socat.c socat-1.6.0.0+maxfds/socat.c --- socat.c.orig 2007-03-06 22:03:28.000000000 +0100 +++ socat.c 2007-03-31 22:24:37.000000000 +0200 @@ -642,8 +642,8 @@ returns >0 if child died and left data */ int childleftdata(xiofile_t *xfd) { - fd_set in, out, expt; - int retval; + fd_set *in = NULL, *out = NULL, *expt = NULL; + int max, retval; /* have to check if a child process died before, but left read data */ if (XIO_READABLE(xfd) && (XIO_RDSTREAM(xfd)->howtoend == END_KILL || @@ -652,25 +652,59 @@ XIO_RDSTREAM(xfd)->para.exec.pid == 0) { struct timeval time0 = { 0,0 }; - FD_ZERO(&in); FD_ZERO(&out); FD_ZERO(&expt); +#ifndef howmany +#define howmany(x, y) (((x) + ((y) - 1)) / (y)) +#endif + +#ifndef NFDBITS +# ifndef HAVE_FDS_BITS +# define NFDBITS (sizeof(__fd_mask) * 8) +# else +# define NFDBITS (sizeof(fd_mask) * 8) +# endif +#endif + +#ifndef HAVE_FDS_BITS +# define FD_MASK_SIZE (sizeof(__fd_mask)) +#else +# define FD_MASK_SIZE (sizeof(fd_mask)) +#endif + + max = XIO_GETRDFD(xfd); + in = (fd_set *)Calloc(howmany(max+1, NFDBITS), FD_MASK_SIZE); + out = (fd_set *)Calloc(howmany(max+1, NFDBITS), FD_MASK_SIZE); + expt = (fd_set *)Calloc(howmany(max+1, NFDBITS), FD_MASK_SIZE); + if (in == NULL || out == NULL || expt == NULL) { + Error2("select(%d): %s", max+1, strerror(errno)); + if (in != NULL) + free(in); + if (out != NULL) + free(out); + if (expt != NULL) + free(expt); + return -1; + } if (XIO_READABLE(xfd) && !(XIO_RDSTREAM(xfd)->eof >= 2 && !XIO_RDSTREAM(xfd)->ignoreeof)) { - FD_SET(XIO_GETRDFD(xfd), &in); + FD_SET(XIO_GETRDFD(xfd), in); /*0 FD_SET(XIO_GETRDFD(xfd), &expt);*/ } do { - retval = Select(FOPEN_MAX, &in, &out, &expt, &time0); + retval = Select(max+1, in, out, expt, &time0); } while (retval < 0 && errno == EINTR); if (retval < 0) { #if HAVE_FDS_BITS Error5("select(%d, &0x%lx, &0x%lx, &0x%lx, {0}): %s", - FOPEN_MAX, in.fds_bits[0], out.fds_bits[0], - expt.fds_bits[0], strerror(errno)); + max+1, in->fds_bits[0], out->fds_bits[0], + expt->fds_bits[0], strerror(errno)); #else Error5("select(%d, &0x%lx, &0x%lx, &0x%lx, {0}): %s", - FOPEN_MAX, in.__fds_bits[0], out.__fds_bits[0], - expt.__fds_bits[0], strerror(errno)); + max+1, in->__fds_bits[0], out->__fds_bits[0], + expt->__fds_bits[0], strerror(errno)); #endif + free(in); + free(out); + free(expt); return -1; } else if (retval == 0) { Info("terminated child did not leave data for us"); @@ -679,6 +713,9 @@ closing = MAX(closing, 1); } } + free(in); + free(out); + free(expt); return 0; } @@ -694,14 +731,34 @@ and their options are set/applied returns -1 on error or 0 on success */ int _socat(void) { - fd_set in, out, expt; - int retval; + fd_set *in, *out, *expt; + int max, retval; unsigned char *buff; ssize_t bytes1, bytes2; int polling = 0; /* handling ignoreeof */ int wasaction = 1; /* last select was active, do NOT sleep before next */ struct timeval total_timeout; /* the actual total timeout timer */ +#ifndef MAX +# define MAX(x, y) (((y) > (x)) ? (y) : (x)) +#endif + + max = MAX( + MAX(XIO_GETRDFD(sock1), XIO_GETWRFD(sock1)), + MAX(XIO_GETRDFD(sock2), XIO_GETWRFD(sock2))); + in = (fd_set *)Calloc(howmany(max+1, NFDBITS), FD_MASK_SIZE); + out = (fd_set *)Calloc(howmany(max+1, NFDBITS), FD_MASK_SIZE); + expt = (fd_set *)Calloc(howmany(max+1, NFDBITS), FD_MASK_SIZE); + if (in == NULL || out == NULL || expt == NULL) { + Error2("select(%d): %s", max+1, strerror(errno)); + if (in != NULL) + free(in); + if (out != NULL) + free(out); + if (expt != NULL) + free(expt); + return -1; + } #if WITH_FILAN if (socat_opts.debug) { int fdi, fdo; @@ -733,7 +790,7 @@ /* when converting nl to crnl, size might double */ buff = Malloc(2*socat_opts.bufsiz+1); - if (buff == NULL) return -1; + if (buff == NULL) { free(in); free(out); free(expt); return -1; } if (socat_opts.logopt == 'm' && xioinqopt('l', NULL, 0) == 'm') { Info("switching to syslog"); @@ -772,6 +829,9 @@ if (total_timeout.tv_sec < 0 || total_timeout.tv_sec == 0 && total_timeout.tv_usec < 0) { Notice("inactivity timeout triggered"); + free(in); + free(out); + free(expt); return 0; } } @@ -803,7 +863,9 @@ do { int _errno; - FD_ZERO(&in); FD_ZERO(&out); FD_ZERO(&expt); + memset(in, 0, howmany(max+1, NFDBITS) * FD_MASK_SIZE); + memset(out, 0, howmany(max+1, NFDBITS) * FD_MASK_SIZE); + memset(expt, 0, howmany(max+1, NFDBITS) * FD_MASK_SIZE); childleftdata(sock1); childleftdata(sock2); @@ -819,23 +881,23 @@ !(XIO_RDSTREAM(sock1)->eof > 1 && !XIO_RDSTREAM(sock1)->ignoreeof) && !socat_opts.righttoleft) { if (!mayrd1) { - FD_SET(XIO_GETRDFD(sock1), &in); + FD_SET(XIO_GETRDFD(sock1), in); } if (!maywr2) { - FD_SET(XIO_GETWRFD(sock2), &out); + FD_SET(XIO_GETWRFD(sock2), out); } } if (XIO_READABLE(sock2) && !(XIO_RDSTREAM(sock2)->eof > 1 && !XIO_RDSTREAM(sock2)->ignoreeof) && !socat_opts.lefttoright) { if (!mayrd2) { - FD_SET(XIO_GETRDFD(sock2), &in); + FD_SET(XIO_GETRDFD(sock2), in); } if (!maywr1) { - FD_SET(XIO_GETWRFD(sock1), &out); + FD_SET(XIO_GETWRFD(sock1), out); } } - retval = Select(FOPEN_MAX, &in, &out, &expt, to); + retval = Select(max+1, in, out, expt, to); _errno = errno; if (retval < 0 && errno == EINTR) { Info1("select(): %s", strerror(errno)); @@ -851,15 +913,18 @@ if (retval < 0) { #if HAVE_FDS_BITS Error7("select(%d, &0x%lx, &0x%lx, &0x%lx, %s%lu): %s", - FOPEN_MAX, in.fds_bits[0], out.fds_bits[0], - expt.fds_bits[0], to?"&":"NULL/", to?to->tv_sec:0, + max+1, in->fds_bits[0], out->fds_bits[0], + expt->fds_bits[0], to?"&":"NULL/", to?to->tv_sec:0, strerror(errno)); #else Error7("select(%d, &0x%lx, &0x%lx, &0x%lx, %s%lu): %s", - FOPEN_MAX, in.__fds_bits[0], out.__fds_bits[0], - expt.__fds_bits[0], to?"&":"NULL/", to?to->tv_sec:0, + max+1, in->__fds_bits[0], out->__fds_bits[0], + expt->__fds_bits[0], to?"&":"NULL/", to?to->tv_sec:0, strerror(errno)); #endif + free(in); + free(out); + free(expt); return -1; } else if (retval == 0) { Info2("select timed out (no data within %ld.%06ld seconds)", @@ -872,6 +937,9 @@ socat_opts.total_timeout.tv_usec != 0) { /* there was a total inactivity timeout */ Notice("inactivity timeout triggered"); + free(in); + free(out); + free(expt); return 0; } @@ -884,17 +952,17 @@ } if (XIO_READABLE(sock1) && XIO_GETRDFD(sock1) >= 0 && - FD_ISSET(XIO_GETRDFD(sock1), &in)) { + FD_ISSET(XIO_GETRDFD(sock1), in)) { mayrd1 = true; } if (XIO_READABLE(sock2) && XIO_GETRDFD(sock2) >= 0 && - FD_ISSET(XIO_GETRDFD(sock2), &in)) { + FD_ISSET(XIO_GETRDFD(sock2), in)) { mayrd2 = true; } - if (XIO_GETWRFD(sock1) >= 0 && FD_ISSET(XIO_GETWRFD(sock1), &out)) { + if (XIO_GETWRFD(sock1) >= 0 && FD_ISSET(XIO_GETWRFD(sock1), out)) { maywr1 = true; } - if (XIO_GETWRFD(sock2) >= 0 && FD_ISSET(XIO_GETWRFD(sock2), &out)) { + if (XIO_GETWRFD(sock2) >= 0 && FD_ISSET(XIO_GETWRFD(sock2), out)) { maywr2 = true; } @@ -989,6 +1057,10 @@ xioclose(sock1); xioclose(sock2); + free(in); + free(out); + free(expt); + return 0; } diff -r -N -U 3 socat-1.6.0.0/test.sh socat-1.6.0.0+maxfds/test.sh --- test.sh.orig 2007-03-06 22:06:20.000000000 +0100 +++ test.sh 2007-04-06 10:16:30.000000000 +0200 @@ -1425,7 +1425,8 @@ local arg1="$3"; [ -z "$arg1" ] && arg1="-" local arg2="$4"; [ -z "$arg2" ] && arg2="echo" local opts="$5" - local T="$6"; [ -z "$T" ] && T=0 + local redir="$6" + local T="$7"; [ -z "$T" ] && T=0 local tf="$td/test$N.stdout" local te="$td/test$N.stderr" local tdiff="$td/test$N.diff" @@ -1435,14 +1436,16 @@ $PRINTF "test $F_n %s... " $num "$title" #echo "$da" |$cmd >"$tf" 2>"$te" #set -vx - (echo "$da"; sleep $T) |$SOCAT $opts "$arg1" "$arg2" >"$tf" 2>"$te" & +# (echo "$da"; sleep $T) |($SOCAT $opts "$arg1" "$arg2" >"$tf" 2>"$te"; echo $? >"$td/test$N.rc") & +# echo eval $SOCAT $opts "$arg1" "$arg2" >"$tf" 2>"$te" $redir + (echo "$da"; sleep $T) |(eval $SOCAT $opts "$arg1" "$arg2" >"$tf" 2>"$te" $redir; echo $? >"$td/test$N.rc") & export rc1=$! #sleep 5 && kill $rc1 2>/dev/null & # rc2=$! wait $rc1 # kill $rc2 2>/dev/null -#set +vx - if [ "$?" != 0 ]; then +set +vx + if [ "$(cat "$td/test$N.rc")" != 0 ]; then $PRINTF "$FAILED: $SOCAT:\n" echo "$SOCAT $opts $arg1 $arg2" cat "$te" @@ -4129,7 +4132,7 @@ case "$TESTS" in *%functions%*|*%$NAME%*) TEST="$NAME: inheritance of stdout to single exec with socketpair" -testecho "$N" "$TEST" "-!!exec:cat" "" "$opts" 1 +testecho "$N" "$TEST" "-!!exec:cat" "" "$opts" "" 1 esac N=$((N+1)) @@ -4137,7 +4140,7 @@ case "$TESTS" in *%functions%*|*%$NAME%*) TEST="$NAME: inheritance of stdout to single exec with pipe" -testecho "$N" "$TEST" "-!!exec:cat,pipes" "" "$opts" 1 +testecho "$N" "$TEST" "-!!exec:cat,pipes" "" "$opts" "" 1 esac N=$((N+1)) @@ -4149,7 +4152,7 @@ $PRINTF "test $F_n $TEST... ${YELLOW}PTY not available${NORMAL}\n" $N numCANT=$((numCANT+1)) else -testecho "$N" "$TEST" "-!!exec:cat,pty,raw" "" "$opts" 1 +testecho "$N" "$TEST" "-!!exec:cat,pty,raw" "" "$opts" "" 1 fi esac N=$((N+1)) @@ -4178,7 +4181,7 @@ $PRINTF "test $F_n $TEST... ${YELLOW}PTY not available${NORMAL}\n" $N numCANT=$((numCANT+1)) else -testecho "$N" "$TEST" "exec:cat,pty,raw!!-" "" "$opts" $MISCDELAY +testecho "$N" "$TEST" "exec:cat,pty,raw!!-" "" "$opts" "" $MISCDELAY fi esac N=$((N+1)) @@ -5662,7 +5665,7 @@ case "$TESTS" in *%parse%*|*%functions%*|*%$NAME%*) TEST="$NAME: does lexical analysis work sensibly (exec)" -testecho "$N" "$TEST" "" "exec:'$SOCAT - exec:$CAT,pipes'" "$opts" 1 +testecho "$N" "$TEST" "" "exec:'$SOCAT - exec:$CAT,pipes'" "$opts" "" 1 esac N=$((N+1)) @@ -5670,7 +5673,7 @@ case "$TESTS" in *%parse%*|*%functions%*|*%$NAME%*) TEST="$NAME: does lexical analysis work sensibly (system)" -testecho "$N" "$TEST" "" "system:\"$SOCAT - exec:$CAT,pipes\"" "$opts" 1 +testecho "$N" "$TEST" "" "system:\"$SOCAT - exec:$CAT,pipes\"" "$opts" "" 1 esac N=$((N+1)) @@ -7737,6 +7740,81 @@ esac N=$((N+1)) + +# test: up to socat 1.6.0.0, the highest file descriptor supported in socats +# transfer engine was FOPEN_MAX-1; this usually worked fine but would fail when +# socat was invoked with many file descriptors already opened. socat would +# just hang in the select() call. Daniel Lucq found this problem and provided a +# patch that replaced the FOPEN_MAX limit: this patch not only allows FDs to be +# up to NFDBITS-1, but should work even when the processes maximum number of +# open files is increased with ulimit -n. +# FOPEN_MAX on different OS's: +# OS FOPEN_ ulimit ulimit FD_ +# MAX -H -n -S -n SETSIZE +# Linux 2.6: 16 1024 1024 1024 +# HP-UX 11.11: 60 2048 2048 2048 +# FreeBSD: 20 11095 11095 1024 +# Cygwin: 20 unlimit 256 64 +# AIX: 32767 65534 65534 +NAME=EXCEED_FOPEN_MAX +case "$TESTS" in +*%functions%*|*%maxfds%*|*%$NAME%*) +TEST="$NAME: more than FOPEN_MAX FDs in use" +# this test opens a number of FDs before socat is invoked. socat will have to +# allocate higher FD numbers and thus hang if it cannot handle them. +REDIR= +FOPEN_MAX=$($PROCAN |grep '^#define FOPEN_MAX' |awk '{print($3);}') +OPEN_FILES=$FOPEN_MAX # more than the highest FOPEN_MAX +i=3; while [ "$i" -lt "$OPEN_FILES" ]; do + REDIR="$REDIR $i>&2" + i=$((i+1)) +done +#echo $REDIR +#testecho "$N" "$TEST" "" "pipe" "$opts -T 3" "" 1 +#set -vx +testecho "$N" "$TEST" "" "pipe" "$opts -T 1" "$REDIR" 1 +#set +vx +esac +N=$((N+1)) + +OPEN_FILES="$(ulimit -n)" +NAME=EXCEED_FD_SETSIZE +case "$TESTS" in +*%functions%*|*%maxfds%*|*%root%*|*%$NAME%*) +TEST="$NAME: more than FD_SETSIZE FDs in use" +# this test opens a number of FDs before socat is invoked. This number exceeds +# the size of the fd_set type proposed for use with select(). socat will have +# to allocate space for a larger record and will hang or crash if it cannot +# handle this. +# ulimit -n must be above FD_SETSIZE which might require root +REDIR= +FD_SETSIZE=$($PROCAN |grep '^#define FD_SETSIZE' |awk '{print($3);}') +OPEN_FILES=$(($FD_SETSIZE+2)) # more than the highest FD_SETSIZE +while true; do # just go once; so we can break inside + if [ $(ulimit -S -n) -lt $OPEN_FILES ]; then + # if it increases, -H must be first + if [ $(ulimit -H -n) -lt $OPEN_FILES ]; then + if ! ulimit -H -n $((OPEN_FILES)); then + $PRINTF "test $F_n $TEST... ${YELLOW}must be root${NORMAL}\n" $N + numCANT=$((numCANT+1)) + break; + fi + fi + ulimit -S -n $((OPEN_FILES)) + fi + i=3; while [ "$i" -lt "$FD_SETSIZE" ]; do + REDIR="$REDIR $i>&2" + i=$((i+1)) + done + testecho "$N" "$TEST" "" "pipe" "$opts -T 1" "$REDIR" 1 + set +vx +break +done +;; +esac +N=$((N+1)) + + echo "summary: $((N-1)) tests; $numOK ok, $numFAIL failed, $numCANT could not be performed" if [ "$numFAIL" -gt 0 ]; then diff -r -N -U 3 socat-1.6.0.0/VERSION socat-1.6.0.0+maxfds/VERSION --- VERSION.orig 2007-03-06 21:58:44.000000000 +0100 +++ VERSION 2007-04-06 23:20:11.000000000 +0200 @@ -1 +1 @@ -"1.6.0.0" +"1.6.0.0_1"