New options so-rcvtimeo, so-sndtimeo

This commit is contained in:
Gerhard Rieger 2023-10-26 16:48:37 +02:00
parent 2db04378ae
commit 9faf068949
7 changed files with 139 additions and 24 deletions

View file

@ -32,6 +32,11 @@ Features:
Added Info log of resulting OpenSSL max fragment length.
Implemented options rcvtimeo and sndtimeo, the first of which may be
useful to prevent endlessly hanging DTLS connection etablishment.
Test: RCVTIMEO_DTLS
Feature proposed by Vladimir Nikishkin.
Corrections:
When a sub process (EXEC, SYSTEM) terminated with exit code other than
0, its last sent data might have been lost depending on timing of read/

View file

@ -608,7 +608,8 @@ label(ADDRESS_OPENSSL_DTLS_CLIENT)dit(bf(tt(OPENSSL-DTLS-CLIENT:<host>:<port>)))
link(bind)(OPTION_BIND),
link(pf)(OPTION_PROTOCOL_FAMILY),
link(sourceport)(OPTION_SOURCEPORT),
link(retry)(OPTION_RETRY)nl()
link(retry)(OPTION_RETRY),
link(rcvtimeo)(OPTION_SO_RCVTIMOE)nl()
See also:
link(OPENSSL-DTLS-SERVER)(ADDRESS_OPENSSL_DTLS_SERVER),
link(OPENSSL-CONNECT)(ADDRESS_OPENSSL_CONNECT),
@ -644,6 +645,7 @@ label(ADDRESS_OPENSSL_DTLS_SERVER)dit(bf(tt(OPENSSL-DTLS-SERVER:<port>)))
link(su)(OPTION_SUBSTUSER),
link(reuseaddr)(OPTION_REUSEADDR),
link(retry)(OPTION_RETRY)nl()
link(rcvtimeo)(OPTION_SO_RCVTIMOE)nl()
See also:
link(OPENSSL-DTLS-CLIENT)(ADDRESS_OPENSSL_DTLS_CLIENT),
link(OPENSSL-LISTEN)(ADDRESS_OPENSSL_LISTEN),
@ -1983,9 +1985,15 @@ label(OPTION_RCVBUF_LATE)dit(bf(tt(rcvbuf-late=<bytes>)))
connected to <bytes> [link(int)(TYPE_INT)].
With TCP sockets, this value corresponds to the socket's
maximal window size.
label(OPTION_RCVLOWAT)dit(bf(tt(rcvlowat=<bytes>)))
Specifies the minimum number of received bytes [link(int)(TYPE_INT)] until
the socket layer will pass the buffered data to socat().
label(OPTION_SO_RCVTIMOE)dit(bf(tt(so-rcvtimeo=<time>, rcvtimeo=<time>)))
Specifies the time [link(int)(TYPE_TIMEVAL)] until code(recv()), code(read())
etc. functions timeout when no data is received. Note that in the transfer
phase socat() only calls these functions when code(select()) has reported
that data is available. However this option is useful with DTLS addresses to
timeout during connection negotiation.
label(OPTION_SO_SNDTIMOE)dit(bf(tt(so-sndtimeo=<time>, sndtimeo=<time>)))
Like link(so-recvtimeo)(OPTION_SO_RCVTIMOE), but for code(send). Not usecase
known.
label(OPTION_REUSEADDR)dit(bf(tt(reuseaddr)))
Allows other sockets to bind to an address even if parts of it (e.g. the
local port) are already in use by socat() (link(example)(EXAMPLE_OPTION_REUSEADDR)).
@ -2896,11 +2904,11 @@ label(OPTION_OPENSSL_EGD)dit(bf(tt(egd=<filename>)))
On some systems, openssl requires an explicit source of random data. Specify
the socket name where an entropy gathering daemon like egd provides random
data, e.g. /dev/egd-pool.
label(OPTION_OPENSSL_MAXFRAGLEN)dit(bf(tt(maxfraglen=<int>, openssl-maxfraglen=<int>)))
label(OPTION_OPENSSL_MAXFRAGLEN)dit(bf(tt(openssl-maxfraglen=<int>, maxfraglen=<int>)))
For client connections, make a Max Fragment Length Negotiation Request to the server to limit the
maximum size fragment the server will send to us. Supported lengths are: 512, 1024, 2048, or
4096. Note that this option is not applicable for link(OPENSSL-LISTEN)(ADDRESS_OPENSSL_LISTEN).
label(OPTION_OPENSSL_MAXSENDFRAG)dit(bf(tt(maxsendfrag=<int>, openssl-maxsendfrag=<int>)))
label(OPTION_OPENSSL_MAXSENDFRAG)dit(bf(tt(openssl-maxsendfrag=<int>, maxsendfrag=<int>)))
Limit the maximum size of the fragment we will send to the other side. Supported length range:
512 - 16384. Note that under link(OPENSSL-LISTEN)(ADDRESS_OPENSSL_LISTEN), the maximum fragment
size may be further limited by the client's Maximum Fragment Length Negotiation Request, if it

111
test.sh
View file

@ -5559,7 +5559,7 @@ elif ! feat=$(testoptions connect-timeout); then
numCANT=$((numCANT+1))
listCANT="$listCANT $N"
else
# we need a hanging connection attempt, guess an address for this
# We need a hanging connection attempt, guess an address for this
case "$UNAME" in
Linux) HANGIP=1.0.0.1 ;;
*) HANGIP=255.255.255.254 ;;
@ -5569,31 +5569,35 @@ 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
#$TRACE $SOCAT $opts - tcp:$HANGIP:1 >"$te1" 2>&1 </dev/null &
CMD="$TRACE $SOCAT $opts - tcp:$HANGIP:1"
$CMD >"$te1" 2>&1 </dev/null &
# First, try to make socat hang and see if it can be killed
CMD="$TRACE $SOCAT $opts - TCP:$HANGIP:1"
$CMD >"$te1" 2>$te1 </dev/null &
pid1=$!
sleep 2
if ! kill $pid1 2>"$tk1"; then
$PRINTF "${YELLOW}does not hang${NORMAL}\n"
echo "$CMD" >&2
cat "$te1" >&2
numCANT=$((numCANT+1))
listCANT="$listCANT $N"
else
# second, set connect-timeout and see if socat exits before kill
$TRACE $SOCAT $opts - tcp:$HANGIP:1,connect-timeout=1.0 >"$te2" 2>&1 </dev/null &
# Second, set connect-timeout and see if socat exits before kill
CMD="$TRACE $SOCAT $opts - TCP:$HANGIP:1,connect-timeout=1.0"
$CMD >"$te1" 2>$te2 </dev/null &
pid2=$!
sleep 2
if kill $pid2 2>"$tk2"; then
$PRINTF "$FAILED\n"
echo "$CMD"
cat "$te1"
cat "$te2"
echo "$CMD" >&2
cat "$te2" >&2
numFAIL=$((numFAIL+1))
listFAIL="$listFAIL $N"
else
$PRINTF "$OK\n"
numOK=$((numOK+1))
if [ "$VERBOSE" ]; then
echo "$CMD" >&2
fi
numOK=$((numOK+1))
fi
fi
wait
@ -15169,7 +15173,7 @@ elif ! $(type proxyecho.sh >/dev/null 2>&1); then
numCANT=$((numCANT+1))
listCANT="$listCANT $N"
elif ! F=$(testfeats IP4 TCP LISTEN EXEC STDIO PROXY); then
$PRINTF "test $F_n $TEST... ${YELLOW}Feature $F not available${NORMAL}\n" $N
$PRINTF "test $F_n $TEST... ${YELLOW}Feature $F not configured${NORMAL}\n" $N
numCANT=$((numCANT+1))
listCANT="$listCANT $N"
elif ! A=$(testaddrs TCP4-LISTEN EXEC STDIO PROXY-CONNECT); then
@ -15199,12 +15203,12 @@ waittcp4port $PORT 1
echo "$da" |$CMD1 >"$tf" 2>"${te}0"
if ! echo "$da" |diff - "$tf" >"$tdiff"; then
$PRINTF "$FAILED: $TRACE $SOCAT:\n"
echo "$CMD0 &" >&2
cat "${te}0"
echo "$CMD1" >&2
cat "${te}1"
echo "diff:"
cat "$tdiff"
echo "$CMD0 &"
cat "${te}0" >&2
echo "$CMD1"
cat "${te}1" >&2
echo "diff:" >&2
cat "$tdiff" >&2
numFAIL=$((numFAIL+1))
listFAIL="$listFAIL $N"
else
@ -15222,6 +15226,77 @@ esac
PORT=$((PORT+1))
# Test the so-rcvtimeo address option with DTLS
NAME=RCVTIMEO_DTLS
case "$TESTS" in
*%$N%*|*%functions%*|*%ip4%*|*%ipapp%*|*%udp%*|*%timeout%*|*%openssl%*|*%dtls%*|*%$NAME%*)
TEST="$NAME: test the so-rcvtimeo option with DTLS"
if ! eval $NUMCOND; then :;
elif ! F=$(testfeats STDIO OPENSSL); then
$PRINTF "test $F_n $TEST... ${YELLOW}Feature $F not configured in $SOCAT${NORMAL}\n" $N
numCANT=$((numCANT+1))
listCANT="$listCANT $N"
elif ! A=$(testaddrs STDIO DTLS); then
$PRINTF "test $F_n $TEST... ${YELLOW}Address $A not available in $SOCAT${NORMAL}\n" $N
numCANT=$((numCANT+1))
listCANT="$listCANT $N"
elif ! o=$(testoptions verify so-rcvtimeo) >/dev/null; then
$PRINTF "test $F_n $TEST... ${YELLOW}Option $o not available in $SOCAT${NORMAL}\n" $N
numCANT=$((numCANT+1))
listCANT="$listCANT $N"
elif ! runsip4 >/dev/null; then
$PRINTF "test $F_n $TEST... ${YELLOW}IPv4 not available${NORMAL}\n" $N
numCANT=$((numCANT+1))
listCANT="$listCANT $N"
else
# We need a hanging connection attempt, guess an address for this
HANGIP=0.0.0.1
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 - DTLS:$HANGIP:1,verify=0"
$CMD1 >"$te1" 2>$te1 </dev/null &
pid1=$!
sleep 2
if ! kill -0 $pid1 2>"$tk1"; then
$PRINTF "${YELLOW}does not hang${NORMAL}\n"
if [ "$VERBOSE" ]; then echo "$CMD1 &"; fi
if [ "$DEBUG" ]; then cat "${te}1" >&2; fi
numCANT=$((numCANT+1))
listCANT="$listCANT $N"
wait
else
# DTLS restarts read() a few times
while kill $pid1 2>/dev/null; do :; done
# Second, set so-rcvtimeo and see if Socat exits before kill
CMD2="$TRACE $SOCAT $opts - DTLS:$HANGIP:1,verify=0,so-rcvtimeo=1.0"
$CMD2 >"$te1" 2>$te2 </dev/null &
pid2=$!
sleep 3 # in OpenSSL 1.1.1f DTLS takes two timeouts
if kill $pid2 2>"$tk2"; then
$PRINTF "$FAILED\n"
echo "$CMD2"
cat "$te2" >&2
numFAIL=$((numFAIL+1))
listFAIL="$listFAIL $N"
while kill $pid2 2>/dev/null; do :; done
wait
else
$PRINTF "$OK\n"
if [ "$VERBOSE" ]; then echo "$CMD2 &"; fi
if [ "$DEBUG" ]; then cat "${te}2" >&2; fi
numOK=$((numOK+1))
fi
fi
wait
fi ;; # testfeats, NUMCOND
esac
N=$((N+1))
# end of common tests
##################################################################################

View file

@ -119,6 +119,13 @@ const struct optdesc opt_so_sndlowat = { "so-sndlowat", "sndlowat", OPT_SO_SNDL
#endif
/* end of setsockopt options of UNIX98 standard */
#ifdef SO_RCVTIMEO
const struct optdesc opt_so_rcvtimeo = { "so-rcvtimeo", "rcvtimeo", OPT_SO_RCVTIMEO, GROUP_SOCKET, PH_PASTSOCKET, TYPE_TIMEVAL, OFUNC_SOCKOPT, SOL_SOCKET, SO_RCVTIMEO };
#endif
#ifdef SO_SNDTIMEO
const struct optdesc opt_so_sndtimeo = { "so-sndtimeo", "sndtimeo", OPT_SO_SNDTIMEO, GROUP_SOCKET, PH_PASTSOCKET, TYPE_TIMEVAL, OFUNC_SOCKOPT, SOL_SOCKET, SO_SNDTIMEO };
#endif
#ifdef SO_AUDIT /* AIX 4.3.3 */
const struct optdesc opt_so_audit = { "so-audit", "audit", OPT_SO_AUDIT, GROUP_SOCKET, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_SOCKET, SO_AUDIT };
#endif /* SO_AUDIT */

View file

@ -44,6 +44,8 @@ extern const struct optdesc opt_so_type;
extern const struct optdesc opt_so_dontroute;
extern const struct optdesc opt_so_rcvlowat;
extern const struct optdesc opt_so_sndlowat;
extern const struct optdesc opt_so_rcvtimeo;
extern const struct optdesc opt_so_sndtimeo;
extern const struct optdesc opt_so_audit;
extern const struct optdesc opt_so_attach_filter;
extern const struct optdesc opt_so_detach_filter;

View file

@ -1310,6 +1310,9 @@ const struct optname optionnames[] = {
IF_SOCKET ("rcvbuf-late", &opt_so_rcvbuf_late)
#ifdef SO_RCVLOWAT
IF_SOCKET ("rcvlowat", &opt_so_rcvlowat)
#endif
#ifdef SO_RCVTIMEO
IF_SOCKET ("rcvtimeo", &opt_so_rcvtimeo)
#endif
IF_OPEN ("rdonly", &opt_o_rdonly)
IF_OPEN ("rdwr", &opt_o_rdwr)
@ -1485,6 +1488,9 @@ const struct optname optionnames[] = {
#ifdef SO_SNDLOWAT
IF_SOCKET ("sndlowat", &opt_so_sndlowat)
#endif
#ifdef SO_SNDTIMEO
IF_SOCKET ("sndtimeo", &opt_so_sndtimeo)
#endif
#if defined(HAVE_SSL_set_tlsext_host_name) || defined(SSL_set_tlsext_host_name)
IF_OPENSSL("snihost", &opt_openssl_snihost)
#endif
@ -1548,6 +1554,9 @@ const struct optname optionnames[] = {
IF_SOCKET ("so-rcvbuf-late", &opt_so_rcvbuf_late)
#ifdef SO_RCVLOWAT
IF_SOCKET ("so-rcvlowat", &opt_so_rcvlowat)
#endif
#ifdef SO_RCVTIMEO
IF_SOCKET ("so-rcvtimeo", &opt_so_rcvtimeo)
#endif
IF_SOCKET ("so-reuseaddr", &opt_so_reuseaddr)
#ifdef SO_REUSEPORT /* AIX 4.3.3 */
@ -1567,6 +1576,9 @@ const struct optname optionnames[] = {
#ifdef SO_SNDLOWAT
IF_SOCKET ("so-sndlowat", &opt_so_sndlowat)
#endif
#ifdef SO_SNDTIMEO
IF_SOCKET ("so-sndtimeo", &opt_so_sndtimeo)
#endif
#ifdef SO_TIMESTAMP
IF_SOCKET ("so-timestamp", &opt_so_timestamp)
#endif

View file

@ -701,6 +701,9 @@ enum e_optcode {
OPT_SO_RCVBUF_LATE,
#ifdef SO_RCVLOWAT
OPT_SO_RCVLOWAT,
#endif
#ifdef SO_RCVTIMEO
OPT_SO_RCVTIMEO,
#endif
OPT_SO_REUSEADDR,
#ifdef SO_REUSEPORT
@ -719,6 +722,9 @@ enum e_optcode {
OPT_SO_SNDBUF_LATE,
#ifdef SO_SNDLOWAT
OPT_SO_SNDLOWAT,
#endif
#ifdef SO_SNDTIMEO
OPT_SO_SNDTIMEO,
#endif
OPT_SO_TIMESTAMP, /* Linux */
OPT_SO_TYPE,