mirror of
https://repo.or.cz/socat.git
synced 2024-12-22 07:22:34 +00:00
New option netns for network namespace setting
This commit is contained in:
parent
c82e3df210
commit
f152c55584
26 changed files with 686 additions and 119 deletions
4
CHANGES
4
CHANGES
|
@ -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/
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
24
configure.ac
24
configure.ac
|
@ -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
|
||||
|
|
63
doc/socat.yo
63
doc/socat.yo
|
@ -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 \
|
||||
TCP4-LISTEN:8000,reuseaddr,fork,netns=namespace1 \
|
||||
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 \
|
||||
TUN:192.168.2.1/24,up \
|
||||
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)
|
||||
|
|
11
procan.c
11
procan.c
|
@ -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?? */
|
||||
|
|
5
socat.c
5
socat.c
|
@ -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
21
sycls.c
|
@ -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) {
|
||||
|
|
2
sycls.h
2
sycls.h
|
@ -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()
|
||||
|
|
|
@ -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
210
test.sh
|
@ -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
|
||||
|
||||
##################################################################################
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
73
xio-namespaces.c
Normal 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
18
xio-namespaces.h
Normal 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 */
|
|
@ -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;
|
||||
|
|
|
@ -70,4 +70,3 @@ int _xioopen_setdelayeduser(void) {
|
|||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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"
|
||||
|
|
37
xioopen.c
37
xioopen.c
|
@ -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
286
xioopts.c
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue