New option netns for network namespace setting

This commit is contained in:
Gerhard Rieger 2023-07-21 07:10:38 +02:00
parent c82e3df210
commit f152c55584
26 changed files with 686 additions and 119 deletions

View file

@ -107,6 +107,10 @@ Features:
DNS resolver Options (res-*) are now set for the complete open phase of
the address, not per getaddrinfo() invocation.
Added the netns option that tries to open an address in the given
network namespace.
Tests: NETNS NETNS_EXEC
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

@ -51,7 +51,7 @@ XIOSRCS = xioinitialize.c xiohelp.c xioparam.c xiodiag.c xioopen.c xioopts.c \
xio-sctp.c xio-rawip.c \
xio-socks.c xio-proxy.c xio-udp.c \
xio-progcall.c xio-exec.c xio-system.c xio-termios.c xio-readline.c \
xio-pty.c xio-openssl.c xio-streams.c\
xio-pty.c xio-openssl.c xio-streams.c xio-namespaces.c \
xio-ascii.c xiolockfile.c xio-tcpwrap.c xio-fs.c xio-tun.c
XIOOBJS = $(XIOSRCS:.c=.o)
UTLSRCS = error.c dalan.c procan.c procan-cdefs.c hostan.c fdname.c sysutils.c utils.c nestlex.c vsnprintf_r.c snprinterr.c @FILAN@ sycls.c @SSLCLS@
@ -69,7 +69,7 @@ HFILES = sycls.h sslcls.h error.h dalan.h procan.h filan.h hostan.h sysincludes.
xio-ipapp.h xio-tcp.h xio-udp.h xio-sctp.h \
xio-socks.h xio-proxy.h xio-progcall.h xio-exec.h \
xio-system.h xio-termios.h xio-readline.h \
xio-pty.h xio-openssl.h xio-streams.h \
xio-pty.h xio-openssl.h xio-streams.h xio-namespaces.h \
xio-ascii.h xiolockfile.h xio-tcpwrap.h xio-fs.h xio-tun.h

View file

@ -285,6 +285,9 @@
/* Define if you have the <linux/vm_sockets.h> header file. */
#undef HAVE_LINUX_VM_SOCKETS_H
/* Define if you have the <sched.h> header file. */
#undef HAVE_SCHED_H
/* Define if you have the <linux/if_packet.h> header file. */
#undef HAVE_LINUX_IF_PACKET_H
@ -439,6 +442,9 @@
/* define if your struct ip has ip_hl; otherwise assume ip_vhl */
#undef HAVE_STRUCT_IP_IP_HL
/* Define if you have the setns function */
#undef HAVE_SETNS
/* Define if you have the setenv function */
#undef HAVE_SETENV
@ -700,6 +706,7 @@
#undef WITH_SOCKS4
#undef WITH_SOCKS4A
#undef WITH_VSOCK
#undef WITH_NAMESPACES
#undef WITH_PROXY
#undef WITH_EXEC
#undef WITH_SYSTEM

View file

@ -404,6 +404,30 @@ if test "$WITH_VSOCK"; then
AC_DEFINE(WITH_VSOCK)
fi
AC_ARG_ENABLE(namespaces, [ --disable-namespaces disable Linux namespaces support],
[case "$enableval" in
no) TRY_NAMESPACES= ;;
*) TRY_NAMESPACES=1 ;;
esac],
[TRY_NAMESPACES=1 ])
if test "TRY_NAMESPACES"; then
AC_TRY_LINK([#include <sched.h>],
[int x=setns(0,0);],
[],
[TRY_NAMESPACES=failed])
fi
AC_MSG_CHECKING(whether to include Linux namespaces support)
if test "$TRY_NAMESPACES" = 1; then
AC_MSG_RESULT(YES)
AC_DEFINE(WITH_NAMESPACES)
AC_DEFINE(HAVE_SCHED_H)
AC_DEFINE(HAVE_SETNS)
elif test "$TRY_NAMESPACES" = yes; then
AC_MSG_RESULT(NO (failed))
else
AC_MSG_RESULT(NO)
fi
AC_MSG_CHECKING(whether to include listen support)
AC_ARG_ENABLE(listen, [ --disable-listen disable listen support],
[case "$enableval" in

View file

@ -341,7 +341,8 @@ label(ADDRESS_EXEC)dit(bf(tt(EXEC:<command-line>)))
link(pipes)(OPTION_PIPES),
link(login)(OPTION_LOGIN),
link(sigint)(OPTION_SIGINT),
link(sigquit)(OPTION_SIGQUIT)nl()
link(sigquit)(OPTION_SIGQUIT),
link(netns)(OPTION_NETNS)nl()
See also: link(SYSTEM)(ADDRESS_SYSTEM)
label(ADDRESS_FD)dit(bf(tt(FD:<fdnum>)))
Uses the file descriptor link(<fdnum>)(TYPE_FDNUM). It must already exist as
@ -999,7 +1000,8 @@ label(ADDRESS_SYSTEM)dit(bf(tt(SYSTEM:<shell-command>)))
link(setsid)(OPTION_SETSID),
link(pipes)(OPTION_PIPES),
link(sigint)(OPTION_SIGINT),
link(sigquit)(OPTION_SIGQUIT)nl()
link(sigquit)(OPTION_SIGQUIT),
link(netns)(OPTION_NETNS)nl()
See also: link(EXEC)(ADDRESS_EXEC)
label(ADDRESS_TCP_CONNECT)dit(bf(tt(TCP:<host>:<port>)))
Connects to <port> [link(TCP service)(TYPE_TCP_SERVICE)] on
@ -1008,17 +1010,18 @@ label(ADDRESS_TCP_CONNECT)dit(bf(tt(TCP:<host>:<port>)))
link(pf)(OPTION_PROTOCOL_FAMILY).nl()
Option groups: link(FD)(GROUP_FD),link(SOCKET)(GROUP_SOCKET),link(IP4)(GROUP_IP4),link(IP6)(GROUP_IP6),link(TCP)(GROUP_TCP),link(RETRY)(GROUP_RETRY) nl()
Useful options:
link(connect-timeout)(OPTION_CONNECT_TIMEOUT),
link(retry)(OPTION_RETRY),
link(sourceport)(OPTION_SOURCEPORT),
link(netns)(OPTION_NETNS),
link(crnl)(OPTION_CRNL),
link(bind)(OPTION_BIND),
link(pf)(OPTION_PROTOCOL_FAMILY),
link(connect-timeout)(OPTION_CONNECT_TIMEOUT),
link(tos)(OPTION_TOS),
link(mtudiscover)(OPTION_MTUDISCOVER),
link(mss)(OPTION_MSS),
link(nodelay)(OPTION_TCP_NODELAY),
link(nonblock)(OPTION_NONBLOCK),
link(sourceport)(OPTION_SOURCEPORT),
link(retry)(OPTION_RETRY),
link(readbytes)(OPTION_READBYTES)nl()
See also:
link(TCP4)(ADDRESS_TCP4_CONNECT),
@ -1090,7 +1093,8 @@ label(ADDRESS_TUN)dit(bf(tt(TUN[:<if-addr>/<bits>])))
link(tun-device)(OPTION_TUN_DEVICE),
link(tun-name)(OPTION_TUN_NAME),
link(tun-type)(OPTION_TUN_TYPE),
link(iff-no-pi)(OPTION_IFF_NO_PI) nl()
link(iff-no-pi)(OPTION_IFF_NO_PI),
link(netns)(OPTION_NETNS)nl()
See also:
link(ip-recv)(ADDRESS_IP_RECV)
label(ADDRESS_UDP_CONNECT)dit(bf(tt(UDP:<host>:<port>)))
@ -1429,7 +1433,9 @@ dit(bf(tt(ABSTRACT-CLIENT:<string>)))
unixdomain() address space. To achieve this the socket address strings are
prefixed with "\0" internally. This feature is available (only?) on Linux.
Option groups are the same as with the related UNIX addresses, except that
the ABSTRACT addresses are not member of the NAMED group.
the ABSTRACT addresses are not member of the NAMED group.nl()
Useful options:
link(netns)(OPTION_NETNS)
enddit()
@ -1878,6 +1884,12 @@ label(OPTION_SETPGID)dit(bf(tt(setpgid=<pid_t>)))
process group.
label(OPTION_SETSID)dit(bf(tt(setsid)))
Makes the process the leader of a new session (link(example)(EXAMPLE_OPTION_SETSID)).
label(OPTION_NETNS)dit(bf(tt(netns=<net-namespace-name>)))
Before opening the address it tries to switch to the named network namespace.
After opening the address it switches back to the previous namespace.
(link(Example with TCP forwarder)(EXAMPLE_OPTION_NETNS),
link(example with virtual network connection)(EXAMPLE_TUN_NETNS).nl()
Only on Linux; requires root; use option tt(--experimental).nl()
enddit()
startdit()enddit()nl()
@ -3609,6 +3621,43 @@ to a modemserver via ssh where another socat instance links it to
file(/dev/ttyS0).
label(EXAMPLE_OPTION_NETNS)
mancommand(\.LP)
mancommand(\.nf)
mancommand(\fBsudo socat --experimental \\)
mancommand(\.RS)
mancommand(\fBTCP4-LISTEN:8000,reuseaddr,fork,netns=namespace1 \\
TCP4-CONNECT:server2:8000\fP)
mancommand(\.RE)
mancommand(\.fi)
htmlcommand(<hr><div class="shell">sudo socat --experimental &#x5C;
TCP4-LISTEN:8000,reuseaddr,fork,netns=namespace1 &#x5C;
TCP4-CONNECT:server2:8000</div>)
creates a listener in the given network namespace that accepts TCP connections
on port 8000 and forwards them to server2.
label(EXAMPLE_TUN_NETNS)
mancommand(\.LP)
mancommand(\.nf)
mancommand(\fBsudo socat --experimental \\)
mancommand(\.RS)
mancommand(\fBTUN:192.168.2.1/24,up \\
TUN:192.168.2.2/24,up,netns=namespace2\fP)
mancommand(\.RE)
mancommand(\.fi)
htmlcommand(<hr><div class="shell">sudo socat --experimental &#x5C;
TUN:192.168.2.1/24,up &#x5C;
TUN:192.168.2.2/24,up,netns=namespace2</div>)
creates two virtual network interfaces, one in default namespace, the other one
in namespace2, and forwards packets between them, acting as a virtual
network connection.
label(EXAMPLE_PROXY_CONNECT)
mancommand(\.LP)
mancommand(\.nf)

View file

@ -13,6 +13,7 @@
#include "error.h"
#include "sycls.h"
#include "sysutils.h"
#include "sched.h"
#include "filan.h"
#include <sys/resource.h>
@ -163,6 +164,16 @@ int procan(FILE *outfile) {
#endif
}
/* Name spaces */
{
char path[PATH_MAX];
char link[PATH_MAX];
snprintf(path, sizeof(path)-1, "/proc/"F_pid"/ns/net", getpid());
if (readlink(path, link, sizeof(link)-1) >= 0) {
fprintf(outfile, "Network namespace: %s", link);
}
}
/* file descriptors */
/* what was this for?? */

View file

@ -610,6 +610,11 @@ void socat_version(FILE *fd) {
#else
fputs(" #undef WITH_VSOCK\n", fd);
#endif
#ifdef WITH_NAMESPACES
fprintf(fd, " #define WITH_NAMESPACES %d\n", WITH_NAMESPACES);
#else
fputs(" #undef WITH_NAMESPACES\n", fd);
#endif
#ifdef WITH_PROXY
fprintf(fd, " #define WITH_PROXY %d\n", WITH_PROXY);
#else

21
sycls.c
View file

@ -1744,6 +1744,27 @@ void Unsetenv(const char *name) {
#endif
#if WITH_NAMESPACES
int Setns(
int fd,
int nstype)
{
int _errno;
int result;
Debug2("setns(%d, 0x%x)", fd, nstype);
result = setns(fd, nstype);
if (result < 0) {
_errno = errno;
Debug2("setns() -> %d (errno=%d)", result, errno);
errno = _errno;
return result;
}
Debug1("setns() -> %d", result);
return result;
}
#endif /* WITH_NAMESPACES */
#if WITH_READLINE
char *Readline(const char *prompt) {

View file

@ -167,6 +167,7 @@ void Abort(void);
int Mkstemp(char *template);
int Setenv(const char *name, const char *value, int overwrite);
void Unsetenv(const char *name);
int Setns(int fd, int nstype);
#endif /* WITH_SYCLS */
#if WITH_SYCLS
@ -271,6 +272,7 @@ void Add_history(const char *string);
#define Mkstemp(t) mkstemp(t)
#define Setenv(n,v,o) setenv(n,v,o)
#define Unsetenv(n) unsetenv(n)
#define Setns(f,n) setns(f,n)
#define Readline(p) readline(p)
#define Using_history() using_history()

View file

@ -177,6 +177,9 @@
#if HAVE_LINUX_EXT2_FS_H
#include <linux/ext2_fs.h> /* Linux ext2 filesystem definitions */
#endif
#if WITH_NAMESPACES && HAVE_SCHED_H
#include <sched.h>
#endif
#if WITH_READLINE
# if HAVE_READLINE_READLINE_H
#include <readline/readline.h>

210
test.sh
View file

@ -1671,7 +1671,7 @@ NAME=SYSTEMPIPES
case "$TESTS" in
*%$N%*|*%functions%*|*%system%*|*%pipes%*|*%$NAME%*)
TEST="$NAME: simple echo via system() of cat with pipes"
testecho "$N" "$TEST" "" "sYSTEM:$CAT,pipes" "$opts"
testecho "$N" "$TEST" "" "SYSTEM:$CAT,pipes" "$opts"
esac
N=$((N+1))
@ -16338,6 +16338,214 @@ esac
N=$((N+1))
# Test the netns (net namespace) feature
NAME=NETNS
case "$TESTS" in
*%$N%*|*%functions%*|*%root%*|*%namespace%*|*%netns%*|*%socket%*|*%$NAME%*)
ns=socat-$$-$N
TEST="$NAME: option netns (net namespace $ns)"
# Start a simple echo server with option netns on localhost of a net namespace;
# use a client process with option netns to send data to the net namespace
# net server and check the reply.
if ! eval $NUMCOND; then :;
elif [ "$UNAME" != Linux ]; then
$PRINTF "test $F_n $TEST... ${YELLOW}Only on Linux${NORMAL}\n" $N
numCANT=$((numCANT+1))
listCANT="$listCANT $N"
elif [ $(id -u) -ne 0 -a "$withroot" -eq 0 ]; then
$PRINTF "test $F_n $TEST... ${YELLOW}Must be root${NORMAL}\n" $N
numCANT=$((numCANT+1))
listCANT="$listCANT $N"
elif ! $(type ip >/dev/null 2>&1); then
$PRINTF "test $F_n $TEST... ${YELLOW}ip program not available${NORMAL}\n" $N
numCANT=$((numCANT+1))
listCANT="$listCANT $N"
elif ! F=$(testfeats IP4 TCP LISTEN NAMESPACES); then
$PRINTF "test $F_n $TEST... ${YELLOW}Feature $F not available${NORMAL}\n" $N
numCANT=$((numCANT+1))
listCANT="$listCANT $N"
elif ! A=$(testaddrs STDIO TCP-LISTEN TCP EXEC); then
$PRINTF "test $F_n $TEST... ${YELLOW}Address $A not available${NORMAL}\n" $N
numCANT=$((numCANT+1))
listCANT="$listCANT $N"
elif ! o=$(testoptions netns) >/dev/null; then
$PRINTF "test $F_n $TEST... ${YELLOW}Option $o 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 # or whatever proto, or drop this line
CMD0="$TRACE $SOCAT $opts --experimental TCP4-LISTEN:$PORT,netns=$ns EXEC:'od -c'"
CMD1="$TRACE $SOCAT $opts --experimental - TCP4:127.0.0.1:$PORT,netns=$ns"
printf "test $F_n $TEST... " $N
ip netns del $ns 2>/dev/null # make sure it does not exist
ip netns add $ns
ip netns exec $ns ip -4 addr add dev lo 127.0.0.1/8
ip netns exec $ns ip link set lo up
eval "$CMD0" >/dev/null 2>"${te}0" &
pid0=$!
relsleep 1 # if no matching wait*port function
echo "$da" |$CMD1 >"${tf}1" 2>"${te}1"
rc1=$?
kill $pid0 2>/dev/null; wait
ip netns del $ns
if [ $rc1 -ne 0 ]; then
$PRINTF "$FAILED (client failed)\n"
echo "ip netns del $ns"
echo "ip netns add $ns"
echo "ip netns exec $ns ip -4 addr add dev lo 127.0.0.1/8"
echo "ip netns exec $ns ip link set lo up"
echo "$CMD0 &"
cat "${te}0" >&2
echo "$CMD1"
cat "${te}1" >&2
echo "ip netns del $ns"
numFAIL=$((numFAIL+1))
listFAIL="$listFAIL $N"
namesFAIL="$namesFAIL $NAME"
elif echo "$da" |od -c |diff - ${tf}1 >"$tdiff"; then
$PRINTF "$OK\n"
if [ "$VERBOSE" ]; then
echo "ip netns del $ns"
echo "ip netns add $ns"
echo "ip netns exec $ns ip -4 addr add dev lo 127.0.0.1/8"
echo "ip netns exec $ns ip link set lo up"
fi
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
if [ "$VERBOSE" ]; then echo "ip netns del $ns"; fi
numOK=$((numOK+1))
else
$PRINTF "$FAILED (bad output)\n"
echo "$CMD0 &"
cat "${te}0" >&2
echo "$CMD1"
cat "${te}1" >&2
diff:
cat "$tdiff"
numFAIL=$((numFAIL+1))
listFAIL="$listFAIL $N"
namesFAIL="$namesFAIL $NAME"
fi
fi # NUMCOND
;;
esac
N=$((N+1))
# Test the netns (net namespace) feature with EXEC and reset
NAME=NETNS_EXEC
case "$TESTS" in
*%$N%*|*%functions%*|*%root%*|*%namespace%*|*%netns%*|*%socket%*|*%$NAME%*)
ns=socat-$$-test$N
TEST="$NAME: option netns with EXEC (net namespace $ns)"
# Start a simple server with option netns on localhost of a net namespace that
# stores data it receives;
# use a middle process that EXECs a socat client that connects to the server on
# the net namespace; then it listens on default namespace.
# With a third command line connect and send data to the middle process.
# When the data received by the server is correct the test succeeded.
if ! eval $NUMCOND; then :;
elif [ "$UNAME" != Linux ]; then
$PRINTF "test $F_n $TEST... ${YELLOW}Only on Linux${NORMAL}\n" $N
numCANT=$((numCANT+1))
listCANT="$listCANT $N"
elif [ $(id -u) -ne 0 -a "$withroot" -eq 0 ]; then
$PRINTF "test $F_n $TEST... ${YELLOW}Must be root${NORMAL}\n" $N
numCANT=$((numCANT+1))
listCANT="$listCANT $N"
elif ! $(type ip >/dev/null 2>&1); then
$PRINTF "test $F_n $TEST... ${YELLOW}ip program not available${NORMAL}\n" $N
numCANT=$((numCANT+1))
listCANT="$listCANT $N"
elif ! F=$(testfeats IP4 ABSTRACT_UNIXSOCKET UDP LISTEN NAMESPACES STDIO); then
$PRINTF "test $F_n $TEST... ${YELLOW}Feature $F not available${NORMAL}\n" $N
numCANT=$((numCANT+1))
listCANT="$listCANT $N"
elif ! A=$(testaddrs ABSTRACT-RECV ABSTRACT-SENDTO CREATE EXEC UDP4-RECV STDIO UDP4); then
$PRINTF "test $F_n $TEST... ${YELLOW}Address $a not available${NORMAL}\n" $N
numCANT=$((numCANT+1))
listCANT="$listCANT $N"
elif ! o=$(testoptions netns) >/dev/null; then
$PRINTF "test $F_n $TEST... ${YELLOW}Option $o 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 udp4
CMD0="$TRACE $SOCAT $opts --experimental -u -T 1 ABSTRACT-RECV:test$N,netns=$ns CREAT:${tf}0"
CMD1="$TRACE $SOCAT $opts --experimental -U -T 1 EXEC:\"$SOCAT STDIO ABSTRACT-SENDTO\:test$N\",netns=$ns UDP4-RECV:$PORT"
CMD2="$TRACE $SOCAT $opts -u STDIO UDP4:127.0.0.1:$PORT"
printf "test $F_n $TEST... " $N
ip netns del $ns 2>/dev/null # make sure it does not exist
ip netns add $ns
#ip netns exec $ns ip -4 addr add dev lo 127.0.0.1/8
#ip netns exec $ns ip link set lo up
eval "$CMD0" >/dev/null 2>"${te}0" &
pid0=$!
relsleep 1 # if no matching wait*port function
eval "$CMD1" 2>${te}1 &
pid1=$!
relsleep 1
echo "$da" |$CMD2 >"${tf}2" 2>"${te}2"
rc1=$?
kill $pid0 $pid1 2>/dev/null; wait
ip netns del $ns
if [ $rc1 -ne 0 ]; then
$PRINTF "$FAILED (client failed)\n"
echo "$CMD0 &"
cat "${te}0" >&2
echo "$CMD1"
cat "${te}1" >&2
echo "$CMD2"
cat "${te}2" >&2
numFAIL=$((numFAIL+1))
listFAIL="$listFAIL $N"
namesFAIL="$namesFAIL $NAME"
elif echo "$da" |diff - ${tf}0 >"$tdiff" 2>/dev/null; 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
if [ "$VERBOSE" ]; then echo "$CMD2"; fi
if [ "$DEBUG" ]; then cat "${te}2" >&2; fi
numOK=$((numOK+1))
else
$PRINTF "$FAILED (bad output)\n"
echo "$CMD0 &"
cat "${te}0" >&2
echo "$CMD1"
cat "${te}1" >&2
echo "$CMD2"
cat "${te}2" >&2
echo diff:
cat "$tdiff"
numFAIL=$((numFAIL+1))
listFAIL="$listFAIL $N"
namesFAIL="$namesFAIL $NAME"
fi
fi # NUMCOND
;;
esac
N=$((N+1))
# end of common tests
##################################################################################

View file

@ -111,9 +111,10 @@ static int xioopen_exec(int argc, const char *argv[], struct opt *opts,
Exit(1);
}
dropopts(opts, PH_PASTEXEC);
if ((numleft = leftopts(opts)) > 0) {
Error1("%d option(s) could not be used", numleft);
showleft(opts);
Error1("INTERNAL: %d option(s) remained unused", numleft);
return STAT_NORETRY;
}

View file

@ -46,7 +46,8 @@ int xioopen_ipapp_connect(int argc, const char *argv[], struct opt *opts,
xfd->howtoend = END_SHUTDOWN;
if (applyopts_single(xfd, opts, PH_INIT) < 0) return -1;
if (applyopts_single(xfd, opts, PH_INIT) < 0)
return -1;
applyopts(-1, opts, PH_INIT);
retropt_bool(opts, OPT_FORK, &dofork);

73
xio-namespaces.c Normal file
View file

@ -0,0 +1,73 @@
/* Source: xio-namespaces.c */
/* Copyright Gerhard Rieger and contributors (see file CHANGES) */
/* Published under the GNU General Public License V.2, see file COPYING */
/* This file contains Linux namespace related code */
#include "xiosysincludes.h"
#include "xioopen.h"
#include "xio-namespaces.h"
#if WITH_NAMESPACES
const struct optdesc opt_set_netns = { "netns", NULL, OPT_SET_NETNS, GROUP_PROCESS, PH_INIT, TYPE_STRING, OFUNC_SET_NAMESPACE, 0, 0, 0 };
/* Set the given namespace. Requires root or the appropriate CAP_*-
Returns 0 on success, or -1 on error. */
int xio_set_namespace(
const char *nstype,
const char *nsname)
{
char nspath[PATH_MAX];
int nsfd;
int rc;
if (!xioparms.experimental) {
Error1("option \"%s\" requires use of --experimental", nstype);
}
snprintf(nspath, sizeof(nspath)-1, "/run/%s/%s", nstype, nsname);
Info1("switching to net namespace \"%s\"", nsname);
nsfd = Open(nspath, O_RDONLY|O_CLOEXEC, 000);
if (nsfd < 0) {
Error2("open(%s, O_RDONLY|O_CLOEXEC): %s", nspath, strerror(errno));
return -1;
}
rc = Setns(nsfd, CLONE_NEWNET);
if (rc < 0) {
Error2("setns(%d, CLONE_NEWNET): %s", nsfd, strerror(errno));
Close(nsfd);
}
Close(nsfd);
return 0;
}
/* Sets the given namespace to that of process 1, this is assumed to be the
systems default.
Returns 0 on success, or -1 on error. */
int xio_reset_namespace(
const char *nstype)
{
char nspath[PATH_MAX];
int nsfd;
int rc;
snprintf(nspath, sizeof(nspath)-1, "/proc/1/ns/%s", nstype);
Info("switching back to default namespace");
nsfd = Open(nspath, O_RDONLY|O_CLOEXEC, 000);
if (nsfd < 0) {
Error2("open(%s, O_RDONLY|O_CLOEXEC): %s", nspath, strerror(errno));
return -1;
}
rc = Setns(nsfd, CLONE_NEWNET);
if (rc < 0) {
Error2("setns(%d, CLONE_NEWNET): %s", nsfd, strerror(errno));
Close(nsfd);
}
Close(nsfd);
return 0;
}
#endif /* WITH_NAMESPACES */

18
xio-namespaces.h Normal file
View file

@ -0,0 +1,18 @@
/* Source: xio-namespaces.h */
/* Copyright Gerhard Rieger and contributors (see file CHANGES) */
/* Published under the GNU General Public License V.2, see file COPYING */
#ifndef __xio_namespaces_h_included
#define __xio_namespaces_h_included 1
#if WITH_NAMESPACES
extern const struct optdesc opt_set_netns;
extern const struct optdesc opt_reset_netns;
extern int xio_set_namespace(const char *nstype, const char *nsname);
extern int xio_reset_namespace(const char *nstype);
#endif /* WITH_NAMESPACES */
#endif /* __xio_namespaces_h_included */

View file

@ -66,8 +66,8 @@ static int xioopen_fifo_unnamed(xiofile_t *sock, struct opt *opts) {
}
if ((numleft = leftopts(opts)) > 0) {
Error1("%d option(s) could not be used", numleft);
showleft(opts);
Error1("INTERNAL: %d option(s) remained unused", numleft);
}
Notice("writing to and reading from unnamed pipe");
return 0;

View file

@ -70,4 +70,3 @@ int _xioopen_setdelayeduser(void) {
}
return 0;
}

View file

@ -374,11 +374,7 @@ int _xioopen_foxec(int xioflags, /* XIO_RDONLY etc. */
/* this for parent, was after fork */
fd->fd = sv[0];
applyopts(fd->fd, popts, PH_FD);
applyopts(fd->fd, popts, PH_LATE);
if (applyopts_single(fd, popts, PH_LATE) < 0) return -1;
}
/*0 if ((optpr = copyopts(*copts, GROUP_PROCESS)) == NULL)
return -1;*/
retropt_bool(*copts, OPT_STDERR, &withstderr);
xiosetchilddied(); /* set SIGCHLD handler */
@ -390,7 +386,8 @@ int _xioopen_foxec(int xioflags, /* XIO_RDONLY etc. */
return -1;
}
}
if (!withfork || pid == 0) { /* child */
if (!withfork || pid == 0) { /* in single process, or in child */
applyopts_optgroup(-1, *copts, PH_PRESOCKET, PH_PRESOCKET, GROUP_PROCESS);
if (withfork) {
Close(trigger[0]); /* in child: not needed here */
/* The child should have default handling for SIGCHLD. */
@ -593,9 +590,12 @@ int _xioopen_foxec(int xioflags, /* XIO_RDONLY etc. */
if (applyopts_single(fd, popts, PH_LATE) < 0) return -1;
applyopts_signal(fd, popts);
applyopts(-1, popts, PH_LATE);
applyopts(-1, popts, PH_LATE2);
applyopts(-1, popts, PH_PASTEXEC);
if ((numleft = leftopts(popts)) > 0) {
Error1("%d option(s) could not be used", numleft);
showleft(popts);
Error1("INTERNAL: %d option(s) remained unused", numleft);
return STAT_NORETRY;
}

View file

@ -2024,6 +2024,7 @@ xiosocket(struct opt *opts, int pf, int socktype, int proto, int msglevel) {
retropt_int(opts, OPT_SO_TYPE, &socktype);
retropt_int(opts, OPT_SO_PROTOTYPE, &proto);
applyopts(-1, opts, PH_PRESOCKET);
result = Socket(pf, socktype, proto);
if (result < 0) {
int _errno = errno;

View file

@ -53,7 +53,7 @@ void dummy(void) {
if (Ioctl(fd, I_PUSH, opt->value.u_string) < 0) {
Warn3("ioctl(%d, I_PUSH, \"%s\"): %s",
fd, opt->value.u_string, strerror(errno));
opt->desc = ODESC_ERROR; ++opt; continue;
return -1;
}
#endif
#if 0

View file

@ -50,8 +50,8 @@ static int xioopen_system(int argc, const char *argv[], struct opt *opts,
}
if ((numleft = leftopts(opts)) > 0) {
Error1("%d option(s) could not be used", numleft);
showleft(opts);
Error1("INTERNAL: %d option(s) remained unused", numleft);
return STAT_NORETRY;
}

View file

@ -79,6 +79,7 @@ static int xioopen_tun(int argc, const char *argv[], struct opt *opts, int xiofl
/*========================= the tunnel interface =========================*/
Notice("creating tunnel network interface");
applyopts_optgroup(-1, opts, PH_PRESOCKET, PH_PRESOCKET, GROUP_PROCESS);
if ((result = _xioopen_open(tundevice, rw, opts)) < 0)
return result;
xfd->stream.fd = result;

View file

@ -34,6 +34,7 @@
#include "xio-proxy.h"
#include "xio-vsock.h"
#endif /* _WITH_SOCKET */
#include "xio-namespaces.h"
#include "xio-progcall.h"
#include "xio-exec.h"
#include "xio-system.h"

View file

@ -592,14 +592,38 @@ int xioopen_single(xiofile_t *xfd, int xioflags) {
const struct addrdesc *addrdesc;
int result;
/* Values to be saved until xioopen() is finished */
int do_res;
#if HAVE_RESOLV_H
int do_res;
struct __res_state save_res;
#endif /* HAVE_RESOLV_H */
#if WITH_NAMESPACES
char *temp_netns;
int save_netfd = -1;
#endif
int rc;
if (applyopts_single(sfd, sfd->opts, PH_OFFSET) < 0)
return -1;
#if WITH_NAMESPACES
if (retropt_string(sfd->opts, OPT_SET_NETNS, &temp_netns) >= 0) {
char nspath[PATH_MAX];
snprintf(nspath, sizeof(nspath)-1, "/proc/"F_pid"/ns/net",
Getpid());
save_netfd = Open(nspath, O_RDONLY|O_CLOEXEC, 000);
if (save_netfd < 0) {
Error2("open(%s, O_RDONLY|O_CLOEXEC): %s", nspath, strerror(errno));
return -1;
}
rc = xio_set_namespace("netns", temp_netns);
free(temp_netns);
if (rc < 0)
return -1;
}
#endif /* WITH_NAMESPACES */
#if HAVE_RESOLV_H
if ((do_res = xio_res_init(sfd, &save_res)) < 0)
return STAT_NORETRY;
@ -627,6 +651,17 @@ int xioopen_single(xiofile_t *xfd, int xioflags) {
xio_res_init(sfd, &save_res);
#endif /* HAVE_RESOLV_H */
#if WITH_NAMESPACES
if (save_netfd >= 0) {
rc = Setns(save_netfd, CLONE_NEWNET);
if (rc < 0) {
Error2("setns(%d, CLONE_NEWNET): %s", save_netfd, strerror(errno));
Close(save_netfd);
return STAT_NORETRY;
}
}
#endif /* WITH_NAMESPACES */
return result;
}

286
xioopts.c
View file

@ -1011,6 +1011,9 @@ const struct optname optionnames[] = {
IF_ANY ("ndelay", &opt_o_ndelay)
#else
IF_ANY ("ndelay", &opt_nonblock)
#endif
#if WITH_NAMESPACES
IF_ANY ("netns", &opt_set_netns)
#endif
IF_NAMED ("new", &opt_unlink_early)
#ifdef NLDLY
@ -2766,7 +2769,7 @@ int leftopts(const struct opt *opts) {
if (!opts) return 0;
while (opt->desc != ODESC_END) {
if (opt->desc != ODESC_DONE) {
if (opt->desc != ODESC_DONE && opt->desc != ODESC_ERROR) {
++num;
}
++opt;
@ -2779,7 +2782,7 @@ int showleft(const struct opt *opts) {
const struct opt *opt = opts;
while (opt->desc != ODESC_END) {
if (opt->desc != ODESC_DONE) {
if (opt->desc != ODESC_DONE && opt->desc != ODESC_ERROR) {
Warn1("showleft(): option \"%s\" not inquired", opt->desc->defname);
}
++opt;
@ -2852,7 +2855,8 @@ int retropt(struct opt *opts, int optcode, union integral *result) {
struct opt *opt = opts;
while (opt->desc != ODESC_END) {
if (opt->desc != ODESC_DONE && opt->desc->optcode == optcode) {
if (opt->desc != ODESC_DONE && opt->desc != ODESC_ERROR &&
opt->desc->optcode == optcode) {
*result = opt->value;
opt->desc = ODESC_DONE;
return 0;
@ -2867,7 +2871,8 @@ static struct opt *xio_findopt(struct opt *opts, int optcode) {
struct opt *opt = opts;
while (opt->desc != ODESC_END) {
if (opt->desc != ODESC_DONE && opt->desc->optcode == optcode) {
if (opt->desc != ODESC_DONE && opt->desc != ODESC_ERROR &&
opt->desc->optcode == optcode) {
return opt;
}
++opt;
@ -2895,7 +2900,8 @@ int retropt_bool(struct opt *opts, int optcode, bool *result) {
struct opt *opt = opts;
while (opt->desc != ODESC_END) {
if (opt->desc != ODESC_DONE && opt->desc->optcode == optcode) {
if (opt->desc != ODESC_DONE && opt->desc != ODESC_ERROR &&
opt->desc->optcode == optcode) {
*result = opt->value.u_bool;
opt->desc = ODESC_DONE;
return 0;
@ -2914,7 +2920,8 @@ int retropt_short(struct opt *opts, int optcode, short *result) {
struct opt *opt = opts;
while (opt->desc != ODESC_END) {
if (opt->desc != ODESC_DONE && opt->desc->optcode == optcode) {
if (opt->desc != ODESC_DONE && opt->desc != ODESC_ERROR &&
opt->desc->optcode == optcode) {
*result = opt->value.u_short;
opt->desc = ODESC_DONE;
return 0;
@ -2933,7 +2940,8 @@ int retropt_ushort(struct opt *opts, int optcode, unsigned short *result) {
struct opt *opt = opts;
while (opt->desc != ODESC_END) {
if (opt->desc != ODESC_DONE && opt->desc->optcode == optcode) {
if (opt->desc != ODESC_DONE && opt->desc != ODESC_ERROR &&
opt->desc->optcode == optcode) {
*result = opt->value.u_ushort;
opt->desc = ODESC_DONE;
return 0;
@ -2951,7 +2959,8 @@ int retropt_int(struct opt *opts, int optcode, int *result) {
struct opt *opt = opts;
while (opt->desc != ODESC_END) {
if (opt->desc != ODESC_DONE && opt->desc->optcode == optcode) {
if (opt->desc != ODESC_DONE && opt->desc != ODESC_ERROR &&
opt->desc->optcode == optcode) {
char *rest;
switch (opt->desc->type) {
case TYPE_INT: *result = opt->value.u_int; break;
@ -3012,7 +3021,8 @@ int retropt_uint(struct opt *opts, int optcode, unsigned int *result) {
struct opt *opt = opts;
while (opt->desc != ODESC_END) {
if (opt->desc != ODESC_DONE && opt->desc->optcode == optcode) {
if (opt->desc != ODESC_DONE && opt->desc != ODESC_ERROR &&
opt->desc->optcode == optcode) {
*result = opt->value.u_uint;
opt->desc = ODESC_DONE;
return 0;
@ -3030,7 +3040,8 @@ int retropt_long(struct opt *opts, int optcode, long *result) {
struct opt *opt = opts;
while (opt->desc != ODESC_END) {
if (opt->desc != ODESC_DONE && opt->desc->optcode == optcode) {
if (opt->desc != ODESC_DONE && opt->desc != ODESC_ERROR &&
opt->desc->optcode == optcode) {
*result = opt->value.u_long;
opt->desc = ODESC_DONE;
return 0;
@ -3048,7 +3059,8 @@ int retropt_ulong(struct opt *opts, int optcode, unsigned long *result) {
struct opt *opt = opts;
while (opt->desc != ODESC_END) {
if (opt->desc != ODESC_DONE && opt->desc->optcode == optcode) {
if (opt->desc != ODESC_DONE && opt->desc != ODESC_ERROR &&
opt->desc->optcode == optcode) {
*result = opt->value.u_ulong;
opt->desc = ODESC_DONE;
return 0;
@ -3066,7 +3078,8 @@ int retropt_flag(struct opt *opts, int optcode, flags_t *result) {
struct opt *opt = opts;
while (opt->desc != ODESC_END) {
if (opt->desc != ODESC_DONE && opt->desc->optcode == optcode) {
if (opt->desc != ODESC_DONE && opt->desc != ODESC_ERROR &&
opt->desc->optcode == optcode) {
if (opt->value.u_bool) {
*result |= opt->desc->major;
} else {
@ -3092,7 +3105,8 @@ int retropt_string(struct opt *opts, int optcode, char **result) {
struct opt *opt = opts;
while (opt->desc != ODESC_END) {
if (opt->desc != ODESC_DONE && opt->desc->optcode == optcode) {
if (opt->desc != ODESC_DONE && opt->desc != ODESC_ERROR &&
opt->desc->optcode == optcode) {
if (opt->value.u_string == NULL) {
*result = NULL;
} else if ((*result = strdup(opt->value.u_string)) == NULL) {
@ -3231,30 +3245,28 @@ int retropt_bind(struct opt *opts,
#endif /* _WITH_SOCKET */
/* applies to fd all options belonging to phase */
/* note: not all options can be applied this way (e.g. OFUNC_SPEC with PH_OPEN)
implemented are: OFUNC_FCNTL, OFUNC_SOCKOPT (probably not all types),
OFUNC_TERMIOS_FLAG, OFUNC_TERMIOS_PATTERN, and some OFUNC_SPEC */
int applyopts(int fd, struct opt *opts, enum e_phase phase) {
struct opt *opt;
opt = opts; while (opt && opt->desc != ODESC_END) {
if (opt->desc == ODESC_DONE ||
(phase != PH_ALL && opt->desc->phase != phase)) {
++opt; continue; }
/* Applies to FD all options belonging to phase */
/* Note: not all options can be applied this way */
int applyopt(
int fd,
struct opt *opt)
{
if (opt->desc == ODESC_DONE || opt->desc == ODESC_ERROR)
return 0;
if (opt->desc->func == OFUNC_SEEK32) {
if (Lseek(fd, opt->value.u_off, opt->desc->major) < 0) {
Error4("lseek(%d, "F_off", %d): %s",
fd, opt->value.u_off, opt->desc->major, strerror(errno));
return -1;
}
#if HAVE_LSEEK64
} else if (opt->desc->func == OFUNC_SEEK64) {
/*! this depends on off64_t atomic type */
if (Lseek64(fd, opt->value.u_off64, opt->desc->major) < 0) {
Error4("lseek64(%d, "F_off64", %d): %s",
fd, opt->value.u_off64, opt->desc->major, strerror(errno));
return -1;
}
#endif /* HAVE_LSEEK64 */
@ -3265,7 +3277,7 @@ int applyopts(int fd, struct opt *opts, enum e_phase phase) {
if ((flag = Fcntl(fd, opt->desc->major-1)) < 0) {
Error3("fcntl(%d, %d): %s",
fd, opt->desc->major, strerror(errno));
opt->desc = ODESC_ERROR; ++opt; continue;
return -1;
} else {
if (opt->value.u_bool) {
flag |= opt->desc->minor;
@ -3275,7 +3287,7 @@ int applyopts(int fd, struct opt *opts, enum e_phase phase) {
if (Fcntl_l(fd, opt->desc->major, flag) < 0) {
Error4("fcntl(%d, %d, %d): %s",
fd, opt->desc->major, flag, strerror(errno));
opt->desc = ODESC_ERROR; ++opt; continue;
return -1;
}
}
@ -3283,7 +3295,7 @@ int applyopts(int fd, struct opt *opts, enum e_phase phase) {
if (Ioctl(fd, opt->desc->major, (void *)&opt->value) < 0) {
Error4("ioctl(%d, 0x%x, %p): %s",
fd, opt->desc->major, (void *)&opt->value, strerror(errno));
opt->desc = ODESC_ERROR; ++opt; continue;
return -1;
}
} else if (opt->desc->func == OFUNC_IOCTL_MASK_LONG) {
@ -3295,14 +3307,14 @@ int applyopts(int fd, struct opt *opts, enum e_phase phase) {
if (Ioctl(fd, getreq, (void *)&val) < 0) {
Error4("ioctl(%d, 0x%x, %p): %s",
fd, opt->desc->major, (void *)&val, strerror(errno));
opt->desc = ODESC_ERROR; ++opt; continue;
return -1;
}
val &= ~mask;
if (opt->value.u_bool) val |= mask;
if (Ioctl(fd, setreq, (void *)&val) < 0) {
Error4("ioctl(%d, 0x%x, %p): %s",
fd, opt->desc->major, (void *)&val, strerror(errno));
opt->desc = ODESC_ERROR; ++opt; continue;
return -1;
}
} else if (opt->desc->func == OFUNC_IOCTL_GENERIC) {
@ -3311,40 +3323,41 @@ int applyopts(int fd, struct opt *opts, enum e_phase phase) {
if (Ioctl(fd, opt->value.u_int, NULL) < 0) {
Error3("ioctl(%d, 0x%x, NULL): %s",
fd, opt->value.u_int, strerror(errno));
opt->desc = ODESC_ERROR; ++opt; continue;
return -1;
}
break;
case TYPE_INT_INT:
if (Ioctl_int(fd, opt->value.u_int, opt->value2.u_int) < 0) {
Error4("ioctl(%d, 0x%x, 0x%x): %s",
fd, opt->value.u_int, opt->value2.u_int, strerror(errno));
opt->desc = ODESC_ERROR; ++opt; continue;
return -1;
}
break;
case TYPE_INT_INTP:
if (Ioctl(fd, opt->value.u_int, (void *)&opt->value2.u_int) < 0) {
Error4("ioctl(%d, 0x%x, %p): %s",
fd, opt->value.u_int, (void *)&opt->value2.u_int, strerror(errno));
opt->desc = ODESC_ERROR; ++opt; continue;
return -1;
}
break;
case TYPE_INT_BIN:
if (Ioctl(fd, opt->value.u_int, (void *)opt->value2.u_bin.b_data) < 0) {
Error4("ioctl(%d, 0x%x, %p): %s",
fd, opt->value.u_int, (void *)opt->value2.u_bin.b_data, strerror(errno));
opt->desc = ODESC_ERROR; ++opt; continue;
return -1;
}
break;
case TYPE_INT_STRING:
if (Ioctl(fd, opt->value.u_int, (void *)opt->value2.u_string) < 0) {
Error4("ioctl(%d, 0x%x, %p): %s",
fd, opt->value.u_int, (void *)opt->value2.u_string, strerror(errno));
opt->desc = ODESC_ERROR; ++opt; continue;
return -1;
}
break;
default:
Error1("ioctl() data type %d not implemented",
opt->desc->type);
return -1;
}
#if _WITH_SOCKET
@ -3361,7 +3374,7 @@ int applyopts(int fd, struct opt *opts, enum e_phase phase) {
Error6("setsockopt(%d, %d, %d, {%d,%d}, "F_Zu,
fd, opt->desc->major, opt->desc->minor, lingstru.l_onoff,
lingstru.l_linger, sizeof(lingstru));
opt->desc = ODESC_ERROR; ++opt; continue;
return -1;
}
#endif /* HAVE_STRUCT_LINGER */
} else {
@ -3374,7 +3387,7 @@ int applyopts(int fd, struct opt *opts, enum e_phase phase) {
fd, opt->desc->major, opt->desc->minor,
opt->value.u_bin.b_data, opt->value.u_bin.b_len,
strerror(errno));
opt->desc = ODESC_ERROR; ++opt; continue;
return -1;
}
break;
case TYPE_BOOL:
@ -3385,7 +3398,7 @@ int applyopts(int fd, struct opt *opts, enum e_phase phase) {
opt->desc->major, opt->desc->minor,
opt->value.u_bool, sizeof(opt->value.u_bool),
strerror(errno));
opt->desc = ODESC_ERROR; ++opt; continue;
return -1;
}
break;
case TYPE_BYTE:
@ -3394,7 +3407,7 @@ int applyopts(int fd, struct opt *opts, enum e_phase phase) {
Error6("setsockopt(%d, %d, %d, {%u}, "F_Zu"): %s",
fd, opt->desc->major, opt->desc->minor,
opt->value.u_byte, sizeof(uint8_t), strerror(errno));
opt->desc = ODESC_ERROR; ++opt; continue;
return -1;
}
break;
case TYPE_INT:
@ -3403,7 +3416,7 @@ int applyopts(int fd, struct opt *opts, enum e_phase phase) {
Error6("setsockopt(%d, %d, %d, {%d}, "F_Zu"): %s",
fd, opt->desc->major, opt->desc->minor,
opt->value.u_int, sizeof(int), strerror(errno));
opt->desc = ODESC_ERROR; ++opt; continue;
return -1;
}
break;
case TYPE_INT_NULL:
@ -3413,7 +3426,7 @@ int applyopts(int fd, struct opt *opts, enum e_phase phase) {
Error6("setsockopt(%d, %d, %d, {%d}, "F_Zu"): %s",
fd, opt->desc->major, opt->desc->minor,
opt->value.u_int, sizeof(int), strerror(errno));
opt->desc = ODESC_ERROR; ++opt; continue;
return -1;
}
break;
case TYPE_LONG:
@ -3422,7 +3435,7 @@ int applyopts(int fd, struct opt *opts, enum e_phase phase) {
Error6("setsockopt(%d, %d, %d, {%ld}, "F_Zu"): %s",
fd, opt->desc->major, opt->desc->minor,
opt->value.u_long, sizeof(long), strerror(errno));
opt->desc = ODESC_ERROR; ++opt; continue;
return -1;
}
break;
case TYPE_STRING:
@ -3433,7 +3446,7 @@ int applyopts(int fd, struct opt *opts, enum e_phase phase) {
fd, opt->desc->major, opt->desc->minor,
opt->value.u_string, strlen(opt->value.u_string)+1,
strerror(errno));
opt->desc = ODESC_ERROR; ++opt; continue;
return -1;
}
break;
case TYPE_UINT:
@ -3443,7 +3456,7 @@ int applyopts(int fd, struct opt *opts, enum e_phase phase) {
fd, opt->desc->major, opt->desc->minor,
opt->value.u_uint, sizeof(unsigned int),
strerror(errno));
opt->desc = ODESC_ERROR; ++opt; continue;
return -1;
}
break;
case TYPE_TIMEVAL:
@ -3453,7 +3466,7 @@ int applyopts(int fd, struct opt *opts, enum e_phase phase) {
fd, opt->desc->major, opt->desc->minor,
opt->value.u_timeval.tv_sec, opt->value.u_timeval.tv_usec,
sizeof(struct timeval), strerror(errno));
opt->desc = ODESC_ERROR; ++opt; continue;
return -1;
}
break;
#if HAVE_STRUCT_LINGER
@ -3468,7 +3481,7 @@ int applyopts(int fd, struct opt *opts, enum e_phase phase) {
fd, opt->desc->major, opt->desc->minor,
lingstru.l_onoff, lingstru.l_linger,
strerror(errno));
opt->desc = ODESC_ERROR; ++opt; continue;
return -1;
}
}
break;
@ -3476,13 +3489,13 @@ int applyopts(int fd, struct opt *opts, enum e_phase phase) {
#if defined(HAVE_STRUCT_IP_MREQ) || defined (HAVE_STRUCT_IP_MREQN)
case TYPE_IP_MREQN:
/* handled in applyopts_single */
++opt; continue;
break;
#endif /* defined(HAVE_STRUCT_IP_MREQ) || defined (HAVE_STRUCT_IP_MREQN) */
#if defined(HAVE_STRUCT_GROUP_SOURCE_REQ)
case TYPE_GROUP_SOURCE_REQ:
/* handled in applyopts_single */
++opt; continue;
break;
#endif /* defined(HAVE_STRUCT_GROUP_SOURCE_REQ) */
/*! still many types missing; implement on demand */
@ -3494,6 +3507,7 @@ int applyopts(int fd, struct opt *opts, enum e_phase phase) {
fd, opt->desc->major, opt->desc->minor,
*(uint32_t *)&opt->value.u_ip4addr, sizeof(opt->value.u_ip4addr),
strerror(errno));
return -1;
}
break;
#endif /* defined(WITH_IP4) */
@ -3505,7 +3519,7 @@ int applyopts(int fd, struct opt *opts, enum e_phase phase) {
Warn1("applyopts(): type %d not implemented",
opt->desc->type);
#endif
opt->desc = ODESC_ERROR; ++opt; continue;
return -1;
}
}
@ -3521,7 +3535,7 @@ int applyopts(int fd, struct opt *opts, enum e_phase phase) {
Error6("getsockopt(%d, %d, %d, %p, {"F_socklen"}): %s",
fd, opt->desc->major, opt->desc->minor, data, oldlen,
strerror(errno));
opt->desc = ODESC_ERROR; ++opt; continue;
return -1;
}
memcpy(&data[oldlen], opt->value.u_bin.b_data,
MIN(opt->value.u_bin.b_len, sizeof(data)-oldlen));
@ -3532,7 +3546,7 @@ int applyopts(int fd, struct opt *opts, enum e_phase phase) {
Error6("setsockopt(%d, %d, %d, %p, %d): %s",
fd, opt->desc->major, opt->desc->minor, data, newlen,
strerror(errno));
opt->desc = ODESC_ERROR; ++opt; continue;
return -1;
}
break;
default:
@ -3548,6 +3562,7 @@ int applyopts(int fd, struct opt *opts, enum e_phase phase) {
Error6("setsockopt(%d, %d, %d, {%d}, "F_Zu"): %s",
fd, opt->value.u_int, opt->value2.u_int,
opt->value3.u_int, sizeof(int), strerror(errno));
return -1;
}
break;
case TYPE_INT_INT_BIN:
@ -3556,6 +3571,7 @@ int applyopts(int fd, struct opt *opts, enum e_phase phase) {
Error5("setsockopt(%d, %d, %d, {...}, "F_Zu"): %s",
fd, opt->value.u_int, opt->value2.u_int,
opt->value3.u_bin.b_len, strerror(errno));
return -1;
}
break;
case TYPE_INT_INT_STRING:
@ -3566,11 +3582,13 @@ int applyopts(int fd, struct opt *opts, enum e_phase phase) {
fd, opt->value.u_int, opt->value2.u_int,
opt->value3.u_string, strlen(opt->value3.u_string)+1,
strerror(errno));
return -1;
}
break;
default:
Error1("setsockopt() data type %d not implemented",
opt->desc->type);
return -1;
}
#endif /* _WITH_SOCKET */
@ -3579,7 +3597,7 @@ int applyopts(int fd, struct opt *opts, enum e_phase phase) {
if (Flock(fd, opt->desc->major) < 0) {
Error3("flock(%d, %d): %s",
fd, opt->desc->major, strerror(errno));
opt->desc = ODESC_ERROR; ++opt; continue;
return -1;
}
#endif /* defined(HAVE_FLOCK) */
@ -3591,7 +3609,7 @@ int applyopts(int fd, struct opt *opts, enum e_phase phase) {
if (Fchown(fd, opt->value.u_uidt, -1) < 0) {
Error3("fchown(%d, "F_uid", -1): %s",
fd, opt->value.u_uidt, strerror(errno));
opt->desc = ODESC_ERROR; ++opt; continue;
return -1;
}
break;
case OPT_GROUP:
@ -3599,7 +3617,7 @@ int applyopts(int fd, struct opt *opts, enum e_phase phase) {
if (Fchown(fd, -1, opt->value.u_gidt) < 0) {
Error3("fchown(%d, -1, "F_gid"): %s",
fd, opt->value.u_gidt, strerror(errno));
opt->desc = ODESC_ERROR; ++opt; continue;
return -1;
}
break;
case OPT_PERM:
@ -3607,14 +3625,14 @@ int applyopts(int fd, struct opt *opts, enum e_phase phase) {
if (Fchmod(fd, opt->value.u_modet) < 0) {
Error3("fchmod(%d, %u): %s",
fd, opt->value.u_modet, strerror(errno));
opt->desc = ODESC_ERROR; ++opt; continue;
return -1;
}
break;
case OPT_FTRUNCATE32:
if (Ftruncate(fd, opt->value.u_off) < 0) {
Error3("ftruncate(%d, "F_off"): %s",
fd, opt->value.u_off, strerror(errno));
opt->desc = ODESC_ERROR; ++opt; continue;
return -1;
}
break;
#if HAVE_FTRUNCATE64
@ -3622,7 +3640,7 @@ int applyopts(int fd, struct opt *opts, enum e_phase phase) {
if (Ftruncate64(fd, opt->value.u_off64) < 0) {
Error3("ftruncate64(%d, "F_off64"): %s",
fd, opt->value.u_off64, strerror(errno));
opt->desc = ODESC_ERROR; ++opt; continue;
return -1;
}
#endif /* HAVE_FTRUNCATE64 */
break;
@ -3639,7 +3657,7 @@ int applyopts(int fd, struct opt *opts, enum e_phase phase) {
l.l_pid = 0; /* hope this uses our current process */
if (Fcntl_lock(fd, opt->desc->major, &l) < 0) {
Error3("fcntl(%d, %d, {type=F_WRLCK,whence=SEEK_SET,start=0,len=LONG_MAX,pid=0}): %s", fd, opt->desc->major, strerror(errno));
opt->desc = ODESC_ERROR; ++opt; continue;
return -1;
}
}
break;
@ -3648,7 +3666,7 @@ int applyopts(int fd, struct opt *opts, enum e_phase phase) {
if (Setuid(opt->value.u_uidt) < 0) {
Error2("setuid("F_uid"): %s", opt->value.u_uidt,
strerror(errno));
opt->desc = ODESC_ERROR; ++opt; continue;
return -1;
}
break;
case OPT_SETGID_EARLY:
@ -3656,7 +3674,7 @@ int applyopts(int fd, struct opt *opts, enum e_phase phase) {
if (Setgid(opt->value.u_gidt) < 0) {
Error2("setgid("F_gid"): %s", opt->value.u_gidt,
strerror(errno));
opt->desc = ODESC_ERROR; ++opt; continue;
return -1;
}
break;
case OPT_SUBSTUSER_EARLY:
@ -3666,36 +3684,44 @@ int applyopts(int fd, struct opt *opts, enum e_phase phase) {
if ((pwd = getpwuid(opt->value.u_uidt)) == NULL) {
Error1("getpwuid("F_uid"): no such user",
opt->value.u_uidt);
opt->desc = ODESC_ERROR; ++opt; continue;
return -1;
}
if (Initgroups(pwd->pw_name, pwd->pw_gid) < 0) {
Error3("initgroups(%s, "F_gid"): %s",
pwd->pw_name, pwd->pw_gid, strerror(errno));
opt->desc = ODESC_ERROR; ++opt; continue;
return -1;
}
if (Setgid(pwd->pw_gid) < 0) {
Error2("setgid("F_gid"): %s", pwd->pw_gid,
strerror(errno));
opt->desc = ODESC_ERROR; ++opt; continue;
return -1;
}
if (Setuid(opt->value.u_uidt) < 0) {
Error2("setuid("F_uid"): %s", opt->value.u_uidt,
strerror(errno));
opt->desc = ODESC_ERROR; ++opt; continue;
return -1;
}
#if 1
if (setenv("USER", pwd->pw_name, 1) < 0)
if (setenv("USER", pwd->pw_name, 1) < 0) {
Error1("setenv(\"USER\", \"%s\", 1): insufficient space",
pwd->pw_name);
if (setenv("LOGNAME", pwd->pw_name, 1) < 0)
return -1;
}
if (setenv("LOGNAME", pwd->pw_name, 1) < 0) {
Error1("setenv(\"LOGNAME\", \"%s\", 1): insufficient space",
pwd->pw_name);
if (setenv("HOME", pwd->pw_dir, 1) < 0)
return -1;
}
if (setenv("HOME", pwd->pw_dir, 1) < 0) {
Error1("setenv(\"HOME\", \"%s\", 1): insufficient space",
pwd->pw_dir);
if (setenv("SHELL", pwd->pw_shell, 1) < 0)
return -1;
}
if (setenv("SHELL", pwd->pw_shell, 1) < 0) {
Error1("setenv(\"SHELL\", \"%s\", 1): insufficient space",
pwd->pw_shell);
return -1;
}
#endif
}
break;
@ -3707,24 +3733,24 @@ int applyopts(int fd, struct opt *opts, enum e_phase phase) {
if ((pwd = getpwuid(opt->value.u_uidt)) == NULL) {
Error1("getpwuid("F_uid"): no such user",
opt->value.u_uidt);
opt->desc = ODESC_ERROR; ++opt; continue;
return -1;
}
delayeduser_uid = opt->value.u_uidt;
delayeduser_gid = pwd->pw_gid;
if ((delayeduser_name = strdup(pwd->pw_name)) == NULL) {
Error1("strdup("F_Zu"): out of memory",
strlen(pwd->pw_name)+1);
opt->desc = ODESC_ERROR; ++opt; continue;
return -1;
}
if ((delayeduser_dir = strdup(pwd->pw_dir)) == NULL) {
Error1("strdup("F_Zu"): out of memory",
strlen(pwd->pw_dir)+1);
opt->desc = ODESC_ERROR; ++opt; continue;
return -1;
}
if ((delayeduser_shell = strdup(pwd->pw_shell)) == NULL) {
Error1("strdup("F_Zu"): out of memory",
strlen(pwd->pw_shell)+1);
opt->desc = ODESC_ERROR; ++opt; continue;
return -1;
}
/* function to get all supplementary groups of user */
delayeduser_ngids = sizeof(delayeduser_gids)/sizeof(gid_t);
@ -3739,10 +3765,11 @@ int applyopts(int fd, struct opt *opts, enum e_phase phase) {
if (Chroot(opt->value.u_string) < 0) {
Error2("chroot(\"%s\"): %s", opt->value.u_string,
strerror(errno));
opt->desc = ODESC_ERROR; ++opt; continue;
return -1;
}
if (Chdir("/") < 0) {
Error1("chdir(\"/\"): %s", strerror(errno));
return -1;
}
break;
case OPT_SETSID:
@ -3754,6 +3781,7 @@ int applyopts(int fd, struct opt *opts, enum e_phase phase) {
} else {
if (Setsid() < 0) {
Error1("setsid(): %s", strerror(errno));
return -1;
}
}
}
@ -3805,15 +3833,15 @@ int applyopts(int fd, struct opt *opts, enum e_phase phase) {
break;
#endif /* _WITH_INTERFACE */
default: Error1("applyopts(): option \"%s\" not implemented",
default: Error1("INTERNAL: applyopts(): option \"%s\" not implemented",
opt->desc->defname);
opt->desc = ODESC_ERROR; ++opt; continue;
return -1;
}
#if WITH_TERMIOS
} else if (opt->desc->func == OFUNC_TERMIOS_FLAG) {
if (xiotermiosflag_applyopt(fd, opt) < 0) {
opt->desc = ODESC_ERROR; ++opt; continue;
return -1;
}
} else if (opt->desc->func == OFUNC_TERMIOS_VALUE) {
@ -3821,33 +3849,33 @@ int applyopts(int fd, struct opt *opts, enum e_phase phase) {
(opt->value.u_uint << opt->desc->arg3)) {
Error2("option %s: invalid value %u",
opt->desc->defname, opt->value.u_uint);
opt->desc = ODESC_ERROR; ++opt; continue;
return -1;
}
if (xiotermios_value(fd, opt->desc->major, opt->desc->minor,
(opt->value.u_uint << opt->desc->arg3) & opt->desc->minor) < 0) {
opt->desc = ODESC_ERROR; ++opt; continue;
return -1;
}
} else if (opt->desc->func == OFUNC_TERMIOS_PATTERN) {
if (xiotermios_value(fd, opt->desc->major, opt->desc->arg3, opt->desc->minor) < 0) {
opt->desc = ODESC_ERROR; ++opt; continue;
return -1;
}
} else if (opt->desc->func == OFUNC_TERMIOS_CHAR) {
if (xiotermios_char(fd, opt->desc->major, opt->value.u_byte) < 0) {
opt->desc = ODESC_ERROR; ++opt; continue;
return -1;
}
#ifdef HAVE_TERMIOS_ISPEED
} else if (opt->desc->func == OFUNC_TERMIOS_SPEED) {
if (xiotermios_speed(fd, opt->desc->major, opt->value.u_uint) < 0) {
opt->desc = ODESC_ERROR; ++opt; continue;
return -1;
}
#endif /* HAVE_TERMIOS_ISPEED */
} else if (opt->desc->func == OFUNC_TERMIOS_SPEC) {
if (xiotermios_spec(fd, opt->desc->optcode) < 0) {
opt->desc = ODESC_ERROR; ++opt; continue;
return -1;
}
#endif /* WITH_TERMIOS */
@ -3864,14 +3892,30 @@ int applyopts(int fd, struct opt *opts, enum e_phase phase) {
if (opt->desc->func != OFUNC_EXT && opt->desc->func != OFUNC_SIGNAL) {
Error1("applyopts(): internal error: option \"%s\" does not apply",
opt->desc->defname);
opt->desc = ODESC_ERROR;
++opt;
continue;
return -1;
}
++opt;
continue;
return 0;
}
opt->desc = ODESC_DONE;
return 0;
}
/* Note: not all options can be applied this way (e.g. OFUNC_SPEC with PH_OPEN)
implemented are: OFUNC_FCNTL, OFUNC_SOCKOPT (probably not all types),
OFUNC_TERMIOS_FLAG, OFUNC_TERMIOS_PATTERN, and some OFUNC_SPEC */
int applyopts(int fd, struct opt *opts, enum e_phase phase)
{
struct opt *opt;
int rc;
opt = opts;
while (opt && opt->desc != ODESC_END) {
if (opt->desc != ODESC_DONE && opt->desc != ODESC_ERROR &&
(phase == PH_ALL || phase == opt->desc->phase)) {
rc = applyopt(fd, opt);
if (rc < 0)
opt->desc = ODESC_ERROR;
}
++opt;
}
@ -3898,6 +3942,56 @@ int applyopts2(int fd, struct opt *opts, unsigned int from, unsigned int to) {
return 0;
}
/* Apply and consume all options of type FLAG and group.
Return 0 when everything went right, or -1 if an error occurred. */
int applyopts_optgroup(
int fd,
struct opt *opts,
unsigned int from, /* -1: from first phase, not in order */
unsigned int to, /* -1: to last phase, not in order */
groups_t groups)
{
struct opt *opt = opts;
unsigned int i;
if (opts == NULL)
return 0;
/* Just apply all opts matching from/to phases, in their stored order */
if (from < 0 || to < 0) {
if (from < 0)
from = 0;
if (to < 0)
to = UINT_MAX;
while (opt->desc != ODESC_END) {
if (opt->desc == ODESC_DONE || opt->desc == ODESC_ERROR)
continue;
if ((opt->desc->group & groups) == 0)
continue;
if (opt->desc->phase < from ||
opt->desc->phase > to)
continue;
applyopt(fd, opt);
/* Dont check rc: on error is should not come here */
++opt;
}
}
/* Just apply all opts from, to phase, in their phases order */
for (i = from; i <= to; ++i) {
while (opt->desc != ODESC_END) {
if (opt->desc != ODESC_DONE && opt->desc != ODESC_ERROR &&
(opt->desc->group & groups) && opt->desc->phase == i) {
applyopt(fd, opt);
/* Dont check rc: on error is should not come here */
}
++opt;
}
}
return 0;
}
/* apply and consume all options of type FLAG and group.
Return 0 if everything went right, or -1 if an error occurred. */
int applyopts_flags(struct opt *opts, groups_t group, flags_t *result) {
@ -3906,7 +4000,7 @@ int applyopts_flags(struct opt *opts, groups_t group, flags_t *result) {
if (!opts) return 0;
while (opt->desc != ODESC_END) {
if (opt->desc != ODESC_DONE &&
if (opt->desc != ODESC_DONE && opt->desc != ODESC_ERROR &&
(opt->desc->group & group)) {
if (opt->desc->func == OFUNC_FLAG) {
if (opt->value.u_bool) {
@ -3927,7 +4021,6 @@ int applyopts_flags(struct opt *opts, groups_t group, flags_t *result) {
}
/* set the FD_CLOEXEC fcntl if the options do not set it to 0 */
int applyopts_cloexec(int fd, struct opt *opts) {
bool docloexec = 1;
@ -4002,7 +4095,7 @@ int applyopts_offset(struct single *xfd, struct opt *opts) {
struct opt *opt;
opt = opts; while (opt->desc != ODESC_END) {
if ((opt->desc == ODESC_DONE) ||
if ((opt->desc == ODESC_DONE || opt->desc == ODESC_ERROR) ||
opt->desc->func != OFUNC_OFFSET) {
++opt; continue; }
@ -4022,7 +4115,7 @@ int applyopts_single(struct single *xfd, struct opt *opts, enum e_phase phase) {
if (!opts) return 0;
opt = opts; while (opt->desc != ODESC_END) {
if ((opt->desc == ODESC_DONE) ||
if ((opt->desc == ODESC_DONE || opt->desc == ODESC_ERROR) ||
(opt->desc->phase != phase && phase != PH_ALL)) {
/* option not handled in this function */
++opt; continue;
@ -4193,7 +4286,8 @@ int applyopts_signal(struct single *xfd, struct opt *opts) {
if (!opts) return 0;
opt = opts; while (opt->desc != ODESC_END) {
if (opt->desc == ODESC_DONE || opt->desc->func != OFUNC_SIGNAL) {
if (opt->desc == ODESC_DONE || opt->desc == ODESC_ERROR ||
opt->desc->func != OFUNC_SIGNAL) {
++opt; continue;
}
@ -4223,10 +4317,13 @@ int _xio_openlate(struct single *fd, struct opt *opts) {
if ((result = applyopts(fd->fd, opts, PH_LATE2)) < 0) {
return result;
}
if ((result = applyopts(fd->fd, opts, PH_PASTEXEC)) < 0) {
return result;
}
if ((numleft = leftopts(opts)) > 0) {
showleft(opts);
Error1("%d option(s) could not be used", numleft);
Error1("INTERNAL: %d option(s) remained unused", numleft);
return -1;
}
return 0;
@ -4240,7 +4337,8 @@ int dropopts(struct opt *opts, unsigned int phase) {
return 0;
}
opt = opts; while (opt && opt->desc != ODESC_END) {
if (opt->desc != ODESC_DONE && opt->desc->phase == phase) {
if (opt->desc != ODESC_DONE && opt->desc != ODESC_ERROR &&
opt->desc->phase == phase) {
Debug1("ignoring option \"%s\"", opt->desc->defname);
opt->desc = ODESC_DONE;
}

View file

@ -121,6 +121,8 @@ enum e_func {
# define ENABLE_OFUNC
# include "xio-streams.h" /* push a POSIX STREAMS module */
# undef ENABLE_OFUNC
OFUNC_SET_NAMESPACE, /* set/change Linux namespace */
OFUNC_RESET_NAMESPACE, /* set Linux namespace back to default */
} ;
/* for simpler handling of option-to-connection-type relations we define
@ -610,6 +612,7 @@ enum e_optcode {
OPT_RANGE, /* restrict client socket address */
OPT_RAW, /* termios */
OPT_READBYTES,
OPT_RESET_NETNS, /* reset net namespace - not an option, just op! */
OPT_RES_AAONLY, /* resolver(3) */
OPT_RES_DEBUG, /* resolver(3) */
OPT_RES_DEFNAMES, /* resolver(3) */
@ -641,6 +644,7 @@ enum e_optcode {
OPT_SETSOCKOPT_STRING,
OPT_SETUID,
OPT_SETUID_EARLY,
OPT_SET_NETNS, /* set net namespace */
OPT_SHUT_CLOSE,
OPT_SHUT_DOWN,
OPT_SHUT_NONE,
@ -924,6 +928,7 @@ enum e_phase {
PH_LATE2, /* FD is ready, dropping privileges */
PH_PREEXEC, /* before exec() or system() */
PH_EXEC, /* during exec() or system() */
PH_PASTEXEC, /* only reached on addresses that do NOT exec() */
PH_SPEC /* specific to situation, not fix */
} ;
@ -961,8 +966,8 @@ extern int retropt_string(struct opt *opts, int optcode, char **result);
extern int retropt_timespec(struct opt *opts, int optcode, struct timespec *result);
extern int retropt_bind(struct opt *opts, int af, int socktype, int ipproto, struct sockaddr *sa, socklen_t *salen, int feats, const int ai_flags[2]);
extern int applyopts(int fd, struct opt *opts, enum e_phase phase);
extern int applyopts2(int fd, struct opt *opts, unsigned int from,
unsigned int to);
extern int applyopts2(int fd, struct opt *opts, unsigned int from, unsigned int to);
extern int applyopts_optgroup(int fd, struct opt *opts, unsigned int from, unsigned int to, groups_t groups);
extern int applyopts_flags(struct opt *opts, groups_t group, flags_t *result);
extern int applyopts_cloexec(int fd, struct opt *opts);
extern int applyopts_early(const char *path, struct opt *opts);