diff --git a/CHANGES b/CHANGES index 0968d37..23ba40a 100644 --- a/CHANGES +++ b/CHANGES @@ -8,6 +8,9 @@ corrections: socket using address GOPEN. Thanks to Martin Forssen for bug report and patch. + UDP-LISTEN would alway set SO_REUSEADDR even without fork option and + when user set it to 0. Thanks to Michal Svoboda for reporting this bug. + ####################### V 1.7.1.3: security: diff --git a/test.sh b/test.sh index 7285ae0..f2b922e 100755 --- a/test.sh +++ b/test.sh @@ -5945,6 +5945,63 @@ esac N=$((N+1)) +NAME=UDPLISTENFORK +case "$TESTS" in +*%functions%*|*%ip4%*|*%udp%*|*%listen%*|*%fork%*|*%$NAME%*) +TEST="$NAME: UDP socket rebinds after first connection" +if ! eval $NUMCOND; then :; else +tf="$td/test$N.stdout" +te="$td/test$N.stderr" +tdiff="$td/test$N.diff" +da1="test$N $(date) $RANDOM" +da2="test$N $(date) $RANDOM" +#establish a listening and forking udp socket in background +SRV="$SOCAT $opts -lpserver UDP4-LISTEN:$PORT,bind=$LOCALHOST,fork PIPE" +#make a first and a second connection +CLI="$SOCAT $opts -lpclient - UDP4-CONNECT:$LOCALHOST:$PORT" +$PRINTF "test $F_n $TEST... " $N +eval "$SRV 2>${te}s &" +pids=$! +waitudp4port "$PORT" +echo "$da1" |eval "$CLI" >"${tf}1" 2>"${te}1" +if [ $? -ne 0 ]; then + kill "$pids" 2>/dev/null + $PRINTF "$NO_RESULT (first conn failed):\n" + echo "$SRV &" + echo "$CLI" + cat "${te}s" "${te}1" + numCANT=$((numCANT+1)) +elif ! echo "$da1" |diff - "${tf}1" >"$tdiff"; then + kill "$pids" 2>/dev/null + $PRINTF "$NO_RESULT (first conn failed); diff:\n" + cat "$tdiff" + numCANT=$((numCANT+1)) +else +sleep 2 # UDP-LISTEN sleeps 1s +echo "$da2" |eval "$CLI" >"${tf}2" 2>"${te}2" +rc="$?"; kill "$pids" 2>/dev/null +if [ $rc -ne 0 ]; then + $PRINTF "$FAILED:\n" + echo "$SRV &" + echo "$CLI" + cat "${te}s" "${te}2" + numFAIL=$((numFAIL+1)) +elif ! echo "$da2" |diff - "${tf}2" >"$tdiff"; then + $PRINTF "$FAILED: diff\n" + cat "$tdiff" + numFAIL=$((numFAIL+1)) +else + $PRINTF "$OK\n" + numOK=$((numOK+1)) +fi # !( $? -ne 0) +fi # !(rc -ne 0) +wait +fi ;; # NUMCOND, feats +esac +PORT=$((PORT+1)) +N=$((N+1)) + + NAME=UNIXLISTENFORK case "$TESTS" in *%functions%*|*%unix%*|*%listen%*|*%fork%*|*%$NAME%*) diff --git a/xio-udp.c b/xio-udp.c index 77ffdd6..3f443bc 100644 --- a/xio-udp.c +++ b/xio-udp.c @@ -1,5 +1,5 @@ /* source: xio-udp.c */ -/* Copyright Gerhard Rieger 2001-2008 */ +/* Copyright Gerhard Rieger 2001-2010 */ /* Published under the GNU General Public License V.2, see file COPYING */ /* this file contains the source for handling UDP addresses */ @@ -170,7 +170,8 @@ int xioopen_ipdgram_listen(int argc, const char *argv[], struct opt *opts, while (true) { /* we loop with fork or prohibited packets */ /* now wait for some packet on this datagram socket, get its sender address, connect there, and return */ - int one = 1; + int reuseaddr = dofork; + int doreuseaddr = (dofork != 0); char infobuff[256]; union sockaddr_union _sockname; union sockaddr_union *la = &_sockname; /* local address */ @@ -178,12 +179,17 @@ int xioopen_ipdgram_listen(int argc, const char *argv[], struct opt *opts, if ((fd->stream.fd = xiosocket(opts, pf, socktype, ipproto, E_ERROR)) < 0) { return STAT_RETRYLATER; } + doreuseaddr |= (retropt_int(opts, OPT_SO_REUSEADDR, &reuseaddr) >= 0); applyopts(fd->stream.fd, opts, PH_PASTSOCKET); - if (Setsockopt(fd->stream.fd, opt_so_reuseaddr.major, - opt_so_reuseaddr.minor, &one, sizeof(one)) < 0) { - Warn6("setsockopt(%d, %d, %d, {%d}, "F_Zd"): %s", - fd->stream.fd, opt_so_reuseaddr.major, - opt_so_reuseaddr.minor, one, sizeof(one), strerror(errno)); + if (doreuseaddr) { + if (Setsockopt(fd->stream.fd, opt_so_reuseaddr.major, + opt_so_reuseaddr.minor, &reuseaddr, sizeof(reuseaddr)) + < 0) { + Warn6("setsockopt(%d, %d, %d, {%d}, "F_Zd"): %s", + fd->stream.fd, opt_so_reuseaddr.major, + opt_so_reuseaddr.minor, reuseaddr, sizeof(reuseaddr), + strerror(errno)); + } } applyopts_cloexec(fd->stream.fd, opts); applyopts(fd->stream.fd, opts, PH_PREBIND);