new address options shut-null, null-eof

This commit is contained in:
Gerhard Rieger 2009-04-02 17:02:29 +02:00
parent 793763677a
commit 806bccbf94
13 changed files with 96 additions and 10 deletions

10
CHANGES
View file

@ -1,7 +1,13 @@
new features: new features:
new address options shut-none, shut-down, and shut-close allow to address options shut-none, shut-down, and shut-close allow to control
control socat's half close behaviour 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: ####################### V 1.7.0.1:

View file

@ -1 +1 @@
"1.7.0.1" "1.7.0.1+shutnull"

View file

@ -1490,6 +1490,16 @@ label(OPTION_SHUT_DOWN)dit(bf(tt(shut-down)))
label(OPTION_SHUT_CLOSE)dit(bf(tt(shut-close))) label(OPTION_SHUT_CLOSE)dit(bf(tt(shut-close)))
Changes the (address dependent) method of shutting down the write part of a Changes the (address dependent) method of shutting down the write part of a
connection to tt(close\(fd)). 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=<request>))) label(OPTION_IOCTL_VOID)dit(bf(tt(ioctl-void=<request>)))
Calls tt(ioctl()) with the request value as second argument and NULL as Calls tt(ioctl()) with the request value as second argument and NULL as
third argument. This option allows to utilize ioctls that are not third argument. This option allows to utilize ioctls that are not

52
test.sh
View file

@ -7558,6 +7558,58 @@ esac
N=$((N+1)) 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 NAME=UDP6LISTENBIND
# this tests for a bug in (up to) 1.5.0.0: # this tests for a bug in (up to) 1.5.0.0:
# with udp*-listen, the bind option supported only IPv4 # with udp*-listen, the bind option supported only IPv4

View file

@ -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_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_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_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 ******/ /****** 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 }; const struct optdesc opt_ioctl_void = { "ioctl-void", "ioctl", OPT_IOCTL_VOID, GROUP_FD, PH_FD, TYPE_INT, OFUNC_IOCTL_GENERIC, 0, 0, 0 };

View file

@ -45,6 +45,7 @@ extern const struct optdesc opt_end_close;
extern const struct optdesc opt_shut_none; extern const struct optdesc opt_shut_none;
extern const struct optdesc opt_shut_down; extern const struct optdesc opt_shut_down;
extern const struct optdesc opt_shut_close; extern const struct optdesc opt_shut_close;
extern const struct optdesc opt_shut_null;
extern const struct optdesc opt_streams_i_push; extern const struct optdesc opt_streams_i_push;
#endif /* !defined(__xio_fd_h_included) */ #endif /* !defined(__xio_fd_h_included) */

View file

@ -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_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_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 #if WITH_GENERICSOCKET

View file

@ -1,5 +1,5 @@
/* source: xio-socket.h */ /* 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 */ /* Published under the GNU General Public License V.2, see file COPYING */
#ifndef __xio_socket_h_included #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_int;
extern const struct optdesc opt_setsockopt_bin; extern const struct optdesc opt_setsockopt_bin;
extern const struct optdesc opt_setsockopt_string; extern const struct optdesc opt_setsockopt_string;
extern const struct optdesc opt_null_eof;
extern extern

7
xio.h
View file

@ -147,10 +147,11 @@ typedef struct single {
char *unlink_close; /* name of a symlink or unix socket to be removed */ char *unlink_close; /* name of a symlink or unix socket to be removed */
int dtype; int dtype;
enum { 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_NONE, /* do nothing on shutdown */
XIOSHUT_CLOSE, /* close the FD */ XIOSHUT_CLOSE, /* close the FD */
XIOSHUT_DOWN /* shutdown() */ XIOSHUT_DOWN, /* shutdown() */
XIOSHUT_NULL /* send an empty packet (dgram socket) */
} howtoshut; } howtoshut;
enum { enum {
END_UNSPEC, /* after init, when no end-close... option */ END_UNSPEC, /* after init, when no end-close... option */
@ -183,7 +184,7 @@ typedef struct single {
struct { struct {
struct timeval connect_timeout; /* how long to hang in connect() */ struct timeval connect_timeout; /* how long to hang in connect() */
union sockaddr_union la; /* local socket address */ 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; bool dorange;
struct xiorange range; /* restrictions for peer address */ struct xiorange range; /* restrictions for peer address */
#if _WITH_IP4 || _WITH_IP6 #if _WITH_IP4 || _WITH_IP6

View file

@ -926,6 +926,7 @@ const struct optname optionnames[] = {
#ifdef O_NSHARE #ifdef O_NSHARE
IF_OPEN ("nshare", &opt_o_nshare) IF_OPEN ("nshare", &opt_o_nshare)
#endif #endif
IF_SOCKET ("null-eof", &opt_null_eof)
#ifdef O_ASYNC #ifdef O_ASYNC
IF_ANY ("o-async", &opt_async) IF_ANY ("o-async", &opt_async)
#endif #endif
@ -1316,6 +1317,7 @@ const struct optname optionnames[] = {
IF_ANY ("shut-close", &opt_shut_close) IF_ANY ("shut-close", &opt_shut_close)
IF_ANY ("shut-down", &opt_shut_down) IF_ANY ("shut-down", &opt_shut_down)
IF_ANY ("shut-none", &opt_shut_none) IF_ANY ("shut-none", &opt_shut_none)
IF_ANY ("shut-null", &opt_shut_null)
#if WITH_EXEC || WITH_SYSTEM #if WITH_EXEC || WITH_SYSTEM
IF_ANY ("sid", &opt_setsid) IF_ANY ("sid", &opt_setsid)
#endif #endif

View file

@ -444,6 +444,7 @@ enum e_optcode {
OPT_NOFLSH, /* termios.c_lflag */ OPT_NOFLSH, /* termios.c_lflag */
OPT_NOFORK, /* exec, system */ OPT_NOFORK, /* exec, system */
OPT_NOPROMPT, /* readline */ OPT_NOPROMPT, /* readline */
OPT_NULL_EOF, /* receiving empty packet triggers EOF */
#ifdef OCRNL #ifdef OCRNL
OPT_OCRNL, /* termios.c_oflag */ OPT_OCRNL, /* termios.c_oflag */
#endif #endif
@ -593,6 +594,7 @@ enum e_optcode {
OPT_SHUT_CLOSE, OPT_SHUT_CLOSE,
OPT_SHUT_DOWN, OPT_SHUT_DOWN,
OPT_SHUT_NONE, OPT_SHUT_NONE,
OPT_SHUT_NULL, /* send 0 bytes on shutdown */
OPT_SIGHUP, OPT_SIGHUP,
OPT_SIGINT, OPT_SIGINT,
OPT_SIGQUIT, OPT_SIGQUIT,

View file

@ -1,5 +1,5 @@
/* source: xioread.c */ /* 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 */ /* Published under the GNU General Public License V.2, see file COPYING */
/* this is the source of the extended read function */ /* 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, bytes,
sockaddr_info(&from.soa, fromlen, infobuff, sizeof(infobuff))); sockaddr_info(&from.soa, fromlen, infobuff, sizeof(infobuff)));
if (bytes == 0) { if (bytes == 0) {
if (!pipe->para.socket.emptyiseof) { if (!pipe->para.socket.null_eof) {
errno = EAGAIN; return -1; errno = EAGAIN; return -1;
} }
return bytes; return bytes;
@ -350,7 +350,7 @@ ssize_t xioread(xiofile_t *file, void *buff, size_t bufsiz) {
bytes, bytes,
sockaddr_info(&from.soa, fromlen, infobuff, sizeof(infobuff))); sockaddr_info(&from.soa, fromlen, infobuff, sizeof(infobuff)));
if (bytes == 0) { if (bytes == 0) {
if (!pipe->para.socket.emptyiseof) { if (!pipe->para.socket.null_eof) {
errno = EAGAIN; return -1; errno = EAGAIN; return -1;
} }
return bytes; return bytes;

View file

@ -35,6 +35,7 @@ int xioshutdown(xiofile_t *sock, int how) {
} }
switch (sock->stream.howtoshut) { switch (sock->stream.howtoshut) {
char writenull;
case XIOSHUT_NONE: case XIOSHUT_NONE:
return 0; return 0;
case XIOSHUT_CLOSE: case XIOSHUT_CLOSE:
@ -49,9 +50,16 @@ int xioshutdown(xiofile_t *sock, int how) {
sock->stream.fd, how, strerror(errno)); sock->stream.fd, how, strerror(errno));
} }
return 0; 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: ; default: ;
} }
#if WITH_OPENSSL #if WITH_OPENSSL
if ((sock->stream.dtype & XIODATA_MASK) == XIODATA_OPENSSL) { if ((sock->stream.dtype & XIODATA_MASK) == XIODATA_OPENSSL) {
sycSSL_shutdown (sock->stream.para.openssl.ssl); sycSSL_shutdown (sock->stream.para.openssl.ssl);