diff --git a/CHANGES b/CHANGES index ecd8445..f7c37b3 100644 --- a/CHANGES +++ b/CHANGES @@ -1,7 +1,13 @@ new features: - new address options shut-none, shut-down, and shut-close allow to - control socat's half close behaviour + address options shut-none, shut-down, and shut-close allow to control + socat's half close behaviour + + with address option shut-null socat sends an empty packet to the peer + to indicate EOF + + option null-eof changes the behaviour of sockets that receive an empty + packet to see EOF instead of ignoring it ####################### V 1.7.0.1: diff --git a/VERSION b/VERSION index ac13dd8..447515f 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -"1.7.0.1" +"1.7.0.1+shutnull" diff --git a/doc/socat.yo b/doc/socat.yo index 870a87e..590e384 100644 --- a/doc/socat.yo +++ b/doc/socat.yo @@ -1490,6 +1490,16 @@ label(OPTION_SHUT_DOWN)dit(bf(tt(shut-down))) label(OPTION_SHUT_CLOSE)dit(bf(tt(shut-close))) Changes the (address dependent) method of shutting down the write part of a connection to tt(close\(fd)). +label(OPTION_SHUT_NULL)dit(bf(tt(shut-null))) + When one address indicates EOF, socat() will send a zero sized packet to the + write channel of the other address to transfer the EOF condition. This is + useful with UDP and other datagram protocols. Has been tested against + netcat and socat with option link(null-eof)(OPTION_NULL_EOF). +label(OPTION_NULL_EOF)dit(bf(tt(null-eof))) + Normally socat() will ignore empty (zero size payload) packets arriving on + datagram sockets, so it survives port scans. With this option socat() + interprets empty datagram packets as EOF indicator (see + link(shut-null)(OPTION_SHUT_NULL). label(OPTION_IOCTL_VOID)dit(bf(tt(ioctl-void=))) Calls tt(ioctl()) with the request value as second argument and NULL as third argument. This option allows to utilize ioctls that are not diff --git a/test.sh b/test.sh index 5e366b6..d8b7404 100755 --- a/test.sh +++ b/test.sh @@ -7558,6 +7558,58 @@ esac N=$((N+1)) +# test the shut-null and null-eof options +NAME=SHUTNULLEOF +case "$TESTS" in +*%functions%*|*%socket%*|*%$NAME%*) +TEST="$NAME: options shut-null and null-eof" +# run a receiving background process with option null-eof. +# start a sending process with option shut-null that sends a test record to the +# receiving process and then terminates. +# send another test record. +# whe the receiving process just got the first test record the test succeeded +if ! eval $NUMCOND; then :; else +tf="$td/test$N.stout" +te="$td/test$N.stderr" +tdiff="$td/test$N.diff" +da="test$N $(date) $RANDOM" +CMD0="$SOCAT $opts -u UDP-RECV:$PORT,null-eof CREAT:$tf" +CMD1="$SOCAT $opts -u - UDP-SENDTO:127.0.0.1:$PORT,shut-null" +printf "test $F_n $TEST... " $N +$CMD0 >/dev/null 2>"${te}0" & +pid0=$! +waitudp4port $PORT 1 +echo "$da" |$CMD1 >"${tf}1" 2>"${te}1" +rc1=$? +echo "xyz" |$CMD1 >"${tf}2" 2>"${te}2" +rc2=$? +kill $pid0 2>/dev/null; wait +if [ $rc1 != 0 -o $rc2 != 0 ]; then + $PRINTF "$FAILED\n" + echo "$CMD0 &" + echo "$CMD1" + cat "${te}0" + cat "${te}1" + cat "${te}2" + numFAIL=$((numFAIL+1)) +elif echo "$da" |diff - "${tf}" >"$tdiff"; then + $PRINTF "$OK\n" + numOK=$((numOK+1)) +else + $PRINTF "$FAILED\n" + echo "$CMD0 &" + echo "$CMD1" + cat "${te}0" + cat "${te}1" + cat "${tdiff}" + numFAIL=$((numFAIL+1)) +fi +fi # NUMCOND + ;; +esac +N=$((N+1)) + + NAME=UDP6LISTENBIND # this tests for a bug in (up to) 1.5.0.0: # with udp*-listen, the bind option supported only IPv4 diff --git a/xio-fd.c b/xio-fd.c index 504babd..0d4b6b9 100644 --- a/xio-fd.c +++ b/xio-fd.c @@ -78,6 +78,7 @@ const struct optdesc opt_end_close = { "end-close", "close", OPT_END_CLOSE, GROU const struct optdesc opt_shut_none = { "shut-none", NULL, OPT_SHUT_NONE, GROUP_FD, PH_INIT, TYPE_BOOL, OFUNC_OFFSET, (bool)&((xiofile_t *)0)->stream.howtoshut, XIOSHUT_NONE }; const struct optdesc opt_shut_down = { "shut-down", NULL, OPT_SHUT_DOWN, GROUP_FD, PH_INIT, TYPE_BOOL, OFUNC_OFFSET, (bool)&((xiofile_t *)0)->stream.howtoshut, XIOSHUT_DOWN }; const struct optdesc opt_shut_close= { "shut-close", NULL, OPT_SHUT_CLOSE, GROUP_FD, PH_INIT, TYPE_BOOL, OFUNC_OFFSET, (bool)&((xiofile_t *)0)->stream.howtoshut, XIOSHUT_CLOSE }; +const struct optdesc opt_shut_null = { "shut-null", NULL, OPT_SHUT_NULL, GROUP_FD, PH_INIT, TYPE_CONST, OFUNC_OFFSET, (bool)&((xiofile_t *)0)->stream.howtoshut, XIOSHUT_NULL }; /****** generic ioctl() options ******/ const struct optdesc opt_ioctl_void = { "ioctl-void", "ioctl", OPT_IOCTL_VOID, GROUP_FD, PH_FD, TYPE_INT, OFUNC_IOCTL_GENERIC, 0, 0, 0 }; diff --git a/xio-fd.h b/xio-fd.h index 87a3d24..4b74b55 100644 --- a/xio-fd.h +++ b/xio-fd.h @@ -45,6 +45,7 @@ extern const struct optdesc opt_end_close; extern const struct optdesc opt_shut_none; extern const struct optdesc opt_shut_down; extern const struct optdesc opt_shut_close; +extern const struct optdesc opt_shut_null; extern const struct optdesc opt_streams_i_push; #endif /* !defined(__xio_fd_h_included) */ diff --git a/xio-socket.c b/xio-socket.c index e8139a0..b58cf56 100644 --- a/xio-socket.c +++ b/xio-socket.c @@ -196,6 +196,8 @@ const struct optdesc opt_setsockopt_int = { "setsockopt-int", "sockopt-int const struct optdesc opt_setsockopt_bin = { "setsockopt-bin", "sockopt-bin", OPT_SETSOCKOPT_BIN, GROUP_SOCKET,PH_PASTSOCKET,TYPE_INT_INT_BIN, OFUNC_SOCKOPT_GENERIC, 0, 0 }; const struct optdesc opt_setsockopt_string = { "setsockopt-string", "sockopt-string", OPT_SETSOCKOPT_STRING, GROUP_SOCKET,PH_PASTSOCKET,TYPE_INT_INT_STRING, OFUNC_SOCKOPT_GENERIC, 0, 0 }; +const struct optdesc opt_null_eof = { "null-eof", NULL, OPT_NULL_EOF, GROUP_SOCKET, PH_INIT, TYPE_BOOL, OFUNC_OFFSET, (bool)&((xiofile_t *)0)->stream.para.socket.null_eof }; + #if WITH_GENERICSOCKET diff --git a/xio-socket.h b/xio-socket.h index 40e214d..a4bef66 100644 --- a/xio-socket.h +++ b/xio-socket.h @@ -1,5 +1,5 @@ /* source: xio-socket.h */ -/* Copyright Gerhard Rieger 2001-2008 */ +/* Copyright Gerhard Rieger 2001-2009 */ /* Published under the GNU General Public License V.2, see file COPYING */ #ifndef __xio_socket_h_included @@ -67,6 +67,7 @@ extern const struct optdesc opt_protocol_family; extern const struct optdesc opt_setsockopt_int; extern const struct optdesc opt_setsockopt_bin; extern const struct optdesc opt_setsockopt_string; +extern const struct optdesc opt_null_eof; extern diff --git a/xio.h b/xio.h index be7e46f..5419826 100644 --- a/xio.h +++ b/xio.h @@ -147,10 +147,11 @@ typedef struct single { char *unlink_close; /* name of a symlink or unix socket to be removed */ int dtype; enum { - XIOSHUT_UNSPEC, /* fall back to old (up to 1.7.0.0) behaviour */ + XIOSHUT_UNSPEC, /* standard (address dependent) behaviour */ XIOSHUT_NONE, /* do nothing on shutdown */ XIOSHUT_CLOSE, /* close the FD */ - XIOSHUT_DOWN /* shutdown() */ + XIOSHUT_DOWN, /* shutdown() */ + XIOSHUT_NULL /* send an empty packet (dgram socket) */ } howtoshut; enum { END_UNSPEC, /* after init, when no end-close... option */ @@ -183,7 +184,7 @@ typedef struct single { struct { struct timeval connect_timeout; /* how long to hang in connect() */ union sockaddr_union la; /* local socket address */ - bool emptyiseof; /* with dgram: empty packet means EOF */ + bool null_eof; /* with dgram: empty packet means EOF */ bool dorange; struct xiorange range; /* restrictions for peer address */ #if _WITH_IP4 || _WITH_IP6 diff --git a/xioopts.c b/xioopts.c index 67127c9..3f1e1e5 100644 --- a/xioopts.c +++ b/xioopts.c @@ -926,6 +926,7 @@ const struct optname optionnames[] = { #ifdef O_NSHARE IF_OPEN ("nshare", &opt_o_nshare) #endif + IF_SOCKET ("null-eof", &opt_null_eof) #ifdef O_ASYNC IF_ANY ("o-async", &opt_async) #endif @@ -1316,6 +1317,7 @@ const struct optname optionnames[] = { IF_ANY ("shut-close", &opt_shut_close) IF_ANY ("shut-down", &opt_shut_down) IF_ANY ("shut-none", &opt_shut_none) + IF_ANY ("shut-null", &opt_shut_null) #if WITH_EXEC || WITH_SYSTEM IF_ANY ("sid", &opt_setsid) #endif diff --git a/xioopts.h b/xioopts.h index 613ea24..c111dd6 100644 --- a/xioopts.h +++ b/xioopts.h @@ -444,6 +444,7 @@ enum e_optcode { OPT_NOFLSH, /* termios.c_lflag */ OPT_NOFORK, /* exec, system */ OPT_NOPROMPT, /* readline */ + OPT_NULL_EOF, /* receiving empty packet triggers EOF */ #ifdef OCRNL OPT_OCRNL, /* termios.c_oflag */ #endif @@ -593,6 +594,7 @@ enum e_optcode { OPT_SHUT_CLOSE, OPT_SHUT_DOWN, OPT_SHUT_NONE, + OPT_SHUT_NULL, /* send 0 bytes on shutdown */ OPT_SIGHUP, OPT_SIGINT, OPT_SIGQUIT, diff --git a/xioread.c b/xioread.c index 01ffa50..04db1eb 100644 --- a/xioread.c +++ b/xioread.c @@ -1,5 +1,5 @@ /* source: xioread.c */ -/* Copyright Gerhard Rieger 2001-2008 */ +/* Copyright Gerhard Rieger 2001-2009 */ /* Published under the GNU General Public License V.2, see file COPYING */ /* this is the source of the extended read function */ @@ -159,7 +159,7 @@ ssize_t xioread(xiofile_t *file, void *buff, size_t bufsiz) { bytes, sockaddr_info(&from.soa, fromlen, infobuff, sizeof(infobuff))); if (bytes == 0) { - if (!pipe->para.socket.emptyiseof) { + if (!pipe->para.socket.null_eof) { errno = EAGAIN; return -1; } return bytes; @@ -350,7 +350,7 @@ ssize_t xioread(xiofile_t *file, void *buff, size_t bufsiz) { bytes, sockaddr_info(&from.soa, fromlen, infobuff, sizeof(infobuff))); if (bytes == 0) { - if (!pipe->para.socket.emptyiseof) { + if (!pipe->para.socket.null_eof) { errno = EAGAIN; return -1; } return bytes; diff --git a/xioshutdown.c b/xioshutdown.c index a66bba5..69621cf 100644 --- a/xioshutdown.c +++ b/xioshutdown.c @@ -35,6 +35,7 @@ int xioshutdown(xiofile_t *sock, int how) { } switch (sock->stream.howtoshut) { + char writenull; case XIOSHUT_NONE: return 0; case XIOSHUT_CLOSE: @@ -49,9 +50,16 @@ int xioshutdown(xiofile_t *sock, int how) { sock->stream.fd, how, strerror(errno)); } return 0; +#if _WITH_SOCKET + case XIOSHUT_NULL: + /* send an empty packet; only useful on datagram sockets? */ + xiowrite(sock, &writenull, 0); + return 0; +#endif /* _WITH_SOCKET */ default: ; } + #if WITH_OPENSSL if ((sock->stream.dtype & XIODATA_MASK) == XIODATA_OPENSSL) { sycSSL_shutdown (sock->stream.para.openssl.ssl);