From ff8de6c5cd4e380d7867f5fdeda15098cb55514d Mon Sep 17 00:00:00 2001 From: Gerhard Rieger Date: Sat, 26 Dec 2020 19:03:32 +0100 Subject: [PATCH] Prevent error loop on RECVFROM addresses with fork --- CHANGES | 7 +++++++ doc/socat.yo | 14 +++++++++++--- xio-socket.c | 9 +++++++++ 3 files changed, 27 insertions(+), 3 deletions(-) diff --git a/CHANGES b/CHANGES index f3d304b..781ac09 100644 --- a/CHANGES +++ b/CHANGES @@ -43,6 +43,13 @@ Corrections: is now increased to 2047 bytes. Change suggested by Mario Camou. + Addresses of type RECVFROM with option fork looped with an error + message in case that the second address failed before consuming the + packet. The fix makes RECVFROM drop the packet when the second address + failed before reading it. Use retry or forever option with the second + address if you want to avoid data loss. + Thanks to Chunmei Xu for reporting this issue and proving the patch. + Porting: In gcc version 10 the default changed from -fcommon to -fno-common. Consequently, linking filan and procan failed with error diff --git a/doc/socat.yo b/doc/socat.yo index 4511aa8..391b010 100644 --- a/doc/socat.yo +++ b/doc/socat.yo @@ -419,6 +419,7 @@ label(ADDRESS_IP_RECVFROM)dit(bf(tt(IP-RECVFROM:))) This address works well with IP-SENDTO address peers (see above). Protocol 255 uses the raw socket with the IP header being part of the data.nl() + See the link(note about RECVFROM addresses)(NOTE_RECVFROM).nl() Option groups: link(FD)(GROUP_FD),link(SOCKET)(GROUP_SOCKET),link(IP4)(GROUP_IP4),link(IP6)(GROUP_IP6),link(CHILD)(GROUP_CHILD),link(RANGE)(GROUP_RANGE) nl() Useful options: link(pf)(OPTION_PROTOCOL_FAMILY), @@ -790,6 +791,7 @@ label(ADDRESS_SOCKET_RECVFROM)dit(bf(tt(SOCKET-RECVFROM:::))) UDP/IP version 4 or 6 depending on option link(pf)(OPTION_PROTOCOL_FAMILY). It receives one packet from an unspecified peer and may send one or more - answer packets to that peer. This mode is particularly useful with fork - option + answer packets to that peer. This mode is particularly useful with + link(fork)(OPTION_FORK) option where each arriving packet - from arbitrary peers - is handled by its own sub process. This allows a behaviour similar to typical UDP based servers like ntpd or named. This address works well with socat UDP-SENDTO address peers.nl() + label(NOTE_RECVFROM)Note: When the second address fails before entering the transfer loop the + packet is dropped. Use option link(retry)(OPTION_RETRY) or + link(forever)(OPTION_FOREVER) on the second address to avoid data loss. + nl() Option groups: link(FD)(GROUP_FD),link(SOCKET)(GROUP_SOCKET),link(IP4)(GROUP_IP4),link(IP6)(GROUP_IP6),link(CHILD)(GROUP_CHILD),link(RANGE)(GROUP_RANGE) nl() Useful options: link(fork)(OPTION_FORK), @@ -1233,6 +1239,7 @@ label(ADDRESS_UNIX_RECVFROM)dit(bf(tt(UNIX-RECVFROM:))) This mode is particularly useful with fork option where each arriving packet - from arbitrary peers - is handled by its own sub process. This address works well with socat UNIX-SENDTO address peers.nl() Option groups: link(FD)(GROUP_FD),link(SOCKET)(GROUP_SOCKET),link(NAMED)(GROUP_NAMED),link(CHILD)(GROUP_CHILD),link(UNIX)(GROUP_SOCK_UNIX) nl() + See the link(note about RECVFROM addresses)(NOTE_RECVFROM).nl() Useful options: link(fork)(OPTION_FORK)nl() See also: @@ -2310,7 +2317,8 @@ label(OPTION_FORK)dit(bf(tt(fork))) child: OPENSSL-LISTEN forks em(before) the SSL handshake, while OPENSSL-CONNECT forks em(afterwards). - RETRY and FOREVER options are not inherited by the child process.nl() + link(retry)(OPTION_RETRY) and link(forever)(OPTION_FOREVER) options are not + inherited by the child process.nl() On some operating systems (e.g. FreeBSD) this option does not work for UDP-LISTEN addresses.nl() enddit() diff --git a/xio-socket.c b/xio-socket.c index b891469..3508254 100644 --- a/xio-socket.c +++ b/xio-socket.c @@ -1128,6 +1128,8 @@ static pid_t xio_waitingfor; /* info from recv loop to signal handler: that should send us the USR1 signal */ static bool xio_hashappened; /* info from signal handler to loop: child process has read ("consumed") the packet */ +static int xio_childstatus; + /* this is the signal handler for USR1 and CHLD */ void xiosigaction_hasread(int signum #if HAVE_STRUCT_SIGACTION_SA_SIGACTION && defined(SA_SIGINFO) @@ -1179,6 +1181,7 @@ void xiosigaction_hasread(int signum } if (pid == xio_waitingfor) { xio_hashappened = true; + xio_childstatus = WEXITSTATUS(status); Debug("xiosigaction_hasread() ->"); diag_in_handler = 0; errno = _errno; @@ -1480,6 +1483,12 @@ int _xioopen_dgram_recvfrom(struct single *xfd, int xioflags, xio_waitingfor = 0; /* so this child will not set hashappened again */ xio_hashappened = false; + if (xio_childstatus != 0) { + char buff[512]; + Recv(xfd->fd, buff, sizeof(buff), 0); + xio_childstatus = 0; + Info("drop data because of child exit failed"); + } Info("continue listening"); } else { break;