From 6ccae440f1a1cce0b25ca7cb27576360621b5616 Mon Sep 17 00:00:00 2001 From: Gerhard Rieger Date: Fri, 21 Mar 2014 09:44:16 +0100 Subject: [PATCH] LISTEN based addresses applied some address options to the listening FD instead of the connected FD --- CHANGES | 5 ++++ DEVELOPMENT | 5 ++-- bin/predialog.sh | 12 +++++++-- test.sh | 64 ++++++++++++++++++++++++++++++++++++++++++++++-- xio-ip6.c | 4 +-- xio-listen.c | 10 ++++---- xio-socket.c | 2 +- 7 files changed, 88 insertions(+), 14 deletions(-) diff --git a/CHANGES b/CHANGES index 06802e5..38f75d1 100644 --- a/CHANGES +++ b/CHANGES @@ -1,5 +1,10 @@ corrections: + LISTEN based addresses applied some address options, e.g. so-keepalive, + to the listening file descriptor instead of the connected file + descriptor + Thanks to Ulises Alonso for reporting this bug + make failed after configure with non gcc compiler due to missing include. Thanks to Horacio Mijail for reporting this problem diff --git a/DEVELOPMENT b/DEVELOPMENT index 3564ed8..4817956 100644 --- a/DEVELOPMENT +++ b/DEVELOPMENT @@ -131,13 +131,12 @@ PH_LATE FD is ready, before start of data loop PH_LATE2 FD is ready, dropping privileges -SOCKET with LISTEN and FORK: +SOCKET with LISTEN and ACCEPT: PH_INIT retrieving info from original state PH_EARLY before any other processing PH_PRESOCKET before socket call PH_SOCKET for socket call -PH_PASTSOCKET after socket call PH_PREBIND before socket bind() PH_BIND during socket bind() PH_PASTBIND past socket bind() @@ -147,7 +146,9 @@ PH_PASTLISTEN after listen() PH_PREACCEPT before accept() PH_ACCEPT during accept() PH_PASTACCEPT after accept() +# and the following on the new FD: PH_FD soon after FD creation or identification +PH_PASTSOCKET after socket call PH_CONNECTED phase common with connect PH_PREFORK before forking PH_FORK during fork() diff --git a/bin/predialog.sh b/bin/predialog.sh index d0ba39f..5f40d96 100755 --- a/bin/predialog.sh +++ b/bin/predialog.sh @@ -1,6 +1,6 @@ #! /bin/bash # source: predialog.sh -# Copyright Gerhard Rieger 2009 +# Copyright Gerhard Rieger # Published under the GNU General Public License V.2, see file COPYING # This is an example script that shows how to write a script for use with socat @@ -15,6 +15,14 @@ RINFD=3 ROUTFD=4 +if [ -z "$SOCAT" ]; then + if type socat2 >/dev/null 2>&1; then + SOCAT=socat2 + else + SOCAT="./socat" + fi +fi + verbose= # parse options SPACES=" " @@ -50,4 +58,4 @@ wait msg "starting data transfer" # now just pass traffic in both directions #SOCAT_OPTS="-lu -d -d -d -d" -exec socat $SOCAT_OPTS - "fd:$ROUTFD:$RINFD" +exec $SOCAT $SOCAT_OPTS - "fd:$ROUTFD:$RINFD" diff --git a/test.sh b/test.sh index 885ec2f..91db0c0 100755 --- a/test.sh +++ b/test.sh @@ -2835,10 +2835,13 @@ esac PORT=$((PORT+1)) N=$((N+1)) +# TCP6-LISTEN may also listen for IPv4 connections. Test if option +# ipv6-v6only=0 shows this behaviour. NAME=IPV6ONLY0 case "$TESTS" in *%$N%*|*%functions%*|*%ip6%*|*%ipapp%*|*%tcp%*|*%$NAME%*) TEST="$NAME: option ipv6-v6only=0 listens on IPv4" +# create a listening TCP6 socket and try to connect to the port using TCP4 if ! eval $NUMCOND; then :; elif ! testaddrs tcp ip4 >/dev/null || ! runsip4 >/dev/null; then $PRINTF "test $F_n $TEST... ${YELLOW}TCP4 not available${NORMAL}\n" $N @@ -2856,8 +2859,8 @@ tdiff="$td/test$N.diff" tsl=$PORT ts="127.0.0.1:$tsl" da="test$N $(date) $RANDOM" -CMD1="$SOCAT $opts TCP6-listen:$tsl,ipv6-v6only=0,reuseaddr PIPE" -CMD2="$SOCAT $opts stdout%stdin TCP4:$ts" +CMD1="$SOCAT $opts TCP6-LISTEN:$tsl,ipv6-v6only=0,reuseaddr PIPE" +CMD2="$SOCAT $opts STDOUT%STDIN TCP4:$ts" printf "test $F_n $TEST... " $N $CMD1 >"$tf" 2>"${te}1" & pid=$! # background process id @@ -2886,6 +2889,8 @@ esac PORT=$((PORT+1)) N=$((N+1)) +# TCP6-LISTEN may also listen for IPv4 connections. Test if option +# ipv6-v6only=1 turns off this behaviour. NAME=IPV6ONLY1 case "$TESTS" in *%$N%*|*%functions%*|*%ip6%*|*%ipapp%*|*%tcp%*|*%$NAME%*) @@ -10559,6 +10564,8 @@ SOL_SOCKET="$($PROCAN -c |grep "^#define[[:space:]]*SOL_SOCKET[[:space:]]" |cut SO_REUSEADDR="$($PROCAN -c |grep "^#define[[:space:]]*SO_REUSEADDR[[:space:]]" |cut -d' ' -f3)" # test the generic setsockopt-int option +if false; then +# this test no longer works due to fix for options on listening sockets NAME=SETSOCKOPT_INT case "$TESTS" in *%$N%*|*%functions%*|*%ip4%*|*%tcp%*|*%generic%*|*%$NAME%*) @@ -10628,6 +10635,8 @@ fi # NUMCOND, SO_REUSEADDR esac PORT=$((PORT+1)) N=$((N+1)) +# +fi NAME=SCTP4STREAM @@ -11562,6 +11571,57 @@ N=$((N+1)) fi # false +# LISTEN addresses in socat up to 1.7.2.1 and 2.0.0-b7 applied many file +# descriptor, socket, and TCP options only to the listening socket instead of the +# connection socket. +NAME=LISTEN_KEEPALIVE +case "$TESTS" in +*%functions%*|*%bugs%*|*%listen%*|*%keepalive%*|*%socket%*|*%$NAME%*) +TEST="$NAME: keepalive option is applied to connection socket" +# instance 0 has TCP-LISTEN with option so-keepalive and invokes filan after +# accept(). filan writes its output to the socket. instance 1 connects to +# instance 0. The value of the sockets so-keepalive option is checked, it must +# be 1 +if ! eval $NUMCOND; then :; else +tf="$td/test$N.stdout" +te="$td/test$N.stderr" +#tdiff="$td/test$N.diff" +#da="test$N $(date) $RANDOM" +CMD0="$SOCAT $opts TCP4-LISTEN:$PORT,reuseaddr,so-keepalive EXEC:\"$FILAN -i 1\",nofork" +CMD1="$SOCAT $opts - TCP4:$LOCALHOST:$PORT" +printf "test $F_n $TEST... " $N +eval $CMD0 >/dev/null 2>"${te}0" & +pid0=$! +waittcp4port $PORT 1 +$CMD1 >"${tf}1" 2>"${te}1" +KEEPALIVE="$(cat "${tf}1" |tail -n +2 |sed -e "s/.*KEEPALIVE=//" -e "s/[[:space:]].*//")" +rc1=$? +kill $pid0 2>/dev/null; wait +if [ -z "$KEEPALIVE" ]; then + $PRINTF "$NO_RESULT\n" + echo "$CMD0 &" + echo "$CMD1" + cat "${te}0" + cat "${te}1" + numWARN=$((numWARN+1)) +elif [ "$KEEPALIVE" = "1" ]; then + $PRINTF "$OK\n"; + numOK=$((numOK+1)) +else + $PRINTF "$FAILED\n" + echo "$CMD0 &" + echo "$CMD1" + cat "${te}0" + cat "${te}1" + numFAIL=$((numFAIL+1)) +fi +fi # NUMCOND + ;; +esac +PORT=$((PORT+1)) +N=$((N+1)) + + ############################################################################### # here come tests that might affect your systems integrity. Put normal tests # before this paragraph. diff --git a/xio-ip6.c b/xio-ip6.c index 147e954..6ca44a1 100644 --- a/xio-ip6.c +++ b/xio-ip6.c @@ -1,5 +1,5 @@ /* source: xio-ip6.c */ -/* Copyright Gerhard Rieger 2001-2012 */ +/* Copyright Gerhard Rieger */ /* Published under the GNU General Public License V.2, see file COPYING */ /* this file contains the source for IP6 related functions */ @@ -20,7 +20,7 @@ static char *inet6addr_info(const struct in6_addr *sa, char *buff, size_t blen); #ifdef IPV6_V6ONLY -const struct optdesc opt_ipv6_v6only = { "ipv6-v6only", "ipv6only", OPT_IPV6_V6ONLY, GROUP_SOCK_IP6, PH_PASTSOCKET, TYPE_BOOL, OFUNC_SOCKOPT, SOL_IPV6, IPV6_V6ONLY }; +const struct optdesc opt_ipv6_v6only = { "ipv6-v6only", "ipv6only", OPT_IPV6_V6ONLY, GROUP_SOCK_IP6, PH_PREBIND, TYPE_BOOL, OFUNC_SOCKOPT, SOL_IPV6, IPV6_V6ONLY }; #endif #ifdef IPV6_JOIN_GROUP const struct optdesc opt_ipv6_join_group = { "ipv6-join-group", "join-group", OPT_IPV6_JOIN_GROUP, GROUP_SOCK_IP6, PH_PASTBIND, TYPE_IP_MREQN, OFUNC_SOCKOPT, SOL_IPV6, IPV6_JOIN_GROUP }; diff --git a/xio-listen.c b/xio-listen.c index cf00ef9..4d4f0c6 100644 --- a/xio-listen.c +++ b/xio-listen.c @@ -148,8 +148,6 @@ int _xioopen_listen(struct single *xfd, int xioflags, struct sockaddr *us, sockl return STAT_RETRYLATER; } - applyopts(xfd->rfd, opts, PH_PASTSOCKET); - applyopts_cloexec(xfd->rfd, opts); applyopts(xfd->rfd, opts, PH_PREBIND); @@ -184,6 +182,7 @@ int _xioopen_listen(struct single *xfd, int xioflags, struct sockaddr *us, sockl } #endif /* WITH_UNIX */ + applyopts(xfd->rfd, opts, PH_PRELISTEN); retropt_int(opts, OPT_BACKLOG, &backlog); if (Listen(xfd->rfd, backlog) < 0) { Error3("listen(%d, %d): %s", xfd->rfd, backlog, strerror(errno)); @@ -284,9 +283,6 @@ int _xioopen_listen(struct single *xfd, int xioflags, struct sockaddr *us, sockl sockaddr_info((struct sockaddr *)pa, pas, infobuff, sizeof(infobuff))); - applyopts(xfd->rfd, opts, PH_FD); - applyopts(xfd->rfd, opts, PH_CONNECTED); - if (dofork) { pid_t pid; /* mostly int; only used with fork */ sigset_t mask_sigchld; @@ -352,6 +348,10 @@ int _xioopen_listen(struct single *xfd, int xioflags, struct sockaddr *us, sockl break; } } + + applyopts(xfd->rfd, opts, PH_FD); + applyopts(xfd->rfd, opts, PH_PASTSOCKET); + applyopts(xfd->rfd, opts, PH_CONNECTED); if ((result = _xio_openlate(xfd, opts)) < 0) return result; diff --git a/xio-socket.c b/xio-socket.c index beae1ba..4b7d8a4 100644 --- a/xio-socket.c +++ b/xio-socket.c @@ -115,7 +115,7 @@ const struct optdesc opt_so_debug = { "so-debug", "debug", OPT_SO_DEBUG, const struct optdesc opt_so_acceptconn={ "so-acceptconn","acceptconn",OPT_SO_ACCEPTCONN,GROUP_SOCKET,PH_PASTSOCKET,TYPE_INT, OFUNC_SOCKOPT, SOL_SOCKET, SO_ACCEPTCONN}; #endif /* SO_ACCEPTCONN */ const struct optdesc opt_so_broadcast= { "so-broadcast", "broadcast", OPT_SO_BROADCAST,GROUP_SOCKET, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_SOCKET, SO_BROADCAST}; -const struct optdesc opt_so_reuseaddr= { "so-reuseaddr", "reuseaddr", OPT_SO_REUSEADDR,GROUP_SOCKET, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_SOCKET, SO_REUSEADDR}; +const struct optdesc opt_so_reuseaddr= { "so-reuseaddr", "reuseaddr", OPT_SO_REUSEADDR,GROUP_SOCKET, PH_PREBIND, TYPE_INT, OFUNC_SOCKOPT, SOL_SOCKET, SO_REUSEADDR}; const struct optdesc opt_so_keepalive= { "so-keepalive", "keepalive", OPT_SO_KEEPALIVE,GROUP_SOCKET, PH_FD, TYPE_INT, OFUNC_SOCKOPT, SOL_SOCKET, SO_KEEPALIVE}; #if HAVE_STRUCT_LINGER const struct optdesc opt_so_linger = { "so-linger", "linger", OPT_SO_LINGER, GROUP_SOCKET, PH_PASTSOCKET, TYPE_LINGER,OFUNC_SOCKOPT,SOL_SOCKET, SO_LINGER };