New address ACCEPT-FD

This commit is contained in:
Gerhard Rieger 2023-09-30 18:39:13 +02:00
parent c311542e11
commit ebacb7c4e8
10 changed files with 232 additions and 56 deletions

View file

@ -117,6 +117,11 @@ Features:
network namespace.
Tests: NETNS NETNS_EXEC
New address ACCEPT-FD (ACCEPT) expects a listening file descriptor
passed from parent, and accepts one or more connections for data
transfer. This can be used with "inetd mode" of systemd.
Test: ACCEPT_FD
Corrections:
When a sub process (EXEC, SYSTEM) terminated with exit code other than
0, its last sent data might have been lost depending on timing of read/

View file

@ -945,6 +945,18 @@ label(ADDRESS_SOCKET_SENDTO)dit(bf(tt(SOCKET-SENDTO:<domain>:<type>:<protocol>:<
link(SOCKET-DATAGRAM)(ADDRESS_SOCKET_DATAGRAM),
link(SOCKET-RECV)(ADDRESS_SOCKET_RECV)
link(SOCKET-RECVFROM)(ADDRESS_SOCKET_RECVFROM)
label(ADDRESS_ACCEPT_FD)dit(bf(tt(ACCEPT-FD:<fdnum>)))
Expects a listening socket in <fdnum> and accepts one or (with option
link(fork)(OPTION_FORK)) more connections. This address type is useful under
systemd control with "inetd mode".nl()
Example: (link(example)(EXAMPLE_ADDRESS_ACCEPT_FD))
Option groups: link(FD)(GROUP_FD),link(SOCKET)(GROUP_SOCKET)link(TCP)(GROUP_TCP),link(CHILD)(GROUP_CHILD),link(RETRY)(GROUP_RETRY) nl()
Useful options:
link(fork)(OPTION_FORK)),
link(range)(OPTION_RANGE),
link(sourceport)(OPTION_SOURCEPORT),
link(lowport)(OPTION_LOWPORT),
link(tcpwrap)(OPTION_TCPWRAPPERS),
label(ADDRESS_SOCKS4)dit(bf(tt(SOCKS4:<socks-server>:<host>:<port>)))
Connects via <socks-server> [link(IP address)(TYPE_IP_ADDRESS)]
to <host> [link(IPv4 address)(TYPE_IPV4_ADDRESS)]
@ -4140,6 +4152,23 @@ socat - &#x5C;
sends an SSDP (Simple Service Discovery Protocol) query to the local network
and collects and outputs the answers received.
label(EXAMPLE_ADDRESS_ACCEPT_FD)
mancommand(\.LP)
mancommand(\.nf)
mancommand(\fBsystemd-socket-activate -l 1077 --inetd socat ACCEPT:0,fork PIPE\fP)
mancommand(\.RE)
mancommand(\.fi)
htmlcommand(<hr><div class="shell">systemd-socket-activate -l 1077 --inetd socat ACCEPT:0,fork PIPE</div>)
tt(systemd-socket-activate) is a program for testing tt(systemd) socket
activation of daemons. With tt(--inetd) it waits for a connection on the
specified port. It does not accept the connection but passes the listening file
descriptor as FDs 0 and 1. Socat() accepts the waiting connection and starts
data transfer.
dit(bf(tt()))

73
test.sh
View file

@ -6168,8 +6168,11 @@ if ! diff "$tref" "$tf" >"$tdiff"; then
$PRINTF "$FAILED\n"
echo "$CMD0 &"
cat "${te}0" >&2
echo "$CMD1"
echo "$CMD1 &"
cat "${te}1" >&2
echo "$CMD1"
cat "${te}2" >&2
cat "$tdiff" >&2
numFAIL=$((numFAIL+1))
listFAIL="$listFAIL $N"
else
@ -6178,6 +6181,8 @@ else
if [ "$DEBUG" ]; then cat "${te}0" >&2; fi
if [ "$VERBOSE" ]; then echo "$CMD1"; fi
if [ "$DEBUG" ]; then cat "${te}1" >&2; fi
if [ "$VERBOSE" ]; then echo "$CMD1"; fi
if [ "$DEBUG" ]; then cat "${te}2" >&2; fi
numOK=$((numOK+1))
fi # !(rc -ne 0)
wait
@ -16648,6 +16653,70 @@ PORT=$((PORT+1))
N=$((N+1))
# Test the ACCEPT-FD address
NAME=ACCEPT_FD
case "$TESTS" in
*%$N%*|*%functions%*|*%bugs%*|*%socket%*|*%systemd%*|*%accept%*|*%$NAME%*)
TEST="$NAME: ACCEPT-FD address"
# Start Socat with address ACCEPT-FD via systemd-socket-activate for echoing
# data.
# Connect with a client; the test succeeds when the client gets its data back.
if ! eval $NUMCOND; then :;
elif ! $(type systemd-socket-activate >/dev/null 2>&1); then
$PRINTF "test $F_n $TEST... ${YELLOW}systemd-socket-activate not available${NORMAL}\n" $N
numCANT=$((numCANT+1))
listCANT="$listCANT $N"
elif ! F=$(testfeats IP4 TCP LISTEN); then
$PRINTF "test $F_n $TEST... ${YELLOW}Feature $F not available${NORMAL}\n" $N
numCANT=$((numCANT+1))
listCANT="$listCANT $N"
elif ! A=$(testaddrs ACCEPT-FD PIPE STDIO TCP4); then
$PRINTF "test $F_n $TEST... ${YELLOW}Address $A not available${NORMAL}\n" $N
numCANT=$((numCANT+1))
listCANT="$listCANT $N"
elif ! runsip4 >/dev/null; then
$PRINTF "test $F_n $TEST... ${YELLOW}IPv4 not available${NORMAL}\n" $N
numCANT=$((numCANT+1))
listCANT="$listCANT $N"
else
tf="$td/test$N.stdout"
te="$td/test$N.stderr"
tdiff="$td/test$N.diff"
da="test$N $(date) $RANDOM"
newport tcp4
CMD0="systemd-socket-activate -l $PORT --inetd $TRACE $SOCAT $opts ACCEPT-FD:0 PIPE"
CMD1="$TRACE $SOCAT $opts - TCP4:$LOCALHOST:$PORT"
printf "test $F_n $TEST... " $N
$CMD0 >/dev/null 2>"${te}0" &
pid0=$!
waittcp4port $PORT 1
echo "$da" |$CMD1 >"${tf}1" 2>"${te}1"
rc1=$?
kill $pid0 2>/dev/null; wait
if echo "$da" |diff "${tf}1" - >$tdiff; then
$PRINTF "$OK\n"
if [ "$VERBOSE" ]; then echo "$CMD0 &"; fi
if [ "$DEBUG" ]; then cat "${te}0" >&2; fi
if [ "$VERBOSE" ]; then echo "$CMD1"; fi
if [ "$DEBUG" ]; then cat "${te}1" >&2; fi
numOK=$((numOK+1))
else
$PRINTF "$FAILED\n"
echo "$CMD0 &"
cat "${te}0" >&2
echo "$CMD1"
cat "${te}1" >&2
cat $tdiff >&2
numFAIL=$((numFAIL+1))
listFAIL="$listFAIL $N"
namesFAIL="$namesFAIL $NAME"
fi
fi # NUMCOND
;;
esac
N=$((N+1))
# end of common tests
##################################################################################
@ -16840,7 +16909,7 @@ wait<something>port $PORT 1
echo "$da" |$CMD1 >"${tf}1" 2>"${te}1"
rc1=$?
kill $pid0 2>/dev/null; wait
if [ !!! ]; then
if echo "$da" |diff "${tf}1" - >$tdiff !!!; then
$PRINTF "$OK\n"
if [ "$VERBOSE" ]; then echo "$CMD0 &"; fi
if [ "$DEBUG" ]; then cat "${te}0" >&2; fi

View file

@ -35,7 +35,7 @@ static int xioopen_exec(int argc, const char *argv[], struct opt *opts,
int duptostderr;
if (argc != 2) {
Error3("\"%s:%s\": wrong number of parameters (%d instead of 1)", argv[0], argv[1], argc-1);
Error2("\"%s\": wrong number of parameters (%d instead of 1)", argv[0], argc-1);
}
retropt_bool(opts, OPT_DASH, &dash);

View file

@ -7,15 +7,21 @@
#include "xiosysincludes.h"
#include "xioopen.h"
#include "xio-listen.h"
#include "xio-fdnum.h"
#if WITH_FDNUM
static int xioopen_fdnum(int argc, const char *argv[], struct opt *opts, int rw, xiofile_t *xfd, groups_t groups, int dummy1, int dummy2, int dummy3);
static int xioopen_accept_fd(int argc, const char *argv[], struct opt *opts, int xioflags, xiofile_t *xfd, groups_t groups, int dummy1, int dummy2, int dummy3);
const struct addrdesc xioaddr_fd = { "FD", 3, xioopen_fdnum, GROUP_FD|GROUP_FIFO|GROUP_CHR|GROUP_BLK|GROUP_FILE|GROUP_SOCKET|GROUP_TERMIOS|GROUP_SOCK_UNIX|GROUP_SOCK_IP|GROUP_IPAPP, 0, 0, 0 HELP(":<num>") };
const struct addrdesc xioaddr_fd = { "FD", 1+XIO_RDWR, xioopen_fdnum, GROUP_FD|GROUP_FIFO|GROUP_CHR|GROUP_BLK|GROUP_FILE|GROUP_SOCKET|GROUP_TERMIOS|GROUP_SOCK_UNIX|GROUP_SOCK_IP|GROUP_IPAPP, 0, 0, 0 HELP(":<fdnum>") };
#if WITH_LISTEN
const struct addrdesc xioaddr_accept_fd = { "ACCEPT-FD", 1+XIO_RDWR, xioopen_accept_fd, GROUP_FD|GROUP_SOCKET|GROUP_SOCK_UNIX|GROUP_SOCK_IP|GROUP_IPAPP|GROUP_CHILD|GROUP_RANGE|GROUP_RETRY, 0, 0, 0 HELP(":<fdnum>") };
#endif /* WITH_LISTEN */
/* use some file descriptor and apply the options. Set the FD_CLOEXEC flag. */
@ -28,7 +34,7 @@ static int xioopen_fdnum(int argc, const char *argv[], struct opt *opts,
int result;
if (argc != 2) {
Error3("%s:%s: wrong number of parameters (%d instead of 1)", argv[0], argv[1], argc-1);
Error2("%s: wrong number of parameters (%d instead of 1)", argv[0], argc-1);
}
numfd = strtoul(argv[1], &a1, 0);
@ -46,8 +52,55 @@ static int xioopen_fdnum(int argc, const char *argv[], struct opt *opts,
return 0;
}
#if WITH_LISTEN
/* Use some file descriptor and apply the options. Set the FD_CLOEXEC flag. */
static int xioopen_accept_fd(
int argc,
const char *argv[],
struct opt *opts,
int xioflags,
xiofile_t *xfd,
groups_t groups,
int dummy1,
int dummy2,
int dummy3)
{
char *a1;
int rw = (xioflags&XIO_ACCMODE);
int numfd;
union sockaddr_union us;
socklen_t uslen = sizeof(union sockaddr_union);
int result;
if (argc != 2) {
Error2("%s: wrong number of parameters (%d instead of 1)", argv[0], argc-1);
}
numfd = strtoul(argv[1], &a1, 0);
if (*a1 != '\0') {
Error1("error in FD number \"%s\"", argv[1]);
}
/* we dont want to see these fds in child processes */
if (Fcntl_l(numfd, F_SETFD, FD_CLOEXEC) < 0) {
Warn2("fcntl(%d, F_SETFD, FD_CLOEXEC): %s", numfd, strerror(errno));
}
if (Getsockname(numfd, (struct sockaddr *)&us, &uslen) < 0) {
Warn2("getsockname(fd=%d, ...): %s", numfd, strerror(errno));
}
Notice2("using file descriptor %d accepting a connection for %s", numfd, ddirection[rw]);
xfd->stream.fd = numfd;
if ((result = _xioopen_accept_fd(&xfd->stream, xioflags, (struct sockaddr *)&us, uslen, opts, us.soa.sa_family, 0, 0)) < 0) {
return result;
}
return 0;
}
#endif /* WITH_LISTEN */
#endif /* WITH_FDNUM */
#if WITH_FD
/* retrieve and apply options to a standard file descriptor.

View file

@ -6,6 +6,7 @@
#define __xio_fdnum_h_included 1
extern const struct addrdesc xioaddr_fd;
extern const struct addrdesc xioaddr_accept_fd;
extern int xioopen_fd(struct opt *opts, int rw, xiosingle_t *xfd, int numfd, int dummy2, int dummy3);

View file

@ -27,7 +27,6 @@ const struct optdesc opt_range = { "range", NULL, OPT_RANGE, GROUP_R
#endif
const struct optdesc opt_accept_timeout = { "accept-timeout", "listen-timeout", OPT_ACCEPT_TIMEOUT, GROUP_LISTEN, PH_LISTEN, TYPE_TIMEVAL, OFUNC_OFFSET, XIO_OFFSETOF(para.socket.accept_timeout) };
/*
applies and consumes the following option:
PH_INIT, PH_PASTSOCKET, PH_PREBIND, PH_BIND, PH_PASTBIND, PH_EARLY,
@ -106,45 +105,11 @@ int
*/
int _xioopen_listen(struct single *xfd, int xioflags, struct sockaddr *us, socklen_t uslen,
struct opt *opts, int pf, int socktype, int proto, int level) {
struct sockaddr sa;
socklen_t salen;
int backlog = 5; /* why? 1 seems to cause problems under some load */
char *rangename;
bool dofork = false;
int maxchildren = 0;
char infobuff[256];
char lisname[256];
union sockaddr_union _peername;
union sockaddr_union _sockname;
union sockaddr_union *pa = &_peername; /* peer address */
union sockaddr_union *la = &_sockname; /* local address */
socklen_t pas = sizeof(_peername); /* peer address size */
socklen_t las = sizeof(_sockname); /* local address size */
int result;
retropt_bool(opts, OPT_FORK, &dofork);
if (dofork) {
if (!(xioflags & XIO_MAYFORK)) {
Error("option fork not allowed here");
return STAT_NORETRY;
}
xfd->flags |= XIO_DOESFORK;
}
retropt_int(opts, OPT_MAX_CHILDREN, &maxchildren);
if (! dofork && maxchildren) {
Error("option max-children not allowed without option fork");
return STAT_NORETRY;
}
if (applyopts_single(xfd, opts, PH_INIT) < 0) return -1;
if (dofork) {
xiosetchilddied(); /* set SIGCHLD handler */
}
if ((xfd->fd = xiosocket(opts, pf?pf:us->sa_family, socktype, proto, level)) < 0) {
return STAT_RETRYLATER;
}
@ -175,13 +140,6 @@ int _xioopen_listen(struct single *xfd, int xioflags, struct sockaddr *us, sockl
}
}
#endif
/* under some circumstances (e.g., TCP listen on port 0) bind() fills empty
fields that we want to know. */
salen = sizeof(sa);
if (Getsockname(xfd->fd, us, &uslen) < 0) {
Warn4("getsockname(%d, %p, {%d}): %s",
xfd->fd, &us, uslen, strerror(errno));
}
applyopts(xfd->fd, opts, PH_PASTBIND);
#if WITH_UNIX
@ -197,6 +155,70 @@ int _xioopen_listen(struct single *xfd, int xioflags, struct sockaddr *us, sockl
}
#endif /* WITH_UNIX */
applyopts(xfd->fd, opts, PH_PRELISTEN);
retropt_int(opts, OPT_BACKLOG, &backlog);
applyopts(xfd->fd, opts, PH_LISTEN);
if (Listen(xfd->fd, backlog) < 0) {
Error3("listen(%d, %d): %s", xfd->fd, backlog, strerror(errno));
return STAT_RETRYLATER;
}
return _xioopen_accept_fd(xfd, xioflags, us, uslen, opts, pf, proto,level);
}
int _xioopen_accept_fd(
struct single *xfd,
int xioflags,
struct sockaddr *us,
socklen_t uslen,
struct opt *opts,
int pf,
int proto,
int level)
{
struct sockaddr sa;
socklen_t salen;
char *rangename;
bool dofork = false;
int maxchildren = 0;
char infobuff[256];
char lisname[256];
union sockaddr_union _peername;
union sockaddr_union _sockname;
union sockaddr_union *pa = &_peername; /* peer address */
union sockaddr_union *la = &_sockname; /* local address */
socklen_t pas = sizeof(_peername); /* peer address size */
socklen_t las = sizeof(_sockname); /* local address size */
int result;
retropt_bool(opts, OPT_FORK, &dofork);
if (dofork) {
if (!(xioflags & XIO_MAYFORK)) {
Error("option fork not allowed here");
return STAT_NORETRY;
}
xfd->flags |= XIO_DOESFORK;
}
retropt_int(opts, OPT_MAX_CHILDREN, &maxchildren);
if (! dofork && maxchildren) {
Error("option max-children not allowed without option fork");
return STAT_NORETRY;
}
if (dofork) {
xiosetchilddied(); /* set SIGCHLD handler */
}
/* Under some circumstances (e.g., TCP listen on port 0) bind() fills empty
fields that we want to know. */
salen = sizeof(sa);
if (Getsockname(xfd->fd, us, &uslen) < 0) {
Warn4("getsockname(%d, %p, {%d}): %s",
xfd->fd, &us, uslen, strerror(errno));
}
#if WITH_IP4 /*|| WITH_IP6*/
if (retropt_string(opts, OPT_RANGE, &rangename) >= 0) {
if (xioparserange(rangename, pf, &xfd->para.socket.range,
@ -221,14 +243,6 @@ int _xioopen_listen(struct single *xfd, int xioflags, struct sockaddr *us, sockl
retropt_bool(opts, OPT_LOWPORT, &xfd->para.socket.ip.lowport);
#endif /* WITH_TCP || WITH_UDP */
applyopts(xfd->fd, opts, PH_PRELISTEN);
retropt_int(opts, OPT_BACKLOG, &backlog);
applyopts(xfd->fd, opts, PH_LISTEN);
if (Listen(xfd->fd, backlog) < 0) {
Error3("listen(%d, %d): %s", xfd->fd, backlog, strerror(errno));
return STAT_RETRYLATER;
}
if (xioparms.logopt == 'm') {
Info("starting accept loop, switching to syslog");
diag_set('y', xioparms.syslogfac); xioparms.logopt = 'y';

View file

@ -20,5 +20,6 @@ int
int _xioopen_listen(struct single *fd, int xioflags,
struct sockaddr *us, socklen_t uslen,
struct opt *opts, int pf, int socktype, int proto, int level);
extern int _xioopen_accept_fd(struct single *xfd, int xioflags, struct sockaddr *us, socklen_t uslen, struct opt *opts, int pf, int proto, int level);
#endif /* !defined(__xio_listen_h_included) */

View file

@ -1847,7 +1847,7 @@ int xioparsenetwork(
char *addrname;
const char *maskname;
if ((maskname = strchr(rangename, ':')) == NULL) {
Error1("syntax error in range \"%s\": use <addr>:<mask>", rangename);
Error1("syntax error in range \"%s\" of unspecified address family: use <addr>:<mask>", rangename);
return STAT_NORETRY;
}
++maskname; /* skip ':' */

View file

@ -33,6 +33,10 @@ const struct addrname addressnames[] = {
{ "ABSTRACT-RECVFROM", &xioaddr_abstract_recvfrom },
{ "ABSTRACT-SENDTO", &xioaddr_abstract_sendto },
#endif /* defined(WITH_UNIX) && defined(WITH_ABSTRACT_UNIXSOCKET) */
#if WITH_LISTEN
{ "ACCEPT", &xioaddr_accept_fd },
{ "ACCEPT-FD", &xioaddr_accept_fd },
#endif
#if WITH_CREAT
{ "CREAT", &xioaddr_creat },
{ "CREATE", &xioaddr_creat },