From 13ac417410288295cfb26b48640fd2438722fc11 Mon Sep 17 00:00:00 2001 From: Gerhard Rieger Date: Thu, 31 Dec 2020 14:56:04 +0100 Subject: [PATCH] Option accept-timeout (listen-timeout) --- CHANGES | 4 ++++ doc/socat.yo | 5 +++++ test.sh | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++++ xio-listen.c | 48 ++++++++++++++++++++++++++++++++++++++++++ xio-listen.h | 1 + xio.h | 3 +++ xioopts.c | 4 +++- xioopts.h | 1 + 8 files changed, 124 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index df00a74..022e719 100644 --- a/CHANGES +++ b/CHANGES @@ -151,6 +151,10 @@ New features: Tests: OPENSSL_SNI OPENSSL_NO_SNI Thanks to Travis Burtrum for providing the initial patch + New option accept-timeout (listen-timeout) + Test: ACCEPTTIMEOUT + Proposed by Roland + ####################### V 1.7.3.4: Corrections: diff --git a/doc/socat.yo b/doc/socat.yo index 589c0cc..0a388f1 100644 --- a/doc/socat.yo +++ b/doc/socat.yo @@ -749,6 +749,7 @@ label(ADDRESS_SCTP_LISTEN)dit(bf(tt(SCTP-LISTEN:))) link(pf)(OPTION_PROTOCOL_FAMILY), link(max-children)(OPTION_MAX_CHILDREN), link(backlog)(OPTION_BACKLOG), + link(accept-timeout)(OPTION_ACCEPT_TIMEOUT), link(sctp-maxseg)(OPTION_SCTP_MAXSEG), link(sctp-nodelay)(OPTION_SCTP_NODELAY), link(su)(OPTION_SUBSTUSER), @@ -1015,6 +1016,7 @@ label(ADDRESS_TCP_LISTEN)dit(bf(tt(TCP-LISTEN:))) link(pf)(OPTION_PROTOCOL_FAMILY), link(max-children)(OPTION_MAX_CHILDREN), link(backlog)(OPTION_BACKLOG), + link(accept-timeout)(OPTION_ACCEPT_TIMEOUT), link(mss)(OPTION_MSS), link(su)(OPTION_SUBSTUSER), link(reuseaddr)(OPTION_REUSEADDR), @@ -2375,6 +2377,9 @@ startdit() label(OPTION_BACKLOG)dit(bf(tt(backlog=))) Sets the backlog value passed with the code(listen()) system call to [link(int)(TYPE_INT)]. Default is 5. +label(OPTION_ACCEPT_TIMEOUT)dit(bf(tt(accept-timeout=))) + End waiting for a connection after [link(timeval)(TYPE_TIMEVAL)] + with error status. label(OPTION_MAX_CHILDREN)dit(bf(tt(max-children=))) Limits the number of concurrent child processes [link(int)(TYPE_INT)]. Default is no limit. diff --git a/test.sh b/test.sh index c4a2b73..6d4e543 100755 --- a/test.sh +++ b/test.sh @@ -6548,6 +6548,7 @@ esac N=$((N+1)) +# Test the connect-timeout address option NAME=CONNECTTIMEOUT case "$TESTS" in *%$N%*|*%functions%*|*%ip4%*|*%ipapp%*|*%tcp%*|*%timeout%*|*%$NAME%*) @@ -14406,6 +14407,64 @@ esac N=$((N+1)) +# Test the accept-timeout (listen-timeout) address option +NAME=ACCEPTTIMEOUT +case "$TESTS" in +*%$N%*|*%functions%*|*%ip4%*|*%ipapp%*|*%tcp%*|*%listen%*|*%timeout%*|*%$NAME%*) +TEST="$NAME: test the accept-timeout option" +if ! eval $NUMCOND; then :; +elif ! feat=$(testaddrs tcp); then + $PRINTF "test $F_n $TEST... ${YELLOW}$(echo "$feat"| tr 'a-z' 'A-Z') not available${NORMAL}\n" $N + numCANT=$((numCANT+1)) + listCANT="$listCANT $N" +elif ! feat=$(testoptions accept-timeout); then + $PRINTF "test $F_n $TEST... ${YELLOW}$(echo "$feat"| tr 'a-z' 'A-Z') not available${NORMAL}\n" $N + numCANT=$((numCANT+1)) + listCANT="$listCANT $N" +else +# Just start a process with accept-timeout 1s and check if it still runs 2s later +# but before this, we test if the process waits at all +te1="$td/test$N.stderr1" +tk1="$td/test$N.kill1" +te2="$td/test$N.stderr2" +tk2="$td/test$N.kill2" +$PRINTF "test $F_n $TEST... " $N +# First, try to make socat hang and see if it can be killed +CMD1="$TRACE $SOCAT $opts TCP-LISTEN:$PORT,reuseaddr PIPE" +$CMD1 >"$te1" 2>&1 "$tk1"; then + $PRINTF "${YELLOW}does not hang${NORMAL}\n" + echo $CMD1 >&2 + cat "$te1" >&2 + cat "$tk1" >&2 + numCANT=$((numCANT+1)) + listCANT="$listCANT $N" +else +# Second, set accept-timeout and see if socat exits before kill +CMD2="$TRACE $SOCAT $opts TCP-LISTEN:$PORT,reuseaddr,accept-timeout=1 PIPE" >"$te1" & +$CMD2 >"$te1" 2>&1 "$tk2"; then + $PRINTF "$FAILED\n" + echo "$CMD2" >&2 + cat "$te2" >&2 + cat "$tk2" >&2 + numFAIL=$((numFAIL+1)) + listFAIL="$listFAIL $N" +else + $PRINTF "$OK\n" + numOK=$((numOK+1)) +fi +fi +wait +fi ;; # testaddrs, NUMCOND +esac +N=$((N+1)) + + ################################################################################## #================================================================================= # here come tests that might affect your systems integrity. Put normal tests diff --git a/xio-listen.c b/xio-listen.c index be85eca..c06335e 100644 --- a/xio-listen.c +++ b/xio-listen.c @@ -24,6 +24,7 @@ const struct optdesc opt_max_children = { "max-children", NULL, OPT_MAX_CHI #if (WITH_UDP || WITH_TCP) const struct optdesc opt_range = { "range", NULL, OPT_RANGE, GROUP_RANGE, PH_ACCEPT, TYPE_STRING, OFUNC_SPEC }; #endif +const struct optdesc opt_accept_timeout = { "accept-timeout", "listen-timeout", OPT_ACCEPT_TIMEOUT, GROUP_LISTEN, PH_LISTEN, TYPE_TIMEVAL, OFUNC_OFFSET, XIO_OFFSETOF(para.socket.accept_timeout) }; /* @@ -148,6 +149,7 @@ int _xioopen_listen(struct single *xfd, int xioflags, struct sockaddr *us, sockl } applyopts(xfd->fd, opts, PH_PASTSOCKET); + applyopts_offset(xfd, opts); applyopts_cloexec(xfd->fd, opts); applyopts(xfd->fd, opts, PH_PREBIND); @@ -207,6 +209,7 @@ int _xioopen_listen(struct single *xfd, int xioflags, struct sockaddr *us, sockl applyopts(xfd->fd, opts, PH_PRELISTEN); retropt_int(opts, OPT_BACKLOG, &backlog); + applyopts(xfd->fd, opts, PH_LISTEN); if (Listen(xfd->fd, backlog) < 0) { Error3("listen(%d, %d): %s", xfd->fd, backlog, strerror(errno)); return STAT_RETRYLATER; @@ -229,6 +232,51 @@ int _xioopen_listen(struct single *xfd, int xioflags, struct sockaddr *us, sockl do { /*? int level = E_ERROR;*/ Notice1("listening on %s", sockaddr_info(us, uslen, lisname, sizeof(lisname))); + if (xfd->para.socket.accept_timeout.tv_sec > 0 || + xfd->para.socket.accept_timeout.tv_usec > 0) { + fd_set rfd; + struct timeval tmo; + FD_ZERO(&rfd); + FD_SET(xfd->fd, &rfd); + tmo.tv_sec = xfd->para.socket.accept_timeout.tv_sec; + tmo.tv_usec = xfd->para.socket.accept_timeout.tv_usec; + while (1) { + if (Select(xfd->fd+1, &rfd, NULL, NULL, &tmo) < 0) { + if (errno != EINTR) { + Error5("Select(%d, &0x%lx, NULL, NULL, {%ld.%ld}): %s", xfd->fd+1, 1<<(xfd->fd+1), + xfd->para.socket.accept_timeout.tv_sec, xfd->para.socket.accept_timeout.tv_usec, + strerror(errno)); + } + } else { + break; + } + } + if (!FD_ISSET(xfd->fd, &rfd)) { + struct sigaction act; + + Warn1("accept: %s", strerror(ETIMEDOUT)); + Close(xfd->fd); + Notice("Waiting for child processes to terminate"); + memset(&act, 0, sizeof(struct sigaction)); + act.sa_flags = SA_NOCLDSTOP/*|SA_RESTART*/ +#ifdef SA_SIGINFO /* not on Linux 2.0(.33) */ + |SA_SIGINFO +#endif +#ifdef SA_NOMASK + |SA_NOMASK +#endif + ; +#if HAVE_STRUCT_SIGACTION_SA_SIGACTION && defined(SA_SIGINFO) + act.sa_sigaction = 0; +#else /* Linux 2.0(.33) does not have sigaction.sa_sigaction */ + act.sa_handler = 0; +#endif + sigemptyset(&act.sa_mask); + Sigaction(SIGCHLD, &act, NULL); + wait(NULL); + Exit(0); + } + } ps = Accept(xfd->fd, (struct sockaddr *)&sa, &salen); if (ps >= 0) { /*0 Info4("accept(%d, %p, {"F_Zu"}) -> %d", xfd->fd, &sa, salen, ps);*/ diff --git a/xio-listen.h b/xio-listen.h index 3e61fa5..58bc53d 100644 --- a/xio-listen.h +++ b/xio-listen.h @@ -9,6 +9,7 @@ extern const struct optdesc opt_backlog; extern const struct optdesc opt_fork; extern const struct optdesc opt_max_children; extern const struct optdesc opt_range; +extern const struct optdesc opt_accept_timeout; int xioopen_listen(struct single *xfd, int xioflags, diff --git a/xio.h b/xio.h index 741328e..beb3188 100644 --- a/xio.h +++ b/xio.h @@ -182,6 +182,9 @@ typedef struct single { #if _WITH_SOCKET struct { struct timeval connect_timeout; /* how long to hang in connect() */ +#if WITH_LISTEN + struct timeval accept_timeout; /* how long to wait for incoming connection */ +#endif union sockaddr_union la; /* local socket address */ bool null_eof; /* with dgram: empty packet means EOF */ bool dorange; diff --git a/xioopts.c b/xioopts.c index 1774e87..08dd7c6 100644 --- a/xioopts.c +++ b/xioopts.c @@ -150,6 +150,7 @@ const struct optname optionnames[] = { #ifdef TCP_ABORT_THRESHOLD /* HP_UX */ IF_TCP ("abort-threshold", &opt_tcp_abort_threshold) #endif + IF_SOCKET ("accept-timeout", &opt_accept_timeout) #ifdef SO_ACCEPTCONN /* AIX433 */ IF_SOCKET ("acceptconn", &opt_so_acceptconn) #endif /* SO_ACCEPTCONN */ @@ -875,6 +876,7 @@ const struct optname optionnames[] = { IF_TCP ("linger2", &opt_tcp_linger2) #endif IF_PTY ("link", &opt_symbolic_link) + IF_SOCKET ("listen-timeout", &opt_accept_timeout) IF_TERMIOS("lnext", &opt_vlnext) #if defined(F_SETLKW) IF_ANY ("lock", &opt_f_setlkw_wr) /* POSIX, first choice */ @@ -2216,7 +2218,7 @@ int parseopts_table(const char **a, unsigned int groups, struct opt **opts, } (*opts)[i].value.u_timeval.tv_sec = val; (*opts)[i].value.u_timeval.tv_usec = - (val-(*opts)[i].value.u_timeval.tv_sec) * 1000000; + (val-(*opts)[i].value.u_timeval.tv_sec+0.0000005) * 1000000; } break; diff --git a/xioopts.h b/xioopts.h index a87b66d..85b0a3b 100644 --- a/xioopts.h +++ b/xioopts.h @@ -436,6 +436,7 @@ enum e_optcode { OPT_IXANY, /* termios.c_iflag */ OPT_IXOFF, /* termios.c_iflag */ OPT_IXON, /* termios.c_iflag */ + OPT_ACCEPT_TIMEOUT, /* listening socket */ OPT_LOCKFILE, OPT_LOWPORT, OPT_MAX_CHILDREN,