From e0fc82203d677a6f1e808e9a1a46176c109d89be Mon Sep 17 00:00:00 2001 From: Laurent Bercot Date: Mon, 15 Dec 2014 23:08:59 +0000 Subject: Initial commit --- .gitignore | 5 + AUTHORS | 7 + COPYING | 13 + INSTALL | 134 +++++++ Makefile | 121 ++++++ README | 25 ++ README.macosx | 4 + README.solaris | 12 + configure | 378 ++++++++++++++++++ doc/index.html | 182 +++++++++ doc/libs6net/accessrules.html | 331 ++++++++++++++++ doc/libs6net/ident.html | 124 ++++++ doc/libs6net/index.html | 63 +++ doc/localservice.html | 151 +++++++ doc/minidentd.html | 83 ++++ doc/s6-accessrules-cdb-from-fs.html | 141 +++++++ doc/s6-accessrules-fs-from-cdb.html | 60 +++ doc/s6-clockadd.html | 61 +++ doc/s6-clockview.html | 41 ++ doc/s6-connlimit.html | 96 +++++ doc/s6-getservbyname.html | 48 +++ doc/s6-ident-client.html | 52 +++ doc/s6-ioconnect.html | 84 ++++ doc/s6-ipcclient.html | 65 +++ doc/s6-ipcserver-access.html | 172 ++++++++ doc/s6-ipcserver.html | 148 +++++++ doc/s6-sntpclock.html | 166 ++++++++ doc/s6-sudo.html | 59 +++ doc/s6-sudoc.html | 80 ++++ doc/s6-sudod.html | 162 ++++++++ doc/s6-taiclock.html | 114 ++++++ doc/s6-taiclockd.html | 51 +++ doc/s6-tcpclient.html | 133 +++++++ doc/s6-tcpserver-access.html | 239 +++++++++++ doc/s6-tcpserver.html | 72 ++++ doc/s6-tcpserver4.html | 123 ++++++ doc/s6-tcpserver6.html | 122 ++++++ doc/seekablepipe.html | 36 ++ doc/upgrade.html | 34 ++ package/deps-build | 3 + package/deps.mak | 69 ++++ package/info | 4 + package/modes | 24 ++ package/targets.mak | 40 ++ patch-for-solaris | 17 + src/clock/deps-exe/s6-clockadd | 2 + src/clock/deps-exe/s6-clockview | 2 + src/clock/deps-exe/s6-sntpclock | 3 + src/clock/deps-exe/s6-taiclock | 3 + src/clock/deps-exe/s6-taiclockd | 3 + src/clock/s6-clockadd.c | 58 +++ src/clock/s6-clockview.c | 31 ++ src/clock/s6-sntpclock.c | 238 +++++++++++ src/clock/s6-taiclock.c | 182 +++++++++ src/clock/s6-taiclockd.c | 54 +++ src/conn-tools/deps-exe/s6-accessrules-cdb-from-fs | 4 + src/conn-tools/deps-exe/s6-accessrules-fs-from-cdb | 1 + src/conn-tools/deps-exe/s6-connlimit | 1 + src/conn-tools/deps-exe/s6-getservbyname | 1 + src/conn-tools/deps-exe/s6-ident-client | 4 + src/conn-tools/deps-exe/s6-ioconnect | 2 + src/conn-tools/deps-exe/s6-ipcclient | 2 + src/conn-tools/deps-exe/s6-ipcserver | 2 + src/conn-tools/deps-exe/s6-ipcserver-access | 2 + src/conn-tools/deps-exe/s6-sudo | 1 + src/conn-tools/deps-exe/s6-sudoc | 2 + src/conn-tools/deps-exe/s6-sudod | 2 + src/conn-tools/deps-exe/s6-tcpclient | 5 + src/conn-tools/deps-exe/s6-tcpserver | 1 + src/conn-tools/deps-exe/s6-tcpserver-access | 5 + src/conn-tools/deps-exe/s6-tcpserver4 | 2 + src/conn-tools/deps-exe/s6-tcpserver6 | 2 + src/conn-tools/deps-exe/seekablepipe | 1 + src/conn-tools/s6-accessrules-cdb-from-fs.c | 195 +++++++++ src/conn-tools/s6-accessrules-fs-from-cdb.c | 177 +++++++++ src/conn-tools/s6-connlimit.c | 39 ++ src/conn-tools/s6-getservbyname.c | 28 ++ src/conn-tools/s6-ident-client.c | 71 ++++ src/conn-tools/s6-ioconnect.c | 189 +++++++++ src/conn-tools/s6-ipcclient.c | 66 +++ src/conn-tools/s6-ipcserver-access.c | 211 ++++++++++ src/conn-tools/s6-ipcserver.c | 441 +++++++++++++++++++++ src/conn-tools/s6-sudo.c | 67 ++++ src/conn-tools/s6-sudo.h | 11 + src/conn-tools/s6-sudoc.c | 115 ++++++ src/conn-tools/s6-sudod.c | 233 +++++++++++ src/conn-tools/s6-tcpclient.c | 377 ++++++++++++++++++ src/conn-tools/s6-tcpserver-access.c | 387 ++++++++++++++++++ src/conn-tools/s6-tcpserver.c | 71 ++++ src/conn-tools/s6-tcpserver4.c | 414 +++++++++++++++++++ src/conn-tools/s6-tcpserver6.c | 412 +++++++++++++++++++ src/conn-tools/seekablepipe.c | 41 ++ src/include/s6-networking/accessrules.h | 53 +++ src/include/s6-networking/ident.h | 26 ++ src/include/s6-networking/s6net.h | 9 + src/libs6net/deps-lib/s6net | 12 + src/libs6net/s6net_accessrules_backend_cdb.c | 38 ++ src/libs6net/s6net_accessrules_backend_fs.c | 58 +++ src/libs6net/s6net_accessrules_keycheck_ip4.c | 24 ++ src/libs6net/s6net_accessrules_keycheck_ip6.c | 27 ++ .../s6net_accessrules_keycheck_reversedns.c | 27 ++ src/libs6net/s6net_accessrules_keycheck_uidgid.c | 16 + src/libs6net/s6net_accessrules_uidgid_cdb.c | 11 + src/libs6net/s6net_accessrules_uidgid_fs.c | 10 + src/libs6net/s6net_ident_client.c | 22 + src/libs6net/s6net_ident_error.c | 33 ++ src/libs6net/s6net_ident_reply_get.c | 46 +++ src/libs6net/s6net_ident_reply_parse.c | 58 +++ src/minidentd/deps-exe/minidentd | 4 + src/minidentd/mgetuid-default.c | 15 + src/minidentd/mgetuid-linux.c | 169 ++++++++ src/minidentd/mgetuid.h | 11 + src/minidentd/minidentd.c | 273 +++++++++++++ tools/gen-deps.sh | 79 ++++ tools/install.sh | 64 +++ 115 files changed, 9395 insertions(+) create mode 100644 .gitignore create mode 100644 AUTHORS create mode 100644 COPYING create mode 100644 INSTALL create mode 100644 Makefile create mode 100644 README create mode 100644 README.macosx create mode 100644 README.solaris create mode 100755 configure create mode 100644 doc/index.html create mode 100644 doc/libs6net/accessrules.html create mode 100644 doc/libs6net/ident.html create mode 100644 doc/libs6net/index.html create mode 100644 doc/localservice.html create mode 100644 doc/minidentd.html create mode 100644 doc/s6-accessrules-cdb-from-fs.html create mode 100644 doc/s6-accessrules-fs-from-cdb.html create mode 100644 doc/s6-clockadd.html create mode 100644 doc/s6-clockview.html create mode 100644 doc/s6-connlimit.html create mode 100644 doc/s6-getservbyname.html create mode 100644 doc/s6-ident-client.html create mode 100644 doc/s6-ioconnect.html create mode 100644 doc/s6-ipcclient.html create mode 100644 doc/s6-ipcserver-access.html create mode 100644 doc/s6-ipcserver.html create mode 100644 doc/s6-sntpclock.html create mode 100644 doc/s6-sudo.html create mode 100644 doc/s6-sudoc.html create mode 100644 doc/s6-sudod.html create mode 100644 doc/s6-taiclock.html create mode 100644 doc/s6-taiclockd.html create mode 100644 doc/s6-tcpclient.html create mode 100644 doc/s6-tcpserver-access.html create mode 100644 doc/s6-tcpserver.html create mode 100644 doc/s6-tcpserver4.html create mode 100644 doc/s6-tcpserver6.html create mode 100644 doc/seekablepipe.html create mode 100644 doc/upgrade.html create mode 100644 package/deps-build create mode 100644 package/deps.mak create mode 100644 package/info create mode 100644 package/modes create mode 100644 package/targets.mak create mode 100755 patch-for-solaris create mode 100644 src/clock/deps-exe/s6-clockadd create mode 100644 src/clock/deps-exe/s6-clockview create mode 100644 src/clock/deps-exe/s6-sntpclock create mode 100644 src/clock/deps-exe/s6-taiclock create mode 100644 src/clock/deps-exe/s6-taiclockd create mode 100644 src/clock/s6-clockadd.c create mode 100644 src/clock/s6-clockview.c create mode 100644 src/clock/s6-sntpclock.c create mode 100644 src/clock/s6-taiclock.c create mode 100644 src/clock/s6-taiclockd.c create mode 100644 src/conn-tools/deps-exe/s6-accessrules-cdb-from-fs create mode 100644 src/conn-tools/deps-exe/s6-accessrules-fs-from-cdb create mode 100644 src/conn-tools/deps-exe/s6-connlimit create mode 100644 src/conn-tools/deps-exe/s6-getservbyname create mode 100644 src/conn-tools/deps-exe/s6-ident-client create mode 100644 src/conn-tools/deps-exe/s6-ioconnect create mode 100644 src/conn-tools/deps-exe/s6-ipcclient create mode 100644 src/conn-tools/deps-exe/s6-ipcserver create mode 100644 src/conn-tools/deps-exe/s6-ipcserver-access create mode 100644 src/conn-tools/deps-exe/s6-sudo create mode 100644 src/conn-tools/deps-exe/s6-sudoc create mode 100644 src/conn-tools/deps-exe/s6-sudod create mode 100644 src/conn-tools/deps-exe/s6-tcpclient create mode 100644 src/conn-tools/deps-exe/s6-tcpserver create mode 100644 src/conn-tools/deps-exe/s6-tcpserver-access create mode 100644 src/conn-tools/deps-exe/s6-tcpserver4 create mode 100644 src/conn-tools/deps-exe/s6-tcpserver6 create mode 100644 src/conn-tools/deps-exe/seekablepipe create mode 100644 src/conn-tools/s6-accessrules-cdb-from-fs.c create mode 100644 src/conn-tools/s6-accessrules-fs-from-cdb.c create mode 100644 src/conn-tools/s6-connlimit.c create mode 100644 src/conn-tools/s6-getservbyname.c create mode 100644 src/conn-tools/s6-ident-client.c create mode 100644 src/conn-tools/s6-ioconnect.c create mode 100644 src/conn-tools/s6-ipcclient.c create mode 100644 src/conn-tools/s6-ipcserver-access.c create mode 100644 src/conn-tools/s6-ipcserver.c create mode 100644 src/conn-tools/s6-sudo.c create mode 100644 src/conn-tools/s6-sudo.h create mode 100644 src/conn-tools/s6-sudoc.c create mode 100644 src/conn-tools/s6-sudod.c create mode 100644 src/conn-tools/s6-tcpclient.c create mode 100644 src/conn-tools/s6-tcpserver-access.c create mode 100644 src/conn-tools/s6-tcpserver.c create mode 100644 src/conn-tools/s6-tcpserver4.c create mode 100644 src/conn-tools/s6-tcpserver6.c create mode 100644 src/conn-tools/seekablepipe.c create mode 100644 src/include/s6-networking/accessrules.h create mode 100644 src/include/s6-networking/ident.h create mode 100644 src/include/s6-networking/s6net.h create mode 100644 src/libs6net/deps-lib/s6net create mode 100644 src/libs6net/s6net_accessrules_backend_cdb.c create mode 100644 src/libs6net/s6net_accessrules_backend_fs.c create mode 100644 src/libs6net/s6net_accessrules_keycheck_ip4.c create mode 100644 src/libs6net/s6net_accessrules_keycheck_ip6.c create mode 100644 src/libs6net/s6net_accessrules_keycheck_reversedns.c create mode 100644 src/libs6net/s6net_accessrules_keycheck_uidgid.c create mode 100644 src/libs6net/s6net_accessrules_uidgid_cdb.c create mode 100644 src/libs6net/s6net_accessrules_uidgid_fs.c create mode 100644 src/libs6net/s6net_ident_client.c create mode 100644 src/libs6net/s6net_ident_error.c create mode 100644 src/libs6net/s6net_ident_reply_get.c create mode 100644 src/libs6net/s6net_ident_reply_parse.c create mode 100755 src/minidentd/deps-exe/minidentd create mode 100644 src/minidentd/mgetuid-default.c create mode 100644 src/minidentd/mgetuid-linux.c create mode 100644 src/minidentd/mgetuid.h create mode 100644 src/minidentd/minidentd.c create mode 100755 tools/gen-deps.sh create mode 100755 tools/install.sh diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5c6415e --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +*.o +*.a +*.lo +*.so +*.so.* diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..bd87419 --- /dev/null +++ b/AUTHORS @@ -0,0 +1,7 @@ +Main author: + Laurent Bercot + +Thanks to: + Jean Marot + Dan J. Bernstein + Vallo Kallaste diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..63309ba --- /dev/null +++ b/COPYING @@ -0,0 +1,13 @@ +Copyright (c) 2011-2014 Laurent Bercot + +Permission to use, copy, modify, and distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..77f3e3f --- /dev/null +++ b/INSTALL @@ -0,0 +1,134 @@ +Build Instructions +------------------ + +* Requirements + ------------ + + - A POSIX-compliant C development environment + - GNU make version 3.81 or later + - skalibs version 2.0.0.0 or later: http://skarnet.org/software/skalibs/ + - execline version 2.0.0.0 or later: http://skarnet.org/software/execline/ + - s6-dns version 2.0.0.0 or later: http://skarnet.org/software/s6-dns/ + + This software will run on any operating system that implements +POSIX.1-2008, available at: + http://pubs.opengroup.org/onlinepubs/9699919799/ + + +* Standard usage + -------------- + + ./configure && make && sudo make install + + will work for most users. + It will install the binaries in /bin and the static libraries in +/usr/lib/s6-networking. + + You can strip the binaries and libraries of their extra symbols via +"make strip" before the "make install" phase. It will shave a few bytes +off them. + + +* Customization + ------------- + + You can customize paths via flags given to configure. + See ./configure --help for a list of all available configure options. + + +* Environment variables + --------------------- + + Controlling a build process via environment variables is a big and +dangerous hammer. You should try and pass flags to configure instead; +nevertheless, the standard environment variables are recognized. + + The value of the CROSS_COMPILE environment variable will prefix the +building tools' names. The --enable-cross option is preferred, see +"Cross-compilation" below. + + If the CC environment variable is set, its value will override compiler +detection by configure. + + The values of CFLAGS, CPPFLAGS and LDFLAGS will be appended to flags +auto-detected by configure. To entirely override the flags set by +configure, use make -e. + + The value of LDLIBS will be appended by make to command lines that link +an executable, even without the -e option. + + The Makefile supports the DESTDIR convention for staging. + + +* Shared libraries + ---------------- + + Software from skarnet.org is small enough that shared libraries are +generally not worth using. Static linking is simpler and incurs less +runtime overhead and less points of failure: so by default, shared +libraries are not built and binaries are linked against the static +versions of the skarnet.org libraries. Nevertheless, you can: + * build shared libraries: --enable-shared + * link binaries against shared libraries: --disable-allstatic + + +* Static binaries + --------------- + + By default, binaries are linked against static versions of all the +libraries they depend on, except for the libc. You can enforce +linking against the static libc with --enable-static-libc. + + (If you are using a GNU/Linux system, be aware that the GNU libc +behaves badly with static linking and produces huge executables, +which is why it is not the default. Other libcs are better suited +to static linking, for instance musl: http://musl-libc.org/) + + +* Cross-compilation + ----------------- + + skarnet.org packages centralize all the difficulty of +cross-compilation in one place: skalibs. Once you have +cross-compiled skalibs, the rest is easy. + + Use the --enable-cross=PREFIX option to configure, or simply +--enable-cross if your default toolchain is a cross-compiling +toolchain. And make sure to use the correct version of skalibs +for your target, and the correct sysdeps directory, making use +of the --with-include, --with-lib, --with-dynlib and --with-sysdeps +options as necessary. + + +* The slashpackage convention + --------------------------- + + The slashpackage convention (http://cr.yp.to/slashpackage.html) +is a package installation scheme that provides a few guarantees +over other conventions such as the FHS, for instance fixed +absolute pathnames. skarnet.org packages support it: use the +--enable-slashpackage option to configure, or +--enable-slashpackage=DIR for a prefixed DIR/package tree. +This option will activate slashpackage support during the build +and set slashpackage-compatible installation directories. +Other options setting individual installation directories will be +ignored. + + When using slashpackage, two additional Makefile targets are +available after "make install": + - "make update" changes the default version of the software to the +freshly installed one. (This is useful when you have several installed +versions of the same software, which slashpackage supports.) + - "make -L global-links" adds links from /command and /library.so to the +default version of the binaries and shared libraries. The "-L" option to +make is necessary because targets are symbolic links, and the default make +behaviour is to check the pointed file's timestamp and not the symlink's +timestamp. + + +* Out-of-tree builds + ------------------ + + skarnet.org packages do not support out-of-tree builds. They +are small, so it does not cost much to duplicate the entire +source tree if parallel builds are needed. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..c14a09d --- /dev/null +++ b/Makefile @@ -0,0 +1,121 @@ +# +# This Makefile requires GNU make. +# +# Do not make changes here. +# Use the included .mak files. +# + +it: all + +CC = $(error Please use ./configure first) + +-include config.mak +include package/deps.mak +include package/targets.mak + +version_m := $(basename $(version)) +version_M := $(basename $(version_m)) +version_l := $(basename $(version_M)) +CPPFLAGS_ALL := -iquote src/include-local -Isrc/include $(CPPFLAGS) +CFLAGS_ALL := $(CFLAGS) -pipe -Wall +CFLAGS_SHARED := -fPIC +LDFLAGS_ALL := $(LDFLAGS) +LDFLAGS_SHARED := -shared +LDLIBS_ALL := $(LDLIBS) +REALCC = $(CROSS_COMPILE)$(CC) +AR := $(CROSS_COMPILE)ar +RANLIB := $(CROSS_COMPILE)ranlib +STRIP := $(CROSS_COMPILE)strip +INSTALL := ./tools/install.sh + +ALL_BINS := $(LIBEXEC_TARGETS) $(BIN_TARGETS) $(SBIN_TARGETS) +ALL_LIBS := $(SHARED_LIBS) $(STATIC_LIBS) +ALL_INCLUDES := $(wildcard src/include/$(package)/*.h) + +all: $(ALL_LIBS) $(ALL_BINS) $(ALL_INCLUDES) + +clean: + @exec rm -f $(ALL_LIBS) $(ALL_BINS) $(wildcard src/*/*.o src/*/*.lo) src/minidentd/mgetuid.c + +distclean: clean + @exec rm -f config.mak src/include/${package}/config.h + +tgz: distclean + @. package/info && \ + rm -rf /tmp/$$package-$$version && \ + cp -a . /tmp/$$package-$$version && \ + cd /tmp && \ + tar -zpcv --owner=0 --group=0 --numeric-owner --exclude=.git* -f /tmp/$$package-$$version.tar.gz $$package-$$version && \ + exec rm -rf /tmp/$$package-$$version + +strip: $(ALL_LIBS) $(ALL_BINS) +ifneq ($(strip $(ALL_LIBS)),) + exec ${STRIP} -x -R .note -R .comment -R .note.GNU-stack $(ALL_LIBS) +endif +ifneq ($(strip $(ALL_BINS)),) + exec ${STRIP} -R .note -R .comment -R .note.GNU-stack $(ALL_BINS) +endif + +install: install-dynlib install-libexec install-bin install-sbin install-lib install-include +install-dynlib: $(SHARED_LIBS:lib%.so=$(DESTDIR)$(dynlibdir)/lib%.so) +install-libexec: $(LIBEXEC_TARGETS:%=$(DESTDIR)$(libexecdir)/%) +install-bin: $(BIN_TARGETS:%=$(DESTDIR)$(bindir)/%) +install-sbin: $(SBIN_TARGETS:%=$(DESTDIR)$(sbindir)/%) +install-lib: $(STATIC_LIBS:lib%.a=$(DESTDIR)$(libdir)/lib%.a) +install-include: $(ALL_INCLUDES:src/include/$(package)/%.h=$(DESTDIR)$(includedir)/$(package)/%.h) + +ifneq ($(exthome),) + +update: + exec $(INSTALL) -l $(notdir $(home)) $(DESTDIR)$(exthome) + +global-links: $(DESTDIR)$(exthome) $(SHARED_LIBS:lib%.so=$(DESTDIR)$(sproot)/library.so/lib%.so) $(BIN_TARGETS:%=$(DESTDIR)$(sproot)/command/%) $(SBIN_TARGETS:%=$(DESTDIR)$(sproot)/command/%) + +$(DESTDIR)$(sproot)/command/%: $(DESTDIR)$(home)/command/% + exec $(INSTALL) -D -l ..$(subst $(sproot),,$(exthome))/command/$( + + Please use the mailing-list for +questions about s6-networking. diff --git a/README.macosx b/README.macosx new file mode 100644 index 0000000..d71a096 --- /dev/null +++ b/README.macosx @@ -0,0 +1,4 @@ + + This package will compile and run on Darwin (MacOS X), but the building of +shared libraries is not supported. + Make sure you use the --disable-shared option to configure. diff --git a/README.solaris b/README.solaris new file mode 100644 index 0000000..91a5b26 --- /dev/null +++ b/README.solaris @@ -0,0 +1,12 @@ + + This package assumes the existence of a POSIX shell in /bin/sh. + On Solaris, /bin/sh is not POSIX. Most versions of Solaris provide +a POSIX shell in /usr/xpg4/bin/sh. + + To compile this package on Solaris, you will need to run + + ./patch-for-solaris + + before you run ./configure. This script will change the #! invocation +of the configure script and various tools so that a POSIX shell is used +for the compilation process. diff --git a/configure b/configure new file mode 100755 index 0000000..c030856 --- /dev/null +++ b/configure @@ -0,0 +1,378 @@ +#!/bin/sh + +usage () { +cat </dev/null 2>&1 && { echo "$1" ; return 0 ; } +$1 +EOF + echo "$1" | sed -e "s/'/'\\\\''/g" -e "1s/^/'/" -e "\$s/\$/'/" -e "s#^'\([-[:alnum:]_,./:]*\)=\(.*\)\$#\1='\2#" -e "s|\*/|* /|g" +} + +fail () { + echo "$*" + exit 1 +} + +fnmatch () { + eval "case \"\$2\" in $1) return 0 ;; *) return 1 ;; esac" +} + +cmdexists () { + type "$1" >/dev/null 2>&1 +} + +trycc () { + test -z "$CC_AUTO" && cmdexists "$1" && CC_AUTO=$1 +} + +stripdir () { + while eval "fnmatch '*/' \"\${$1}\"" ; do + eval "$1=\${$1%/}" + done +} + +tryflag () { + echo "checking whether compiler accepts $2 ..." + echo "typedef int x;" > "$tmpc" + if $CC_AUTO $CPPFLAGS_AUTO $CFLAGS_AUTO "$2" -c -o /dev/null "$tmpc" >/dev/null 2>&1 ; then + echo " ... yes" + eval "$1=\"\${$1} \$2\"" + eval "$1=\${$1# }" + return 0 + else + echo " ... no" + return 1 + fi +} + +tryldflag () { + echo "checking whether linker accepts $2 ..." + echo "typedef int x;" > "$tmpc" + if $CC_AUTO $CFLAGS_AUTO $LDFLAGS_AUTO -nostdlib "$2" -o /dev/null "$tmpc" >/dev/null 2>&1 ; then + echo " ... yes" + eval "$1=\"\${$1} \$2\"" + eval "$1=\${$1# }" + return 0 + else + echo " ... no" + return 1 + fi +} + + +# Actual script + +. package/info + +CC_AUTO="$CC" +CFLAGS_AUTO="$CFLAGS" +CPPFLAGS_AUTO="-D_POSIX_C_SOURCE=200809L -D_XOPEN_SOURCE=700 -O2 $CPPFLAGS" +LDFLAGS_AUTO="$LDFLAGS" +LDFLAGS_NOSHARED= +prefix= +exec_prefix='$prefix' +dynlibdir='$prefix/lib' +libexecdir='$exec_prefix/libexec' +bindir='$exec_prefix/bin' +sbindir='$exec_prefix/sbin' +libdir='$prefix/usr/lib/'$package +includedir='$prefix/usr/include' +sysdeps='$prefix/usr/lib/skalibs/sysdeps' +manualsysdeps=false +shared=false +static=true +slashpackage=false +sproot= +home= +exthome= +allstatic=true +evenmorestatic=false +addincpath='' +addlibspath='' +addlibdpath='' +vpaths='' +vpathd='' +cross="$CROSS_COMPILE" + +for arg ; do + case "$arg" in + --help) usage ;; + --prefix=*) prefix=${arg#*=} ;; + --exec-prefix=*) exec_prefix=${arg#*=} ;; + --dynlibdir=*) dynlibdir=${arg#*=} ;; + --libexecdir=*) libexecdir=${arg#*=} ;; + --bindir=*) bindir=${arg#*=} ;; + --sbindir=*) sbindir=${arg#*=} ;; + --libdir=*) libdir=${arg#*=} ;; + --includedir=*) includedir=${arg#*=} ;; + --with-sysdeps=*) sysdeps=${arg#*=} manualsysdeps=true ;; + --with-include=*) var=${arg#*=} ; stripdir var ; addincpath="$addincpath -I$var" ;; + --with-lib=*) var=${arg#*=} ; stripdir var ; addlibspath="$addlibspath -L$var" ; vpaths="$vpaths $var" ;; + --with-dynlib=*) var=${arg#*=} ; stripdir var ; addlibdpath="$addlibdpath -L$var" ; vpathd="$vpathd $var" ;; + --enable-shared|--enable-shared=yes) shared=true ;; + --disable-shared|--enable-shared=no) shared=false ;; + --enable-static|--enable-static=yes) static=true ;; + --disable-static|--enable-static=no) static=false ;; + --enable-allstatic|--enable-allstatic=yes) allstatic=true ;; + --disable-allstatic|--enable-allstatic=no) allstatic=false ;; + --enable-static-libc|--enable-static-libc=yes) evenmorestatic=true ;; + --disable-static-libc|--enable-static-libc=no) evenmorestatic=false ;; + --enable-slashpackage=*) sproot=${arg#*=} ; slashpackage=true ; ;; + --enable-slashpackage) sproot= ; slashpackage=true ;; + --disable-slashpackage) sproot= ; slashpackage=false ;; + --enable-cross=*) cross=${arg#*=} ;; + --enable-cross) cross= ;; + --disable-cross) cross= ;; + --enable-*|--disable-*|--with-*|--without-*|--*dir=*|--build=*) ;; + --host=*|--target=*) target=${arg#*=} ;; + -* ) echo "$0: unknown option $arg" ;; + *=*) ;; + *) target=$arg ;; + esac +done + +for i in prefix exec_prefix dynlibdir libexecdir bindir sbindir libdir includedir linkdynlibdir linkbindir linksbindir sysdeps sproot skalibs ; do + eval tmp=\${$i} + eval $i=$tmp + stripdir $i +done + +# Get usable temp filenames +i=0 +set -C +while : ; do + i=$(($i+1)) + tmpc="./tmp-configure-$$-$PPID-$i.c" + tmpe="./tmp-configure-$$-$PPID-$i.tmp" + 2>|/dev/null > "$tmpc" && break + 2>|/dev/null > "$tmpe" && break + test "$i" -gt 50 && fail "$0: cannot create temporary files" +done +set +C +trap 'rm -f "$tmpc" "$tmpe"' EXIT ABRT INT QUIT TERM HUP + +# Set slashpackage values +if $slashpackage ; then + home=${sproot}/package/${category}/${package}-${version} + exthome=${sproot}/package/${category}/${package} + if $manualsysdeps ; then + : + else + sysdeps=${sproot}/package/prog/skalibs/sysdeps + fi + binprefix=${home}/command + extbinprefix=${exthome}/command + dynlibdir=${home}/library.so + libexecdir=$binprefix + bindir=$binprefix + sbindir=$binprefix + libdir=${home}/library + includedir=${home}/include + while read dep ; do + addincpath="$addincpath -I${sproot}${dep}/include" + vpaths="$vpaths ${sproot}${dep}/library" + addlibspath="$addlibspath -L${sproot}${dep}/library" + if $allstatic ; then : ; else + vpathd="$vpathd ${sproot}${dep}/library.so" + addlibdpath="$addlibdpath -L${sproot}${dep}/library.so" + fi + done < package/deps-build +fi + +# Find a C compiler to use +echo "checking for C compiler..." +trycc ${cross}gcc +trycc ${cross}c99 +trycc ${cross}cc +test -n "$CC_AUTO" || { echo "$0: cannot find a C compiler" ; exit 1 ; } +echo " ... $CC_AUTO" +echo "checking whether C compiler works... " +echo "typedef int x;" > "$tmpc" +if $CC_AUTO $CPPFLAGS_AUTO $CFLAGS_AUTO -c -o /dev/null "$tmpc" 2>"$tmpe" ; then + echo " ... yes" +else + echo " ... no. Compiler output follows:" + cat < "$tmpe" + exit 1 +fi + +echo "checking target system type..." +test -n "$target" || target=$($CC_AUTO -dumpmachine 2>/dev/null) || target=unknown +echo " ... $target" +if test ! -d $sysdeps || test ! -f $sysdeps/target ; then + echo "$0: error: $sysdeps is not a valid sysdeps directory" + exit 1 +fi +if [ "x$target" != "x$(cat $sysdeps/target)" ] ; then + echo "$0: error: target $target does not match the contents of $sysdeps/target" + exit 1 +fi + +rt_lib=$(cat $sysdeps/rt.lib) +socket_lib=$(cat $sysdeps/socket.lib) +sysclock_lib=$(cat $sysdeps/sysclock.lib) +tainnow_lib=$(cat $sysdeps/tainnow.lib) +util_lib=$(cat $sysdeps/util.lib) + +tryflag CFLAGS_AUTO -std=c99 +tryflag CFLAGS_AUTO -fomit-frame-pointer +tryflag CFLAGS_AUTO -fno-exceptions +tryflag CFLAGS_AUTO -fno-unwind-tables +tryflag CFLAGS_AUTO -fno-asynchronous-unwind-tables +tryflag CFLAGS_AUTO -Wa,--noexecstack +tryflag CFLAGS_AUTO -fno-stack-protector +tryflag CPPFLAGS_AUTO -Werror=implicit-function-declaration +tryflag CPPFLAGS_AUTO -Werror=implicit-int +tryflag CPPFLAGS_AUTO -Werror=pointer-sign +tryflag CPPFLAGS_AUTO -Werror=pointer-arith + +if $evenmorestatic ; then + LDFLAGS_NOSHARED=-static +fi + +if $shared ; then + tryldflag LDFLAGS_AUTO -Wl,--hash-style=both +fi + +if test -z "$vpaths" ; then + while read dep ; do + base=$(basename $dep) ; + vpaths="$vpaths /usr/lib/$base" + addlibspath="$addlibspath -L/usr/lib/$base" + done < package/deps-build +fi + +CPPFLAGS_AUTO="$CPPFLAGS_AUTO $addincpath" +LDFLAGS_AUTO="$LDFLAGS_AUTO $addlibspath" +$allstatic || LDFLAGS_AUTO="$LDFLAGS_AUTO $addlibdpath" + +echo "creating config.mak..." +cmdline=$(quote "$0") +for i ; do cmdline="$cmdline $(quote "$i")" ; done +exec 3>&1 1>config.mak +cat << EOF +# This file was generated by: +# $cmdline +# Any changes made here will be lost if configure is re-run. + +target := $target +package := $package +prefix := $prefix +exec_prefix := $exec_prefix +dynlibdir := $dynlibdir +libexecdir := $libexecdir +bindir := $bindir +sbindir := $sbindir +libdir := $libdir +includedir := $includedir +sysdeps := $sysdeps +slashpackage := $slashpackage +sproot := $sproot +version := $version +home := $home +exthome := $exthome +RT_LIB := ${rt_lib} +SOCKET_LIB := ${socket_lib} +SYSCLOCK_LIB := ${sysclock_lib} +TAINNOW_LIB := ${tainnow_lib} +UTIL_LIB := ${util_lib} + +CC := $CC_AUTO +CFLAGS := $CFLAGS_AUTO +CPPFLAGS := $CPPFLAGS_AUTO +LDFLAGS := $LDFLAGS_AUTO +LDFLAGS_NOSHARED := $LDFLAGS_NOSHARED +CROSS_COMPILE := $cross + +vpath lib%a$vpaths +EOF +if $allstatic ; then + echo ".LIBPATTERNS := lib%.a" + vpathd= +fi + echo "vpath lib%.so$vpathd" +echo +$static || echo "STATIC_LIBS :=" +$shared || echo "SHARED_LIBS :=" +exec 1>&3 3>&- +echo " ... done." + +echo "creating src/include/${package}/config.h..." +mkdir -p -m 0755 src/include/${package} +exec 3>&1 1> src/include/${package}/config.h +cat <&3 3>&- +echo " ... done." diff --git a/doc/index.html b/doc/index.html new file mode 100644 index 0000000..93d68ce --- /dev/null +++ b/doc/index.html @@ -0,0 +1,182 @@ + + + + + s6-networking - small networking utilities + + + + + + +

+Software
+www.skarnet.org +

+ +

s6-networking

+ +

What is it ?

+ +

+ s6-networking is a suite of small networking utilities for Unix systems. +It includes command-line client and server management, TCP access +control, privilege escalation across UNIX domain sockets, IDENT +protocol management and clock synchronization. +

+ +

+ If the underlying +skalibs has been +compiled with IPv6 support, s6-networking is IPv6-ready. +

+ +
+ +

Installation

+ +

Requirements

+ +
    +
  • A POSIX-compliant system with a standard C development environment
  • +
  • GNU make, version 3.81 or later
  • +
  • skalibs version +2.0.0.0 or later
  • +
  • execline version +2.0.0.0 or later
  • +
  • s6-dns version +2.0.0.0 or later
  • +
+ +

Licensing

+ +

+ s6-networking is free software. It is available under the +ISC license. +

+ +

Download

+ +
    +
  • The current released version of s6-networking is 2.0.0.0.
  • +
  • Alternatively, you can checkout a copy of the s6-networking git repository: +
     git clone git://git.skarnet.org/s6-networking 
  • +
+ +

Compilation

+ +
    +
  • See the enclosed INSTALL file for installation details.
  • +
+ +

Upgrade notes

+ +
    +
  • This page lists the differences to be aware of between +the previous versions of s6-networking and the current one.
  • +
+ +

Reference

+ +

Commands

+ +

+ All these commands exit 111 if they encounter a temporary error or +hardware error, and +100 if they encounter a permanent error - such as a misuse. Short-lived +commands exit 0 on success. Other exit codes are documented in the +relevant page. +

+ +

System clock synchronization

+ + + +

UCSPI implementation

+ + + +

TCP and Unix access control

+ + + +

suidless privilege gain

+ + + +

IDENT protocol implementation

+ + + +

Miscellaneous utilities

+ + + +

Libraries

+ + + +
+ + +

Related resources

+
+ +

s6-networking discussion

+ +
    +
  • s6-networking is discussed on the +skaware mailing-list.
  • +
  • s6-networking has a +freecode page. +
  • +
+ +

Similar work

+ +
    +
  • ucspi-tcp is the +original inspiration for s6-networking. +It works, but is unfortunately unmaintained by the author. s6-networking +follows the +same design principles.
  • +
  • ipsvd is a very similar program +suite.
  • +
+ + + diff --git a/doc/libs6net/accessrules.html b/doc/libs6net/accessrules.html new file mode 100644 index 0000000..ea996b7 --- /dev/null +++ b/doc/libs6net/accessrules.html @@ -0,0 +1,331 @@ + + + + + s6-networking: the accessrules library interface + + + + + + +

+libs6net
+s6-networking
+Software
+skarnet.org +

+ +

The accessrules library interface

+ +

+ The following functions and structures are declared in the s6-networking/accessrules.h header, +and implemented in the libs6net.a or libs6net.so library. +

+ +

General information

+ +

+ s6net_accessrules is an access control library. It looks up +a key in a user-specified database, then returns a code depending on +whether the database allows access (in which case additional information +can also be returned), denies access, or does not contain the key. +

+ +

+ accessrules has been designed to be easily extensible to any +database format and any key format. +

+ +

+ Check the s6-networking/accessrules.h header for the exact definitions. +

+ +

Data structures

+ +
    +
  • A s6net_accessrules_result_t is a scalar that +can have the following values: S6NET_ACCESSRULES_ERROR, +S6NET_ACCESSRULES_DENY, S6NET_ACCESSRULES_ALLOW or S6NET_ACCESSRULES_NOTFOUND.
  • +
  • A s6net_accessrules_params_t is a structure containing two +strallocs, +.env and .exec, used to return data contained in the +database when a key has been allowed. The interpretation of this data is +application-defined.
  • +
+ +

Function types

+ +

Backend lookups

+ +

+ A s6net_accessrules_backend_func_t is the type of a function +that takes a single key, looks it up in a database, and returns the result. +Namely: +

+ +

+s6net_accessrules_result_t f (char const *key, unsigned int keylen, void *handle, s6net_accessrules_params_t *params) +

+ +

+ f looks up key key of length keylen in the database +represented by handle in an implementation-defined way. It returns a +number that says the key has been allowed, denied or not found, or an error +occurred. If the key has been allowed, f stores additional information +from the database into *params. +

+ +

+ Two s6net_accessrules_backend_func_t functions are natively implemented: +

+ +
    +
  • s6net_accessrules_backend_fs takes a char const * +handle and interprets it as a base directory to look up key +under, in the format understood by +s6-accessrules-cdb-from-fs.
  • +
  • s6net_accessrules_backend_cdb takes a struct cdb * +handle and looks up key in the +CDB it points to. handle must +already be mapped to a CDB file. Such a file can be built with the +s6-accessrules-cdb-from-fs +utility.
  • +
+ +

Frontend key checking

+ +

+ A s6net_accessrules_keycheck_func_t is the type of a function that +takes a user-level key, makes a list of corresponding backend-level keys and +calls a s6net_accessrules_backend_func_t function until it finds +a match. Namely: +

+ +

+s6net_accessrules_result_t f (void const *key, void *handle, s6net_accessrules_params_t *params, s6net_accessrules_backend_func_t *backend) +

+ +

+ f derives a list of low-level keys to check from key. +Then, for each key k of length klen in this list, it calls +(*backend)(k, klen, handle, params), returning *backend's result if it +is not S6NET_ACCESSRULES_NOTFOUND. If no match can be found in the whole list, +f finally returns S6NET_ACCESSRULES_NOTFOUND. +

+ +

+ Five s6net_accessrules_keycheck_func_t functions are natively implemented: +

+ + + +

Ready-to-use functions

+ + Those functions are mostly macros; they're built by associating a frontend +function with a backend function. + +

+ s6net_accessrules_result_t s6net_accessrules_uidgid_cdb +(unsigned int u, unsigned int g, struct cdb *c, +s6net_accessrules_params_t *params)
+Checks the *c CDB database for an authorization for uid u +and gid g. If the result is S6NET_ACCESSRULES_ALLOW, additional +information may be stored into params. +

+ +

+ s6net_accessrules_result_t s6net_accessrules_uidgid_fs +(unsigned int u, unsigned int g, char const *dir, +s6net_accessrules_params_t *params)
+Checks the dir base directory for an authorization for uid u +and gid g. If the result is S6NET_ACCESSRULES_ALLOW, additional +information may be stored into params. +

+ +

+ s6net_accessrules_result_t s6net_accessrules_reversedns_cdb +(char const *name, struct cdb *c, +s6net_accessrules_params_t *params)
+Checks the *c CDB database for an authorization for the +name FQDN. If the result is S6NET_ACCESSRULES_ALLOW, additional +information may be stored into params. +

+ +

+ s6net_accessrules_result_t s6net_accessrules_reversedns_fs +(char const *name, char const *dir, +s6net_accessrules_params_t *params)
+Checks the dir base directory for an authorization for the +name FQDN. If the result is S6NET_ACCESSRULES_ALLOW, additional +information may be stored into params. +

+ +

+ s6net_accessrules_result_t s6net_accessrules_ip4_cdb +(char const *ip4, struct cdb *c, +s6net_accessrules_params_t *params)
+Checks the *c CDB database for an authorization for the +ip4 IPv4 address (4 network byte order characters). +If the result is S6NET_ACCESSRULES_ALLOW, additional +information may be stored into params. +

+ +

+ s6net_accessrules_result_t s6net_accessrules_ip4_fs +(char const *ip4, char const *dir, +s6net_accessrules_params_t *params)
+Checks the dir base directory for an authorization for the +ip4 IPv4 address (4 network byte order characters). +If the result is S6NET_ACCESSRULES_ALLOW, additional +information may be stored into params. +

+ +

+ s6net_accessrules_result_t s6net_accessrules_ip6_cdb +(char const *ip6, struct cdb *c, +s6net_accessrules_params_t *params)
+Checks the *c CDB database for an authorization for the +ip6 IPv6 address (16 network byte order characters). +If the result is S6NET_ACCESSRULES_ALLOW, additional +information may be stored into params. +

+ +

+ s6net_accessrules_result_t s6net_accessrules_ip6_fs +(char const *ip6, char const *dir, +s6net_accessrules_params_t *params)
+Checks the dir base directory for an authorization for the +ip6 IPv6 address (16 network byte order characters). +If the result is S6NET_ACCESSRULES_ALLOW, additional +information may be stored into params. +

+ +

+ s6net_accessrules_result_t s6net_accessrules_ip46_cdb +(ip46_t *ip, struct cdb *c, +s6net_accessrules_params_t *params)
+Checks the *c CDB database for an authorization for the +ip IP address. +If the result is S6NET_ACCESSRULES_ALLOW, additional +information may be stored into params. +

+ +

+ s6net_accessrules_result_t s6net_accessrules_ip46_fs +(ip46_t const *ip, char const *dir, +s6net_accessrules_params_t *params)
+Checks the dir base directory for an authorization for the +ip IP address. +If the result is S6NET_ACCESSRULES_ALLOW, additional +information may be stored into params. +

+ + + diff --git a/doc/libs6net/ident.html b/doc/libs6net/ident.html new file mode 100644 index 0000000..74a9217 --- /dev/null +++ b/doc/libs6net/ident.html @@ -0,0 +1,124 @@ + + + + + s6-networking: the ident library interface + + + + + + +

+libs6net
+s6-networking
+Software
+skarnet.org +

+ +

The ident library interface

+ +

+ The following functions and structures are declared in the s6-networking/ident.h header, +and implemented in the libs6net.a or libs6net.so library. +

+ +

General information

+ +

+ ident provides a C IDENT client, following RFC 1413. +

+ +

+ Please note that this protocol is of historical interest exclusively; +this client, as well as the minidentd +server, is only provided for convenience and interoperability with +legacy systems. The IDENT protocol absolutely cannot be relied on for +any kind of authentication or secure operation. +

+ +

Functions

+ +

+ Check the s6-networking/ident.h header for the exact function prototypes. +

+ +

Main interface

+ +

+ int s6net_ident_client (char *s, unsigned int max, ip46_t const *remoteip, uint16 remoteport, ip46_t const *localip, uint16 localport, +struct taia const *deadline, struct taia *stamp) +

+ +

+Makes an IDENT request to a server listening on IP remoteip port 113 +about the connection from IP remoteip port remoteport to +IP localip port localport. Writes the answer into +preallocated string s of max length max, and returns the +number of bytes in the answer. +

+ +
    +
  • An ip46_t +is a structure holding either an IPv4 address or an IPv6 address.
  • +
  • If an error occurs, the function returns -1 and sets errno to a +suitable value. If no answer can be gotten from the server, the function +returns 0 and sets errno to a suitable value.
  • +
  • If max is too small for s to hold the answer, the +function returns -1 ENAMETOOLONG. +This can be avoided by using S6NET_IDENT_REPLY_SIZE +as max.
  • +
  • Negative answers are mapped to errno in the following way: +
      +
    • INVALID-PORT is reported as EINVAL
    • +
    • NO-USER is reported as ESRCH
    • +
    • HIDDEN-USER is reported as EPERM
    • +
    • UNKNOWN-ERROR is reported as EIO
    • +
    • extended error codes are reported as EEXIST
    • +
  • +
  • *deadline and *stamp are +absolute dates: +*stamp must be an accurate enough approximation of the current time, and +is automatically updated when the function returns. If no answer has been gotten +from the server by *deadline, then the call is aborted and returns +-1 ETIMEDOUT.
  • +
+ +

+ char const *s6net_ident_error_str (int e) +

+ +

+ Maps an error code representing a negative answer (i.e. errno when +s6net_ident_client returned 0) to a suitable string. +

+ +

Low-level functions

+ +

+ int s6net_ident_reply_get (char *s, ip46_t const *ra, uint16 rp, ip46_t const *la, uint16 lp, +struct taia const *deadline, struct taia *stamp) +

+ +

+The network part of s6net_ident_client. Connects to *ra:113 +and asks the server about (*ra:rp, *la:lp), +aborting if *deadline goes by. Writes the server answer into s; +at least S6NET_IDENT_REPLY_SIZE bytes must be preallocated in s. +Returns -1 if an error occurs, or the number of bytes written into s. +

+ +

+ int s6net_ident_reply_parse (char const *s, uint16 rp, uint16 lp) +

+ +

+The local part of s6net_ident_client. Parses the server answer in +s for the connection from port rp to port lp. +Returns -1 EPROTO if the answer does not make sense, 0 if the answer is +negative, or a positive number if the answer is positive. This number is +an index where the ID can be found in s. +

+ + + diff --git a/doc/libs6net/index.html b/doc/libs6net/index.html new file mode 100644 index 0000000..7a6a75b --- /dev/null +++ b/doc/libs6net/index.html @@ -0,0 +1,63 @@ + + + + + s6-networking: the s6net library interface + + + + + + +

+s6-networking
+Software
+skarnet.org +

+ +

The s6net library interface

+ +

General information

+ +

+ libs6net is a collection of networking-related utility +C interfaces, used in the s6-networking executables. +

+ +

Compiling

+ +
    +
  • Make sure the s6-networking headers, as well as the skalibs headers, +are visible in your header search path.
  • +
  • Use #include <s6-networking/s6net.h>
  • +
+ +

Linking

+ +
    +
  • Make sure the s6-networking libraries, as well as the skalibs +libraries, are visible in your library search path.
  • +
  • Link against -ls6net, -lskarnet,
  • +`cat $SYSDEPS/socket.lib` and +`cat $SYSDEPS/tainnow.lib`, $SYSDEPS being your skalibs +sysdeps directory. +
+ +

Programming

+ +

+ The s6-networking/s6net.h header is actually a +concatenation of other headers: +the libs6net is separated into several modules, each of them with its +own header. +

+ + + + + diff --git a/doc/localservice.html b/doc/localservice.html new file mode 100644 index 0000000..af7aafb --- /dev/null +++ b/doc/localservice.html @@ -0,0 +1,151 @@ + + + + + s6-networking: what is a local service + + + + + + +

+s6-networking
+Software
+skarnet.org +

+ +

Local services

+ +

+ A local service is a daemon that listens to incoming connections +on a Unix domain socket. Clients of the service are programs connecting to +this socket: the daemon performs operations on their behalf. +

+ +

+ The service is called local because it is not accessible to +clients from the network. +

+ +

+ A widely known example of a local service is the syslogd daemon. +On most implementations, it listens to the /dev/log socket. +Its clients connect to it and send their logs via the socket. The +openlog() function is just a wrapper arround the connect() +system call, the syslog() function a wrapper around write(), +and so on. +

+ +

Benefits

+ +

Privileges

+ +

+ The most important benefit of a local service is that it permits +controlled privilege gains without using setuid programs. +The daemon is run as user S; a client running as user C and connecting to +the daemon asks it to perform operations: those will be done as user S. +

+ +

+ Standard Unix permissions on the listening socket can be used to implement +some basic access control: to restrict access to clients belonging to group +G, change the socket to user S and group G, and give it 0420 permissions. +This is functionally equivalent to the basic access control for setuid +programs: a program having user S, group G and permissions 4750 will be +executable by group G and run with S rights. +

+ +

+ But modern systems implement the +getpeereid() +system call or library function. This function allows the server to know the +client's credentials: so fine-grained access control is possible. On those +systems, local services can do as much authentication as setuid programs, +in a much more controlled environment. +

+ +

fd-passing

+ +

+ The most obvious difference between a local service and a network service +is that a local service does not serve network clients. But local services +have another nice perk: while network services usually only provide you +with a single channel (a TCP or UDP socket) of communication between the +client and the server, forcing you to multiplex your data into that +channel, local services allow you to have as many +communication channels as you want. +

+ +

+(The SCTP transport layer provides a way for network services to use +several communication channels. Unfortunately, it is not widely deployed +yet, and a lot of network services still depend on TCP.) +

+ +

+ The fd-passing mechanism is Unix domain socket black magic +that allows one peer of the socket to send open file descriptors to +the other peer. So, if the server opens a pipe and sends one end of +this pipe to a client via this mechanism, there is effectively a +socket and a pipe between the client and the server. +

+ +

UCSPI

+ +

+ The UCSPI protocol +is an easy way of abstracting clients and servers from the network. +A server written as a UCSPI server, just as it can be run +under inetd or s6-tcpserver, can be run under +s6-ipcserver: choose a socket +location and you have a local service. +

+ +

+ Fine-grained access control can be added by inserting +s6-ipcserver-access in +your server command line after s6-ipcserver. +

+ +

+ A client written as an UCSPI client, i.e. assuming it has descriptor +6 (resp. 7) open and reading from (resp. writing to) the server socket, +can be run under s6-ipcclient. +

+ +

Use in skarnet.org software

+ +

+ skarnet.org libraries often use a separate process to handle +asynchronicity and background work in a way that's invisible to +the user. Among them are: +

+ +
    +
  • s6-ftrigrd, +managing the reception of notifications and only waking up the client process +when the notification pattern matches a regular expression.
  • +
  • s6lockd, +handling time-constrained lock acquisition on client behalf.
  • +
  • skadnsd, +performing asynchronous DNS queries and only waking up the client process +when an answer arrives.
  • +
+ +

+ Those processes are usually spawned from a client, via the corresponding +*_startf*() library call. But they can also be spawned from a +s6-ipcserver program in a local service configuration. In both cases, they +need an additional control channel to be passed from the server to +the client: the main socket is used for synchronous commands from the client +to the server and their answers, whereas the additional channel, which is +now implemented as a socket as well (but created by the server on-demand +and not bound to a local path), is used for asynchronous +notifications from the server to the client. The fd-passing mechanism +is used to transfer the additional channel from the server to the client. +

+ + + diff --git a/doc/minidentd.html b/doc/minidentd.html new file mode 100644 index 0000000..e23fa86 --- /dev/null +++ b/doc/minidentd.html @@ -0,0 +1,83 @@ + + + + + s6-networking: the minidentd program + + + + + + +

+s6-networking
+Software
+skarnet.org +

+ +

The minidentd program

+ +

+minidentd is a small +UCSPI server application +that answers IDENT requests. +

+ +

Interface

+ +
+     minidentd [ -v ] [ -n | -i | -r ] [ -y file ] [ -t timeout ]
+
+ +

+minidentd reads a series of IDENT requests on stdin and answers +them on stdout. It logs what it's doing on stderr. The environment +variables xLOCALIP and xREMOTEIP, where x is +the value of the PROTO environment variable, must contain the IDENT +server address and the IDENT client address, respectively. +

+ +

+ minidentd exits 0 on success, 100 on a usage error and 111 on a system +call failure. +

+ +

+ minidentd does not contact the network directly. It's meant to +run under a superserver like +s6-tcpserver. minidentd will +work with IPv4 as well as IPv6. +

+ +

Options

+ +
    +
  • -v : verbose mode. Log queries and replies..
  • +
  • -n : send ERROR : HIDDEN-USER replies if +the user has a .ident file in his home directory.
  • +
  • -i : user-defined answers. The first 14 chars of the +user's .ident file, up to EOF or newline, are used instead of +the user name. If the file exists and is empty, send +ERROR : HIDDEN-USER. If it doesn't exist, send a normal reply.
  • +
  • -r : send random replies.
  • +
  • -y file : valid with -n or -i. +Use file instead of .ident.
  • +
  • -t timeout : close connection after +timeout milliseconds without a client request.
  • +
+ +

Notes

+ +
    +
  • minidentd works only under Linux (2.2 or later); +on other systems, it will compile and run, but report an error for every +request. +The problem is that there is no portable Unix way of listing active +outgoing TCP connections with the relevant uids. On Linux, minidentd parses +the /proc/net/tcp or /proc/net/tcp6 virtual file. Other +systems have their own way of doing this, if you want your system to be +supported by minidentd, please contact the author.
  • +
+ + + diff --git a/doc/s6-accessrules-cdb-from-fs.html b/doc/s6-accessrules-cdb-from-fs.html new file mode 100644 index 0000000..26105b1 --- /dev/null +++ b/doc/s6-accessrules-cdb-from-fs.html @@ -0,0 +1,141 @@ + + + + + s6-networking: the s6-accessrules-cdb-from-fs program + + + + + + +

+s6-networking
+Software
+skarnet.org +

+ +

The s6-accessrules-cdb-from-fs program

+ +

+s6-accessrules-cdb-from-fs compiles a directory +containing a ruleset suitable for +s6-ipcserver-access or +s6-tcpserver-access into a +CDB file. +

+ +

Interface

+ +
+     s6-accessrules-cdb-from-fs cdbfile dir
+
+ +
    +
  • s6-accessrules-cdb-from-fs compiles the dir +directory containing a ruleset into a +CDB file +cdbfile then exits 0.
  • +
+ +

Ruleset directory format

+ +

+ To be understood by s6-accessrules-cdb-from-fs, +s6-ipcserver-access, or +s6-tcpserver-access, +dir must have a specific format. +

+ +

+ dir contains a series of directories: +

+ +
    +
  • ip4 for rules on IPv4 addresses
  • +
  • ip6 for rules on IPv6 addresses
  • +
  • reversedns for rules on host names
  • +
  • uid for rules on user IDs
  • +
  • gid for rules on group IDs
  • +
+ +

+Depending on the application, other directories can appear in dir +and be compiled into cdbfile, but +s6-tcpserver-access only +uses the first three, and +s6-ipcserver-access only +uses the last two. +

+ +

+ Each of those directories contains a set of rules. A rule is +a subdirectory named after the set of keys it matches, and containing +actions that will be executed if the rule is the first matching rule +for the tested key. +

+ +

+ The syntax for the rule name is dependent on the nature of keys, and +fully documented on the +accessrules +library page. For instance, a subdirectory named 192.168.0.0_27 +in the ip4 directory will match every IPv4 address in the +192.168.0.0/27 network that does not match a more precise rule. +

+ +

+ The syntax for the actions, however, is the same for every type of key. +A rule subdirectory can contain the following elements: +

+ +
    +
  • a file (that can be empty) named allow. If such a file exists, +a key matching this rule will be immediately accepted.
  • +
  • a file (that can be empty) named deny. If such a file exists and +no allow file exists, a key matching this rule will be immediately +denied.
  • +
  • a subdirectory named env. If such a directory exists along +with an allow file, then its contents represent environment +modifications that will be applied after accepting the connection and +before executing the next program in the chain, as if the +s6-envdir +program, without options, was applied to env. env +has exactly the same format as a directory suitable for s6-envdir; +however, if the modifications take up more than 4096 bytes when +compiled into cdbfile, then s6-accessrules-cdb-from-fs will +complain and exit 100.
  • +
  • a file named exec. If such a file exists along with an +allow file, then its contents represent a command line that, +interpreted by the +execlineb +launcher, will be executed after accepting the connection, totally bypassing the +original command line. s6-accessrules-cdb-from-fs truncates the exec +file to 4096 bytes max when embedding it into cdbfile, so make +sure it is not larger than that.
  • +
+ +

Notes

+ +
    +
  • cdbfile can exist prior to, and during, the compilation, +which actually works in a temporary file in the same directory as +cdbfile and performs an atomic replacement when it is done. +So it is not necessary to interrupt a running service during the +compilation.
  • +
  • If s6-accessrules-cdb-from-fs fails at some point, the temporary +file is removed. However, this doesn't happen if +s6-accessrules-cdb-from-fs is interrupted by a signal.
  • +
  • After the program successfully completes, if dir +was a suitable candidate for the -i option of +s6-ipcserver-access or +s6-tcpserver-access, then +cdbfile will be a suitable candidate for the -x option +of the same program, implementing the same ruleset.
  • +
  • cdbfile can be decompiled by the +s6-accessrules-fs-from-cdb +program.
  • +
+ + + diff --git a/doc/s6-accessrules-fs-from-cdb.html b/doc/s6-accessrules-fs-from-cdb.html new file mode 100644 index 0000000..91ec98e --- /dev/null +++ b/doc/s6-accessrules-fs-from-cdb.html @@ -0,0 +1,60 @@ + + + + + s6-networking: the s6-accessrules-fs-from-cdb program + + + + + + +

+s6-networking
+Software
+skarnet.org +

+ +

The s6-accessrules-fs-from-cdb program

+ +

+s6-accessrules-fs-from-cdb decompiles a CDB database +containing a ruleset suitable for +s6-ipcserver-access or +s6-tcpserver-access and +that has been compiled with +s6-accessrules-cdb-from-fs. +

+ +

Interface

+ +
+     s6-accessrules-fs-from-cdb dir cdbfile
+
+ +
+ +

Notes

+ +
    +
  • dir must not exist prior to the decompilation.
  • +
  • dir must be considered as a work in progress as long as +s6-accessrules-fs-from-cdb is running. It is only safe to use dir +as a ruleset once the program has exited.
  • +
  • If s6-accessrules-fs-from-cdb fails at some point, the partial +arborescence at dir is removed. However, this doesn't happen if +s6-accessrules-fs-from-cdb is interrupted by a signal.
  • +
  • After the program successfully completes, if cdbfile +was a suitable candidate for the -x option of +s6-ipcserver-access or +s6-tcpserver-access, then +dir will be a suitable candidate for the -i option +of the same program, implementing the same ruleset.
  • +
+ + + diff --git a/doc/s6-clockadd.html b/doc/s6-clockadd.html new file mode 100644 index 0000000..a998cd1 --- /dev/null +++ b/doc/s6-clockadd.html @@ -0,0 +1,61 @@ + + + + + s6-networking: the s6-clockadd program + + + + + + +

+s6-networking
+Software
+skarnet.org +

+ +

The s6-clockadd program

+ +

+s6-clockadd adjusts the system clock depending on the input +provided by a time client. +

+ +

Interface

+ +
+     s6-taiclock server | s6-clockadd [ -f ] [ -e errmax ]
+
+ +
    +
  • s6-clockadd reads its stdin, expecting input from a program such +s6-taiclock or +s6-sntpclock (which get time from a +time server).
  • +
  • It sets the system clock so the system time becomes the one +given by the server. Then it exits 0.
  • +
+ +

Options

+ +
    +
  • -f : force. Normally, s6-clockadd exits 1 if the +time discrepancy read from stdin is bigger than errmax +milliseconds. If this +option is set, it will print a warning message, but still set the +system time and exit 0.
  • +
  • -e errmax : accept a maximum time +discrepancy of errmax milliseconds. By default, errmax +is 2000.
  • +
+ +

Notes

+ +
    +
  • s6-clockadd must be run as root. The client getting the time +from a time server, however, does not have to.
  • +
+ + + diff --git a/doc/s6-clockview.html b/doc/s6-clockview.html new file mode 100644 index 0000000..58d8ac9 --- /dev/null +++ b/doc/s6-clockview.html @@ -0,0 +1,41 @@ + + + + + s6-networking: the s6-clockview program + + + + + + +

+s6-networking
+Software
+skarnet.org +

+ +

The s6-clockview program

+ +

+s6-clockview prints the time discrepancy between the local +system clock and the time provided by a time server. +

+ +

Interface

+ +
+     s6-taiclock server | s6-clockview
+
+ +
    +
  • s6-clockview reads its stdin, expecting input from a program such +s6-taiclock or +s6-sntpclock (which get time from a +time server).
  • +
  • It prints the local system time, and the time given by the +server, in human-readable form, then exits 0.
  • +
+ + + diff --git a/doc/s6-connlimit.html b/doc/s6-connlimit.html new file mode 100644 index 0000000..f0422e6 --- /dev/null +++ b/doc/s6-connlimit.html @@ -0,0 +1,96 @@ + + + + + s6-networking: the s6-connlimit program + + + + + + +

+s6-networking
+Software
+skarnet.org +

+ +

The s6-connlimit program

+ +

+s6-connlimit is a small utility to perform IP-based +control on the number of client connections to a TCP socket, and +uid-based control on the number of client connections to a Unix +domain socket. +

+ +

Interface

+ +
+     s6-connlimit prog...
+
+ +
    +
  • s6-connlimit reads its environment for the PROTO +environment variable, and then for ${PROTO}CONNNUM and ${PROTO}CONNMAX, +which must contain integers.
  • +
  • If the value of ${PROTO}CONNNUM is superior or equal to the value +of ${PROTO}CONNMAX, s6-connlimit exits 1 with an error message.
  • +
  • Else it execs into prog....
  • +
  • If ${PROTO}CONNMAX is unset, s6-connlimit directly execs into +prog... without performing any check: +no maximum number of connection has been defined.
  • +
+ +

Usage

+ +

+ The s6-tcpserver4 and +s6-tcpserver6 define the PROTO environment +variable to "TCP", and spawn every child server with the TCPCONNNUM environment +variable set to the number of connections from the same IP address. + The s6-tcpserver-access program +can set environment variables depending on the client's IP address. If the +s6-tcpserver-access database is configured to set the TCPCONNMAX environment +variable for a given set of IP addresses, and s6-tcpserver-access execs into +s6-connlimit, then s6-connlimit will drop connections if there already are +${TCPCONNMAX} connections from the same client IP address. +

+ +

+ The s6-ipcserver and +s6-ipcserver-access programs can +be used the same way, with "IPC" instead of "TCP", to limit the number +of client connections by UID. +

+ +

Example

+ +

+ The following command line: +

+ +
+     s6-tcpserver4 -v2 -c1000 -C40 1.2.3.4 80 \
+     s6-tcpserver-access -v2 -RHl0 -i dir \
+     s6-connlimit \
+     prog...
+
+ +

+ will run a server listening to IPv4 address 1.2.3.4, on port 80, +serving up to 1000 concurrent connections, and up to 40 concurrent +connections from the same IP address, no matter what the IP address. +For every client connection, it will look up the database set up +in dir; if the connection is accepted, it will run prog.... +

+ +

+ If the dir/ip4/5.6.7.8_32/env/TCPCONNMAX file +exists and contains the string 30, then at most 30 concurrent +connections from 5.6.7.8 will execute prog..., instead of the +default of 40. +

+ + + diff --git a/doc/s6-getservbyname.html b/doc/s6-getservbyname.html new file mode 100644 index 0000000..29a8235 --- /dev/null +++ b/doc/s6-getservbyname.html @@ -0,0 +1,48 @@ + + + + + s6-networking: the s6-getservbyname program + + + + + + +

+s6-networking
+Software
+skarnet.org +

+ +

The s6-getservbyname program

+ +

+s6-getservbyname is a simple command-line interface to the +getservbyname() +function, converting a service name and protocole into a port number. +

+ +

Interface

+ +
+     s6-getservbyname name proto
+
+ +
    +
  • s6-getservbyname looks up the network services database +for an entry containing name name and protocol proto.
  • +
  • It prints the corresponding port number to stdout, then exits 0.
  • +
  • If it cannot find a related entry, it exits 1.
  • +
+ +

Example

+ +

+ On a standard machine with a correct /etc/services file and +a non-garbled NSS configuration: + s6-getservbyname smtp tcp prints 25. +

+ + + diff --git a/doc/s6-ident-client.html b/doc/s6-ident-client.html new file mode 100644 index 0000000..fb7b0bf --- /dev/null +++ b/doc/s6-ident-client.html @@ -0,0 +1,52 @@ + + + + + s6-networking: the s6-ident-client program + + + + + + +

+s6-networking
+Software
+skarnet.org +

+ +

The s6-ident-client program

+ +

+s6-ident-client is a command-line client application that +performs IDENT (RFC 1413) queries. +

+ +

Interface

+ +
+     s6-ident-client [ -t millisecs ] remoteaddr remoteport localaddr localport
+
+ +
    +
  • s6-ident-client contacts the IDENT server on address remoteaddr +port 113 and asks it about the connection from address remoteaddr +port remoteport to address localaddr port localport.
  • +
  • It prints the answer to stdout.
  • +
  • It exits 0 if the answer is positive or 1 if the answer is negative, +in which case it also prints an informative message to stderr.
  • +
  • remoteaddr and localaddr can be either IPv4 +or IPv6 addresses; however, they must both be of the same type.
  • +
+ +

Options

+ +
    +
  • -t millisecs : if no answer has been +obtained within millisecs milliseconds, s6-ident-client will +exit 99 with an error message. By default, millisecs is 0, +which means no time limit.
  • +
+ + + diff --git a/doc/s6-ioconnect.html b/doc/s6-ioconnect.html new file mode 100644 index 0000000..5e2b6c6 --- /dev/null +++ b/doc/s6-ioconnect.html @@ -0,0 +1,84 @@ + + + + + s6-networking: the s6-ioconnect program + + + + + + +

+s6-networking
+Software
+skarnet.org +

+ +

The s6-ioconnect program

+ +

+s6-ioconnect performs full-duplex data transmission +between two sets of open file descriptors. +

+ +

Interface

+ +
+     s6-ioconnect [ -t millisecs ] [ -r fdr ] [ -w fdw ] [ -0 ] [ -1 ] [ -6 ] [ -7 ]
+
+ +
    +
  • s6-ioconnect reads data from its stdin and writes it as is to +file descriptor 7, which is assumed to be open.
  • +
  • It also reads data from its file descriptor 6, which is assumed +to be open, and writes it as is to its stdout.
  • +
  • When both sides have transmitted EOF and s6-ioconnect has +flushed its buffers, it exits 0.
  • +
+ +

Options

+ +
    +
  • -t millisecs : if no activity on +either side happens for millisecs milliseconds, s6-ioconnect +closes the connection on both ends and exits 1. By default, +millisecs is 0, which means no such timeout.
  • +
  • -r fdr : Use fd fdr for +"remote" reading instead of fd 6.
  • +
  • -w fdw : Use fd fdw for +"remote" writing instead of fd 7.
  • +
  • -0: assume stdin is a socket and needs to be shut down +for reading after an EOF.
  • +
  • -1: assume stdout is a socket and needs to be shut down +for writing to correctly transmit an EOF.
  • +
  • -6: assume the remote reading fd is a socket and needs to be shut down +for reading after an EOF.
  • +
  • -7: assume the remote writing fd is a socket and needs to be shut down +for writing to correctly transmit an EOF.
  • +
+ +

Notes

+ +
    +
  • Transmitting EOF across full-duplex sockets +is ugly. The right thing +in every case cannot be automatically determined, so it is up to the user +to mention that a socket must be shut down. Most of the time, though, +shutting down sockets after EOF is the right thing to do, so +s6-ioconnect -67 should be the common use case.
  • +
  • The point of s6-ioconnect is to be used together with +s6-tcpclient or +s6-ipcclient to establish a full- +duplex connection between the client and the server, for instance +for testing purposes. s6-ioconnect is to s6-tcpclient as +cat is to s6-tcpserver: a program that will just echo +what it gets.
  • +
  • On modern Linux systems, s6-ioconnect will perform zero-copy +data transmission, via the +splice +system call.
  • +
+ + + diff --git a/doc/s6-ipcclient.html b/doc/s6-ipcclient.html new file mode 100644 index 0000000..2bb66aa --- /dev/null +++ b/doc/s6-ipcclient.html @@ -0,0 +1,65 @@ + + + + + s6-networking: the s6-ipcclient program + + + + + + +

+s6-networking
+Software
+skarnet.org +

+ +

The s6-ipcclient program

+ +

+s6-ipcclient is an +UCSPI client tool for +Unix domain sockets. It connects to a socket, then executes into +a program. +

+ +

Interface

+ +
+     s6-ipcclient [ -q | -Q | -v ] [ -p bindpath ] [ -l localname ] path prog...
+
+ +
    +
  • s6-ipcclient connects to a Unix domain socket on path.
  • +
  • It executes into prog... with descriptor 6 reading from +the socket and descriptor 7 writing to it.
  • +
+ +

Environment variables

+ +

+ prog... is run with +the following variables set: +

+ +
    +
  • PROTO: always set to IPC
  • +
  • IPCLOCALPATH: set to the path associated with the local socket, +if any. Be aware that it may contain arbitrary characters.
  • +
+ +

Options

+ +
    +
  • -q : be quiet.
  • +
  • -Q : be normally verbose. This is the default.
  • +
  • -v : be verbose.
  • +
  • -p localpath : bind the local +socket to localpath before connecting to path.
  • +
  • -l localname : use localname +as the value of the IPCLOCALPATH environment variable.
  • +
+ + + diff --git a/doc/s6-ipcserver-access.html b/doc/s6-ipcserver-access.html new file mode 100644 index 0000000..817425b --- /dev/null +++ b/doc/s6-ipcserver-access.html @@ -0,0 +1,172 @@ + + + + + s6-networking: the s6-ipcserver-access program + + + + + + +

+s6-networking
+Software
+skarnet.org +

+ +

The s6-ipcserver-access program

+ +

+s6-ipcserver-access is a command-line access +control tool for Unix domain sockets on systems where the +getpeereid() system call can be implemented. +It is meant to be run after +s6-ipcserver and before +the application program on the s6-ipcserver command line. +

+ +

Interface

+ +
+     s6-ipcserver-access [ -v verbosity ] [ -E | -e ] [ -l localname ] [ -i rulesdir | -x rulesfile ] prog...
+
+ +
    +
  • s6-ipcserver-access checks it is run under a UCSPI server tool +such as s6-ipcserver. +
  • It checks that the remote end of the connection fits the +accepted criteria defined by the database contained in rulesdir +or rulesfile. If the database tells it to reject the connection, +the program exits 1.
  • +
  • It sets up a few additional environment variables.
  • +
  • It executes into prog..., +unless the first matching rule in the rule database +includes instructions to override prog....
  • +
+ +

Environment variables

+ +

+s6-ipcserver-access expects to inherit some environment variables from +its parent: +

+ +
    +
  • PROTO: normally IPC, but could be anything else, like UNIX.
  • +
  • ${PROTO}REMOTEEUID: the effective UID of the client program connecting to the socket.
  • +
  • ${PROTO}REMOTEEGID: the effective GID of the client program connecting to the socket.
  • +
+ +

+ Additionally, it exports the following variables before executing into +prog...: +

+ +
    +
  • ${PROTO}LOCALPATH: set to the local "address" of the socket, as +reported by the +getsockname() +system call, truncated to 99 characters max.
  • +
+ +

+ Also, the access rules database can instruct s6-ipcserver-access to set +up, or unset, more environment variables, depending on the client address. +

+ +

Options

+ +
    +
  • -v verbosity : be more or less verbose, i.e. +print more or less information to stderr: +
      +
    • 0: only log error messages.
    • +
    • 1: only log error and warning messages, and accepted connections. +This is the default.
    • +
    • 2: also log rejected connections and more warning messages.
    • +
  • +
  • -E : no environment. All environment variables potentially +set by s6-ipcserver-access, as well as those set by +s6-ipcserver, will be unset instead.
  • +
  • -e : set up environment variables normally. +This is the default.
  • +
  • -l localname : use localname +as the value for the ${PROTO}LOCALPATH environment variable, instead of +looking it up via getsockname().
  • +
  • -i rulesdir : check client credentials +against a filesystem-based database in the rulesdir directory.
  • +
  • -x rulesfile : check client credentials +against a cdb +database in the rulesfile file. -i and -x are +mutually exclusive. If none of those options is given, no credential checking will be +performed, and a warning will be emitted on every connection if +verbosity is 2 or more.
  • +
+ +

Access rule checking

+ +

+ s6-ipcserver-access checks its client connection against +a ruleset. This ruleset can be implemented: +

+ +
    +
  • either in the filesystem as an arborescence of directories and files, +if the -i option has been given. This option is the most flexible +one: the directory format is simple enough for scripts to understand and +modify it, and the ruleset can be changed dynamically. This is practical, +for instance, for roaming users.
  • +
  • or in a CDB +file, if the -x option has been given. This option is the most +efficient one if the ruleset is static enough: a lot less system calls are +needed to perform searches in a CDB than in the filesystem.
  • +
+ +

+ The exact format of the ruleset is described on the +s6-accessrules-cdb-from-fs page. +

+ +

+s6-ipcserver-access first reads the client UID uid and +GID gid from the +${PROTO}REMOTEEUID and ${PROTO}REMOTEEGID environment variables, and checks +them with the +s6net_accessrules_keycheck_uidgid() +function. In other words, it tries to match: + +

    +
  • uid/uid
  • +
  • gid/gid
  • +
  • uid/default
  • +
+ +

+ in that order. If no S6NET_ACCESSRULES_ALLOW result can be obtained, +the connection is denied. +

+ +

Environment and executable modifications

+ +

+ s6-ipcserver-access interprets non-empty env subdirectories +and exec files +it finds in the first matching rule of the ruleset, as explained +in the s6-accessrules-cdb-from-fs +page. +

+ +
    +
  • An env subdirectory is interpreted as if the +s6-envdir +command had been called before executing prog: the environment +is modified according to the contents of env.
  • +
  • An exec file containing newprog completely +bypasses the rest of s6-ipcserver-access' command line. After +environment modifications, if any, s6-ipcserver-access execs into +execlineb -c newprog.
  • +
+ + + diff --git a/doc/s6-ipcserver.html b/doc/s6-ipcserver.html new file mode 100644 index 0000000..331b139 --- /dev/null +++ b/doc/s6-ipcserver.html @@ -0,0 +1,148 @@ + + + + + s6-networking: the s6-ipcserver program + + + + + + +

+s6-networking
+Software
+skarnet.org +

+ +

The s6-ipcserver program

+ +

+s6-ipcserver is an +UCSPI server tool for +Unix domain sockets, i.e. a super-server. +It accepts connections from clients, and forks a +program to handle each connection. +

+ +

Interface

+ +
+     s6-ipcserver [ -1 ] [ -q | -Q | -v ] [ -d | -D ] [ -P | -p ] [ -c maxconn ] [ -C localmaxconn ] [ -b backlog ] [ -G gidlist ] [ -g gid ] [ -u uid ] [ -U ] path prog...
+
+ +
    +
  • s6-ipcserver binds to a Unix domain socket on path.
  • +
  • It closes its stdin and stdout.
  • +
  • For every client connection to this socket, it +forks. The child sets some environment variables, then +executes prog... with stdin reading from the socket and +stdout writing to it.
  • +
  • Depending on the verbosity level, it logs what it does to stderr.
  • +
  • It runs until killed by a signal. Depending on the received +signal, it may kill its children before exiting.
  • +
+ +

Environment variables

+ +

+ For each connection, an instance of prog... is spawned with +the following variables set: +

+ +
    +
  • PROTO: always set to IPC
  • +
  • IPCREMOTEEUID: set to the effective UID of the client, +unless credentials lookups have been disabled
  • +
  • IPCREMOTEEGID: set to the effective GID of the client, +unless credentials lookups have been disabled
  • +
  • IPCREMOTEPATH: set to the path associated with the remote socket, +if any. Be aware that it may contain arbitrary characters.
  • +
  • IPCCONNNUM: set to the number of connections originating from +the same user (i.e. same uid)
  • +
+ +

+ If client credentials lookup has been disabled, IPCREMOTEEUID and +IPCREMOTEEUID will be set, but empty. +

+ + +

Options

+ +
    +
  • -1 : write path, followed by a newline, +to stdout, before +closing it, right after binding and listening to the Unix socket. +If stdout is suitably redirected, this can be used by monitoring +programs to check when the server is ready to accept connections.
  • +
  • -q : be quiet.
  • +
  • -Q : be normally verbose. This is the default.
  • +
  • -v : be verbose.
  • +
  • -d : allow instant rebinding to the same path +even if it has been used not long ago - this is the SO_REUSEADDR flag to +setsockopt() +and is generally used with server programs. This is the default. Note that +path will be deleted if it already exists at program start time.
  • +
  • -D : disallow instant rebinding to the same path.
  • +
  • -P : disable client credentials lookups. The +IPCREMOTEEUID and IPCREMOTEEGID environment variables will be unset +in every instance of prog.... This is the portable option, +because not every system supports credential lookup across Unix domain +sockets; but it is not as secure.
  • +
  • -p : enable client credentials lookups. This +is the default; it works at least on Linux, Solaris, and +*BSD systems. On systems that do not support it, every connection +attempt will fail with a warning message.
  • +
  • -c maxconn : accept at most +maxconn concurrent connections. Default is 40. It is +impossible to set it higher than 1000.
  • +
  • -C localmaxconn : accept at most +localmaxconn connections from the same user ID. +Default is 40. It is impossible to set it higher than maxconn.
  • +
  • -b backlog : set a maximum of +backlog backlog connections on the socket. Extra +connection attempts will rejected by the kernel.
  • +
  • -G gidlist : change s6-ipcserver's +supplementary group list to gidlist after binding the socket. +This is only valid when run as root. gidlist must be a +comma-separated list of numerical group IDs.
  • +
  • -g gid : change s6-ipcserver's groupid +to gid after binding the socket. This is only valid when run +as root.
  • +
  • -u uid : change s6-ipcserver's userid +to uid after binding the socket. This is only valid when run +as root.
  • +
  • -U : change s6-ipcserver's user id, group id and +supplementary group list +according to the values of the UID, GID and GIDLIST environment variables +after binding the socket. This is only valid when run as root. +This can be used with the +s6-envuidgid +program to easily script a service that binds to a privileged socket +then drops its privileges to those of a named non-root account.
  • +
+ +

Signals

+ +
    +
  • SIGTERM: exit.
  • +
  • SIGHUP: send a SIGTERM and a SIGCONT to all children.
  • +
  • SIGQUIT: send a SIGTERM and a SIGCONT to all children, then exit.
  • +
  • SIGABRT: send a SIGKILL to all children, then exit.
  • +
+ +

Notes

+ +
    +
  • Unlike his close cousin +ipcserver, +s6-ipcserver does not perform operations such as access control. Those are +delegated to the +s6-ipcserver-access program.
  • +
  • s6-ipcserver can be used to set up +local services.
  • +
+ + + diff --git a/doc/s6-sntpclock.html b/doc/s6-sntpclock.html new file mode 100644 index 0000000..0d4c45b --- /dev/null +++ b/doc/s6-sntpclock.html @@ -0,0 +1,166 @@ + + + + + s6-networking: the s6-sntpclock program + + + + + + +

+s6-networking
+Software
+skarnet.org +

+ +

The s6-sntpclock program

+ +

+s6-sntpclock is a small SNTP client. It connects to a +SNTP or NTP server, computes an estimated discrepancy between the +local clock time and the absolute time given by the server, and +outputs it on stdout. +

+ +

Interface

+ +
+     s6-sntpclock [ -f ] [ -v verbosity ] [ -r roundtrips ] [ -t triptimeout ] [ -h throttle ] [ -T totaltimeout ] [ -e errmax ] [ -p port ] ipaddress | s6-clockview
+
+ +
    +
  • s6-sntpclock exchanges SNTPv4 messages with a SNTP server +listening on ipaddress, UDP port 123. +ipaddress can be IPv4 or IPv6.
  • +
  • It computes the mean difference between the absolute time +given by the system clock and the one given by the server.
  • +
  • It prints the difference to stdout in a format understood +by s6-clockadd and +s6-clockview. It then exits 0.
  • +
+ +

Options

+ +
    +
  • -f : force. Normally, s6-sntpclock exits 111 if it cannot +compute a time with a smaller uncertainty than errmax. If this +option is set, it will output a time difference and exit 0 even if the +error is too big.
  • +
  • -v verbosity : be more or less verbose. +By default, verbosity is 1. 0 means only print fatal error +messages; 3 means trace every exchange with the server.
  • +
  • -r roundtrips : perform roundtrips +exchanges with the server. By default, roundtrip is 10. A lower +value yields a higher time uncertainty; a higher value puts more load on +the server.
  • +
  • -t triptimeout : if a SNTP exchange with +the server takes more than triptimeout milliseconds, abort this +exchange and move on to the next one. By default, triptimeout +is 2000.
  • +
  • -h throttle : wait throttle +milliseconds between exchanges with the server. A lower value gets the +final result earlier, but exerts more load on the server. A higher +value puts a lighter load on the server, but delays the computation. +By default, throttle is 0. It is recommended to set it to a +reasonable nonzero value when increasing roundtrips.
  • +
  • -T totaltimeout : if the whole +operation takes more than totaltimeout milliseconds, abort +and exit 1. By default, totaltimeout is 10000.
  • +
  • -e errmax : accept a maximum time +uncertainty of errmax milliseconds. By default, errmax +is 100.
  • +
  • -p port : contact a server on port +port. By default, port is 123.
  • +
+ +

Notes

+ +
    +
  • There are a lot of infelicities in the NTP protocol (which SNTP is +a subset of). The biggest offender is probably that NTP cannot handle +a time difference of more than 34 years: if the time given by the NTP +server is more than 34 years away from the time given by the system clock, +then NTP just cannot compute. This is a problem for CMOS-less systems, +where the system clock is initialized to the Unix Epoch. The solution +is to first manually initialize the system clock with a program such as +date or +s6-clock +to a closer time (such as 2013-01-01, which will be good up to 2047), then +contact the NTP server.
  • +
  • A less obvious problem with NTP is that it works with UTC time, +which means that it gives inaccurate results when close to a leap second, +up to one second off when used during a leap second, and this +is bound to the use of UTC: there is nothing you can do about it. The +only solution to get reliable results even around a leap second is to +use linear time such as TAI; the +s6-taiclock and +s6-taiclockd programs provide tools to +do so.
  • +
+ + +

A word on ntpd

+ +

+ From a Unix software engineering standpoint, the well-known +ntpd program is an +eldritch abomination. The main reason for it is that, just like its +lovely cousin BIND, +ntpd performs a lot of different tasks in a unique process, instead +of separating what could, and should, be separated. This is confusing +for both the programmer and the software user. +

+ +
    +
  • The term "NTP server" means two different things: +
      +
    • A program that serves NTP time to the Internet and can be +accessed by NTP clients.
    • +
    • A daemon, i.e. a long-lived process, that runs on a machine +and handles NTP-related stuff such as keeping the system clock accurate.
    • +
    + The former is the real meaning of "NTP server". The latter is a common +usage for the term, but comes from a misuse of "server" to mean "daemon". +ntpd does not help clear the misunderstanding since it does both. It acts +as an NTP server, and as an NTP client getting its time from +lower-strata NTP servers, and as a local system clock management +daemon. Those are already 3 separate tasks.
  • +
  • Local system clock management itself involves several duties. There +is the regular setting of the system clock, which can be done with +a loop over a simple program such as s6-clockadd. +There is also control of the clock skew, which s6-networking does not +provide because there is no portable interface for that; there is such a tool +in the clockspeed package.
  • +
  • ntpd includes a complete cryptographic key management system for the +crypto part of NTP. NTP is not the only protocol that uses cryptography +and asymmetric keys; managing keys in a separate tool, not in the NTP +daemon itself, would be simpler and smarter.
  • +
  • ntpd provides monitoring support for client and server timekeeping +performance. This would be best implemented as a separate specific log +analyzing tool.
  • +
+ +

+ And of course, no matter how many layers of complexity you add onto +ntpd, it will never be able to give accurate time in the vicinity of a +leap second, since the very protocol is flawed by design - but +the ntpd authors cannot be blamed for that. Also, the ntpd +writers, not the designers, should be praised: the history of +ntpd security flaws is remarkably small, which is quite a feat for a +huge monolithic root daemon. +

+ +

+ Network synchronization is important, NTP has its perks and +valid use cases, and its existence is a good thing. However, I wish that +the main NTP implementation weren't written as a big fat clumsy process +running as root. s6-sntpclock together with +s6-clockadd aims to provide a small, simple +tool to keep system clocks, especially in embedded devices, synchronized +to a NTP server. +

+ + + diff --git a/doc/s6-sudo.html b/doc/s6-sudo.html new file mode 100644 index 0000000..603ad8a --- /dev/null +++ b/doc/s6-sudo.html @@ -0,0 +1,59 @@ + + + + + s6-networking: the s6-sudo program + + + + + + +

+s6-networking
+Software
+skarnet.org +

+ +

The s6-sudo program

+ +

+s6-sudo connects to a Unix domain socket and passes +its standard file descriptors, command-line arguments and +environment to a program running on the server side, potentially +with different privileges. +

+ +

Interface

+ +
+     s6-sudo [ -q | -Q | -v ] [ -p bindpath ] [ -l localname ] [ -e ] [ -t timeoutconn ] [ -T timeoutrun ] path [ args... ]
+
+ +
    +
  • s6-sudo executes into s6-ipcclient path +s6-sudoc args... It does nothing else: it is just a +convenience program. The s6-ipcclient program connects +to a Unix socket at path, and the +s6-sudoc program transmits the desired elements over the +socket.
  • +
  • It should be used to connect to a +local service running the +s6-sudod program, which will run a server program on the +client's behalf.
  • +
+ +

Options

+ +
    +
  • The -q, -Q, -v, -p and -l +options are passed to s6-ipcclient.
  • +
  • The -e, -t and -T options are passed to +s6-sudoc.
  • +
  • Command-line arguments, if any, are also passed to +s6-sudoc, which will transmit them to +s6-sudod over the socket. +
+ + + diff --git a/doc/s6-sudoc.html b/doc/s6-sudoc.html new file mode 100644 index 0000000..0ca9918 --- /dev/null +++ b/doc/s6-sudoc.html @@ -0,0 +1,80 @@ + + + + + s6-networking: the s6-sudoc program + + + + + + +

+s6-networking
+Software
+skarnet.org +

+ +

The s6-sudoc program

+ +

+s6-sudoc talks to a peer s6-sudod +program over a Unix socket, passing it command-line arguments, environment +variables and standard descriptors. +

+ +

Interface

+ +
+     s6-sudoc [ -e ] [ -t timeoutconn ] [ -T timeoutrun ] [ args... ]
+
+ +
    +
  • s6-sudoc transmits its standard input, standard output and standard error +via fd-passing over a Unix socket that must be open on its descriptors 6 and 7. + It expects a s6-sudod process to be receiving them +on the other side.
  • +
  • It also transmits its command-line arguments args, and also its +environment by default. Note that s6-sudod will not necessarily accept all the +environment variables that s6-sudoc tries to transmit.
  • +
  • s6-sudoc waits for the server program run by s6-sudod to finish. It exits +the same exit code as the server program. If the server program is killed by a +signal, s6-sudoc kills itself with the same signal.
  • +
+ +

Options

+ +
    +
  • -e : do not attempt to transmit any environment variables +to s6-sudod.
  • +
  • -t timeoutconn : if s6-sudod has not +managed to process the given information and start the server program after +timeoutconn milliseconds, give up. By default, timeoutconn +is 0, meaning infinite. Note that there is no reason to set up a nonzero +timeoutconn with a large value: s6-sudod is not supposed to block. +The option is only there to protect against ill-written services.
  • +
  • -T timeoutrun : if the server program +has not exited after timeoutrun milliseconds, give up. By +default, timeoutrun is 0, meaning infinite.
  • +
+ +

Notes

+ +
    +
  • If s6-sudoc is killed, or exits after timeoutrun milliseconds, +while the server program is still running, s6-sudod will send a SIGTERM and a +SIGCONT to the server program - but this does not guarantee that it will die. +If the server program keeps running, it might still read from the file that +was s6-sudoc's stdin, or write to the files that were s6-sudod's stdout or +stderr. This is a potential security risk. +Administrators should audit their server programs to make sure this does not +happen.
  • +
  • More generally, anything using signals or terminals will not be +handled transparently by the s6-sudoc + s6-sudod mechanism. The mechanism +was designed to allow programs to gain privileges in specific situations: +short-lived, simple, noninteractive processes. It was not designed to emulate +the full suid functionality and will not go out of its way to do so.
  • +
+ + + diff --git a/doc/s6-sudod.html b/doc/s6-sudod.html new file mode 100644 index 0000000..ac93219 --- /dev/null +++ b/doc/s6-sudod.html @@ -0,0 +1,162 @@ + + + + + s6-networking: the s6-sudod program + + + + + + +

+s6-networking
+Software
+skarnet.org +

+ +

The s6-sudod program

+ +

+s6-sudod receives command-line arguments, environment variables +and standard descriptors from a peer s6-sudoc +program over a Unix socket, then forks another program. +

+ +

Interface

+ +
+     s6-sudod [ -0 ] [ -1 ] [ -2 ] [ -s ] [ -t timeout ] [ sargv... ]
+
+ +
    +
  • s6-sudod gets 3 file descriptors via fd-passing over a Unix socket that +must be open on its descriptors 0 and 1. (The received descriptors will be the +stdin, stdout and stderr of the server program.) It expects a +s6-sudoc process to be sending them on the +client side.
  • +
  • It also receives a list of command-line arguments cargv..., and +an environment clientenv.
  • +
  • s6-sudod forks and executes sargv... cargv... +The client command line is appended to the server command line.
  • +
  • s6-sudod waits for its child to exit and transmits its exit code +to the peer s6-sudoc process. It then exits 0.
  • +
+ +

Environment

+ +

+s6-sudod transmits its own environment to its child, plus the environment sent +by s6-sudoc, filtered in the following manner: +for every variable sent by s6-sudoc, if the +variable is present but empty in s6-sudod's environment, then +its value is overriden by the value given by s6-sudoc. A variable that is +already nonempty, or that doesn't exist, in s6-sudod's environment, will not +be transmitted to the child. +

+ +

Options

+ +
    +
  • -0 : do not inherit stdin from s6-sudoc. The child will be +run with its stdin pointing to /dev/null instead.
  • +
  • -1 : do not inherit stdout from s6-sudoc. The child will be +run with its stdout pointing to /dev/null instead.
  • +
  • -2 : do not inherit stderr from s6-sudoc. The child will be +run with its stderr being a copy of s6-sudod's stderr instead. (This is useful +to still log the child's error messages without sending them to the client.)
  • +
  • -t timeout : if s6-sudod has not +received all the needed data from the client after timeout +milliseconds, it will exit without spawning a child. By default, timeout +is 0, meaning infinite. This mechanism exists to protect the server from +malicious or buggy clients that would uselessly consume resources.
  • +
+ +

Usage example

+ +

+ The typical use of s6-sudod is in a +local service with a +s6-ipcserver process listening on a Unix +socket, a s6-ipcserver-access process +performing client authentication and access control, and possibly a +s6-envdir +process setting up the environment variables that will be accepted by +s6-sudod. The following script, meant to be a run script in a +service directory, +will set up a privileged program: +

+ +
+#!/command/execlineb -P
+fdmove -c 2 1
+s6-envuidgid serveruser
+s6-ipcserver -U -- serversocket
+s6-ipcserver-access -v2 -l0 -i rules --
+exec -c
+s6-envdir env
+s6-sudod
+sargv
+
+ +
    +
  • execlineb +executes the script.
  • +
  • fdmove makes +sure the script's error messages are sent to the service's logger.
  • +
  • s6-envuidgid +sets the UID, GID and GIDLIST environment variables for s6-ipcserver to interpret.
  • +
  • s6-ipcserver binds to serversocket +and drops its privileges to those of serveruser. Then, for every client +connecting to serversocket: +
      +
    • s6-ipcserver-access checks the +client's credentials according to the rules in directory rules. +
    • exec -c +clears the environment.
    • +
    • s6-envdir +sets environment variables according to the directory env. You can +make sure that a variable VAR will be present but empty by performing +echo > env/VAR. (A single newline is interpreted by s6-envdir as +an empty variable; whereas if env/VAR is totally empty, then the +VAR variable will be removed from the environment.)
    • +
    • s6-sudod reads a command line cargv, a client environment +and file descriptors over the socket.
    • +
    • s6-sudod spawns sargv cargv.
    • +
    +
+ +

+ This means that user clientuser running +s6-sudo serversocket cargv will be +able, if authorized by the configuration in rules, to run +sargv cargv as user serveruser, with stdin, +stdout, stderr and the environment variables properly listed in env +transmitted to sargv. +

+ +

Notes

+ +
    +
  • If s6-sudoc is killed, or exits after timeoutrun milliseconds, +while the server program is still running, s6-sudod will send a SIGTERM and a +SIGCONT to its child, then exit 1. However, sending a SIGTERM to the child +does not guarantee that it will die; and +if it keeps running, it might still read from the file that +was s6-sudoc's stdin, or write to the files that were s6-sudod's stdout or +stderr. This is a potential security risk. +Administrators should audit their server programs to make sure this does not +happen.
  • +
  • More generally, anything using signals or terminals will not be +handled transparently by the s6-sudoc + s6-sudod mechanism. The mechanism +was designed to allow programs to gain privileges in specific situations: +short-lived, simple, noninteractive processes. It was not designed to emulate +the full suid functionality and will not go out of its way to do so.
  • +
  • sargv may be empty. In that case, the client is in complete +control of the command line executed as serveruser. This setup is +permitted by s6-sudod, but it is very dangerous, and extreme attention should +be paid to the construction of the s6-ipcserver-access rules.
  • +
+ + + diff --git a/doc/s6-taiclock.html b/doc/s6-taiclock.html new file mode 100644 index 0000000..bbdc680 --- /dev/null +++ b/doc/s6-taiclock.html @@ -0,0 +1,114 @@ + + + + + s6-networking: the s6-taiclock program + + + + + + +

+s6-networking
+Software
+skarnet.org +

+ +

The s6-taiclock program

+ +

+s6-taiclock is a client for the +TAICLOCK protocol. +It connects to a TAICLOCK server, computes an estimated discrepancy +between the local clock time and the absolute time given by the server, +and outputs it on stdout. +

+ +

Interface

+ +
+     s6-taiclock [ -f ] [ -v verbosity ] [ -r roundtrips ] [ -t triptimeout ] [ -h throttle ] [ -T totaltimeout ] [ -e errmax ] [ -p port ] ipaddress | s6-clockview
+
+ +
    +
  • s6-taiclock exchanges TAICLOCK messages with a server such as +s6-taiclockd +listening on ipaddress, UDP port 4014. +ipaddress can be IPv4 or IPv6.
  • +
  • It computes the mean difference between the absolute time +given by the system clock and the one given by the server.
  • +
  • It prints the difference to stdout in a format understood +by s6-clockadd and +s6-clockview. It then exits 0.
  • +
+ +

Options

+ +
    +
  • -f : force. Normally, s6-taiclock exits 111 if it cannot +compute a time with a smaller uncertainty than errmax. If this +option is set, it will output a time difference and exit 0 even if the +error is too big.
  • +
  • -v verbosity : be more or less verbose. +By default, verbosity is 1. 0 means only print fatal error +messages; 2 means print advanced warnings.
  • +
  • -r roundtrips : perform roundtrips +exchanges with the server. By default, roundtrip is 10. A lower +value yields a higher time uncertainty; a higher value puts more load on +the server.
  • +
  • -t triptimeout : if a TAICLOCK exchange with +the server takes more than triptimeout milliseconds, abort this +exchange and move on to the next one. By default, triptimeout +is 2000.
  • +
  • -h throttle : wait throttle +milliseconds between exchanges with the server. A lower value gets the +final result earlier, but exerts more load on the server. A higher +value puts a lighter load on the server, but delays the computation. +By default, throttle is 0. It is recommended to set it to a +reasonable nonzero value when increasing roundtrips.
  • +
  • -T totaltimeout : if the whole +operation takes more than totaltimeout milliseconds, abort +and exit 1. By default, totaltimeout is 10000.
  • +
  • -e errmax : accept a maximum time +uncertainty of errmax milliseconds. By default, errmax +is 100.
  • +
  • -p port : contact a server on port +port. By default, port is 4014.
  • +
+ +

Notes

+ +

On the usage of NTP vs. TAICLOCK

+ +
    +
  • TAICLOCK is not as generic or failproof as NTP. It is not as +resistant to network latency. It has been designed to broadcast +time on a local area network, whereas NTP has been designed to +broadcast time over the whole Internet.
  • +
  • TAICLOCK will produce faster results on a LAN; moreover, the +point of TAICLOCK is to broadcast TAI instead of UTC, so it is +more accurate around a leap second.
  • +
  • The Internet is much more reliable latency-wise today +than it was when dialout connections and broken routing protocols +were the norm. So it is possible to use TAICLOCK +across a WAN if the accuracy expectations are not too strict.
  • +
  • TAICLOCK is much easier to implement. The +s6-sntpclock client binary code +(statically linked on a i386) is 50% bigger than the s6-taiclock +client. Also, the s6-taiclockd +server is extremely small (close to 50% smaller than the client), +whereas NTP servers, even SNTP servers, are behemoths requiring +a project of their own.
  • +
+ +

Related work

+ +
    +
  • The clockspeed package +is the original inspiration for the clock management part of s6-networking. +Unfortunately, it is unmaintained.
  • +
+ + + diff --git a/doc/s6-taiclockd.html b/doc/s6-taiclockd.html new file mode 100644 index 0000000..915c181 --- /dev/null +++ b/doc/s6-taiclockd.html @@ -0,0 +1,51 @@ + + + + + s6-networking: the s6-taiclockd program + + + + + + +

+s6-networking
+Software
+skarnet.org +

+ +

The s6-taiclockd program

+ +

+s6-taiclockd is a server for the +TAICLOCK protocol. +It's a long-lived program listening to the network and answering +to TAICLOCK clients such as s6-taiclock. +

+ +

Interface

+ +
+     s6-taiclockd [ -i ip ] [ -p port ]
+
+ +
    +
  • s6-taiclockd listens to all network addresses on UDP +port 4014.
  • +
  • Whenever it receives a TAICLOCK request, it answers +immediately with the TAI time given by its local system clock.
  • +
  • It runs until killed by a signal.
  • +
+ +

Options

+ +
    +
  • -i ip : bind to ip instead +of all available addresses. ip can be IPv4 or IPv6.
  • +
  • -p port : bind to port +port instead of 4014.
  • +
+ + + diff --git a/doc/s6-tcpclient.html b/doc/s6-tcpclient.html new file mode 100644 index 0000000..42edaa9 --- /dev/null +++ b/doc/s6-tcpclient.html @@ -0,0 +1,133 @@ + + + + + s6-networking: the s6-tcpclient program + + + + + + +

+s6-networking
+Software
+skarnet.org +

+ +

The s6-tcpclient program

+ +

+s6-tcpclient is an +UCSPI client tool for +INET domain sockets. It establishes a TCP connection to a server, +then executes into a program. +

+ +

Interface

+ +
+     s6-tcpclient [ -q | -Q | -v ] [ -4 | -6 ] [ -d | -D ] [ -r | -R ] [ -h | -H ] [ -n | -N ] [ -t timeout ] [ -l localname ] [ -T timeoutconn ] [ -i localip ] [ -p localport ] host port prog...
+
+ +
    +
  • s6-tcpclient establishes a TCP connection to host host +port port.
  • +
  • It executes into prog... with descriptor 6 reading from +the network and descriptor 7 writing to it.
  • +
+ +

Host address determination

+ +
    +
  • host may be an IP address, in which case s6-tcpclient will +connect to that IP address. If the underlying skalibs has been +compiled with IPv6 support, host can be an IPv6 address as +well as an IPv4 one.
  • +
  • host may be a domain name, in which case a DNS +resolution will be performed on it, and a connection will be tried to +all the resulting IP addresses in a round-robin fashion, twice: +first with a small timeout, then with a longer timeout. The first +address to answer wins. The connection attempt fails if no address +in the list is able to answer.
  • +
+ +

Environment variables

+ +

+ prog... is run with the following variables set: +

+ +
    +
  • PROTO: always set to TCP
  • +
  • TCPREMOTEIP: set to the chosen IP address of host.
  • +
  • TCPREMOTEPORT: set to port.
  • +
  • TCPREMOTEHOST: if the -H has been given, set to the +name obtained by a reverse DNS resolution of the IP address chosen +for host. Else unset.
  • +
  • TCPLOCALHOST: if the -l option has been given, set to +localname. Else set to the name obtained by a reverse DNS +resolution of the IP address chosen for the local host.
  • +
  • TCPREMOTEINFO: if the -r option has been given, set +to the information given by an IDENT server on host about +the current connection (very unreliable). Else unset.
  • +
+ +

Options

+ +
    +
  • -q : be quiet.
  • +
  • -Q : be normally verbose. This is the default.
  • +
  • -v : be verbose.
  • +
  • -4 : (only valid if the underlying skalibs has +IPv6 support) Interpret host as an IPv4 address or make A +queries to determine its addresses.
  • +
  • -6 : (only valid if the underlying skalibs has +IPv6 support) Interpret host as an IPv6 address or make +AAAA queries to determine its addresses. This option and the previous +one are not mutually exclusive: if both are given, both IPv6 and +IPv4 addresses will be tried and IPv6 addresses will be given priority. +If neither option is given, only IPv4 address will be tried.
  • +
  • -d : don't use the TCP_NODELAY socket option. This +is the default.
  • +
  • -D : use the TCP_NODELAY socket option, which disables +Nagle's algorithm.
  • +
  • -r : try and obtain a TCPREMOTEINFO string via the +IDENT protocol. This is obsolete and unreliable, and should only be used for +compatibility with legacy programs.
  • +
  • -R : do not use the IDENT protocol. This is the +default.
  • +
  • -h : try and obtain the remote host name via DNS. +This is the default.
  • +
  • -H : do not try and obtain the remote host name +via DNS.
  • +
  • -n : qualify host when resolving it to +find suitable IP addresses. This is the default.
  • +
  • -N : do not qualify host.
  • +
  • -t :timeout : put a global timeout +on the connection attempt. If no fully functional connection has been +established after timeout seconds, abort the program. By +default, timeout is 0, which means no timeout.
  • +
  • -i localip : use localip as +the local socket address for the connection. By default, address selection +is left to the operating system.
  • +
  • -p localport : use localport +as the local socket port for the connection. By default, port selection +is left to the operating system.
  • +
  • -l localname : use localname +as the value of the TCPLOCALPATH environment variable instead of +looking it up via the DNS.
  • +
  • -T :timeoutconn : configure the +connection timeouts. timeoutconn must be of the form +x+y, where x and y are +integers. x is the first timeout and y is the +second one: all suitable addresses for host are first +tried with a timeout of x seconds, and if all of them +fail, then they are tried again with a timeout of y +seconds. (Be aware that the timeout specified with the -t +option overrides everything.) By default, x is 2 and +y is 58.
  • +
+ + + diff --git a/doc/s6-tcpserver-access.html b/doc/s6-tcpserver-access.html new file mode 100644 index 0000000..7bf15a0 --- /dev/null +++ b/doc/s6-tcpserver-access.html @@ -0,0 +1,239 @@ + + + + + s6-networking: the s6-tcpserver-access program + + + + + + +

+s6-networking
+Software
+skarnet.org +

+ +

The s6-tcpserver-access program

+ +

+s6-tcpserver-access is a command-line TCP access +control tool, and additionally performs some fine-tuning on a +TCP socket. It is meant to be run after +s6-tcpserver and before +the application program on the s6-tcpserver command line, +just like tcpwrappers' tcpd program. +

+ +

Interface

+ +
+     s6-tcpserver-access [ -v verbosity ] [ -W | -w ] [ -D | -d ] [ -H | -h ] [ -R | -r ] [ -P | -p ] [ -l localname ] [ -B banner ] [ -t timeout ] [ -i rulesdir | -x rulesfile ] prog...
+
+ +
    +
  • s6-tcpserver-access checks it is run under a UCSPI server tool +such as s6-tcpserver, + s6-tcpserver4 or + s6-tcpserver6.
  • +
  • It checks that the remote end of the connection fits the +accepted criteria defined by the database contained in rulesdir +or rulesfile. If the database tells it to reject the connection, +the program exits 1.
  • +
  • It sets up a few additional environment variables.
  • +
  • It executes into prog..., +unless the first matching rule in the rule database +includes instructions to override prog....
  • +
+ +

Environment variables

+ +

+s6-tcpserver-access expects to inherit some environment variables from +its parent: +

+ +
    +
  • PROTO: normally TCP, but could be anything else, like SSL.
  • +
  • ${PROTO}REMOTEIP: the remote address of the socket, i.e. the client's +IP address. This can be IPv4 or (if the underlying skalibs supports it) IPv6.
  • +
  • ${PROTO}REMOTEPORT: the remote port of the socket.
  • +
+ +

+ Additionally, it exports the following variables before executing into +prog...: +

+ +
    +
  • ${PROTO}LOCALIP: set to the local address of the socket.
  • +
  • ${PROTO}LOCALPORT: set to the local port of the socket.
  • +
  • ${PROTO}REMOTEINFO: normally unset, but set to the information +retrieved from ${PROTO}REMOTEIP via the IDENT protocol if the -R +option has been given.
  • +
  • ${PROTO}REMOTEHOST: set to the remote host name obtained from +a DNS lookup. Unset if the -H option has been given.
  • +
  • ${PROTO}LOCALHOST: set to the local host name obtained from a +DNS lookup. If the -l option has been given, set to +localname instead.
  • +
+ +

+ Also, the access rules database can instruct s6-tcpserver-access to set +up, or unset, more environment variables, depending on the client address. +

+ +

Options

+ +
    +
  • -v verbosity : be more or less verbose, i.e. +print more or less information to stderr: +
      +
    • 0: only log error messages.
    • +
    • 1: only log error and warning messages, and accepted connections. +This is the default.
    • +
    • 2: also log rejected connections and more warning messages.
    • +
    • 3: also log detailed warning messages from DNS and IDENT resolution.
    • +
  • +
  • -W : non-fatal. If errors happen during DNS or IDENT +resolution, the connection process is not aborted. However, incorrect or +incomplete results might still prevent a legitimate connection from being +authenticated against a DNS name. This is the default.
  • +
  • -w : fatal. Errors during DNS or IDENT resolution will +drop the connection.
  • +
  • -D : disable Nagle's algorithm. Sets the TCP_NODELAY +flag on the network socket.
  • +
  • -d : enable Nagle's algorithm. This is the default.
  • +
  • -H : disable DNS lookups for the ${PROTO}LOCALHOST and +${PROTO}REMOTEHOST environment variables.
  • +
  • -h : enable DNS lookups. This is the default.
  • +
  • -R : disable IDENT lookups for the ${PROTO}REMOTEINFO +environment variable. This is the default.
  • +
  • -r : enable IDENT lookups. This should only be done +for legacy programs that need it.
  • +
  • -P : no paranoid DNS lookups. This is the default.
  • +
  • -p : paranoid. After looking up a name for the remote +host, s6-tcpserver-access will lookup IP addresses for this name, and drop +the connection if none of the results matches the address the connection +is originating from. Note that this still does not replace real +authentication via a cryptographic protocol.
  • +
  • -l localname : use localname +as the value for the ${PROTO}LOCALHOST environment variable, instead of +looking it up in the DNS.
  • +
  • -B banner : print banner to +the network as soon as the connection is attempted, even before +checking client credentials. The point is to speed up network protocols +that start with a server-side message.
  • +
  • -t timeout : set a timeout on all the +operations performed by s6-tcpserver-access. If it is not able to do +its job in timeout milliseconds, it will instantly exit 99. +The default is 0, meaning no such timeout.
  • +
  • -i rulesdir : check client credentials +against a filesystem-based database in the rulesdir directory.
  • +
  • -x rulesfile : check client credentials +against a cdb +database in the rulesfile file. -i and -x are +mutually exclusive. If none of those options is given, no credential checking will be +performed, and a warning will be emitted on every connection if +verbosity is 2 or more.
  • +
+ +

Access rule checking

+ +

+ s6-tcpserver-access checks its client connection against +a ruleset. This ruleset can be implemented: +

+ +
    +
  • either in the filesystem as an arborescence of directories and files, +if the -i option has been given. This option is the most flexible +one: the directory format is simple enough for scripts to understand and +modify it, and the ruleset can be changed dynamically. This is practical, +for instance, for roaming users.
  • +
  • or in a CDB +file, if the -x option has been given. This option is the most +efficient one if the ruleset is static enough: a lot less system calls are +needed to perform searches in a CDB than in the filesystem.
  • +
+ +

+ The exact format of the ruleset is described on the +s6-accessrules-cdb-from-fs page. +

+ +

+s6-tcpserver-access first gets the remote address ip of the +client and converts it to canonical form. Then it checks it with the +s6net_accessrules_keycheck_ip46() +function. In other words, it tries to match broader and broader network +prefixes of ip, from ip4/ip_32 to +ip4/0.0.0.0_0 if ip is v4, or from +ip6/ip/128 to ip6/::_0 if ip +is v6. If the result is: +

+ +
  • S6NET_ACCESSRULES_ERROR: it immediately exits 111.
  • +
  • S6NET_ACCESSRULES_DENY: it immediately exits 1.
  • +
  • S6NET_ACCESSRULES_ALLOW: it grants access.
  • +
  • S6NET_ACCESSRULES_NOTFOUND: more information is needed.
  • + + +

    + In the last case, if DNS lookups have been deactivated (-H) then access +is denied. But if s6-tcpserver-access is authorized to perform DNS lookups, +then it gets the remote name of the client, remotehost, and +checks it with the +s6net_accessrules_keycheck_reversedns() +function. In other words, it tries to match shorter and shorter suffixes +of remotehost, from reversedns/remotehost to +reversedns/@. +This time, the connection is denied is the result is anything else than +S6NET_ACCESSRULES_ALLOW. +

    + +

    + Note that even if the access check succeeds, the connection can still be +denied if paranoid mode has been required (-p) and a forward DNS query +on remotehost does not match ip. +

    + +

    Environment and executable modifications

    + +

    + s6-tcpserver-access interprets non-empty env subdirectories +and exec files +it finds in the matching rule of the ruleset, as explained +in the s6-accessrules-cdb-from-fs +page. +

    + +
      +
    • An env subdirectory is interpreted as if the +s6-envdir +command had been called before executing prog: the environment +is modified according to the contents of env.
    • +
    • An exec file containing newprog completely +bypasses the rest of s6-tcpserver-access' command line. After +environment modifications, if any, s6-tcpserver-access execs into +execlineb -c newprog.
    • +
    + +

    Notes

    + +
      +
    • s6-tcpserver-access works with +s6-tcpserver4, handling IPv4 addresses, +as well as +s6-tcpserver6, handling IPv6 addresses. +It will automatically detect the remote address type and match it against the +correct subdatabase.
    • +
    • s6-tcpserver-access may perform several DNS queries. For efficiency +purposes, it does as many of them as possible in parallel. However, if asked +to do an IDENT query, it does not parallelize it with DNS queries. Take +that into account when estimating a proper timeout value.
    • +
    + + + diff --git a/doc/s6-tcpserver.html b/doc/s6-tcpserver.html new file mode 100644 index 0000000..41eb176 --- /dev/null +++ b/doc/s6-tcpserver.html @@ -0,0 +1,72 @@ + + + + + s6-networking: the s6-tcpserver program + + + + + + +

    +s6-networking
    +Software
    +skarnet.org +

    + +

    The s6-tcpserver program

    + +

    +s6-tcpserver is an +UCSPI tool for +TCP connections, i.e. a super-server. It accepts connections from +clients, and forks a program to handle each connection. +

    + +

    Interface

    + +
    +     s6-tcpserver [ -q | -Q | -v ] [ -4 | -6 ] [ -1 ] [ -c maxconn ] [ -C localmaxconn ] [ -b backlog ] [ -G gidlist ] [ -g gid ] [ -u uid ] [ -U ] ip port prog...
    +
    + +
      +
    • s6-tcpserver executes into +s6-tcpserver4 or +s6-tcpserver6 depending on whether +ip is an IPv4 or IPv6 address. It modifies some of its +option syntax to match s6-tcpserver4 and s6-tcpserver6's.
    • +
    • s6-tcpserver4 or s6-tcpserver6 handles the connection itself.
    • +
    + +

    Options

    + +
      +
    • -q : be quiet. This is converted into -v 0 +for s6-tcpserver4 or s6-tcpserver6.
    • +
    • -Q : be normally quiet. This is converted into -v 1 +for s6-tcpserver4 or s6-tcpserver6. This is the default.
    • +
    • -v : be verbose. This is converted into -v 2 +for s6-tcpserver4 or s6-tcpserver6.
    • +
    • -4 : IPv4 only. Interpret ip as IPv4; if it is +invalid, exit 100.
    • +
    • -6 : IPv6 only. Interpret ip as IPv6; if it is +invalid, exit 100. If neither the -4 nor the -6 option is +given, s6-tcpserver will parse ip to determine its family.
    • +
    • Every other option is passed verbatim to s6-tcpserver4 or s6-tcpserver6.
    • +
    + +

    Notes

    + +
      +
    • s6-tcpserver executes either into s6-tcpserver4, which only serves +IPv4, or into IPv6, which only serves IPv6. It will not bind to every +available IP address of the machine whether they are v4 or v6; on the +other hand, it can bind to every available IPv4 address (if ip +is 0.0.0.0) or to every available IPv6 address (if ip +is ::). Two instances of s6-tcpserver can cover every +available address.
    • +
    + + + diff --git a/doc/s6-tcpserver4.html b/doc/s6-tcpserver4.html new file mode 100644 index 0000000..e0270e0 --- /dev/null +++ b/doc/s6-tcpserver4.html @@ -0,0 +1,123 @@ + + + + + s6-networking: the s6-tcpserver4 program + + + + + + +

    +s6-networking
    +Software
    +skarnet.org +

    + +

    The s6-tcpserver4 program

    + +

    +s6-tcpserver4 is a super-server for IPv4 TCP +connections. It accepts connections from clients, and forks a +program to handle each connection. +

    + +

    Interface

    + +
    +     s6-tcpserver4 [ -1 ] [ -v verbosity ] [ -c maxconn ] [ -C localmaxconn ] [ -b backlog ] [ -G gidlist ] [ -g gid ] [ -u uid ] [ -U ] ip port prog...
    +
    + +
      +
    • s6-tcpserver4 binds to local IPv4 address ip, +port port.
    • +
    • It closes its stdin and stdout.
    • +
    • For every TCP connection to this address and port, it +forks. The child sets some environment variables, then +executes prog... with stdin reading from the network +socket and stdout writing to it.
    • +
    • Depending on the verbosity level, it logs what it does to stderr.
    • +
    • It runs until killed by a signal. Depending on the received +signal, it may kill its children before exiting.
    • +
    + +

    Environment variables

    + +

    + For each connection, an instance of prog... is spawned with +the following variables set: +

    + +
      +
    • PROTO: always set to TCP
    • +
    • TCPREMOTEIP: set to the originating address
    • +
    • TCPREMOTEPORT: set to the originating port
    • +
    • TCPCONNNUM: set to the number of connections originating from +the same IP address
    • +
    + +

    Options

    + +
      +
    • -1 : write port to stdout, before +closing it, right after binding and listening to the network socket. +If stdout is suitably redirected, this can be used by monitoring +programs to check when the server is ready to accept connections.
    • +
    • -v verbosity : be more or less verbose. +By default, verbosity is 1: print warning messages to stderr. +0 means only print fatal error messages ; 2 means print status and +connection information for every client.
    • +
    • -c maxconn : accept at most +maxconn concurrent connections. Default is 40. It is +impossible to set it higher than 1000.
    • +
    • -C localmaxconn : accept at most +localmaxconn connections from the same IP address. +Default is 40. It is impossible to set it higher than maxconn.
    • +
    • -b backlog : set a maximum of +backlog backlog connections on the socket. Extra +connection attempts will rejected by the kernel.
    • +
    • -G gidlist : change s6-tcpserver4's +supplementary group list to gidlist after binding the socket. +This is only valid when run as root. gidlist must be a +comma-separated list of numerical group IDs.
    • +
    • -g gid : change s6-tcpserver4's groupid +to gid after binding the socket. This is only valid when run +as root.
    • +
    • -u uid : change s6-tcpserver4's userid +to uid after binding the socket. This is only valid when run +as root.
    • +
    • -U : change s6-tcpserver4's user id, group id and +supplementary group list +according to the values of the UID, GID and GIDLIST environment variables +after binding the socket. This is only valid when run as root. +This can be used with the +s6-envuidgid +program to easily script a service that binds to a privileged socket +then drops its privileges to those of a named non-root account.
    • +
    + +

    Signals

    + +
      +
    • SIGTERM: exit.
    • +
    • SIGHUP: send a SIGTERM and a SIGCONT to all children.
    • +
    • SIGQUIT: send a SIGTERM and a SIGCONT to all children, then exit.
    • +
    • SIGABRT: send a SIGKILL to all children, then exit.
    • +
    + +

    Notes

    + +
      +
    • Unlike its ancestor +tcpserver, +s6-tcpserver4 performs just the bare minimum: the point is to have a +very small and very fast process to serve connections with the least +possible overhead. Features such as additional environment variables, +access control and DNS resolution are provided +via the s6-tcpserver-access +program.
    • +
    + + + diff --git a/doc/s6-tcpserver6.html b/doc/s6-tcpserver6.html new file mode 100644 index 0000000..e571010 --- /dev/null +++ b/doc/s6-tcpserver6.html @@ -0,0 +1,122 @@ + + + + + s6-networking: the s6-tcpserver6 program + + + + + + +

    +s6-networking
    +Software
    +skarnet.org +

    + +

    The s6-tcpserver6 program

    + +

    +s6-tcpserver6 is a super-server for IPv6 TCP +connections. It accepts connections from clients, and forks a +program to handle each connection. +

    + +

    Interface

    + +
    +     s6-tcpserver6 [ -1 ] [ -v verbosity ] [ -c maxconn ] [ -C localmaxconn ] [ -b backlog ] [ -G gidlist ] [ -g gid ] [ -u uid ] [ -U ] ip port prog...
    +
    + +
      +
    • s6-tcpserver6 binds to local IPv6 address ip, +port port.
    • +
    • It closes its stdin and stdout.
    • +
    • For every TCP connection to this address and port, it +forks. The child sets some environment variables, then +executes prog... with stdin reading from the network socket +and stdout writing to it.
    • +
    • Depending on the verbosity level, it logs what it does to stderr.
    • +
    • It runs until killed by a signal. Depending on the received +signal, it may kill its children before exiting.
    • +
    + +

    Environment variables

    + +

    + For each connection, an instance of prog... is spawned with +the following variables set: +

    + +
      +
    • PROTO: always set to TCP
    • +
    • TCPREMOTEIP: set to the originating address, in canonical IPv6 form
    • +
    • TCPREMOTEPORT: set to the originating port
    • +
    • TCPCONNNUM: set to the number of connections originating from +the same IPv6 address
    • +
    + +

    Options

    + +
      +
    • -1 : write port to stdout, before +closing it, right after binding and listening to the network socket. +If stdout is suitably redirected, this can be used by monitoring +programs to check when the server is ready to accept connections.
    • +
    • -v verbosity : be more or less verbose. +By default, verbosity is 1: print warning messages to stderr. +0 means only print fatal error messages ; 2 means print status and +connection information for every client.
    • +
    • -c maxconn : accept at most +maxconn concurrent connections. Default is 40. It is +impossible to set it higher than 1000.
    • +
    • -C localmaxconn : accept at most +localmaxconn connections from the same IP address. +Default is 40. It is impossible to set it higher than maxconn.
    • +
    • -b backlog : set a maximum of +backlog backlog connections on the socket. Extra +connection attempts will rejected by the kernel.
    • +
    • -G gidlist : change s6-tcpserver6's +supplementary group list to gidlist after binding the socket. +This is only valid when run as root. gidlist must be a +comma-separated list of numerical group IDs.
    • +
    • -g gid : change s6-tcpserver6's groupid +to gid after binding the socket. This is only valid when run +as root.
    • +
    • -u uid : change s6-tcpserver6's userid +to uid after binding the socket. This is only valid when run +as root.
    • +
    • -U : change s6-tcpserver6's user id, group id and +supplementary group list +according to the values of the UID, GID and GIDLIST environment variables +after binding the socket. This is only valid when run as root. +This can be used with the +s6-envuidgid +program to easily script a service that binds to a privileged socket +then drops its privileges to those of a named non-root account.
    • +
    + +

    Signals

    + +
      +
    • SIGTERM: exit.
    • +
    • SIGHUP: send a SIGTERM and a SIGCONT to all children.
    • +
    • SIGQUIT: send a SIGTERM and a SIGCONT to all children, then exit.
    • +
    • SIGABRT: send a SIGKILL to all children, then exit.
    • +
    + +

    Notes

    + +
      +
    • s6-tcpserver6 will only serve real IPv6 addresses; it does not +default to an IPv4 address. The +s6-tcpserver4 program should be +used to serve IPv4 addresses.
    • +
    • s6-tcpserver6 will only work if the underlying +skalibs has +been compiled with IPv6 support.
    • +
    + + + diff --git a/doc/seekablepipe.html b/doc/seekablepipe.html new file mode 100644 index 0000000..cd17b2e --- /dev/null +++ b/doc/seekablepipe.html @@ -0,0 +1,36 @@ + + + + + s6-networking: the seekablepipe program + + + + + + +

    +s6-networking
    +Software
    +skarnet.org +

    + +

    The seekablepipe program

    + +seekablepipe turns the reading end of a pipe into a seekable +file descriptor, using a temporary file. + +

    Interface

    + +
    +     writer | seekablepipe tmpfile reader [ args ... ]
    +
    + +

    +seekablepipe writes writer's output to tmpfile, +which is unlinked as soon as it is created. Then it execs into +reader, reading from a file descriptor on tmpfile. +

    + + + diff --git a/doc/upgrade.html b/doc/upgrade.html new file mode 100644 index 0000000..0f7e635 --- /dev/null +++ b/doc/upgrade.html @@ -0,0 +1,34 @@ + + + + + s6-networking: how to upgrade + + + + + + +

    +s6-networking
    +Software
    +skarnet.org +

    + +

    What has changed in s6-networking

    + +

    n 2.0.0.0

    + +
      +
    • The build system has completely changed. It is now a standard +./configure && make && sudo make install +build system. See the enclosed INSTALL file for details.
    • +
    • slashpackage is not activated by default.
    • +
    • shared libraries are not used by default.
    • +
    • skalibs dependency bumped to 2.0.0.0.
    • +
    • execline dependency bumped to 2.0.0.0.
    • +
    • s6-dns dependency bumped to 2.0.0.0.
    • +
    + + + diff --git a/package/deps-build b/package/deps-build new file mode 100644 index 0000000..254622d --- /dev/null +++ b/package/deps-build @@ -0,0 +1,3 @@ +/package/prog/skalibs +/package/admin/execline +/package/web/s6-dns diff --git a/package/deps.mak b/package/deps.mak new file mode 100644 index 0000000..274d375 --- /dev/null +++ b/package/deps.mak @@ -0,0 +1,69 @@ +# +# This file has been generated by tools/gen-deps.sh +# + +src/include/s6-networking/s6net.h: src/include/s6-networking/accessrules.h src/include/s6-networking/ident.h +src/clock/s6-clockadd.o src/clock/s6-clockadd.lo: src/clock/s6-clockadd.c +src/clock/s6-clockview.o src/clock/s6-clockview.lo: src/clock/s6-clockview.c +src/clock/s6-sntpclock.o src/clock/s6-sntpclock.lo: src/clock/s6-sntpclock.c +src/clock/s6-taiclock.o src/clock/s6-taiclock.lo: src/clock/s6-taiclock.c +src/clock/s6-taiclockd.o src/clock/s6-taiclockd.lo: src/clock/s6-taiclockd.c +src/conn-tools/s6-accessrules-cdb-from-fs.o src/conn-tools/s6-accessrules-cdb-from-fs.lo: src/conn-tools/s6-accessrules-cdb-from-fs.c +src/conn-tools/s6-accessrules-fs-from-cdb.o src/conn-tools/s6-accessrules-fs-from-cdb.lo: src/conn-tools/s6-accessrules-fs-from-cdb.c +src/conn-tools/s6-connlimit.o src/conn-tools/s6-connlimit.lo: src/conn-tools/s6-connlimit.c +src/conn-tools/s6-getservbyname.o src/conn-tools/s6-getservbyname.lo: src/conn-tools/s6-getservbyname.c +src/conn-tools/s6-ident-client.o src/conn-tools/s6-ident-client.lo: src/conn-tools/s6-ident-client.c src/include/s6-networking/ident.h +src/conn-tools/s6-ioconnect.o src/conn-tools/s6-ioconnect.lo: src/conn-tools/s6-ioconnect.c +src/conn-tools/s6-ipcclient.o src/conn-tools/s6-ipcclient.lo: src/conn-tools/s6-ipcclient.c +src/conn-tools/s6-ipcserver-access.o src/conn-tools/s6-ipcserver-access.lo: src/conn-tools/s6-ipcserver-access.c src/include/s6-networking/accessrules.h +src/conn-tools/s6-ipcserver.o src/conn-tools/s6-ipcserver.lo: src/conn-tools/s6-ipcserver.c +src/conn-tools/s6-sudo.o src/conn-tools/s6-sudo.lo: src/conn-tools/s6-sudo.c src/include/s6-networking/config.h +src/conn-tools/s6-sudoc.o src/conn-tools/s6-sudoc.lo: src/conn-tools/s6-sudoc.c src/conn-tools/s6-sudo.h +src/conn-tools/s6-sudod.o src/conn-tools/s6-sudod.lo: src/conn-tools/s6-sudod.c src/conn-tools/s6-sudo.h +src/conn-tools/s6-tcpclient.o src/conn-tools/s6-tcpclient.lo: src/conn-tools/s6-tcpclient.c src/include/s6-networking/ident.h +src/conn-tools/s6-tcpserver-access.o src/conn-tools/s6-tcpserver-access.lo: src/conn-tools/s6-tcpserver-access.c src/include/s6-networking/s6net.h +src/conn-tools/s6-tcpserver.o src/conn-tools/s6-tcpserver.lo: src/conn-tools/s6-tcpserver.c src/include/s6-networking/config.h +src/conn-tools/s6-tcpserver4.o src/conn-tools/s6-tcpserver4.lo: src/conn-tools/s6-tcpserver4.c +src/conn-tools/s6-tcpserver6.o src/conn-tools/s6-tcpserver6.lo: src/conn-tools/s6-tcpserver6.c +src/conn-tools/seekablepipe.o src/conn-tools/seekablepipe.lo: src/conn-tools/seekablepipe.c +src/libs6net/s6net_accessrules_backend_cdb.o src/libs6net/s6net_accessrules_backend_cdb.lo: src/libs6net/s6net_accessrules_backend_cdb.c src/include/s6-networking/accessrules.h +src/libs6net/s6net_accessrules_backend_fs.o src/libs6net/s6net_accessrules_backend_fs.lo: src/libs6net/s6net_accessrules_backend_fs.c src/include/s6-networking/accessrules.h +src/libs6net/s6net_accessrules_keycheck_ip4.o src/libs6net/s6net_accessrules_keycheck_ip4.lo: src/libs6net/s6net_accessrules_keycheck_ip4.c src/include/s6-networking/accessrules.h +src/libs6net/s6net_accessrules_keycheck_ip6.o src/libs6net/s6net_accessrules_keycheck_ip6.lo: src/libs6net/s6net_accessrules_keycheck_ip6.c src/include/s6-networking/accessrules.h +src/libs6net/s6net_accessrules_keycheck_reversedns.o src/libs6net/s6net_accessrules_keycheck_reversedns.lo: src/libs6net/s6net_accessrules_keycheck_reversedns.c src/include/s6-networking/accessrules.h +src/libs6net/s6net_accessrules_keycheck_uidgid.o src/libs6net/s6net_accessrules_keycheck_uidgid.lo: src/libs6net/s6net_accessrules_keycheck_uidgid.c src/include/s6-networking/accessrules.h +src/libs6net/s6net_accessrules_uidgid_cdb.o src/libs6net/s6net_accessrules_uidgid_cdb.lo: src/libs6net/s6net_accessrules_uidgid_cdb.c src/include/s6-networking/accessrules.h +src/libs6net/s6net_accessrules_uidgid_fs.o src/libs6net/s6net_accessrules_uidgid_fs.lo: src/libs6net/s6net_accessrules_uidgid_fs.c src/include/s6-networking/accessrules.h +src/libs6net/s6net_ident_client.o src/libs6net/s6net_ident_client.lo: src/libs6net/s6net_ident_client.c src/include/s6-networking/ident.h +src/libs6net/s6net_ident_error.o src/libs6net/s6net_ident_error.lo: src/libs6net/s6net_ident_error.c src/include/s6-networking/ident.h +src/libs6net/s6net_ident_reply_get.o src/libs6net/s6net_ident_reply_get.lo: src/libs6net/s6net_ident_reply_get.c src/include/s6-networking/ident.h +src/libs6net/s6net_ident_reply_parse.o src/libs6net/s6net_ident_reply_parse.lo: src/libs6net/s6net_ident_reply_parse.c src/include/s6-networking/ident.h +src/minidentd/mgetuid-default.o src/minidentd/mgetuid-default.lo: src/minidentd/mgetuid-default.c src/minidentd/mgetuid.h +src/minidentd/mgetuid-linux.o src/minidentd/mgetuid-linux.lo: src/minidentd/mgetuid-linux.c src/minidentd/mgetuid.h +src/minidentd/minidentd.o src/minidentd/minidentd.lo: src/minidentd/minidentd.c src/minidentd/mgetuid.h + +s6-clockadd: src/clock/s6-clockadd.o -lskarnet ${SYSCLOCK_LIB} +s6-clockview: src/clock/s6-clockview.o -lskarnet ${SYSCLOCK_LIB} +s6-sntpclock: src/clock/s6-sntpclock.o -lskarnet ${SOCKET_LIB} ${TAINNOW_LIB} +s6-taiclock: src/clock/s6-taiclock.o -lskarnet ${SOCKET_LIB} ${TAINNOW_LIB} +s6-taiclockd: src/clock/s6-taiclockd.o -lskarnet ${SOCKET_LIB} ${SYSCLOCK_LIB} +s6-accessrules-cdb-from-fs: src/conn-tools/s6-accessrules-cdb-from-fs.o -ls6net -lskarnet ${SOCKET_LIB} ${TAINNOW_LIB} +s6-accessrules-fs-from-cdb: src/conn-tools/s6-accessrules-fs-from-cdb.o -lskarnet +s6-connlimit: src/conn-tools/s6-connlimit.o -lskarnet +s6-getservbyname: src/conn-tools/s6-getservbyname.o -lskarnet +s6-ident-client: src/conn-tools/s6-ident-client.o -ls6net -lskarnet ${SOCKET_LIB} ${TAINNOW_LIB} +s6-ioconnect: src/conn-tools/s6-ioconnect.o -lskarnet ${TAINNOW_LIB} +s6-ipcclient: src/conn-tools/s6-ipcclient.o -lskarnet ${SOCKET_LIB} +s6-ipcserver: src/conn-tools/s6-ipcserver.o -lskarnet ${SOCKET_LIB} +s6-ipcserver-access: src/conn-tools/s6-ipcserver-access.o -ls6net -lskarnet +s6-sudo: src/conn-tools/s6-sudo.o -lskarnet +s6-sudoc: src/conn-tools/s6-sudoc.o -lskarnet ${TAINNOW_LIB} +s6-sudod: src/conn-tools/s6-sudod.o -lskarnet ${TAINNOW_LIB} +s6-tcpclient: src/conn-tools/s6-tcpclient.o -ls6net -ls6dns -lskarnet ${SOCKET_LIB} ${TAINNOW_LIB} +s6-tcpserver: src/conn-tools/s6-tcpserver.o -lskarnet +s6-tcpserver-access: src/conn-tools/s6-tcpserver-access.o -ls6net -ls6dns -lskarnet ${SOCKET_LIB} ${TAINNOW_LIB} +s6-tcpserver4: src/conn-tools/s6-tcpserver4.o -lskarnet ${SOCKET_LIB} +s6-tcpserver6: src/conn-tools/s6-tcpserver6.o -lskarnet ${SOCKET_LIB} +seekablepipe: src/conn-tools/seekablepipe.o -lskarnet +libs6net.a: src/libs6net/s6net_accessrules_backend_cdb.o src/libs6net/s6net_accessrules_backend_fs.o src/libs6net/s6net_accessrules_keycheck_ip4.o src/libs6net/s6net_accessrules_keycheck_ip6.o src/libs6net/s6net_accessrules_keycheck_reversedns.o src/libs6net/s6net_accessrules_keycheck_uidgid.o src/libs6net/s6net_accessrules_uidgid_cdb.o src/libs6net/s6net_accessrules_uidgid_fs.o src/libs6net/s6net_ident_client.o src/libs6net/s6net_ident_reply_get.o src/libs6net/s6net_ident_reply_parse.o src/libs6net/s6net_ident_error.o +minidentd: src/minidentd/minidentd.o src/minidentd/mgetuid.o -lskarnet ${SOCKET_LIB} ${TAINNOW_LIB} diff --git a/package/info b/package/info new file mode 100644 index 0000000..1fa2d13 --- /dev/null +++ b/package/info @@ -0,0 +1,4 @@ +package=s6-networking +version=2.0.0.0 +category=net +package_macro_name=S6_NETWORKING diff --git a/package/modes b/package/modes new file mode 100644 index 0000000..bc896a6 --- /dev/null +++ b/package/modes @@ -0,0 +1,24 @@ +s6-connlimit 0755 +s6-getservbyname 0755 +s6-ioconnect 0755 +s6-ident-client 0755 +s6-ipcclient 0755 +s6-ipcserver 0755 +s6-tcpclient 0755 +s6-tcpserver4 0755 +s6-tcpserver6 0755 +s6-tcpserver 0755 +s6-accessrules-cdb-from-fs 0755 +s6-accessrules-fs-from-cdb 0755 +s6-ipcserver-access 0755 +s6-tcpserver-access 0755 +seekablepipe 0755 +s6-sudo 0755 +s6-sudoc 0755 +s6-sudod 0755 +s6-clockadd 0700 +s6-clockview 0755 +s6-sntpclock 0755 +s6-taiclock 0755 +s6-taiclockd 0755 +minidentd 0755 diff --git a/package/targets.mak b/package/targets.mak new file mode 100644 index 0000000..2125f4e --- /dev/null +++ b/package/targets.mak @@ -0,0 +1,40 @@ +BIN_TARGETS = \ +s6-connlimit \ +s6-getservbyname \ +s6-ioconnect \ +s6-ident-client \ +s6-ipcclient \ +s6-ipcserver \ +s6-tcpclient \ +s6-tcpserver4 \ +s6-tcpserver6 \ +s6-tcpserver \ +s6-accessrules-cdb-from-fs \ +s6-accessrules-fs-from-cdb \ +s6-ipcserver-access \ +s6-tcpserver-access \ +seekablepipe \ +s6-sudo \ +s6-sudoc \ +s6-sudod \ +s6-clockadd \ +s6-clockview \ +s6-sntpclock \ +s6-taiclock \ +s6-taiclockd \ +minidentd + +LIBEXEC_TARGETS = + +SHARED_LIBS = \ +libs6net.so + +STATIC_LIBS = \ +libs6net.a + +src/minidentd/mgetuid.c: src/minidentd/mgetuid-linux.c src/minidentd/mgetuid-default.c + if grep -iF -- -linux- $(sysdeps)/target ; then \ + ln -sf mgetuid-linux.c src/minidentd/mgetuid.c ; \ + else \ + ln -sf mgetuid-default.c src/minidentd/mgetuid.c ; \ + fi diff --git a/patch-for-solaris b/patch-for-solaris new file mode 100755 index 0000000..02f2e3c --- /dev/null +++ b/patch-for-solaris @@ -0,0 +1,17 @@ +#!/usr/xpg4/bin/sh + +patchit () { + echo '#!/usr/xpg4/bin/sh' > $1.tmp + tail -n +2 $1 >> $1.tmp + mv -f $1.tmp $1 + chmod 755 $1 +} + +patchit ./configure +patchit ./tools/install.sh +patchit ./tools/gen-deps.sh + +echo 'SHELL := /usr/xpg4/bin/sh' > Makefile.tmp +echo >> Makefile.tmp +cat Makefile >> Makefile.tmp +mv -f Makefile.tmp Makefile diff --git a/src/clock/deps-exe/s6-clockadd b/src/clock/deps-exe/s6-clockadd new file mode 100644 index 0000000..a11a5f4 --- /dev/null +++ b/src/clock/deps-exe/s6-clockadd @@ -0,0 +1,2 @@ +-lskarnet +${SYSCLOCK_LIB} diff --git a/src/clock/deps-exe/s6-clockview b/src/clock/deps-exe/s6-clockview new file mode 100644 index 0000000..a11a5f4 --- /dev/null +++ b/src/clock/deps-exe/s6-clockview @@ -0,0 +1,2 @@ +-lskarnet +${SYSCLOCK_LIB} diff --git a/src/clock/deps-exe/s6-sntpclock b/src/clock/deps-exe/s6-sntpclock new file mode 100644 index 0000000..e027835 --- /dev/null +++ b/src/clock/deps-exe/s6-sntpclock @@ -0,0 +1,3 @@ +-lskarnet +${SOCKET_LIB} +${TAINNOW_LIB} diff --git a/src/clock/deps-exe/s6-taiclock b/src/clock/deps-exe/s6-taiclock new file mode 100644 index 0000000..e027835 --- /dev/null +++ b/src/clock/deps-exe/s6-taiclock @@ -0,0 +1,3 @@ +-lskarnet +${SOCKET_LIB} +${TAINNOW_LIB} diff --git a/src/clock/deps-exe/s6-taiclockd b/src/clock/deps-exe/s6-taiclockd new file mode 100644 index 0000000..720fe7d --- /dev/null +++ b/src/clock/deps-exe/s6-taiclockd @@ -0,0 +1,3 @@ +-lskarnet +${SOCKET_LIB} +${SYSCLOCK_LIB} diff --git a/src/clock/s6-clockadd.c b/src/clock/s6-clockadd.c new file mode 100644 index 0000000..990f3ba --- /dev/null +++ b/src/clock/s6-clockadd.c @@ -0,0 +1,58 @@ +/* ISC license. */ + +#include +#include +#include +#include +#include + +#define USAGE "s6-clockadd [ -f ] [ -e errmax ]" +#define dieusage() strerr_dieusage(100, USAGE) + +int main (int argc, char const *const *argv) +{ + tain_t now, adj ; + unsigned int emax = 2000 ; + int flagforce = 0 ; + PROG = "s6-clockadd" ; + { + subgetopt_t l = SUBGETOPT_ZERO ; + for (;;) + { + register int opt = subgetopt_r(argc, argv, "fe:", &l) ; + if (opt == -1) break ; + switch (opt) + { + case 'f' : flagforce = 1 ; break ; + case 'e' : if (!uint0_scan(l.arg, &emax)) dieusage() ; break ; + default : dieusage() ; + } + } + argc -= l.ind ; argv += l.ind ; + } + { + char buf[TAIN_PACK] ; + if (allread(0, buf, TAIN_PACK) < TAIN_PACK) + strerr_diefu1sys(111, "read 16 bytes from stdin") ; + tain_unpack(buf, &adj) ; + } + tain_from_millisecs(&now, emax) ; + if (tain_less(&now, &adj)) + { + tain_t tmp = TAIN_ZERO ; + tain_sub(&tmp, &tmp, &adj) ; + if (tain_less(&now, &tmp)) + { + char fmt[UINT_FMT] ; + fmt[uint_fmt(fmt, emax)] = 0 ; + if (flagforce) + strerr_warnw3x("time discrepancy bigger than ", fmt, " milliseconds") ; + else + strerr_dief3x(1, "time discrepancy bigger than ", fmt, " milliseconds") ; + } + } + if (!sysclock_get(&now)) strerr_diefu1sys(111, "sysclock_get") ; + tain_add(&now, &now, &adj) ; + if (!sysclock_set(&now)) strerr_diefu1sys(111, "sysclock_set") ; + return 0 ; +} diff --git a/src/clock/s6-clockview.c b/src/clock/s6-clockview.c new file mode 100644 index 0000000..3600cce --- /dev/null +++ b/src/clock/s6-clockview.c @@ -0,0 +1,31 @@ +/* ISC license. */ + +#include +#include +#include +#include +#include + +int main (void) +{ + char buf[TAIN_PACK] ; + tain_t now, adj ; + localtmn_t l ; + char fmt[LOCALTMN_FMT] ; + PROG = "s6-clockview" ; + + if (allread(0, buf, TAIN_PACK) < TAIN_PACK) strerr_diefu1sys(111, "read from stdin") ; + tain_unpack(buf, &adj) ; + if (!sysclock_get(&now)) strerr_diefu1sys(111, "sysclock_get") ; + if (!localtmn_from_sysclock(&l, &now, 1)) strerr_diefu1sys(111, "localtmn_from_sysclock") ; + if (buffer_puts(buffer_1, "before: ") < 0) goto fail ; + if (buffer_put(buffer_1, fmt, localtmn_fmt(fmt, &l)) < 0) goto fail ; + tain_add(&now, &now, &adj) ; + if (!localtmn_from_sysclock(&l, &now, 1)) strerr_diefu1sys(111, "localtmn_from_sysclock") ; + if (buffer_puts(buffer_1, "\nafter: ") < 0) goto fail ; + if (buffer_put(buffer_1, fmt, localtmn_fmt(fmt, &l)) < 0) goto fail ; + if (buffer_putflush(buffer_1, "\n", 1) < 0) goto fail ; + return 0 ; + fail: + strerr_diefu1sys(111, "write to stdout") ; +} diff --git a/src/clock/s6-sntpclock.c b/src/clock/s6-sntpclock.c new file mode 100644 index 0000000..a7bcc22 --- /dev/null +++ b/src/clock/s6-sntpclock.c @@ -0,0 +1,238 @@ +/* ISC license. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define USAGE "s6-sntpclock [ -f ] [ -v verbosity ] [ -r roundtrips ] [ -t triptimeout ] [ -h throttle ] [ -T totaltimeout ] [ -e errmax ] [ -p port ] ipaddress" +#define dieusage() strerr_dieusage(100, USAGE) + +static unsigned int verbosity = 1 ; + +int ntp_exchange (int s, ip46_t const *ip, uint16 port, tain_t *stamps, tain_t const *deadline) +{ + char query[48] = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" ; + char answer[48] = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" ; + tain_t starttime ; + uint64 ntpstamp ; + ip46_t dummyip ; + uint16 dummyport ; + int r ; + tain_copynow(&starttime) ; + query[0] = 35 ; /* SNTPv4, client */ + if (!ntp_from_tain(&ntpstamp, &starttime)) return 0 ; + uint64_pack_big(query+24, ntpstamp) ; + uint64_pack_big(query+40, ntpstamp) ; + if (verbosity >= 3) + { + char fmtntp[UINT64_XFMT] ; + fmtntp[uint64_xfmt(fmtntp, ntpstamp)] = 0 ; + strerr_warni2x("NTP stamp[0] sent: ", fmtntp) ; + } + r = socket_sendnb46_g(s, query, 48, ip, port, deadline) ; + if (r < 0) return 0 ; + if (r < 48) return (errno = EPIPE, 0) ; + r = socket_recvnb46_g(s, answer, 48, &dummyip, &dummyport, deadline) ; + if (r < 0) return 0 ; + if (r < 48) return (errno = EPROTO, 0) ; + if (((answer[0] & 7) != 2) && ((answer[0] & 7) != 4)) return (errno = EPROTO, 0) ; + if (!(answer[0] & 56)) return (errno = EPROTO, 0) ; + if (byte_diff(query+40, 8, answer+24)) return (errno = EPROTO, 0) ; + stamps[0] = starttime ; + uint64_unpack_big(answer+32, &ntpstamp) ; + tain_from_ntp(stamps+1, ntpstamp) ; + if (verbosity >= 3) + { + char fmtntp[UINT64_XFMT] ; + fmtntp[uint64_xfmt(fmtntp, ntpstamp)] = 0 ; + strerr_warni2x("NTP stamp[1] received: ", fmtntp) ; + } + uint64_unpack_big(answer+40, &ntpstamp) ; + tain_from_ntp(stamps+2, ntpstamp) ; + if (verbosity >= 3) + { + char fmtntp[UINT64_XFMT] ; + fmtntp[uint64_xfmt(fmtntp, ntpstamp)] = 0 ; + strerr_warni2x("NTP stamp[2] received: ", fmtntp) ; + } + tain_copynow(&stamps[3]) ; + return 1 ; +} + +int main (int argc, char const *const *argv) +{ + tain_t deltamin = TAIN_ZERO ; + tain_t deltaoffset ; + tain_t deltamax ; + tain_t errmax ; + tain_t timeouttto, throttletto, globaltto ; + tain_t globaldeadline ; + unsigned int roundtrips = 10 ; + unsigned int i = 0 ; + int sock ; + int flagforce = 0 ; + ip46_t ipremote ; + uint16 portremote = 123 ; + PROG = "s6-sntpclock" ; + + { + unsigned int timeout = 2000 ; + unsigned int throttle = 0 ; + unsigned int bigtimeout = 10000 ; + unsigned int emax = 100 ; + subgetopt_t l = SUBGETOPT_ZERO ; + for (;;) + { + register int opt = subgetopt_r(argc, argv, "fv:r:t:h:T:e:p:", &l) ; + if (opt == -1) break ; + switch (opt) + { + case 'f' : flagforce = 1 ; break ; + case 'v' : if (!uint0_scan(l.arg, &verbosity)) dieusage() ; break ; + case 'r' : if (!uint0_scan(l.arg, &roundtrips)) dieusage() ; break ; + case 't' : if (!uint0_scan(l.arg, &timeout)) dieusage() ; break ; + case 'h' : if (!uint0_scan(l.arg, &throttle)) dieusage() ; break ; + case 'T' : if (!uint0_scan(l.arg, &bigtimeout)) dieusage() ; break ; + case 'e' : if (!uint0_scan(l.arg, &emax)) dieusage() ; break ; + case 'p' : if (!uint160_scan(l.arg, &portremote)) dieusage() ; break ; + default : dieusage() ; + } + } + argc -= l.ind ; argv += l.ind ; + if (timeout) tain_from_millisecs(&timeouttto, timeout) ; + else timeouttto = tain_infinite_relative ; + tain_from_millisecs(&throttletto, throttle) ; + if (bigtimeout) tain_from_millisecs(&globaltto, bigtimeout) ; + else globaltto = tain_infinite_relative ; + tain_from_millisecs(&errmax, emax) ; + } + if (!argc) dieusage() ; + if (!ip46_scan(argv[0], &ipremote)) dieusage() ; + + sock = socket_udp46(ip46_is6(&ipremote)) ; + if (sock < 0) strerr_diefu1sys(111, "socket_udp") ; + + tain_uint(&deltaoffset, 1072224000U) ; /* about 34 years, which is the best we can do with NTP */ + tain_add(&deltamax, &deltaoffset, &deltaoffset) ; + tain_now_g() ; + tain_add_g(&globaldeadline, &globaltto) ; + if (!socket_deadlineconnstamp46_g(sock, &ipremote, portremote, &globaldeadline)) + strerr_diefu1sys(111, "socket_deadlineconnstamp") ; + + for (; i < roundtrips ; i++) + { + tain_t stamps[4] ; + tain_t deadline ; + tain_add_g(&deadline, &timeouttto) ; + if (tain_less(&globaldeadline, &deadline)) deadline = globaldeadline ; + if (verbosity >= 3) + { + char fmt[UINT_FMT] ; + char fmtr[UINT_FMT] ; + fmt[uint_fmt(fmt, i+1)] = 0 ; + fmtr[uint_fmt(fmtr, roundtrips)] = 0 ; + strerr_warni4x("NTP round-trip ", fmt, "/", fmtr) ; + } + if (!ntp_exchange(sock, &ipremote, portremote, stamps, &deadline)) + { + if (verbosity >= 2) + { + char fmt[UINT_FMT] ; + char fmtr[UINT_FMT] ; + fmt[uint_fmt(fmt, i+1)] = 0 ; + fmtr[uint_fmt(fmtr, roundtrips)] = 0 ; + strerr_warni5sys("NTP round-trip ", fmt, "/", fmtr, " failed") ; + } + } + else + { + tain_t cur, min, max ; + if (verbosity >= 3) + { + unsigned int j = 0 ; + for (; j < 4 ; j++) + { + uint64 ntp ; + localtmn_t l ; + char fmt[UINT_FMT] ; + char fmtntp[UINT64_XFMT] ; + char fmttaia[TAIN_FMT] ; + char fmtlocal[LOCALTMN_FMT] ; + ntp_from_tain(&ntp, &stamps[j]) ; + localtmn_from_tain(&l, &stamps[j], 1) ; + fmt[uint_fmt(fmt, j)] = 0 ; + fmttaia[tain_fmt(fmttaia, &stamps[j])] = 0 ; + fmtntp[uint64_xfmt(fmtntp, ntp)] = 0 ; + fmtlocal[localtmn_fmt(fmtlocal, &l)] = 0 ; + strerr_warni6x("stamp[", fmt, "] : taia: ", fmttaia, ", ntp: ", fmtntp) ; + strerr_warni2x("localdate: ", fmtlocal) ; + } + } + tain_add(&cur, &stamps[1], &deltaoffset) ; + tain_add(&min, &stamps[0], &deltamin) ; + tain_add(&max, &stamps[0], &deltamax) ; + if (tain_less(&cur, &max) && !tain_less(&cur, &min)) + tain_sub(&deltamax, &cur, &stamps[0]) ; + tain_add(&cur, &stamps[2], &deltaoffset) ; + tain_add(&min, &stamps[3], &deltamin) ; + tain_add(&max, &stamps[3], &deltamax) ; + if (tain_less(&cur, &max) && !tain_less(&cur, &min)) + tain_sub(&deltamin, &cur, &stamps[3]) ; + } + + tain_add_g(&deadline, &throttletto) ; + if (tain_less(&globaldeadline, &deadline)) deadline = globaldeadline ; + deepsleepuntil_g(&deadline) ; + if (!tain_future(&globaldeadline)) + { + if (verbosity) + { + errno = ETIMEDOUT ; + strerr_diefu1sys(1, "complete series of SNTP exchanges") ; + } + else return 1 ; + } + } + + { + char adj[TAIN_PACK] ; + tain_t delta ; + if (tain_less(&deltamax, &deltamin)) tain_sub(&delta, &deltamin, &deltamax) ; + else tain_sub(&delta, &deltamax, &deltamin) ; + if (tain_less(&errmax, &delta)) + { + if (verbosity) + { + char fmtd[TAIN_FMT] ; + char fmte[TAIN_FMT] ; + fmtd[tain_fmt(fmtd, &delta)] = 0 ; + fmte[tain_fmt(fmte, &errmax)] = 0 ; + strerr_warnw2x("maximum acceptable uncertainty: ", fmte) ; + strerr_warnw2x("current calculated uncertainty: ", fmtd) ; + } + if (!flagforce) strerr_dief1x(111, "time uncertainty too large") ; + } + + tain_add(&delta, &deltamax, &deltamin) ; + tain_half(&delta, &delta) ; + tain_sub(&delta, &delta, &deltaoffset) ; + tain_pack(adj, &delta) ; + if (allwrite(1, adj, TAIN_PACK) < TAIN_PACK) strerr_diefu1sys(111, "write to stdout") ; + } + return 0 ; +} diff --git a/src/clock/s6-taiclock.c b/src/clock/s6-taiclock.c new file mode 100644 index 0000000..99150b5 --- /dev/null +++ b/src/clock/s6-taiclock.c @@ -0,0 +1,182 @@ +/* ISC license. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define USAGE "s6-taiclock [ -f ] [ -v verbosity ] [ -r roundtrips ] [ -t triptimeout ] [ -h throttle ] [ -T totaltimeout ] [ -e errmax ] [ -p port ] ipaddress" +#define dieusage() strerr_dieusage(100, USAGE) + +static unsigned int verbosity = 1 ; + +#define N 28 + +int tain_exchange (int s, ip46_t const *ip, uint16 port, tain_t *serversays, tain_t const *deadline) +{ + char query[N] = "ctai" ; + char answer[N] ; + ip46_t dummyip ; + int r ; + uint16 dummyport ; + tain_pack(query+4, &STAMP) ; + if (badrandom_string(query+20, N-20) < N-20) return 0 ; /* cookie */ + r = socket_sendnb46_g(s, query, N, ip, port, deadline) ; + if (r < 0) return 0 ; + if (r < N) return (errno = EPIPE, 0) ; + r = socket_recvnb46_g(s, answer, N, &dummyip, &dummyport, deadline) ; + if (r < 0) return 0 ; + if (r < N) return (errno = EPROTO, 0) ; + if (byte_diff(answer, 4, "stai")) return (errno = EPROTO, 0) ; + if (byte_diff(query+20, N-20, answer+20)) return (errno = EPROTO, 0) ; + tain_unpack(answer+4, serversays) ; + return 1 ; +} + +int main (int argc, char const *const *argv) +{ + tain_t deltamin = TAIN_ZERO ; + tain_t deltaoffset ; + tain_t deltamax ; + tain_t errmax ; + tain_t timeouttto, throttletto, globaltto ; + tain_t globaldeadline ; + unsigned int roundtrips = 10 ; + unsigned int i = 0 ; + ip46_t ipremote ; + int sock ; + int flagforce = 0 ; + uint16 portremote = 4014 ; + PROG = "s6-taiclock" ; + + { + unsigned int timeout = 2000 ; + unsigned int throttle = 0 ; + unsigned int bigtimeout = 10000 ; + unsigned int emax = 100 ; + subgetopt_t l = SUBGETOPT_ZERO ; + for (;;) + { + register int opt = subgetopt_r(argc, argv, "fv:r:t:h:T:e:p:", &l) ; + if (opt == -1) break ; + switch (opt) + { + case 'f' : flagforce = 1 ; break ; + case 'v' : if (!uint0_scan(l.arg, &verbosity)) dieusage() ; break ; + case 'r' : if (!uint0_scan(l.arg, &roundtrips)) dieusage() ; break ; + case 't' : if (!uint0_scan(l.arg, &timeout)) dieusage() ; break ; + case 'h' : if (!uint0_scan(l.arg, &throttle)) dieusage() ; break ; + case 'T' : if (!uint0_scan(l.arg, &bigtimeout)) dieusage() ; break ; + case 'e' : if (!uint0_scan(l.arg, &emax)) dieusage() ; break ; + case 'p' : if (!uint160_scan(l.arg, &portremote)) dieusage() ; break ; + default : dieusage() ; + } + } + argc -= l.ind ; argv += l.ind ; + if (timeout) tain_from_millisecs(&timeouttto, timeout) ; + else timeouttto = tain_infinite_relative ; + tain_from_millisecs(&throttletto, throttle) ; + if (bigtimeout) tain_from_millisecs(&globaltto, bigtimeout) ; + else globaltto = tain_infinite_relative ; + tain_from_millisecs(&errmax, emax) ; + } + if (!argc) dieusage() ; + if (!ip46_scan(argv[0], &ipremote)) dieusage() ; + + sock = socket_udp46(ip46_is6(&ipremote)) ; + if (sock < 0) strerr_diefu1sys(111, "socket_udp") ; + + tain_uint(&deltaoffset, 0xffffffffU) ; + tain_add(&deltaoffset, &deltaoffset, &deltaoffset) ; /* about 136 years */ + tain_add(&deltamax, &deltaoffset, &deltaoffset) ; + tain_now_g() ; + tain_add_g(&globaldeadline, &globaltto) ; + if (!socket_deadlineconnstamp46_g(sock, &ipremote, portremote, &globaldeadline)) + strerr_diefu1sys(111, "socket_deadlineconnstamp") ; + + for (; i < roundtrips ; i++) + { + tain_t deadline, before, serversays ; + tain_add_g(&deadline, &timeouttto) ; + if (tain_less(&globaldeadline, &deadline)) deadline = globaldeadline ; + tain_copynow(&before) ; + if (!tain_exchange(sock, &ipremote, portremote, &serversays, &deadline)) + { + if (verbosity >= 2) + { + char fmt[UINT_FMT] ; + char fmtr[UINT_FMT] ; + fmt[uint_fmt(fmt, i+1)] = 0 ; + fmtr[uint_fmt(fmtr, roundtrips)] = 0 ; + strerr_warni5sys("TAIA round-trip ", fmt, "/", fmtr, " failed") ; + } + } + else + { + tain_t cur, min, max ; + tain_add(&cur, &serversays, &deltaoffset) ; + tain_add(&min, &before, &deltamin) ; + tain_add(&max, &before, &deltamax) ; + if (tain_less(&cur, &max) && !tain_less(&cur, &min)) + tain_sub(&deltamax, &cur, &before) ; + tain_add_g(&min, &deltamin) ; + tain_add_g(&max, &deltamax) ; + if (tain_less(&cur, &max) && !tain_less(&cur, &min)) + tain_sub(&deltamin, &cur, &STAMP) ; + } + + tain_add_g(&deadline, &throttletto) ; + if (tain_less(&globaldeadline, &deadline)) deadline = globaldeadline ; + deepsleepuntil_g(&deadline) ; + if (!tain_future(&globaldeadline)) + { + if (verbosity) + { + errno = ETIMEDOUT ; + strerr_diefu1sys(1, "complete series of TAIA exchanges") ; + } + else return 1 ; + } + } + + { + char adj[TAIN_PACK] ; + tain_t delta ; + if (tain_less(&deltamax, &deltamin)) tain_sub(&delta, &deltamin, &deltamax) ; + else tain_sub(&delta, &deltamax, &deltamin) ; + if (tain_less(&errmax, &delta)) + { + if (verbosity) + { + char fmtd[TAIN_FMT] ; + char fmte[TAIN_FMT] ; + fmtd[tain_fmt(fmtd, &delta)] = 0 ; + fmte[tain_fmt(fmte, &errmax)] = 0 ; + strerr_warnw2x("maximum acceptable uncertainty: ", fmte) ; + strerr_warnw2x("current calculated uncertainty: ", fmtd) ; + } + if (!flagforce) strerr_dief1x(111, "time uncertainty too large") ; + } + + tain_add(&delta, &deltamax, &deltamin) ; + tain_half(&delta, &delta) ; + tain_sub(&delta, &delta, &deltaoffset) ; + tain_pack(adj, &delta) ; + if (allwrite(1, adj, TAIN_PACK) < TAIN_PACK) strerr_diefu1sys(111, "write to stdout") ; + } + return 0 ; +} diff --git a/src/clock/s6-taiclockd.c b/src/clock/s6-taiclockd.c new file mode 100644 index 0000000..b206400 --- /dev/null +++ b/src/clock/s6-taiclockd.c @@ -0,0 +1,54 @@ +/* ISC license. */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define USAGE "s6-taiclockd [ -i ip ] [ -p port ]" +#define dieusage() strerr_dieusage(100, USAGE) + +int main (int argc, char const *const *argv) +{ + int s ; + ip46_t ip = IP46_ZERO ; + uint16 port = 4014 ; + subgetopt_t l = SUBGETOPT_ZERO ; + PROG = "s6-taiclockd" ; + for (;;) + { + register int opt = subgetopt_r(argc, argv, "i:p:", &l) ; + if (opt == -1) break ; + switch (opt) + { + case 'i' : if (!ip46_scan(l.arg, &ip)) dieusage() ; break ; + case 'p' : if (!uint160_scan(l.arg, &port)) dieusage() ; break ; + default : dieusage() ; + } + } + argc -= l.ind ; argv += l.ind ; + s = socket_udp46(ip46_is6(&ip)) ; + if (s < 0) strerr_diefu1sys(111, "socket_udp") ; + if (ndelay_off(s) < 0) strerr_diefu1sys(111, "ndelay_off") ; + if (socket_bind46_reuse(s, &ip, port) < 0) + strerr_diefu1sys(111, "socket_bind_reuse") ; + + for (;;) + { + char packet[256] ; + register int r = socket_recv46(s, packet, 256, &ip, &port) ; + if ((r >= 20) && !byte_diff(packet, 4, "ctai")) + { + tain_t now ; + packet[0] = 's' ; + if (!tain_sysclock(&now)) strerr_diefu1sys(111, "tain_sysclock") ; + tain_pack(packet + 4, &now) ; + if (socket_send46(s, packet, r, &ip, port) < 0) + strerr_warnwu1sys("socket_send") ; + } + } +} diff --git a/src/conn-tools/deps-exe/s6-accessrules-cdb-from-fs b/src/conn-tools/deps-exe/s6-accessrules-cdb-from-fs new file mode 100644 index 0000000..7229c44 --- /dev/null +++ b/src/conn-tools/deps-exe/s6-accessrules-cdb-from-fs @@ -0,0 +1,4 @@ +-ls6net +-lskarnet +${SOCKET_LIB} +${TAINNOW_LIB} diff --git a/src/conn-tools/deps-exe/s6-accessrules-fs-from-cdb b/src/conn-tools/deps-exe/s6-accessrules-fs-from-cdb new file mode 100644 index 0000000..e7187fe --- /dev/null +++ b/src/conn-tools/deps-exe/s6-accessrules-fs-from-cdb @@ -0,0 +1 @@ +-lskarnet diff --git a/src/conn-tools/deps-exe/s6-connlimit b/src/conn-tools/deps-exe/s6-connlimit new file mode 100644 index 0000000..e7187fe --- /dev/null +++ b/src/conn-tools/deps-exe/s6-connlimit @@ -0,0 +1 @@ +-lskarnet diff --git a/src/conn-tools/deps-exe/s6-getservbyname b/src/conn-tools/deps-exe/s6-getservbyname new file mode 100644 index 0000000..e7187fe --- /dev/null +++ b/src/conn-tools/deps-exe/s6-getservbyname @@ -0,0 +1 @@ +-lskarnet diff --git a/src/conn-tools/deps-exe/s6-ident-client b/src/conn-tools/deps-exe/s6-ident-client new file mode 100644 index 0000000..7229c44 --- /dev/null +++ b/src/conn-tools/deps-exe/s6-ident-client @@ -0,0 +1,4 @@ +-ls6net +-lskarnet +${SOCKET_LIB} +${TAINNOW_LIB} diff --git a/src/conn-tools/deps-exe/s6-ioconnect b/src/conn-tools/deps-exe/s6-ioconnect new file mode 100644 index 0000000..1840bc1 --- /dev/null +++ b/src/conn-tools/deps-exe/s6-ioconnect @@ -0,0 +1,2 @@ +-lskarnet +${TAINNOW_LIB} diff --git a/src/conn-tools/deps-exe/s6-ipcclient b/src/conn-tools/deps-exe/s6-ipcclient new file mode 100644 index 0000000..19869b2 --- /dev/null +++ b/src/conn-tools/deps-exe/s6-ipcclient @@ -0,0 +1,2 @@ +-lskarnet +${SOCKET_LIB} diff --git a/src/conn-tools/deps-exe/s6-ipcserver b/src/conn-tools/deps-exe/s6-ipcserver new file mode 100644 index 0000000..19869b2 --- /dev/null +++ b/src/conn-tools/deps-exe/s6-ipcserver @@ -0,0 +1,2 @@ +-lskarnet +${SOCKET_LIB} diff --git a/src/conn-tools/deps-exe/s6-ipcserver-access b/src/conn-tools/deps-exe/s6-ipcserver-access new file mode 100644 index 0000000..fceb21d --- /dev/null +++ b/src/conn-tools/deps-exe/s6-ipcserver-access @@ -0,0 +1,2 @@ +-ls6net +-lskarnet diff --git a/src/conn-tools/deps-exe/s6-sudo b/src/conn-tools/deps-exe/s6-sudo new file mode 100644 index 0000000..e7187fe --- /dev/null +++ b/src/conn-tools/deps-exe/s6-sudo @@ -0,0 +1 @@ +-lskarnet diff --git a/src/conn-tools/deps-exe/s6-sudoc b/src/conn-tools/deps-exe/s6-sudoc new file mode 100644 index 0000000..1840bc1 --- /dev/null +++ b/src/conn-tools/deps-exe/s6-sudoc @@ -0,0 +1,2 @@ +-lskarnet +${TAINNOW_LIB} diff --git a/src/conn-tools/deps-exe/s6-sudod b/src/conn-tools/deps-exe/s6-sudod new file mode 100644 index 0000000..1840bc1 --- /dev/null +++ b/src/conn-tools/deps-exe/s6-sudod @@ -0,0 +1,2 @@ +-lskarnet +${TAINNOW_LIB} diff --git a/src/conn-tools/deps-exe/s6-tcpclient b/src/conn-tools/deps-exe/s6-tcpclient new file mode 100644 index 0000000..13ea615 --- /dev/null +++ b/src/conn-tools/deps-exe/s6-tcpclient @@ -0,0 +1,5 @@ +-ls6net +-ls6dns +-lskarnet +${SOCKET_LIB} +${TAINNOW_LIB} diff --git a/src/conn-tools/deps-exe/s6-tcpserver b/src/conn-tools/deps-exe/s6-tcpserver new file mode 100644 index 0000000..e7187fe --- /dev/null +++ b/src/conn-tools/deps-exe/s6-tcpserver @@ -0,0 +1 @@ +-lskarnet diff --git a/src/conn-tools/deps-exe/s6-tcpserver-access b/src/conn-tools/deps-exe/s6-tcpserver-access new file mode 100644 index 0000000..13ea615 --- /dev/null +++ b/src/conn-tools/deps-exe/s6-tcpserver-access @@ -0,0 +1,5 @@ +-ls6net +-ls6dns +-lskarnet +${SOCKET_LIB} +${TAINNOW_LIB} diff --git a/src/conn-tools/deps-exe/s6-tcpserver4 b/src/conn-tools/deps-exe/s6-tcpserver4 new file mode 100644 index 0000000..19869b2 --- /dev/null +++ b/src/conn-tools/deps-exe/s6-tcpserver4 @@ -0,0 +1,2 @@ +-lskarnet +${SOCKET_LIB} diff --git a/src/conn-tools/deps-exe/s6-tcpserver6 b/src/conn-tools/deps-exe/s6-tcpserver6 new file mode 100644 index 0000000..19869b2 --- /dev/null +++ b/src/conn-tools/deps-exe/s6-tcpserver6 @@ -0,0 +1,2 @@ +-lskarnet +${SOCKET_LIB} diff --git a/src/conn-tools/deps-exe/seekablepipe b/src/conn-tools/deps-exe/seekablepipe new file mode 100644 index 0000000..e7187fe --- /dev/null +++ b/src/conn-tools/deps-exe/seekablepipe @@ -0,0 +1 @@ +-lskarnet diff --git a/src/conn-tools/s6-accessrules-cdb-from-fs.c b/src/conn-tools/s6-accessrules-cdb-from-fs.c new file mode 100644 index 0000000..82da64c --- /dev/null +++ b/src/conn-tools/s6-accessrules-cdb-from-fs.c @@ -0,0 +1,195 @@ +/* ISC license. */ + +#include +#include +#include /* for rename() */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define USAGE "s6-accessrules-cdb-from-fs cdbfile dir" + +static stralloc tmp = STRALLOC_ZERO ; + +static void cleanup (void) +{ + register int e = errno ; + unlink(tmp.s) ; + errno = e ; +} + +static void dienomem (void) +{ + cleanup() ; + strerr_diefu1sys(111, "stralloc_catb") ; +} + +static void doit (struct cdb_make *c, stralloc *sa, unsigned int start) +{ + unsigned int tmpbase = tmp.len ; + unsigned int k = sa->len ; + if (!stralloc_readyplus(sa, 10)) dienomem() ; + stralloc_catb(sa, "/allow", 7) ; + tmp.s[tmpbase] = 0 ; + if (access(sa->s, R_OK) < 0) + { + if ((errno != ENOENT) && (errno != EACCES)) + { + cleanup() ; + strerr_diefu2sys(111, "access ", sa->s) ; + } + sa->len = k+1 ; + stralloc_catb(sa, "deny", 5) ; + if (access(sa->s, R_OK) < 0) + if ((errno != ENOENT) && (errno != EACCES)) + { + cleanup() ; + strerr_diefu2sys(111, "access ", sa->s) ; + } + else return ; + else if (cdb_make_add(c, sa->s + start, k - start, "D", 1) < 0) + { + cleanup() ; + strerr_diefu1sys(111, "cdb_make_add") ; + } + } + else + { + uint16 envlen = 0 ; + uint16 execlen = 0 ; + register int r ; + tmp.s[tmpbase] = 'A' ; + sa->len = k+1 ; + stralloc_catb(sa, "env", 4) ; + tmp.len = tmpbase + 3 ; + if ((envdir(sa->s, &tmp) < 0) && (errno != ENOENT)) + { + cleanup() ; + strerr_diefu2sys(111, "s6_envdir ", sa->s) ; + } + if (tmp.len > tmpbase + 4103) + { + cleanup() ; + strerr_diefu2sys(100, sa->s, "too big") ; + } + envlen = tmp.len - tmpbase - 3 ; + tmp.len = tmpbase ; + uint16_pack_big(tmp.s + tmpbase + 1, envlen) ; + sa->len = k+1 ; + stralloc_catb(sa, "exec", 5) ; + r = openreadnclose(sa->s, tmp.s + tmpbase + 5 + envlen, 4096) ; + if ((r < 0) && (errno != ENOENT)) + { + cleanup() ; + strerr_diefu2sys(111, "openreadnclose ", sa->s) ; + } + if (r > 0) execlen = r ; + uint16_pack_big(tmp.s + tmpbase + 3 + envlen, execlen) ; + if (cdb_make_add(c, sa->s + start, k - start, tmp.s + tmpbase, 5 + envlen + execlen) < 0) + { + cleanup() ; + strerr_diefu1sys(111, "cdb_make_add") ; + } + } +} + + +int main (int argc, char const *const *argv) +{ + stralloc sa = STRALLOC_ZERO ; + struct cdb_make c = CDB_MAKE_ZERO ; + DIR *dir ; + unsigned int start ; + int fd ; + PROG = "s6-accessrules-cdb-from-fs" ; + if (argc < 3) strerr_dieusage(100, USAGE) ; + if (!stralloc_cats(&tmp, argv[1])) return 0 ; + if (random_sauniquename(&tmp, 8) < 0) + strerr_diefu1sys(111, "random_sauniquename") ; + if (!stralloc_readyplus(&tmp, 8210)) + strerr_diefu1sys(111, "stralloc_catb") ; + stralloc_0(&tmp) ; + fd = open_trunc(tmp.s) ; + if (fd < 0) strerr_diefu2sys(111, "open_trunc ", tmp.s) ; + if (cdb_make_start(&c, fd) < 0) + { + cleanup() ; + strerr_diefu1sys(111, "cdb_make_start") ; + } + dir = opendir(argv[2]) ; + if (!dir) + { + cleanup() ; + strerr_diefu2sys(111, "opendir ", argv[2]) ; + } + if (!stralloc_cats(&sa, argv[2]) || !stralloc_catb(&sa, "/", 1)) dienomem() ; + start = sa.len ; + + for (;;) + { + DIR *subdir ; + direntry *d ; + unsigned int base ; + errno = 0 ; + d = readdir(dir) ; + if (!d) break ; + if (d->d_name[0] == '.') continue ; + sa.len = start ; + if (!stralloc_cats(&sa, d->d_name) || !stralloc_0(&sa)) dienomem() ; + base = sa.len ; + subdir = opendir(sa.s) ; + if (!subdir) + { + cleanup() ; + strerr_diefu2sys(111, "opendir ", sa.s) ; + } + sa.s[base-1] = '/' ; + for (;;) + { + errno = 0 ; + d = readdir(subdir) ; + if (!d) break ; + if (d->d_name[0] == '.') continue ; + sa.len = base ; + if (!stralloc_cats(&sa, d->d_name)) dienomem() ; + doit(&c, &sa, start) ; + } + if (errno) + { + sa.s[base-1] = 0 ; + cleanup() ; + strerr_diefu2sys(111, "readdir ", sa.s) ; + } + dir_close(subdir) ; + } + if (errno) + { + cleanup() ; + strerr_diefu2sys(111, "readdir ", argv[2]) ; + } + dir_close(dir) ; + if (cdb_make_finish(&c) < 0) + { + cleanup() ; + strerr_diefu1sys(111, "cdb_make_finish") ; + } + if (fd_sync(fd) < 0) + { + cleanup() ; + strerr_diefu1sys(111, "fd_sync") ; + } + fd_close(fd) ; + if (rename(tmp.s, argv[1]) < 0) + { + cleanup() ; + strerr_diefu4sys(111, "rename ", tmp.s, " to ", argv[1]) ; + } + return 0 ; +} diff --git a/src/conn-tools/s6-accessrules-fs-from-cdb.c b/src/conn-tools/s6-accessrules-fs-from-cdb.c new file mode 100644 index 0000000..cbe67ef --- /dev/null +++ b/src/conn-tools/s6-accessrules-fs-from-cdb.c @@ -0,0 +1,177 @@ +/* ISC license. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define USAGE "s6-accessrules-fs-from-cdb dir cdbfile" + +static char const *basedir ; +unsigned int basedirlen ; + +static void cleanup () +{ + int e = errno ; + rm_rf(basedir) ; + errno = e ; +} + +static int domkdir (char const *s) +{ + return mkdir(s, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH | S_ISGID) < 0 ? (errno == EEXIST) : 1 ; +} + +static void mkdirp (char *s) +{ + mode_t m = umask(0) ; + unsigned int len = str_len(s) ; + register unsigned int i = basedirlen + 1 ; + for (; i < len ; i++) if (s[i] == '/') + { + s[i] = 0 ; + if (!domkdir(s)) goto err ; + s[i] = '/' ; + } + if (!domkdir(s)) goto err ; + umask(m) ; + return ; + + err: + cleanup() ; + strerr_diefu2sys(111, "mkdir ", s) ; +} + +static void touchtrunc (char const *file) +{ + register int fd = open_trunc(file) ; + if (fd < 0) strerr_diefu2sys(111, "open_trunc ", file) ; + fd_close(fd) ; +} + +static int doenv (char const *dir, unsigned int dirlen, char *env, unsigned int envlen) +{ + mode_t m = umask(0) ; + unsigned int i = 0 ; + if (!domkdir(dir)) + { + cleanup() ; + strerr_diefu2sys(111, "mkdir ", dir) ; + } + umask(m) ; + while (i < envlen) + { + unsigned int n = byte_chr(env + i, envlen - i, 0) ; + if (i + n >= envlen) return 0 ; + { + unsigned int p = byte_chr(env + i, n, '=') ; + char tmp[dirlen + p + 2] ; + byte_copy(tmp, dirlen, dir) ; + tmp[dirlen] = '/' ; + byte_copy(tmp + dirlen + 1, p, env + i) ; + tmp[dirlen + p + 1] = 0 ; + if (p < n) + { + env[i+n] = '\n' ; + if (!openwritenclose_unsafe(tmp, env + i + p + 1, n - p)) + { + cleanup() ; + strerr_diefu2sys(111, "openwritenclose_unsafe ", tmp) ; + } + } + else touchtrunc(tmp) ; + } + i += n + 1 ; + } + return 1 ; +} + +static int doit (struct cdb *c) +{ + unsigned int klen = cdb_keylen(c) ; + unsigned int dlen = cdb_datalen(c) ; + { + uint16 envlen, execlen ; + char name[basedirlen + klen + 8] ; + char data[dlen] ; + byte_copy(name, basedirlen, basedir) ; + name[basedirlen] = '/' ; + if (!dlen || (dlen > 8201)) return (errno = EINVAL, 0) ; + if ((cdb_read(c, name+basedirlen+1, klen, cdb_keypos(c)) < 0) + || (cdb_read(c, data, dlen, cdb_datapos(c)) < 0)) + { + cleanup() ; + strerr_diefu1sys(111, "cdb_read") ; + } + name[basedirlen + klen + 1] = 0 ; + mkdirp(name) ; + name[basedirlen + klen + 1] = '/' ; + if (data[0] == 'A') + { + byte_copy(name + basedirlen + klen + 2, 6, "allow") ; + touchtrunc(name) ; + } + else if (data[0] == 'D') + { + byte_copy(name + basedirlen + klen + 2, 5, "deny") ; + touchtrunc(name) ; + } + if (dlen < 3) return 1 ; + uint16_unpack_big(data + 1, &envlen) ; + if ((envlen > 4096U) || (3U + envlen > dlen)) return (errno = EINVAL, 0) ; + uint16_unpack_big(data + 3 + envlen, &execlen) ; + if ((execlen > 4096U) || (5U + envlen + execlen != dlen)) return (errno = EINVAL, 0) ; + if (envlen) + { + byte_copy(name + basedirlen + klen + 2, 4, "env") ; + if (!doenv(name, basedirlen + klen + 5, data + 3, envlen)) return (errno = EINVAL, 0) ; + } + byte_copy(name + basedirlen + klen + 2, 5, "exec") ; + if (execlen && !openwritenclose_unsafe(name, data + 5 + envlen, execlen)) + { + cleanup() ; + strerr_diefu2sys(111, "openwritenclose_unsafe ", name) ; + } + } + return 1 ; +} + +int main (int argc, char const *const *argv) +{ + struct cdb c = CDB_ZERO ; + uint32 kpos ; + PROG = "s6-accessrules-fs-from-cdb" ; + if (argc < 3) strerr_dieusage(100, USAGE) ; + if (cdb_mapfile(&c, argv[2]) < 0) strerr_diefu1sys(111, "cdb_mapfile") ; + basedir = argv[1] ; + basedirlen = str_len(argv[1]) ; + { + mode_t m = umask(0) ; + if (mkdir(basedir, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH | S_ISGID) < 0) + strerr_diefu2sys(111, "mkdir ", basedir) ; + umask(m) ; + } + cdb_traverse_init(&c, &kpos) ; + for (;;) + { + register int r = cdb_nextkey(&c, &kpos) ; + if (r < 0) + { + cleanup() ; + strerr_diefu1sys(111, "cdb_nextkey") ; + } + else if (!r) break ; + else if (!doit(&c)) + { + cleanup() ; + strerr_diefu1sys(111, "handle key") ; + } + } + return 0 ; +} diff --git a/src/conn-tools/s6-connlimit.c b/src/conn-tools/s6-connlimit.c new file mode 100644 index 0000000..19f4a2d --- /dev/null +++ b/src/conn-tools/s6-connlimit.c @@ -0,0 +1,39 @@ +/* ISC license. */ + +#include +#include +#include +#include +#include + +int main (int argc, char const *const *argv, char const *const *envp) +{ + char const *x ; + unsigned int protolen ; + PROG = "s6-connlimit" ; + x = env_get2(envp, "PROTO") ; + if (!x) strerr_dienotset(100, "PROTO") ; + protolen = str_len(x) ; + if (!protolen) strerr_dief1x(100, "empty PROTO") ; + { + unsigned int num ; + char s[protolen + 8] ; + byte_copy(s, protolen, x) ; + byte_copy(s + protolen, 8, "CONNNUM") ; + x = env_get2(envp, s) ; + if (!x) strerr_dienotset(100, s) ; + if (!uint0_scan(x, &num)) strerr_dief2x(100, "invalid ", s) ; + byte_copy(s + protolen + 4, 4, "MAX") ; + x = env_get2(envp, s) ; + if (x) + { + unsigned int max ; + if (!uint0_scan(x, &max)) strerr_dief2x(100, "invalid ", s) ; + if (num > max) + strerr_dief2x(1, "number of connections from this client limited to ", x) ; + } + } + pathexec0_run(argv+1, envp) ; + (void)argc ; + strerr_dieexec(111, argv[1]) ; +} diff --git a/src/conn-tools/s6-getservbyname.c b/src/conn-tools/s6-getservbyname.c new file mode 100644 index 0000000..0888df5 --- /dev/null +++ b/src/conn-tools/s6-getservbyname.c @@ -0,0 +1,28 @@ +/* ISC license. */ + +#include +#include +#include +#include + +#define USAGE "s6-getservbyname service proto" + +int main (int argc, char const *const *argv) +{ + char fmt[UINT16_FMT] ; + uint16 port ; + PROG = "s6-getservbyname" ; + if (argc < 3) strerr_dieusage(100, USAGE) ; + if (!uint160_scan(argv[1], &port)) + { + struct servent *se = getservbyname(argv[1], argv[2]) ; + uint16 tmpport ; + if (!se) return 1 ; + tmpport = (uint16)se->s_port ; + uint16_unpack_big((char const *)&tmpport, &port) ; + } + if ((buffer_put(buffer_1small, fmt, uint16_fmt(fmt, port)) < 0) + || (buffer_putflush(buffer_1small, "\n", 1) < 1)) + strerr_diefu1sys(111, "write to stdout") ; + return 0 ; +} diff --git a/src/conn-tools/s6-ident-client.c b/src/conn-tools/s6-ident-client.c new file mode 100644 index 0000000..e475870 --- /dev/null +++ b/src/conn-tools/s6-ident-client.c @@ -0,0 +1,71 @@ +/* ISC license. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define USAGE "s6-ident-client [ -t timeout ] ra rp la lp" +#define dieusage() strerr_dieusage(100, USAGE) + +int main (int argc, char const *const *argv) +{ + tain_t deadline ; + ip46_t ra, la ; + uint16 rp, lp ; + PROG = "s6-ident-client" ; + { + unsigned int t = 0 ; + subgetopt_t l = SUBGETOPT_ZERO ; + for (;;) + { + register int opt = subgetopt_r(argc, argv, "t:", &l) ; + if (opt == -1) break ; + switch (opt) + { + case 't' : if (!uint0_scan(l.arg, &t)) dieusage() ; break ; + default : dieusage() ; + } + } + argc -= l.ind ; argv += l.ind ; + if (t) tain_from_millisecs(&deadline, t) ; else deadline = tain_infinite_relative ; + } + if (argc < 4) dieusage() ; + + if (!ip46_scan(argv[0], &ra)) + strerr_dief2x(100, "invalid IP address: ", argv[0]) ; + if (!uint160_scan(argv[1], &rp)) + strerr_dief2x(100, "invalid port number: ", argv[1]) ; + if (!ip46_scan(argv[2], &la)) + strerr_dief2x(100, "invalid IP address: ", argv[2]) ; + if (!uint160_scan(argv[3], &lp)) + strerr_dief2x(100, "invalid port number: ", argv[3]) ; + if (ip46_is6(&ra) != ip46_is6(&la)) + strerr_dief1x(100, "address family mismatch") ; + + tain_now_g() ; + tain_add_g(&deadline, &deadline) ; + + { + char buf[BUFFER_OUTSIZE_SMALL] ; + register int r = s6net_ident_client_g(buf, BUFFER_OUTSIZE_SMALL, &ra, rp, &la, lp, &deadline) ; + if (r < 0) strerr_diefu1sys(errno == ETIMEDOUT ? 99 : 111, "s6net_ident_client") ; + else if (!r) + { + strerr_warnw2x("ident server replied: ", s6net_ident_error_str(errno)) ; + return 1 ; + } + buffer_putnoflush(buffer_1small, buf, r-1) ; + } + buffer_putnoflush(buffer_1small, "\n", 1) ; + if (!buffer_timed_flush_g(buffer_1small, &deadline)) + strerr_diefu1sys(111, "write to stdout") ; + return 0 ; +} diff --git a/src/conn-tools/s6-ioconnect.c b/src/conn-tools/s6-ioconnect.c new file mode 100644 index 0000000..e687d70 --- /dev/null +++ b/src/conn-tools/s6-ioconnect.c @@ -0,0 +1,189 @@ +/* ISC license. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define USAGE "s6-ioconnect [ -t timeout ] [ -r fdr ] [ -w fdw ] [ -0 ] [ -1 ] [ -6 ] [ -7 ]" +#define dieusage() strerr_dieusage(100, USAGE) + + +typedef struct ioblah_s ioblah_t, *ioblah_t_ref ; +struct ioblah_s +{ + unsigned int fd ; + unsigned int xindex ; + unsigned int flagsocket : 1 ; + unsigned int flagopen : 1 ; +} ; + +static ioblah_t a[2][2] = { { { 0, 4, 0, 1 }, { 7, 4, 0, 1 } }, { { 6, 4, 0, 1 }, { 1, 4, 0, 1 } } } ; +static iobuffer b[2] ; +static iopause_fd x[5] = { { -1, IOPAUSE_READ, 0 } } ; + +static void closeit (unsigned int i, unsigned int j) +{ + if (a[i][j].flagsocket) + { + if ((shutdown(a[i][j].fd, j) < 0) && (errno != ENOTSOCK) && (errno != ENOTCONN)) + strerr_warnwu4sys("shutdown ", i ? "incoming" : "outgoing", " socket for ", j ? "writing" : "reading") ; + } + fd_close(a[i][j].fd) ; + a[i][j].flagopen = 0 ; + a[i][j].xindex = 5 ; +} + +static inline void finishit (unsigned int i) +{ + closeit(i, 1) ; + iobuffer_finish(&b[i]) ; +} + +static void handle_signals (void) +{ + for (;;) + { + char c = selfpipe_read() ; + switch (c) + { + case -1 : strerr_diefu1sys(111, "selfpipe_read") ; + case 0 : return ; + case SIGTERM : + { + if (a[0][0].xindex < 5) x[a[0][0].xindex].revents |= IOPAUSE_EXCEPT ; + if (a[1][0].xindex < 5) x[a[1][0].xindex].revents |= IOPAUSE_EXCEPT ; + break ; + } + default : + strerr_dief1x(101, "internal error: inconsistent signal state. Please submit a bug-report.") ; + } + } +} + +int main (int argc, char const *const *argv) +{ + tain_t tto ; + register unsigned int i, j ; + PROG = "s6-ioconnect" ; + { + subgetopt_t l = SUBGETOPT_ZERO ; + unsigned int t = 0 ; + for (;;) + { + register int opt = subgetopt_r(argc, argv, "0167t:r:w:", &l) ; + if (opt < 0) break ; + switch (opt) + { + case '0' : a[0][0].flagsocket = 1 ; break ; + case '1' : a[1][1].flagsocket = 1 ; break ; + case '6' : a[1][0].flagsocket = 1 ; break ; + case '7' : a[0][1].flagsocket = 1 ; break ; + case 't' : if (!uint0_scan(l.arg, &t)) dieusage() ; break ; + case 'r' : if (!uint0_scan(l.arg, &a[1][0].fd)) dieusage() ; break ; + case 'w' : if (!uint0_scan(l.arg, &a[0][1].fd)) dieusage() ; break ; + default : dieusage() ; + } + } + if (t) tain_from_millisecs(&tto, t) ; else tto = tain_infinite_relative ; + argc -= l.ind ; argv += l.ind ; + } + if ((a[0][1].fd < 3) || (a[1][0].fd < 3)) dieusage() ; + for (i = 0 ; i < 2 ; i++) + { + for (j = 0 ; j < 2 ; j++) + if (ndelay_on(a[i][j].fd) == -1) strerr_diefu1sys(111, "ndelay_on") ; + if (!iobuffer_init(&b[i], a[i][0].fd, a[i][1].fd) < 0) strerr_diefu1sys(111, "iobuffer_init") ; + } + if (sig_ignore(SIGPIPE) == -1) strerr_diefu1sys(111, "sig_ignore") ; + + tain_now_g() ; + x[0].fd = selfpipe_init() ; + if (x[0].fd < 0) strerr_diefu1sys(111, "selfpipe_init") ; + if (selfpipe_trap(SIGTERM) < 0) + strerr_diefu1sys(111, "trap SIGTERM") ; + + for (;;) + { + tain_t deadline ; + unsigned int xlen = 1 ; + int r ; + + tain_add_g(&deadline, iobuffer_isempty(&b[0]) && iobuffer_isempty(&b[1]) ? &tto : &tain_infinite_relative) ; + for (i = 0 ; i < 2 ; i++) + { + a[i][0].xindex = 5 ; + if (a[i][0].flagopen && iobuffer_isreadable(&b[i])) + { + x[xlen].fd = a[i][0].fd ; + x[xlen].events = IOPAUSE_READ ; + a[i][0].xindex = xlen++ ; + } + a[i][1].xindex = 5 ; + if (a[i][1].flagopen) + { + x[xlen].fd = a[i][1].fd ; + x[xlen].events = IOPAUSE_EXCEPT | (iobuffer_isempty(&b[i]) ? 0 : IOPAUSE_WRITE) ; + a[i][1].xindex = xlen++ ; + } + } + if (xlen <= 1) break ; + + r = iopause_g(x, xlen, &deadline) ; + if (r < 0) strerr_diefu1sys(111, "iopause") ; + else if (!r) return 1 ; + + if (x[0].revents & IOPAUSE_READ) handle_signals() ; + + for (i = 0 ; i < 2 ; i++) if (a[i][1].xindex < 4) + { + if (x[a[i][1].xindex].revents & IOPAUSE_WRITE) + { + if (!iobuffer_flush(&b[i])) + { + if (!error_isagain(errno)) x[a[i][1].xindex].revents |= IOPAUSE_EXCEPT ; + } + else if (!a[i][0].flagopen) finishit(i) ; + } + if (x[a[i][1].xindex].revents & IOPAUSE_EXCEPT) + { + if (!iobuffer_isempty(&b[i])) + { + iobuffer_flush(&b[i]) ; /* sets errno */ + strerr_warnwu3sys("write ", i ? "incoming" : "outgoing", " data") ; + } + closeit(i, 0) ; finishit(i) ; + } + } + + for (i = 0 ; i < 2 ; i++) if (a[i][0].xindex < 4) + { + if (x[a[i][0].xindex].revents & IOPAUSE_READ) + { + if (sanitize_read(iobuffer_fill(&b[i])) < 0) + { + if (errno != EPIPE) strerr_warnwu3sys("read ", i ? "incoming" : "outgoing", " data") ; + x[a[i][0].xindex].revents |= IOPAUSE_EXCEPT ; + } + } + if (x[a[i][0].xindex].revents & IOPAUSE_EXCEPT) + { + closeit(i, 0) ; + if (iobuffer_isempty(&b[i])) finishit(i) ; + } + } + } + return 0 ; +} diff --git a/src/conn-tools/s6-ipcclient.c b/src/conn-tools/s6-ipcclient.c new file mode 100644 index 0000000..3e3162c --- /dev/null +++ b/src/conn-tools/s6-ipcclient.c @@ -0,0 +1,66 @@ +/* ISC license. */ + +#include +#include +#include +#include +#include + +#define USAGE "s6-ipcclient [ -q | -Q | -v ] [ -p bindpath ] [ -l localname ] path prog..." + +int main (int argc, char const *const *argv, char const *const *envp) +{ + char const *bindpath = 0 ; + char const *localname = 0 ; + unsigned int verbosity = 1 ; + PROG = "s6-ipcclient" ; + { + subgetopt_t l = SUBGETOPT_ZERO ; + for (;;) + { + register int opt = subgetopt_r(argc, argv, "qQvp:l:", &l) ; + if (opt == -1) break ; + switch (opt) + { + case 'q' : if (verbosity) verbosity-- ; break ; + case 'Q' : verbosity = 1 ; break ; + case 'v' : verbosity++ ; break ; + case 'p' : bindpath = l.arg ; break ; + case 'l' : localname = l.arg ; break ; + default : strerr_dieusage(100, USAGE) ; + } + } + argc -= l.ind ; argv += l.ind ; + } + if (argc < 2) strerr_dieusage(100, USAGE) ; + { + char modif[24 + IPCPATH_MAX] = "PROTO=IPC\0IPCLOCALPATH=" ; + unsigned int i = 23 ; + int s = ipc_stream() ; + if (s == -1) strerr_diefu1sys(111, "create socket") ; + if (bindpath && (ipc_bind(s, bindpath) == -1)) + strerr_diefu2sys(111, "bind socket to ", bindpath) ; + if (ipc_connect(s, argv[0]) < 0) + strerr_diefu2sys(111, "connect to ", argv[0]) ; + if (verbosity >= 2) strerr_warn3x(PROG, ": connected to ", argv[0]) ; + if (localname) + { + register unsigned int n = str_len(localname) ; + if (n > IPCPATH_MAX) n = IPCPATH_MAX ; + byte_copy(modif + i, n, localname) ; + i += n ; modif[i++] = 0 ; + } + else + { + int dummy ; + if (ipc_local(s, modif + i, IPCPATH_MAX, &dummy) < 0) modif[--i] = 0 ; + else i += str_len(modif + i) + 1 ; + } + if (fd_move(6, s) < 0) + strerr_diefu2sys(111, "set up fd ", "6") ; + if (fd_copy(7, 6) < 0) + strerr_diefu2sys(111, "set up fd ", "7") ; + pathexec_r(argv+1, envp, env_len(envp), modif, i) ; + } + strerr_dieexec(111, argv[1]) ; +} diff --git a/src/conn-tools/s6-ipcserver-access.c b/src/conn-tools/s6-ipcserver-access.c new file mode 100644 index 0000000..183f56c --- /dev/null +++ b/src/conn-tools/s6-ipcserver-access.c @@ -0,0 +1,211 @@ +/* ISC license. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define USAGE "s6-ipcserver-access [ -v verbosity ] [ -e | -E ] [ -l localname ] [ -i rulesdir | -x rulesfile ] prog..." + +static unsigned int verbosity = 1 ; + + /* Utility functions */ + +static inline void dieusage (void) gccattr_noreturn ; +static inline void dieusage () +{ + strerr_dieusage(100, USAGE) ; +} + +static inline void dienomem (void) gccattr_noreturn ; +static inline void dienomem () +{ + strerr_diefu1sys(111, "update environment") ; +} + +static inline void X (void) gccattr_noreturn ; +static inline void X () +{ + strerr_dief1x(101, "internal inconsistency. Please submit a bug-report.") ; +} + + + /* Logging */ + +static void logit (unsigned int pid, unsigned int uid, unsigned int gid, int h) +{ + char fmtpid[UINT_FMT] ; + char fmtuid[UINT_FMT] ; + char fmtgid[UINT_FMT] ; + fmtpid[uint_fmt(fmtpid, pid)] = 0 ; + fmtuid[uint_fmt(fmtuid, uid)] = 0 ; + fmtgid[uint_fmt(fmtgid, gid)] = 0 ; + if (h) strerr_warni7x("allow", " pid ", fmtpid, " uid ", fmtuid, " gid ", fmtgid) ; + else strerr_warni7sys("deny", " pid ", fmtpid, " uid ", fmtuid, " gid ", fmtgid) ; +} + +static inline void log_accept (unsigned int pid, unsigned int uid, unsigned int gid) +{ + logit(pid, uid, gid, 1) ; +} + +static inline void log_deny (unsigned int pid, unsigned int uid, unsigned int gid) +{ + logit(pid, uid, gid, 0) ; +} + + + /* Checking */ + +static s6net_accessrules_result_t check_cdb (unsigned int uid, unsigned int gid, char const *file, s6net_accessrules_params_t *params) +{ + struct cdb c = CDB_ZERO ; + int fd = open_readb(file) ; + register s6net_accessrules_result_t r ; + if (fd < 0) return -1 ; + if (cdb_init(&c, fd) < 0) strerr_diefu2sys(111, "cdb_init ", file) ; + r = s6net_accessrules_uidgid_cdb(uid, gid, &c, params) ; + cdb_free(&c) ; + fd_close(fd) ; + return r ; +} + +static inline int check (s6net_accessrules_params_t *params, char const *rules, unsigned int rulestype, unsigned int uid, unsigned int gid) +{ + char const *x = "" ; + s6net_accessrules_result_t r ; + switch (rulestype) + { + case 0 : + if (verbosity >= 2) strerr_warnw1x("invoked without a ruleset!") ; + return 1 ; + case 1 : + r = s6net_accessrules_uidgid_fs(uid, gid, rules, params) ; + x = "fs" ; + break ; + case 2 : + r = check_cdb(uid, gid, rules, params) ; + x = "cdb" ; + break ; + default : X() ; + } + switch (r) + { + case S6NET_ACCESSRULES_ERROR : strerr_diefu4sys(111, "check ", x, " ruleset in ", rules) ; + case S6NET_ACCESSRULES_ALLOW : return 1 ; + case S6NET_ACCESSRULES_DENY : return (errno = EACCES, 0) ; + case S6NET_ACCESSRULES_NOTFOUND : return (errno = ENOENT, 0) ; + default : X() ; + } +} + + +int main (int argc, char const *const *argv, char const *const *envp) +{ + s6net_accessrules_params_t params = S6NET_ACCESSRULES_PARAMS_ZERO ; + char const *rules = 0 ; + char const *localname = 0 ; + char const *proto ; + unsigned int protolen ; + unsigned int uid = 0, gid = 0 ; + unsigned int rulestype = 0 ; + int doenv = 1 ; + PROG = "s6-ipcserver-access" ; + { + subgetopt_t l = SUBGETOPT_ZERO ; + for (;;) + { + register int opt = subgetopt_r(argc, argv, "v:Eel:i:x:", &l) ; + if (opt == -1) break ; + switch (opt) + { + case 'v' : if (!uint0_scan(l.arg, &verbosity)) dieusage() ; break ; + case 'E' : doenv = 0 ; break ; + case 'e' : doenv = 1 ; break ; + case 'l' : localname = l.arg ; break ; + case 'i' : rules = l.arg ; rulestype = 1 ; break ; + case 'x' : rules = l.arg ; rulestype = 2 ; break ; + default : dieusage() ; + } + } + argc -= l.ind ; argv += l.ind ; + } + if (!argc) dieusage() ; + if (!*argv[0]) dieusage() ; + + proto = env_get2(envp, "PROTO") ; + if (!proto) strerr_dienotset(100, "PROTO") ; + protolen = str_len(proto) ; + + { + char const *x ; + char tmp[protolen + 11] ; + byte_copy(tmp, protolen, proto) ; + byte_copy(tmp + protolen, 11, "REMOTEEUID") ; + x = env_get2(envp, tmp) ; + if (!x) strerr_dienotset(100, tmp) ; + if (!uint0_scan(x, &uid)) strerr_dieinvalid(100, tmp) ; + tmp[protolen + 7] = 'G' ; + x = env_get2(envp, tmp) ; + if (!x) strerr_dienotset(100, tmp) ; + if (!uint0_scan(x, &gid)) strerr_dieinvalid(100, tmp) ; + } + + if (!check(¶ms, rules, rulestype, uid, gid)) + { + if (verbosity >= 2) log_deny(getpid(), uid, gid) ; + return 1 ; + } + if (verbosity) log_accept(getpid(), uid, gid) ; + + if (doenv) + { + char tmp[protolen + 10] ; + byte_copy(tmp, protolen, proto) ; + byte_copy(tmp + protolen, 10, "LOCALPATH") ; + if (localname) + { + if (!env_addmodif(¶ms.env, tmp, localname)) dienomem() ; + } + else + { + char curname[IPCPATH_MAX+1] ; + int dummy ; + if (ipc_local(0, curname, IPCPATH_MAX+1, &dummy) < 0) + strerr_diefu1sys(111, "ipc_local") ; + if (!env_addmodif(¶ms.env, tmp, curname)) dienomem() ; + } + } + else + { + char tmp[protolen + 11] ; + byte_copy(tmp, protolen, proto) ; + byte_copy(tmp + protolen, 11, "REMOTEEUID") ; + if (!env_addmodif(¶ms.env, "PROTO", 0)) dienomem() ; + if (!env_addmodif(¶ms.env, tmp, 0)) dienomem() ; + tmp[protolen + 7] = 'G' ; + if (!env_addmodif(¶ms.env, tmp, 0)) dienomem() ; + byte_copy(tmp + protolen + 6, 5, "PATH") ; + if (!env_addmodif(¶ms.env, tmp, 0)) dienomem() ; + byte_copy(tmp + protolen, 10, "LOCALPATH") ; + if (!env_addmodif(¶ms.env, tmp, 0)) dienomem() ; + } + + if (params.exec.len) + { + char *specialargv[4] = { EXECLINE_EXTBINPREFIX "execlineb", "-c", params.exec.s, 0 } ; + pathexec_r((char const *const *)specialargv, envp, env_len(envp), params.env.s, params.env.len) ; + strerr_dieexec(111, specialargv[0]) ; + } + + pathexec_r(argv, envp, env_len(envp), params.env.s, params.env.len) ; + strerr_dieexec(111, argv[0]) ; +} diff --git a/src/conn-tools/s6-ipcserver.c b/src/conn-tools/s6-ipcserver.c new file mode 100644 index 0000000..edee033 --- /dev/null +++ b/src/conn-tools/s6-ipcserver.c @@ -0,0 +1,441 @@ +/* ISC license. */ + +#ifndef _BSD_SOURCE +#define _BSD_SOURCE +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define USAGE "s6-ipcserver [ -q | -Q | -v ] [ -d | -D ] [ -P | -p ] [ -1 ] [ -c maxconn ] [ -C localmaxconn ] [ -b backlog ] [ -G gid,gid,... ] [ -g gid ] [ -u uid ] [ -U ] path prog..." + +#define ABSOLUTE_MAXCONN 1000 + +static unsigned int maxconn = 40 ; +static unsigned int localmaxconn = 40 ; +static char fmtmaxconn[UINT_FMT+1] = "/" ; +static char fmtlocalmaxconn[UINT_FMT+1] = "/" ; +static int flaglookup = 1 ; +static unsigned int verbosity = 1 ; +static int cont = 1 ; + +static diuint *piduid ; +static unsigned int numconn = 0 ; +static diuint *uidnum ; +static unsigned int uidlen = 0 ; + + + /* Utility functions */ + +static inline void dieusage () +{ + strerr_dieusage(100, USAGE) ; +} + +static inline void X (void) +{ + strerr_dief1x(101, "internal inconsistency. Please submit a bug-report.") ; +} + + + /* Lookup primitives */ + +static unsigned int lookup_diuint (diuint const *tab, unsigned int tablen, unsigned int key) +{ + register unsigned int i = 0 ; + for (; i < tablen ; i++) if (key == tab[i].left) break ; + return i ; +} + +static inline unsigned int lookup_pid (unsigned int pid) +{ + return lookup_diuint(piduid, numconn, pid) ; +} + +static inline unsigned int lookup_uid (unsigned int uid) +{ + return lookup_diuint(uidnum, uidlen, uid) ; +} + + + /* Logging */ + +static inline void log_start (char const *path) +{ + strerr_warni2x("starting - listening on ", path) ; +} + +static inline void log_exit (void) +{ + strerr_warni1x("exiting") ; +} + +static void log_status (void) +{ + char fmt[UINT_FMT] ; + fmt[uint_fmt(fmt, numconn)] = 0 ; + strerr_warni3x("status: ", fmt, fmtmaxconn) ; +} + +static void log_deny (unsigned int uid, unsigned int gid, unsigned int num) +{ + char fmtuid[UINT_FMT] = "?" ; + char fmtgid[UINT_FMT] = "?" ; + char fmtnum[UINT_FMT] = "?" ; + if (flaglookup) + { + fmtuid[uint_fmt(fmtuid, uid)] = 0 ; + fmtgid[uint_fmt(fmtgid, gid)] = 0 ; + fmtnum[uint_fmt(fmtnum, num)] = 0 ; + } + strerr_warni7sys("deny ", fmtuid, ":", fmtgid, " count ", fmtnum, fmtlocalmaxconn) ; +} + +static void log_accept (unsigned int pid, unsigned int uid, unsigned int gid, unsigned int num) +{ + char fmtuidgid[UINT_FMT * 2 + 1] = "?:?" ; + char fmtpid[UINT_FMT] ; + char fmtnum[UINT_FMT] = "?" ; + if (flaglookup) + { + register unsigned int n = uint_fmt(fmtuidgid, uid) ; + fmtuidgid[n++] = ':' ; + n += uint_fmt(fmtuidgid + n, gid) ; + fmtuidgid[n] = 0 ; + fmtnum[uint_fmt(fmtnum, num)] = 0 ; + } + fmtpid[uint_fmt(fmtpid, pid)] = 0 ; + strerr_warni7x("allow ", fmtuidgid, " pid ", fmtpid, " count ", fmtnum, fmtlocalmaxconn) ; +} + +static void log_close (unsigned int pid, unsigned int uid, int w) +{ + char fmtpid[UINT_FMT] ; + char fmtuid[UINT_FMT] = "?" ; + char fmtw[UINT_FMT] ; + fmtpid[uint_fmt(fmtpid, pid)] = 0 ; + if (flaglookup) fmtuid[uint_fmt(fmtuid, uid)] = 0 ; + fmtw[uint_fmt(fmtw, WIFSIGNALED(w) ? WTERMSIG(w) : WEXITSTATUS(w))] = 0 ; + strerr_warni6x("end pid ", fmtpid, " uid ", fmtuid, WIFSIGNALED(w) ? " signal " : " exitcode ", fmtw) ; +} + + + /* Signal handling */ + +static void killthem (int sig) +{ + register unsigned int i = 0 ; + for (; i < numconn ; i++) kill(piduid[i].left, sig) ; +} + +static void wait_children (void) +{ + for (;;) + { + unsigned int i ; + int w ; + register int pid = wait_nohang(&w) ; + if (pid < 0) + if (errno != ECHILD) strerr_diefu1sys(111, "wait_nohang") ; + else break ; + else if (!pid) break ; + i = lookup_pid(pid) ; + if (i < numconn) + { + unsigned int uid = piduid[i].right ; + register unsigned int j = lookup_uid(uid) ; + if (j >= uidlen) X() ; + if (!--uidnum[j].right) uidnum[j] = uidnum[--uidlen] ; + piduid[i] = piduid[--numconn] ; + if (verbosity >= 2) + { + log_close(pid, uid, w) ; + log_status() ; + } + } + } +} + +static void handle_signals (void) +{ + for (;;) switch (selfpipe_read()) + { + case -1 : strerr_diefu1sys(111, "read selfpipe") ; + case 0 : return ; + case SIGCHLD : wait_children() ; break ; + case SIGTERM : + { + if (verbosity >= 2) + strerr_warni3x("received ", "SIGTERM,", " quitting") ; + cont = 0 ; + break ; + } + case SIGHUP : + { + if (verbosity >= 2) + strerr_warni5x("received ", "SIGHUP,", " sending ", "SIGTERM+SIGCONT", " to all connections") ; + killthem(SIGTERM) ; + killthem(SIGCONT) ; + break ; + } + case SIGQUIT : + { + if (verbosity >= 2) + strerr_warni6x("received ", "SIGQUIT,", " sending ", "SIGTERM+SIGCONT", " to all connections", " and quitting") ; + cont = 0 ; + killthem(SIGTERM) ; + killthem(SIGCONT) ; + break ; + } + case SIGABRT : + { + if (verbosity >= 2) + strerr_warni6x("received ", "SIGABRT,", " sending ", "SIGKILL", " to all connections", " and quitting") ; + cont = 0 ; + killthem(SIGKILL) ; + break ; + } + default : X() ; + } +} + + + /* New connection handling */ + +static void run_child (int, unsigned int, unsigned int, unsigned int, char const *, char const *const *, char const *const *) gccattr_noreturn ; +static void run_child (int s, unsigned int uid, unsigned int gid, unsigned int num, char const *remotepath, char const *const *argv, char const *const *envp) +{ + unsigned int rplen = str_len(remotepath) + 1 ; + unsigned int n = 0 ; + char fmt[65 + UINT_FMT * 3 + rplen] ; + PROG = "s6-ipcserver (child)" ; + if ((fd_move(0, s) < 0) || (fd_copy(1, 0) < 0)) + strerr_diefu1sys(111, "move fds") ; + byte_copy(fmt+n, 23, "PROTO=IPC\0IPCREMOTEEUID") ; n += 23 ; + if (flaglookup) + { + fmt[n++] = '=' ; + n += uint_fmt(fmt+n, uid) ; + } + fmt[n++] = 0 ; + byte_copy(fmt+n, 13, "IPCREMOTEEGID") ; n += 13 ; + if (flaglookup) + { + fmt[n++] = '=' ; + n += uint_fmt(fmt+n, gid) ; + } + fmt[n++] = 0 ; + byte_copy(fmt+n, 11, "IPCCONNNUM=") ; n += 11 ; + if (flaglookup) n += uint_fmt(fmt+n, num) ; + fmt[n++] = 0 ; + byte_copy(fmt+n, 14, "IPCREMOTEPATH=") ; n += 14 ; + byte_copy(fmt+n, rplen, remotepath) ; n += rplen ; + pathexec_r(argv, envp, env_len(envp), fmt, n) ; + strerr_dieexec(111, argv[0]) ; +} + +static void new_connection (int s, char const *remotepath, char const *const *argv, char const *const *envp) +{ + unsigned int uid = 0, gid = 0 ; + unsigned int num, i ; + register int pid ; + if (flaglookup && (ipc_eid(s, &uid, &gid) < 0)) + { + if (verbosity) strerr_warnwu1sys("ipc_eid") ; + return ; + } + i = lookup_uid(uid) ; + num = (i < uidlen) ? uidnum[i].right : 0 ; + if (num >= localmaxconn) + { + log_deny(uid, gid, num) ; + return ; + } + pid = fork() ; + if (pid < 0) + { + if (verbosity) strerr_warnwu1sys("fork") ; + return ; + } + else if (!pid) + { + selfpipe_finish() ; + run_child(s, uid, gid, num+1, remotepath, argv, envp) ; + } + + if (i < uidlen) uidnum[i].right = num + 1 ; + else + { + uidnum[uidlen].left = uid ; + uidnum[uidlen++].right = 1 ; + } + piduid[numconn].left = (unsigned int)pid ; + piduid[numconn++].right = uid ; + if (verbosity >= 2) + { + log_accept((unsigned int)pid, uid, gid, uidnum[i].right) ; + log_status() ; + } +} + + + /* And the main */ + +int main (int argc, char const *const *argv, char const *const *envp) +{ + iopause_fd x[2] = { { -1, IOPAUSE_READ, 0 }, { -1, IOPAUSE_READ | IOPAUSE_EXCEPT, 0 } } ; + PROG = "s6-ipcserver" ; + { + subgetopt_t l = SUBGETOPT_ZERO ; + unsigned int uid = 0, gid = 0 ; + gid_t gids[NGROUPS_MAX] ; + unsigned int gidn = 0 ; + unsigned int backlog = 20 ; + int flagreuse = 1, flag1 = 0 ; + for (;;) + { + register int opt = subgetopt_r(argc, argv, "qQvDd1UPpc:C:b:u:g:G:", &l) ; + if (opt == -1) break ; + switch (opt) + { + case 'q' : verbosity = 0 ; break ; + case 'Q' : verbosity = 1 ; break ; + case 'v' : verbosity = 2 ; break ; + case 'D' : flagreuse = 0 ; break ; + case 'd' : flagreuse = 1 ; break ; + case 'P' : flaglookup = 0 ; break ; + case 'p' : flaglookup = 1 ; break ; + case 'c' : if (!uint0_scan(l.arg, &maxconn)) dieusage() ; break ; + case 'C' : if (!uint0_scan(l.arg, &localmaxconn)) dieusage() ; break ; + case 'b' : if (!uint0_scan(l.arg, &backlog)) dieusage() ; break ; + case 'u' : if (!uint0_scan(l.arg, &uid)) dieusage() ; break ; + case 'g' : if (!uint0_scan(l.arg, &gid)) dieusage() ; break ; + case 'G' : if (!gid_scanlist(gids, NGROUPS_MAX, l.arg, &gidn)) dieusage() ; break ; + case '1' : flag1 = 1 ; break ; + case 'U' : + { + char const *x = env_get2(envp, "UID") ; + if (!x) strerr_dienotset(100, "UID") ; + if (!uint0_scan(x, &uid)) strerr_dieinvalid(100, "UID") ; + x = env_get2(envp, "GID") ; + if (!x) strerr_dienotset(100, "GID") ; + if (!uint0_scan(x, &gid)) strerr_dieinvalid(100, "GID") ; + x = env_get2(envp, "GIDLIST") ; + if (!x) strerr_dienotset(100, "GIDLIST") ; + if (!gid_scanlist(gids, NGROUPS_MAX, x, &gidn) && *x) + strerr_dieinvalid(100, "GIDLIST") ; + break ; + } + default : dieusage() ; + } + } + argc -= l.ind ; argv += l.ind ; + if (argc < 2) dieusage() ; + if (!*argv[0]) dieusage() ; + fd_close(0) ; + if (!flag1) fd_close(1) ; + if (!maxconn) maxconn = 1 ; + if (maxconn > ABSOLUTE_MAXCONN) maxconn = ABSOLUTE_MAXCONN ; + if (!flaglookup || (localmaxconn > maxconn)) localmaxconn = maxconn ; + x[1].fd = ipc_stream() ; + if ((x[1].fd < 0) || (coe(x[1].fd) < 0)) + strerr_diefu1sys(111, "create socket") ; + { + mode_t m = umask(0) ; + if ((flagreuse ? ipc_bind_reuse(x[1].fd, argv[0]) : ipc_bind(x[1].fd, argv[0])) < 0) + strerr_diefu2sys(111, "bind to ", argv[0]) ; + umask(m) ; + } + if (ipc_listen(x[1].fd, backlog) < 0) strerr_diefu1sys(111, "listen") ; + if (gidn && (setgroups(gidn, gids) < 0)) strerr_diefu1sys(111, "setgroups") ; + if (gid && (setgid(gid) < 0)) strerr_diefu1sys(111, "drop gid") ; + if (uid && (setuid(uid) < 0)) strerr_diefu1sys(111, "drop uid") ; + + x[0].fd = selfpipe_init() ; + if (x[0].fd == -1) strerr_diefu1sys(111, "create selfpipe") ; + if (sig_ignore(SIGPIPE) < 0) strerr_diefu1sys(111, "ignore SIGPIPE") ; + { + sigset_t set ; + sigemptyset(&set) ; + sigaddset(&set, SIGCHLD) ; + sigaddset(&set, SIGTERM) ; + sigaddset(&set, SIGHUP) ; + sigaddset(&set, SIGQUIT) ; + sigaddset(&set, SIGABRT) ; + if (selfpipe_trapset(&set) < 0) strerr_diefu1sys(111, "trap signals") ; + } + + if (flag1) + { + unsigned int n = str_len(argv[0]) ; + char s[n+1] ; + byte_copy(s, n, argv[0]) ; + s[n++] = '\n' ; + if (allwrite(1, s, n) < n) strerr_diefu1sys(111, "write to stdout") ; + fd_close(1) ; + } + fmtlocalmaxconn[1+uint_fmt(fmtlocalmaxconn+1, localmaxconn)] = 0 ; + if (verbosity >= 2) + { + fmtmaxconn[1+uint_fmt(fmtmaxconn+1, maxconn)] = 0 ; + log_start(argv[0]) ; + log_status() ; + } + } + + { + diuint inyostack[maxconn + (flaglookup ? maxconn : 1)] ; + piduid = inyostack ; uidnum = inyostack + maxconn ; + + while (cont) + { + if (iopause(x, 1 + (numconn < maxconn), 0, 0) == -1) + strerr_diefu1sys(111, "iopause") ; + + if (x[0].revents & IOPAUSE_EXCEPT) strerr_dief1x(111, "trouble with selfpipe") ; + if (x[0].revents & IOPAUSE_READ) handle_signals() ; + if (numconn < maxconn) + { + if (x[1].revents & IOPAUSE_EXCEPT) strerr_dief1x(111, "trouble with socket") ; + if (x[1].revents & IOPAUSE_READ) + { + int dummy ; + char remotepath[IPCPATH_MAX+1] ; + register int s = ipc_accept(x[1].fd, remotepath, IPCPATH_MAX+1, &dummy) ; + if (s < 0) + { + if (verbosity) strerr_warnwu1sys("accept") ; + } + else + { + new_connection(s, remotepath, argv+1, envp) ; + fd_close(s) ; + } + } + } + } + } + if (verbosity >= 2) log_exit() ; + return 0 ; +} diff --git a/src/conn-tools/s6-sudo.c b/src/conn-tools/s6-sudo.c new file mode 100644 index 0000000..8541b60 --- /dev/null +++ b/src/conn-tools/s6-sudo.c @@ -0,0 +1,67 @@ +/* ISC license. */ + +#include +#include +#include +#include +#include + +#define USAGE "s6-sudo [ -q | -Q | -v ] [ -p bindpath ] [ -l localname ] [ -e ] [ -t timeout ] [ -T timeoutrun ] path [ args... ]" +#define dieusage() strerr_dieusage(100, USAGE) + +int main (int argc, char const *const *argv, char const *const *envp) +{ + unsigned int verbosity = 1, t = 0, T = 0 ; + char const *bindpath = 0 ; + char const *localname = 0 ; + int nodoenv = 0 ; + PROG = "s6-sudo" ; + { + subgetopt_t l = SUBGETOPT_ZERO ; + for (;;) + { + register int opt = subgetopt_r(argc, argv, "qQvp:l:et:T:", &l) ; + if (opt == -1) break ; + switch (opt) + { + case 'q' : if (verbosity) verbosity-- ; break ; + case 'Q' : verbosity = 1 ; break ; + case 'v' : verbosity++ ; break ; + case 'p' : bindpath = l.arg ; break ; + case 'l' : localname = l.arg ; break ; + case 'e' : nodoenv = 1 ; break ; + case 't' : if (!uint0_scan(l.arg, &t)) dieusage() ; break ; + case 'T' : if (!uint0_scan(l.arg, &T)) dieusage() ; break ; + default : dieusage() ; + } + } + argc -= l.ind ; argv += l.ind ; + } + if (!argc) dieusage() ; + { + char const *eargv[9 + argc + ((verbosity < 2 ? 1 : verbosity-1)) + ((!!bindpath + !!localname) << 1) + nodoenv] ; + char fmt1[UINT_FMT] ; + char fmt2[UINT_FMT] ; + unsigned int n = 0 ; + eargv[n++] = "s6-ipcclient" ; + if (!verbosity) eargv[n++] = "-Q" ; + else while (--verbosity) eargv[n++] = "-v" ; + if (bindpath) { eargv[n++] = "-p" ; eargv[n++] = bindpath ; } + if (localname) { eargv[n++] = "-l" ; eargv[n++] = localname ; } + eargv[n++] = "--" ; + eargv[n++] = *argv++ ; argc-- ; + eargv[n++] = S6_NETWORKING_BINPREFIX "s6-sudoc" ; + if (nodoenv) eargv[n++] = "-e" ; + eargv[n++] = "-t" ; + fmt1[uint_fmt(fmt1, t)] = 0 ; + eargv[n++] = fmt1 ; + eargv[n++] = "-T" ; + fmt2[uint_fmt(fmt2, T)] = 0 ; + eargv[n++] = fmt2 ; + eargv[n++] = "--" ; + while (argc--) eargv[n++] = *argv++ ; + eargv[n++] = 0 ; + pathexec_run(eargv[0], eargv, envp) ; + } + strerr_dieexec(111, S6_NETWORKING_BINPREFIX "s6-ipcclient") ; +} diff --git a/src/conn-tools/s6-sudo.h b/src/conn-tools/s6-sudo.h new file mode 100644 index 0000000..8c5797f --- /dev/null +++ b/src/conn-tools/s6-sudo.h @@ -0,0 +1,11 @@ +/* ISC license. */ + +#ifndef S6_SUDO_H +#define S6_SUDO_H + +#define S6_SUDO_BANNERB "s6-sudo b v1.0\n" +#define S6_SUDO_BANNERB_LEN (sizeof(S6_SUDO_BANNERB) - 1) +#define S6_SUDO_BANNERA "s6-sudo a v1.0\n" +#define S6_SUDO_BANNERA_LEN (sizeof(S6_SUDO_BANNERA) - 1) + +#endif diff --git a/src/conn-tools/s6-sudoc.c b/src/conn-tools/s6-sudoc.c new file mode 100644 index 0000000..c417efc --- /dev/null +++ b/src/conn-tools/s6-sudoc.c @@ -0,0 +1,115 @@ +/* ISC license. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "s6-sudo.h" + +#define USAGE "s6-sudoc [ -e ] [ -t timeoutconn ] [ -T timeoutrun ] [ args... ]" +#define dieusage() strerr_dieusage(100, USAGE) +#define dienomem() strerr_diefu1sys(111, "stralloc_catb") + +int main (int argc, char const *const *argv, char const *const *envp) +{ + char buf6[64] ; + buffer b6 = BUFFER_INIT(&buffer_read, 6, buf6, 64) ; + unixmessage_sender_t b7 = UNIXMESSAGE_SENDER_INIT(7) ; + subgetopt_t l = SUBGETOPT_ZERO ; + unsigned int t = 0, T = 0 ; + int doenv = 1 ; + + tain_t deadline = TAIN_INFINITE_RELATIVE ; + PROG = "s6-sudoc" ; + for (;;) + { + register int opt = subgetopt_r(argc, argv, "et:T:", &l) ; + if (opt < 0) break ; + switch (opt) + { + case 'e' : doenv = 0 ; break ; + case 't' : if (!uint0_scan(l.arg, &t)) dieusage() ; break ; + case 'T' : if (!uint0_scan(l.arg, &T)) dieusage() ; break ; + default : dieusage() ; + } + } + argc -= l.ind ; argv += l.ind ; + if (t) tain_from_millisecs(&deadline, t) ; + if ((ndelay_on(6) < 0) || (ndelay_on(7) < 0)) + strerr_diefu1sys(111, "make socket non-blocking") ; + if (!fd_sanitize() || !fd_ensure_open(2, 1)) + strerr_diefu1sys(111, "sanitize stdin/stdout/stderr") ; + + tain_now_g() ; + tain_add_g(&deadline, &deadline) ; + { + char tmp[S6_SUDO_BANNERB_LEN] ; + if (buffer_timed_get_g(&b6, tmp, S6_SUDO_BANNERB_LEN, &deadline) < S6_SUDO_BANNERB_LEN) + strerr_diefu1sys(111, "read banner from s6-sudod") ; + if (str_diffn(tmp, S6_SUDO_BANNERB, S6_SUDO_BANNERB_LEN)) + strerr_dief1x(100, "wrong server banner") ; + } + { + int fds[3] = { 0, 1, 2 } ; + char pack[16] ; + siovec_t v[3] = { + { .s = S6_SUDO_BANNERA, .len = S6_SUDO_BANNERA_LEN }, + { .s = pack, .len = 16 }, + { .s = 0, .len = 0 } } ; + unixmessage_v_t mv = { .v = v, .vlen = 3, .fds = fds, .nfds = 3 } ; + stralloc sa = STRALLOC_ZERO ; + unsigned int envlen = doenv ? env_len(envp) : 0 ; + uint32_pack_big(pack, (uint32)argc) ; + uint32_pack_big(pack + 4, (uint32)envlen) ; + if (!env_string(&sa, argv, argc)) dienomem() ; + uint32_pack_big(pack + 8, (uint32)sa.len) ; + if (doenv) + { + unsigned int start = sa.len ; + if (!env_string(&sa, envp, envlen)) dienomem() ; + envlen = sa.len - start ; + } + else envlen = 0 ; + uint32_pack_big(pack + 12, (uint32)envlen) ; + v[2].s = sa.s ; + v[2].len = sa.len ; + if (!unixmessage_putv_and_close(&b7, &mv, (unsigned char const *)"\003")) + strerr_diefu1sys(111, "unixmessage_putv") ; + stralloc_free(&sa) ; + } + if (!unixmessage_sender_timed_flush_g(&b7, &deadline)) + strerr_diefu1sys(111, "send args to server") ; + unixmessage_sender_free(&b7) ; + { + char c ; + if (buffer_timed_get_g(&b6, &c, 1, &deadline) < 1) + strerr_diefu1sys(111, "get confirmation from server") ; + if (c) + { + errno = c ; + strerr_diefu1sys(111, "start privileged program: server answered: ") ; + } + } + + if (T) tain_from_millisecs(&deadline, T) ; else deadline = tain_infinite_relative ; + tain_add_g(&deadline, &deadline) ; + { + char pack[UINT_PACK] ; + if (buffer_timed_get_g(&b6, pack, UINT_PACK, &deadline) < UINT_PACK) + strerr_diefu1sys(111, "get exit status from server") ; + uint_unpack_big(pack, &t) ; + } + if (WIFSIGNALED(t)) raise(WTERMSIG(t)) ; + return WEXITSTATUS(t) ; +} diff --git a/src/conn-tools/s6-sudod.c b/src/conn-tools/s6-sudod.c new file mode 100644 index 0000000..9ea6167 --- /dev/null +++ b/src/conn-tools/s6-sudod.c @@ -0,0 +1,233 @@ +/* ISC license. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "s6-sudo.h" + +#define USAGE "s6-sudod [ -0 ] [ -1 ] [ -2 ] [ -t timeout ] args..." +#define dieusage() strerr_dieusage(100, USAGE) +#define dienomem() strerr_diefu1sys(111, "stralloc_catb") + +int main (int argc, char const *const *argv, char const *const *envp) +{ + subgetopt_t l = SUBGETOPT_ZERO ; + unixmessage_t m ; + unsigned int nullfds = 0, t = 2000 ; + pid_t pid ; + uint32 envc = env_len(envp) ; + uint32 cargc, cenvc, carglen, cenvlen ; + int spfd ; + tain_t deadline = TAIN_INFINITE_RELATIVE ; + PROG = "s6-sudod" ; + for (;;) + { + register int opt = subgetopt_r(argc, argv, "012t:", &l) ; + if (opt < 0) break ; + switch (opt) + { + case '0' : nullfds |= 1 ; break ; + case '1' : nullfds |= 2 ; break ; + case '2' : nullfds |= 4 ; break ; + case 't' : if (!uint0_scan(l.arg, &t)) dieusage() ; break ; + default : dieusage() ; + } + } + argc -= l.ind ; argv += l.ind ; + if (t) tain_from_millisecs(&deadline, t) ; + if ((ndelay_on(0) < 0) || (ndelay_on(1) < 0)) + strerr_diefu1sys(111, "make socket non-blocking") ; + + tain_now_g() ; + tain_add_g(&deadline, &deadline) ; + buffer_putnoflush(buffer_1small, S6_SUDO_BANNERB, S6_SUDO_BANNERB_LEN) ; + if (!buffer_timed_flush_g(buffer_1small, &deadline)) + strerr_diefu1sys(111, "write banner to client") ; + if (unixmessage_timed_receive_g(unixmessage_receiver_0, &m, &deadline) <= 0) + strerr_diefu1sys(111, "read message from client") ; + if (m.nfds != 3) + strerr_dief1x(100, "client did not send 3 fds") ; + if (m.len < 16 + S6_SUDO_BANNERA_LEN) + strerr_dief1x(100, "wrong client message") ; + if (str_diffn(m.s, S6_SUDO_BANNERA, S6_SUDO_BANNERA_LEN)) + strerr_dief1x(100, "wrong client banner") ; + uint32_unpack_big(m.s + S6_SUDO_BANNERA_LEN, &cargc) ; + uint32_unpack_big(m.s + S6_SUDO_BANNERA_LEN + 4, &cenvc) ; + uint32_unpack_big(m.s + S6_SUDO_BANNERA_LEN + 8, &carglen) ; + uint32_unpack_big(m.s + S6_SUDO_BANNERA_LEN + 12, &cenvlen) ; + if (S6_SUDO_BANNERA_LEN + 16 + carglen + cenvlen != m.len) + strerr_dief1x(100, "wrong client argc/envlen") ; + if ((cargc > 131072) || (cenvc > 131072)) + strerr_dief1x(100, "too many args/envvars from client") ; + + if (nullfds & 1) + { + close(m.fds[0]) ; + m.fds[0] = open2("/dev/null", O_RDONLY) ; + if (m.fds[0] < 0) strerr_diefu2sys(111, "open /dev/null for ", "reading") ; + } + if (nullfds & 2) + { + close(m.fds[1]) ; + m.fds[1] = open2("/dev/null", O_WRONLY) ; + if (m.fds[1] < 0) strerr_diefu2sys(111, "open /dev/null for ", "writing") ; + } + if (nullfds & 4) + { + close(m.fds[2]) ; + m.fds[2] = 2 ; + } + + { + char const *targv[argc + 1 + cargc] ; + char const *tenvp[envc + 1 + cenvc] ; + int p[2] ; + register unsigned int i = 0 ; + for (; i < (unsigned int)argc ; i++) targv[i] = argv[i] ; + for (i = 0 ; i <= envc ; i++) tenvp[i] = envp[i] ; + if (!env_make(targv + argc, cargc, m.s + S6_SUDO_BANNERA_LEN + 16, carglen)) + { + char c = errno ; + buffer_putnoflush(buffer_1small, &c, 1) ; + buffer_timed_flush_g(buffer_1small, &deadline) ; + errno = c ; + strerr_diefu1sys(111, "make child argv") ; + } + if (!env_make(tenvp + envc + 1, cenvc, m.s + S6_SUDO_BANNERA_LEN + 16 + carglen, cenvlen)) + { + char c = errno ; + buffer_putnoflush(buffer_1small, &c, 1) ; + buffer_timed_flush_g(buffer_1small, &deadline) ; + errno = c ; + strerr_diefu1sys(111, "make child envp") ; + } + targv[argc + cargc] = 0 ; + + for (i = 0 ; i < cenvlen ; i++) + { + char const *var = tenvp[envc + 1 + i] ; + register unsigned int j = 0 ; + register unsigned int len = str_chr(var, '=') ; + if (!var[len]) + { + char c = EINVAL ; + buffer_putnoflush(buffer_1small, &c, 1) ; + buffer_timed_flush_g(buffer_1small, &deadline) ; + strerr_dief1x(100, "bad environment from client") ; + } + for (; j < envc ; j++) if (!str_diffn(var, tenvp[j], len+1)) break ; + if ((j < envc) && !tenvp[j][len+1]) tenvp[j] = var ; + } + + spfd = selfpipe_init() ; + if (spfd < 0) strerr_diefu1sys(111, "selfpipe_init") ; + if (selfpipe_trap(SIGCHLD) < 0) strerr_diefu1sys(111, "trap SIGCHLD") ; + if (pipe(p) < 0) strerr_diefu1sys(111, "pipe") ; + if (coe(p[1]) < 0) strerr_diefu1sys(111, "coe pipe") ; + pid = fork() ; + if (pid < 0) strerr_diefu1sys(111, "fork") ; + if (!pid) + { + PROG = "s6-sudod (child)" ; + fd_close(p[0]) ; + if ((fd_move(2, m.fds[2]) < 0) + || (fd_move(1, m.fds[1]) < 0) + || (fd_move(0, m.fds[0]) < 0)) + { + char c = errno ; + fd_write(p[1], &c, 1) ; + strerr_diefu1sys(111, "move fds") ; + } + selfpipe_finish() ; + pathexec0_run(targv, tenvp) ; + { + char c = errno ; + fd_write(p[1], &c, 1) ; + } + strerr_dieexec(111, targv[0]) ; + } + fd_close(p[1]) ; + { + char c ; + register int r = fd_read(p[0], &c, 1) ; + if (r < 0) strerr_diefu1sys(111, "read from child") ; + if (r) + { + buffer_putnoflush(buffer_1small, &c, 1) ; + buffer_timed_flush_g(buffer_1small, &deadline) ; + return 111 ; + } + } + fd_close(p[0]) ; + } + + fd_close(m.fds[0]) ; + fd_close(m.fds[1]) ; + if (!(nullfds & 4)) fd_close(m.fds[2]) ; + unixmessage_receiver_free(unixmessage_receiver_0) ; + buffer_putnoflush(buffer_1small, "", 1) ; + if (!buffer_timed_flush_g(buffer_1small, &deadline)) + strerr_diefu1sys(111, "send confirmation to client") ; + + { + iopause_fd x[2] = { { .fd = 0, .events = 0 }, { .fd = spfd, .events = IOPAUSE_READ } } ; + int cont = 1 ; + while (cont) + { + if (iopause_g(x, 2, 0) < 0) strerr_diefu1sys(111, "iopause") ; + if (x[1].revents) + { + for (;;) + { + int c = selfpipe_read() ; + if (c < 0) strerr_diefu1sys(111, "read from selfpipe") ; + else if (!c) break ; + else if (c == SIGCHLD) + { + int wstat ; + register int r = wait_pid_nohang(pid, &wstat) ; + if ((r < 0) && (errno != ECHILD)) + strerr_diefu1sys(111, "wait_pid_nohang") ; + else if (r > 0) + { + char pack[UINT_PACK] ; + uint_pack_big(pack, (unsigned int)wstat) ; + buffer_putnoflush(buffer_1small, pack, UINT_PACK) ; + cont = 0 ; + } + } + else + strerr_dief1sys(101, "internal inconsistency, please submit a bug-report") ; + } + } + if (x[0].revents && cont) + { + kill(pid, SIGTERM) ; + kill(pid, SIGCONT) ; + x[0].fd = -1 ; + return 1 ; + } + } + } + if (ndelay_off(1) < 0) + strerr_diefu1sys(111, "set stdout blocking") ; + if (!buffer_flush(buffer_1small)) + strerr_diefu1sys(111, "write status to client") ; + return 0 ; +} diff --git a/src/conn-tools/s6-tcpclient.c b/src/conn-tools/s6-tcpclient.c new file mode 100644 index 0000000..4686636 --- /dev/null +++ b/src/conn-tools/s6-tcpclient.c @@ -0,0 +1,377 @@ +/* ISC license. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef SKALIBS_IPV6_ENABLED +# define USAGE "s6-tcpclient [ -q | -Q | -v ] [ -4 | -6 ] [ -d | -D ] [ -r | -R ] [ -h | -H ] [ -n | -N ] [ -t timeoutinfo ] [ -l localname ] [ -T timeoutconn ] [ -i localip ] [ -p localport ] host port prog..." +# define TFLAGS_DEFAULT { 0, 0, { 2, 58 }, IP46_ZERO, 0, 1, 0, 0, 1, 0, 1, 1 } +# define OPTSTRING "qQv46dDrRhHnNt:l:T:i:p:" +#else +# define USAGE "s6-tcpclient [ -q | -Q | -v ] [ -d | -D ] [ -r | -R ] [ -h | -H ] [ -n | -N ] [ -t timeoutinfo ] [ -l localname ] [ -T timeoutconn ] [ -i localip ] [ -p localport ] host port prog..." +# define TFLAGS_DEFAULT { 0, 0, { 2, 58 }, IP46_ZERO, 0, 1, 1, 0, 1, 1 } +# define OPTSTRING "qQvdDrRhHnNt:l:T:i:p:" +#endif + +#define usage() strerr_dieusage(100, USAGE) +#define dienomem() strerr_diefu1sys(111, "allocate") + +#define MAXIP 16 + +typedef struct tflags_s tflags, *tflags_ref ; +struct tflags_s +{ + char const *localname ; + unsigned int timeout ; + unsigned int timeoutconn[2] ; + ip46_t localip ; + uint16 localport ; + unsigned int verbosity : 2 ; +#ifdef SKALIBS_IPV6_ENABLED + unsigned int ip4 : 1 ; + unsigned int ip6 : 1 ; +#endif + unsigned int delay : 1 ; + unsigned int remoteinfo : 1 ; + unsigned int remotehost : 1 ; + unsigned int qualif : 1 ; +} ; + +static tain_t deadline ; + +int main (int argc, char const *const *argv) +{ + int s ; + tflags flags = TFLAGS_DEFAULT ; + uint16 remoteport ; + PROG = "s6-tcpclient" ; + { + subgetopt_t l = SUBGETOPT_ZERO ; + for (;;) + { + register int opt = subgetopt_r(argc, argv, OPTSTRING, &l) ; + if (opt == -1) break ; + switch (opt) + { + case 'q' : if (flags.verbosity) flags.verbosity-- ; break ; + case 'Q' : flags.verbosity = 1 ; break ; + case 'v' : flags.verbosity++ ; break ; +#ifdef SKALIBS_IPV6_ENABLED + case '4' : flags.ip4 = 1 ; break ; + case '6' : flags.ip6 = 1 ; break ; +#endif + case 'd' : flags.delay = 1 ; break ; + case 'D' : flags.delay = 0 ; break ; + case 'r' : flags.remoteinfo = 1 ; break ; + case 'R' : flags.remoteinfo = 0 ; break ; + case 'h' : flags.remotehost = 1 ; break ; + case 'H' : flags.remotehost = 0 ; break ; + case 'n' : flags.qualif = 1 ; break ; + case 'N' : flags.qualif = 0 ; break ; + case 't' : if (!uint0_scan(l.arg, &flags.timeout)) usage() ; break ; + case 'l' : flags.localname = l.arg ; break ; + case 'T' : + { + unsigned int n = uint_scan(l.arg, &flags.timeoutconn[0]) ; + if (!n) usage() ; + if (!l.arg[n]) + { + flags.timeoutconn[1] = 0 ; + break ; + } + if (l.arg[n] != '+') usage() ; + if (!uint0_scan(l.arg + n + 1, &flags.timeoutconn[1])) usage() ; + break ; + } + case 'i' : if (!ip46_scan(l.arg, &flags.localip)) usage() ; break ; + case 'p' : if (!uint160_scan(l.arg, &flags.localport)) usage() ; break ; + default : usage() ; + } + } + argc -= l.ind ; argv += l.ind ; + } + if (argc < 3) usage() ; +#ifdef SKALIBS_IPV6_ENABLED + if (!flags.ip6) flags.ip4 = 1 ; +#endif + if (!uint160_scan(argv[1], &remoteport)) + strerr_dief2x(100, "invalid port number: ", argv[1]) ; + tain_now_g() ; + if (flags.timeout) tain_addsec_g(&deadline, flags.timeout) ; + else tain_add_g(&deadline, &tain_infinite_relative) ; + + { + ip46_t ip[2][MAXIP] ; + unsigned int j = 0 ; + unsigned int n[2] = { 0, 0 } ; + register unsigned int i = 0 ; + if (!**argv || ((**argv == '0') && !argv[0][1])) + { +#ifdef SKALIBS_IPV6_ENABLED + ip46_from_ip6(&ip[0][n[0]++], IP6_LOCAL) ; +#endif + ip46_from_ip4(&ip[0][n[0]++], IP4_LOCAL) ; + } + else + { + if (!flags.remotehost) + { +#ifdef SKALIBS_IPV6_ENABLED + if (flags.ip6 && !flags.ip4) + { + char ip6[MAXIP << 4] ; + register unsigned int i = 0 ; + if (!ip6_scanlist(ip6, MAXIP, argv[0], &n[0])) usage() ; + for (; i < n[0] ; i++) ip46_from_ip6(&ip[0][i], ip6 + (i << 4)) ; + } + else if (!flags.ip6) + { + char ip4[MAXIP << 2] ; + register unsigned int i = 0 ; + if (!ip4_scanlist(ip4, MAXIP, argv[0], &n[0])) usage() ; + for (; i < n[0] ; i++) ip46_from_ip4(&ip[0][i], ip4 + (i << 2)) ; + } + else +#endif + if (!ip46_scanlist(ip[0], MAXIP, argv[0], &n[0])) usage() ; + } + else + { +#ifdef SKALIBS_IPV6_ENABLED + if (flags.ip6 && flags.ip4) + { + if (!ip46_scanlist(ip[0], MAXIP, argv[0], &n[0])) + { + genalloc ips = STRALLOC_ZERO ; + if (s6dns_resolve_aaaaa_g(&ips, argv[0], str_len(argv[0]), flags.qualif, &deadline) <= 0) + strerr_diefu4x(111, "resolve ", argv[0], ": ", s6dns_constants_error_str(errno)) ; + n[0] = genalloc_len(ip46_t, &ips) ; + if (n[0] >= MAXIP) n[0] = MAXIP ; + for (; i < n[0] ; i++) ip[0][i] = genalloc_s(ip46_t, &ips)[i] ; + genalloc_free(ip46_t, &ips) ; + } + } + else if (flags.ip6) + { + char ip6[MAXIP << 4] ; + if (ip6_scanlist(ip6, MAXIP, argv[0], &n[0])) + { + register unsigned int i = 0 ; + for (; i < n[0] ; i++) ip46_from_ip6(&ip[0][i], ip6 + (i << 4)) ; + } + else + { + stralloc ip6s = STRALLOC_ZERO ; + if (s6dns_resolve_aaaa_g(&ip6s, argv[0], str_len(argv[0]), flags.qualif, &deadline) <= 0) + strerr_diefu4x(111, "resolve ", argv[0], ": ", s6dns_constants_error_str(errno)) ; + n[0] = ip6s.len >> 4 ; + if (n[0] >= MAXIP) n[0] = MAXIP ; + for (; i < n[0] ; i++) ip46_from_ip6(&ip[0][i], ip6s.s + (i << 4)) ; + stralloc_free(&ip6s) ; + } + } + else +#endif + { + char ip4[MAXIP << 2] ; + if (ip4_scanlist(ip4, MAXIP, argv[0], &n[0])) + { + register unsigned int i = 0 ; + for (; i < n[0] ; i++) ip46_from_ip4(&ip[0][i], ip4 + (i << 2)) ; + } + else + { + stralloc ip4s = STRALLOC_ZERO ; + if (s6dns_resolve_a_g(&ip4s, argv[0], str_len(argv[0]), flags.qualif, &deadline) <= 0) + strerr_diefu4x(111, "resolve ", argv[0], ": ", s6dns_constants_error_str(errno)) ; + n[0] = ip4s.len >> 2 ; + if (n[0] >= MAXIP) n[0] = MAXIP ; + for (; i < n[0] ; i++) ip46_from_ip4(&ip[0][i], ip4s.s + (i << 2)) ; + stralloc_free(&ip4s) ; + } + } + } + if (!n[0]) strerr_dief2x(100, "no IP address for ", argv[0]) ; + } + + if (n[0] == 1) + { + flags.timeoutconn[0] += flags.timeoutconn[1] ; + flags.timeoutconn[1] = 0 ; + } + + for (; j < 2 ; j++) + { + unsigned int i = 0 ; + for (; i < n[j] ; i++) + { + tain_t localdeadline ; + s = socket_tcp46(ip46_is6(&flags.localip)) ; + if (s < 0) strerr_diefu1sys(111, "create socket") ; + if (socket_bind46(s, &flags.localip, flags.localport) < 0) + strerr_diefu1sys(111, "bind socket") ; + tain_addsec_g(&localdeadline, flags.timeoutconn[j]) ; + if (tain_less(&deadline, &localdeadline)) localdeadline = deadline ; + if (socket_deadlineconnstamp46_g(s, &ip[j][i], remoteport, &localdeadline)) goto connected ; + fd_close(s) ; + if (!j && flags.timeoutconn[1]) ip[1][n[1]++] = ip[0][i] ; + else + { + char fmtip[IP46_FMT] ; + char fmtport[UINT16_FMT] ; + fmtip[ip46_fmt(fmtip, &ip[j][i])] = 0 ; + fmtport[uint16_fmt(fmtport, remoteport)] = 0 ; + strerr_warnwu4sys("connect to ", fmtip, " port ", fmtport) ; + } + } + } + strerr_diefu2x(111, "connect to ", "a suitable IP address") ; + } + + connected: + + if (ndelay_off(s) == -1) + strerr_diefu1sys(111, "ndelay_off") ; + if (!flags.delay) socket_tcpnodelay(s) ; + if (socket_local46(s, &flags.localip, &flags.localport) == -1) + strerr_diefu2sys(111, "get local", " address and port") ; + + { + ip46_t remoteip ; + char fmtip[IP46_FMT] ; + char fmtport[UINT16_FMT] ; + + if (socket_remote46(s, &remoteip, &remoteport) == -1) + strerr_diefu2sys(111, "get remote", " address and port") ; + fmtip[ip46_fmt(fmtip, &remoteip)] = 0 ; + fmtport[uint16_fmt(fmtport, remoteport)] = 0 ; + if (flags.verbosity >= 2) + strerr_warni4x("connected to ", fmtip, " port ", fmtport) ; + if (!pathexec_env("PROTO", "TCP") + || !pathexec_env("TCPREMOTEIP", fmtip) + || !pathexec_env("TCPREMOTEPORT", fmtport)) dienomem() ; + + fmtip[ip46_fmt(fmtip, &flags.localip)] = 0 ; + fmtport[uint16_fmt(fmtport, flags.localport)] = 0 ; + if (!pathexec_env("TCPLOCALIP", fmtip) + || !pathexec_env("TCPLOCALPORT", fmtport)) dienomem() ; + + if (flags.localname) + { + if (!pathexec_env("TCPLOCALHOST", flags.localname)) dienomem() ; + } + + /* DNS resolution for TCPLOCALHOST and TCPREMOTEHOST */ + + if (!flags.localname || flags.remotehost) + { + s6dns_resolve_t blob[2] ; + s6dns_dpag_t data[2] = { S6DNS_DPAG_ZERO, S6DNS_DPAG_ZERO } ; + if (!flags.localname) + { + s6dns_domain_arpafromip46(&blob[0].q, &flags.localip) ; + s6dns_domain_encode(&blob[0].q) ; + blob[0].qtype = S6DNS_T_PTR ; + blob[0].deadline = deadline ; + blob[0].parsefunc = &s6dns_message_parse_answer_domain ; + blob[0].data = &data[0] ; + blob[0].options = S6DNS_O_RECURSIVE ; + data[0].rtype = S6DNS_T_PTR ; + } + if (flags.remotehost) + { + s6dns_domain_arpafromip46(&blob[1].q, &remoteip) ; + s6dns_domain_encode(&blob[1].q) ; + blob[1].qtype = S6DNS_T_PTR ; + blob[1].deadline = deadline ; + blob[1].parsefunc = &s6dns_message_parse_answer_domain ; + blob[1].data = &data[1] ; + blob[1].options = S6DNS_O_RECURSIVE ; + data[1].rtype = S6DNS_T_PTR ; + } + { + tain_t infinite = TAIN_INFINITE ; + if (!s6dns_resolven_parse_g(blob + !!flags.localname, !flags.localname + !!flags.remotehost, &infinite)) + strerr_diefu2x(111, "resolve IP addresses: ", s6dns_constants_error_str(errno)) ; + } + if (!flags.localname) + { + if (blob[0].status) + { + if (!pathexec_env("TCPLOCALHOST", 0)) dienomem() ; + } + else + { + char s[256] ; + register unsigned int len = 0 ; + if (genalloc_len(s6dns_domain_t, &data[0].ds)) + len = s6dns_domain_tostring(s, 255, genalloc_s(s6dns_domain_t, &data[0].ds)) ; + genalloc_free(s6dns_domain_t, &data[0].ds) ; + s[len] = 0 ; + if (!pathexec_env("TCPLOCALHOST", s)) dienomem() ; + } + } + if (flags.remotehost) + { + if (blob[1].status) + { + if (!pathexec_env("TCPREMOTEHOST", 0)) dienomem() ; + } + else + { + char s[256] ; + register unsigned int len = 0 ; + if (genalloc_len(s6dns_domain_t, &data[1].ds)) + len = s6dns_domain_tostring(s, 255, genalloc_s(s6dns_domain_t, &data[1].ds)) ; + genalloc_free(s6dns_domain_t, &data[1].ds) ; + s[len] = 0 ; + if (!pathexec_env("TCPREMOTEHOST", s)) dienomem() ; + } + } + } + + + /* TCPREMOTEINFO */ + /* + Yes, I should have made all the network queries in parallel, + not only the DNS ones, but the IDENT one too. Well, that was + too much work for an obsolete protocol. Sue me. + */ + { + char idbuf[S6NET_IDENT_ID_SIZE] ; + if (flags.remoteinfo) + { + register int r = s6net_ident_client_g(idbuf, S6NET_IDENT_ID_SIZE, &remoteip, remoteport, &flags.localip, flags.localport, &deadline) ; + if (r <= 0) + { + if (flags.verbosity) + { + if (r < 0) strerr_warnwu1sys("s6net_ident_client") ; + else strerr_warnw2x("ident server replied: ", s6net_ident_error_str(errno)) ; + } + if (!pathexec_env("TCPREMOTEINFO", "")) dienomem() ; + } + else if (!pathexec_env("TCPREMOTEINFO", idbuf)) dienomem() ; + } + } + } + + if (fd_move(6, s) < 0) strerr_diefu2sys(111, "set up fd ", "6") ; + if (fd_copy(7, 6) < 0) strerr_diefu2sys(111, "set up fd ", "7") ; + pathexec(argv+2) ; + strerr_dieexec(111, argv[2]) ; +} diff --git a/src/conn-tools/s6-tcpserver-access.c b/src/conn-tools/s6-tcpserver-access.c new file mode 100644 index 0000000..ef2771b --- /dev/null +++ b/src/conn-tools/s6-tcpserver-access.c @@ -0,0 +1,387 @@ +/* ISC license. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define USAGE "s6-tcpserver-access [ -v verbosity ] [ -W | -w ] [ -D | -d ] [ -H | -h ] [ -R | -r ] [ -P | -p ] [ -l localname ] [ -B banner ] [ -t timeout ] [ -i rulesdir | -x rulesfile ] prog..." +#define dieusage() strerr_dieusage(100, USAGE) +#define dienomem() strerr_diefu1sys(111, "update environment") +#define X() strerr_dief1x(101, "internal inconsistency. Please submit a bug-report.") + + +static void logit (unsigned int pid, ip46_t const *ip, int h) +{ + char fmtpid[UINT_FMT] ; + char fmtip[IP46_FMT] ; + fmtip[ip46_fmt(fmtip, ip)] = 0 ; + fmtpid[uint_fmt(fmtpid, pid)] = 0 ; + if (h) strerr_warni5x("allow", " pid ", fmtpid, " ip ", fmtip) ; + else strerr_warni5sys("deny", " pid ", fmtpid, " ip ", fmtip) ; +} + +static inline void log_accept (unsigned int pid, ip46_t const *ip) +{ + logit(pid, ip, 1) ; +} + +static inline void log_deny (unsigned int pid, ip46_t const *ip) +{ + logit(pid, ip, 0) ; +} + + +int main (int argc, char const *const *argv, char const *const *envp) +{ + s6net_accessrules_params_t params = S6NET_ACCESSRULES_PARAMS_ZERO ; + stralloc modifs = STRALLOC_ZERO ; + tain_t deadline, tto ; + char const *rulestypestr[3] = { "no", "fs", "cdb" } ; + char const *rules = 0 ; + char const *localname = 0 ; + char const *proto ; + struct cdb c = CDB_ZERO ; + int cdbfd = -1 ; + unsigned int rulestype = 0 ; + unsigned int verbosity = 1 ; + unsigned int protolen ; + s6net_accessrules_result_t accepted ; + ip46_t remoteip, localip ; + int flagfatal = 1, flagnodelay = 0, flagdnslookup = 1, + flagident = 0, flagparanoid = 0, e = 0 ; + uint16 remoteport, localport ; + PROG = "s6-tcpserver-access" ; + { + unsigned int timeout = 0 ; + subgetopt_t l = SUBGETOPT_ZERO ; + for (;;) + { + register int opt = subgetopt_r(argc, argv, "WwDdHhRrPpv:l:B:t:i:x:", &l) ; + if (opt == -1) break ; + switch (opt) + { + case 'W' : flagfatal = 0 ; break ; + case 'w' : flagfatal = 1 ; break ; + case 'D' : flagnodelay = 1 ; break ; + case 'd' : flagnodelay = 0 ; break ; + case 'H' : flagdnslookup = 0 ; break ; + case 'h' : flagdnslookup = 1 ; break ; + case 'R' : flagident = 0 ; break ; + case 'r' : flagident = 1 ; break ; + case 'P' : flagparanoid = 0 ; break ; + case 'p' : flagparanoid = 1 ; break ; + case 'v' : if (!uint0_scan(l.arg, &verbosity)) dieusage() ; break ; + case 'l' : localname = l.arg ; break ; + case 'B' : + { + register unsigned int n = str_len(l.arg) ; + if (buffer_putnoflush(buffer_1small, l.arg, n) < n) + strerr_dief1x(100, "banner too long") ; + break ; + } + case 't' : if (!uint0_scan(l.arg, &timeout)) dieusage() ; break ; + case 'i' : rules = l.arg ; rulestype = 1 ; break ; + case 'x' : rules = l.arg ; rulestype = 2 ; break ; + default : dieusage() ; + } + } + argc -= l.ind ; argv += l.ind ; + if (timeout) tain_from_millisecs(&tto, timeout) ; + else tto = tain_infinite_relative ; + } + if (!argc) dieusage() ; + if (!*argv[0]) dieusage() ; + + proto = env_get2(envp, "PROTO") ; + if (!proto) strerr_dienotset(100, "PROTO") ; + protolen = str_len(proto) ; + { + char const *x ; + char tmp[protolen + 11] ; + byte_copy(tmp, protolen, proto) ; + byte_copy(tmp + protolen, 9, "REMOTEIP") ; + x = env_get2(envp, tmp) ; + if (!x) strerr_dienotset(100, tmp) ; + if (!ip46_scan(x, &remoteip)) strerr_dieinvalid(100, tmp) ; + byte_copy(tmp + protolen + 6, 5, "PORT") ; + x = env_get2(envp, tmp) ; + if (!x) strerr_dienotset(100, tmp) ; + if (!uint160_scan(x, &remoteport)) strerr_dieinvalid(100, tmp) ; + } + + if (flagnodelay) + { + if (socket_tcpnodelay(1) < 0) + if (verbosity) strerr_warnwu1sys("socket_tcpnodelay") ; + } + tain_now_g() ; + tain_add_g(&deadline, &tto) ; + if (!buffer_timed_flush_g(buffer_1small, &deadline)) + strerr_diefu1sys(111, "write banner") ; + + switch (rulestype) + { + case 0 : + if (verbosity >= 2) strerr_warnw1x("invoked without a ruleset!") ; + accepted = S6NET_ACCESSRULES_ALLOW ; + break ; + case 1 : + accepted = s6net_accessrules_ip46_fs(&remoteip, (void *)rules, ¶ms) ; + break ; + case 2 : + cdbfd = open_readb(rules) ; + if (cdbfd < 0) strerr_diefu2sys(111, "open_readb ", rules) ; + if (cdb_init(&c, cdbfd) < 0) strerr_diefu2sys(111, "cdb_init ", rules) ; + accepted = s6net_accessrules_ip46_cdb(&remoteip, &c, ¶ms) ; + if (accepted == S6NET_ACCESSRULES_ALLOW) + { + cdb_free(&c) ; + fd_close(cdbfd) ; + } + break ; + default : X() ; + } + switch (accepted) + { + case S6NET_ACCESSRULES_ERROR : + strerr_diefu6sys(111, "check ", rulestypestr[rulestype], " ruleset for ", "IP", " in ", rules) ; + case S6NET_ACCESSRULES_ALLOW : break ; + case S6NET_ACCESSRULES_DENY : + if (verbosity >= 2) { errno = EACCES ; log_deny(getpid(), &remoteip) ; } + return 1 ; + case S6NET_ACCESSRULES_NOTFOUND : + if (flagdnslookup) break ; + if (verbosity >= 2) { errno = ENOENT ; log_deny(getpid(), &remoteip) ; } + return 1 ; + default: X() ; + } + + { + char const *x = 0 ; + char idbuf[S6NET_IDENT_ID_SIZE] ; + char fmt[IP46_FMT] ; + char tmp[protolen + 11] ; + if (socket_local46(0, &localip, &localport) < 0) + strerr_diefu1sys(111, "socket_local") ; + fmt[ip46_fmt(fmt, &localip)] = 0 ; + byte_copy(tmp, protolen, proto) ; + byte_copy(tmp + protolen, 8, "LOCALIP") ; + if (!env_addmodif(&modifs, tmp, fmt)) dienomem() ; + fmt[uint16_fmt(fmt, localport)] = 0 ; + byte_copy(tmp + protolen + 5, 5, "PORT") ; + if (!env_addmodif(&modifs, tmp, fmt)) dienomem() ; + byte_copy(tmp + protolen, 11, "REMOTEINFO") ; + if (flagident) + { + register int r = s6net_ident_client_g(idbuf, S6NET_IDENT_ID_SIZE, &remoteip, remoteport, &localip, localport, &deadline) ; + if (r < 0) + { + if (verbosity >= 3) strerr_warnwu1sys("s6net_ident_client") ; + if (flagfatal) + { + e = errno == ETIMEDOUT ? 99 : 111 ; + goto reject ; + } + } + else if (!r) + { + if (verbosity >= 3) strerr_warnw2x("ident server replied: ", s6net_ident_error_str(errno)) ; + if (flagfatal) + { + e = 2 ; + goto reject ; + } + } + else x = idbuf ; + } + + if (!env_addmodif(&modifs, tmp, x)) dienomem() ; + } + + if (!flagdnslookup) + { + char tmp[protolen + 11] ; + byte_copy(tmp, protolen, proto) ; + byte_copy(tmp + protolen, 10, "LOCALHOST") ; + if (!env_addmodif(&modifs, tmp, localname)) dienomem() ; + byte_copy(tmp + protolen, 11, "REMOTEHOST") ; + if (!env_addmodif(&modifs, tmp, 0)) dienomem() ; + } + else + { + static tain_t const infinite = TAIN_INFINITE ; + s6dns_dpag_t data[2] = { S6DNS_DPAG_ZERO, S6DNS_DPAG_ZERO } ; + s6dns_resolve_t blob[2] ; + char remotebuf[256] ; + unsigned int remotelen = 0 ; + char tcplocalhost[(protolen << 1) + 21] ; + char *tcpremotehost = tcplocalhost + protolen + 10 ; + byte_copy(tcplocalhost, protolen, proto) ; + byte_copy(tcplocalhost + protolen, 10, "LOCALHOST") ; + byte_copy(tcpremotehost, protolen, proto) ; + byte_copy(tcpremotehost + protolen, 11, "REMOTEHOST") ; + + if (localname) + { + if (!env_addmodif(&modifs, tcplocalhost, localname)) dienomem() ; + } + { + s6dns_domain_arpafromip46(&blob[0].q, &localip) ; + s6dns_domain_encode(&blob[0].q) ; + blob[0].qtype = S6DNS_T_PTR ; + blob[0].deadline = deadline ; + blob[0].parsefunc = &s6dns_message_parse_answer_domain ; + blob[0].data = &data[0] ; + blob[0].options = S6DNS_O_RECURSIVE ; + data[0].rtype = S6DNS_T_PTR ; + } + s6dns_domain_arpafromip46(&blob[1].q, &remoteip) ; + s6dns_domain_encode(&blob[1].q) ; + blob[1].qtype = S6DNS_T_PTR ; + blob[1].deadline = deadline ; + blob[1].parsefunc = &s6dns_message_parse_answer_domain ; + blob[1].data = &data[1] ; + blob[1].options = S6DNS_O_RECURSIVE ; + data[1].rtype = S6DNS_T_PTR ; + if (!s6dns_resolven_parse_g(blob + !!localname, 1 + !localname, &infinite)) + { + if (verbosity >= 3) strerr_warnwu2x("resolve IP addresses: ", s6dns_constants_error_str(errno)) ; + if (flagfatal) + { + e = 111 ; + goto reject ; + } + } + else + { + if (!localname) + { + if (blob[0].status) + { + if (!env_addmodif(&modifs, tcplocalhost, 0)) dienomem() ; + } + else + { + char s[256] ; + register unsigned int len = 0 ; + if (genalloc_len(s6dns_domain_t, &data[0].ds)) + { + s6dns_domain_noqualify(genalloc_s(s6dns_domain_t, &data[0].ds)) ; + len = s6dns_domain_tostring(s, 255, genalloc_s(s6dns_domain_t, &data[0].ds)) ; + } + genalloc_free(s6dns_domain_t, &data[0].ds) ; + s[len] = 0 ; + if (!env_addmodif(&modifs, tcplocalhost, s)) dienomem() ; + } + } + if (!blob[1].status) + { + if (genalloc_len(s6dns_domain_t, &data[1].ds)) + { + s6dns_domain_noqualify(genalloc_s(s6dns_domain_t, &data[1].ds)) ; + remotelen = s6dns_domain_tostring(remotebuf, 255, genalloc_s(s6dns_domain_t, &data[1].ds)) ; + } + remotebuf[remotelen] = 0 ; + if (flagparanoid) + { + register int r ; + data[1].ds.len = 0 ; + r = ip46_is6(&remoteip) ? s6dns_resolve_aaaa_g(&data[1].ds, remotebuf, remotelen, 0, &deadline) : s6dns_resolve_a_g(&data[1].ds, remotebuf, remotelen, 0, &deadline) ; + if (r <= 0) + { + if (verbosity >= 3) strerr_warnwu4x("(paranoidly) resolve ", remotebuf, ": ", s6dns_constants_error_str(errno)) ; + if (flagfatal) + { + e = errno == ETIMEDOUT ? 99 : 111 ; + goto reject ; + } + remotelen = 0 ; + } + else + { + register unsigned int i = 0 ; + for (; i < data[1].ds.len ; i += ip46_is6(&remoteip) ? 16 : 4) + if (!byte_diff(remoteip.ip, ip46_is6(&remoteip) ? 16 : 4, data[1].ds.s + i)) break ; + if (i >= data[1].ds.len) remotelen = 0 ; + } + } + stralloc_free(&data[1].ds) ; + } + } + if (!env_addmodif(&modifs, tcpremotehost, remotelen ? remotebuf : 0)) dienomem() ; + if (remotelen && (accepted == S6NET_ACCESSRULES_NOTFOUND)) + { + switch (rulestype) + { + case 1 : + accepted = s6net_accessrules_reversedns_fs(remotebuf, (void *)rules, ¶ms) ; + break ; + case 2 : + accepted = s6net_accessrules_reversedns_cdb(remotebuf, &c, ¶ms) ; + break ; + default : X() ; + } + } + + if ((rulestype == 2) && (accepted != S6NET_ACCESSRULES_ALLOW)) + { + cdb_free(&c) ; + fd_close(cdbfd) ; + } + + switch (accepted) + { + case S6NET_ACCESSRULES_ERROR : + strerr_diefu6sys(111, "check ", rulestypestr[rulestype], " ruleset for ", "reverse DNS", " in ", rules) ; + case S6NET_ACCESSRULES_ALLOW : break ; + case S6NET_ACCESSRULES_DENY : + if (verbosity >= 2) { errno = EACCES ; log_deny(getpid(), &remoteip) ; } + return 1 ; + case S6NET_ACCESSRULES_NOTFOUND : + if (verbosity >= 2) { errno = ENOENT ; log_deny(getpid(), &remoteip) ; } + return 1 ; + default : X() ; + } + } + + if (!stralloc_catb(¶ms.env, modifs.s, modifs.len)) dienomem() ; + stralloc_free(&modifs) ; + if (verbosity) log_accept(getpid(), &remoteip) ; + if (params.exec.len) + { + char *specialargv[4] = { EXECLINE_EXTBINPREFIX "execlineb", "-c", params.exec.s, 0 } ; + pathexec_r((char const *const *)specialargv, envp, env_len(envp), params.env.s, params.env.len) ; + strerr_dieexec(111, specialargv[0]) ; + } + + pathexec_r(argv, envp, env_len(envp), params.env.s, params.env.len) ; + strerr_dieexec(111, argv[0]) ; + + reject: + if (verbosity >= 2) + { + char fmtpid[UINT_FMT] ; + char fmtip[IP46_FMT] ; + fmtip[ip46_fmt(fmtip, &remoteip)] = 0 ; + fmtpid[uint_fmt(fmtpid, getpid())] = 0 ; + strerr_dief5x(e, "reject", " pid ", fmtpid, " ip ", fmtip) ; + } + else return e ; +} diff --git a/src/conn-tools/s6-tcpserver.c b/src/conn-tools/s6-tcpserver.c new file mode 100644 index 0000000..fcbf4f2 --- /dev/null +++ b/src/conn-tools/s6-tcpserver.c @@ -0,0 +1,71 @@ +/* ISC license. */ + +#include +#include +#include +#include +#include +#include + +#define USAGE "s6-tcpserver [ -q | -Q | -v ] [ -1 ] [ -4 | -6 ] [ -c maxconn ] [ -C localmaxconn ] [ -b backlog ] [ -G gid,gid,... ] [ -g gid ] [ -u uid ] [ -U ] ip port prog..." +#define dieusage() strerr_dieusage(100, USAGE) + +int main (int argc, char const *const *argv, char const *const *envp) +{ + char const *newargv[2 + (argc << 1)] ; + char const *path ; + unsigned int m = 3 ; + int what = 0 ; + unsigned int verbosity = 1 ; + char fmtv[UINT_FMT] ; + PROG = "s6-tcpserver" ; + newargv[1] = "-v" ; + newargv[2] = fmtv ; + { + subgetopt_t l = SUBGETOPT_ZERO ; + for (;;) + { + register int opt = subgetopt_r(argc, argv, "qQv146Uc:C:b:u:g:G:", &l) ; + if (opt == -1) break ; + switch (opt) + { + case '4' : if (what) dieusage() ; what = 4 ; break ; + case '6' : if (what) dieusage() ; what = 6 ; break ; + case 'q' : verbosity = 0 ; break ; + case 'Q' : verbosity = 1 ; break ; + case 'v' : verbosity = 2 ; break ; + case '1' : newargv[m++] = "-1" ; break ; + case 'U' : newargv[m++] = "-U" ; break ; + case 'c' : newargv[m++] = "-c" ; newargv[m++] = l.arg ; break ; + case 'C' : newargv[m++] = "-C" ; newargv[m++] = l.arg ; break ; + case 'b' : newargv[m++] = "-b" ; newargv[m++] = l.arg ; break ; + case 'u' : newargv[m++] = "-u" ; newargv[m++] = l.arg ; break ; + case 'g' : newargv[m++] = "-g" ; newargv[m++] = l.arg ; break ; + case 'G' : newargv[m++] = "-G" ; newargv[m++] = l.arg ; break ; + default : dieusage() ; + } + } + argc -= l.ind ; argv += l.ind ; + } + if (argc < 3) dieusage() ; + fmtv[uint_fmt(fmtv, verbosity)] = 0 ; + newargv[m++] = 0 ; + if (!what) + { + ip46_t ip ; + if (!ip46_scan(argv[0], &ip)) dieusage() ; + what = ip46_is6(&ip) ? 6 : 4 ; + } + if (what == 6) + { + newargv[0] = "s6-tcpserver6" ; + path = S6_NETWORKING_BINPREFIX "s6-tcpserver6" ; + } + else + { + newargv[0] = "s6-tcpserver4" ; + path = S6_NETWORKING_BINPREFIX "s6-tcpserver4" ; + } + pathexec_run(path, newargv, envp) ; + strerr_dieexec(111, path) ; +} diff --git a/src/conn-tools/s6-tcpserver4.c b/src/conn-tools/s6-tcpserver4.c new file mode 100644 index 0000000..5435f55 --- /dev/null +++ b/src/conn-tools/s6-tcpserver4.c @@ -0,0 +1,414 @@ +/* ISC license. */ + +#ifndef _BSD_SOURCE +#define _BSD_SOURCE +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ABSOLUTE_MAXCONN 1000 + +#define USAGE "s6-tcpserver4 [ -v verbosity ] [ -1 ] [ -c maxconn ] [ -C localmaxconn ] [ -b backlog ] [ -G gid,gid,... ] [ -g gid ] [ -u uid ] [ -U ] ip port prog..." + +static unsigned int maxconn = 40 ; +static unsigned int localmaxconn = 40 ; +static unsigned int verbosity = 1 ; +static int cont = 1 ; +static diuint32 *pidip = 0 ; +static unsigned int numconn = 0 ; +static diuint32 *ipnum = 0 ; +static unsigned int iplen = 0 ; + +static char fmtmaxconn[UINT_FMT+1] = "/" ; +static char fmtlocalmaxconn[UINT_FMT+1] = "/" ; + + + /* Utility functions */ + +static inline void dieusage () +{ + strerr_dieusage(100, USAGE) ; +} + +static inline void X (void) +{ + strerr_dief1x(101, "internal inconsistency. Please submit a bug-report.") ; +} + + + /* Lookup primitives */ + +static unsigned int lookup_diuint32 (diuint32 const *, unsigned int, unsigned int) gccattr_pure ; +static unsigned int lookup_diuint32 (diuint32 const *tab, unsigned int tablen, unsigned int key) +{ + register unsigned int i = 0 ; + for (; i < tablen ; i++) if (key == tab[i].left) break ; + return i ; +} + +static inline unsigned int lookup_pid (uint32 pid) +{ + return lookup_diuint32(pidip, numconn, pid) ; +} + +static inline unsigned int lookup_ip (uint32 ip) +{ + return lookup_diuint32(ipnum, iplen, ip) ; +} + + + /* Logging */ + +static void log_start (char const *ip, uint16 port) +{ + char fmtip[IP4_FMT] ; + char fmtport[UINT16_FMT] ; + fmtip[ip4_fmt(fmtip, ip)] = 0 ; + fmtport[uint16_fmt(fmtport, port)] = 0 ; + strerr_warni4x("starting - listening on ", fmtip, ":", fmtport) ; +} + +static inline void log_exit (void) +{ + strerr_warni1x("exiting") ; +} + +static void log_status (void) +{ + char fmt[UINT_FMT] ; + fmt[uint_fmt(fmt, numconn)] = 0 ; + strerr_warni3x("status: ", fmt, fmtmaxconn) ; +} + +static void log_deny (uint32 ip, uint16 port, unsigned int num) +{ + char fmtip[UINT32_FMT] ; + char fmtport[UINT16_FMT] ; + char fmtnum[UINT_FMT] ; + fmtip[ip4_fmtu32(fmtip, ip)] = 0 ; + fmtport[uint16_fmt(fmtport, port)] = 0 ; + fmtnum[uint_fmt(fmtnum, num)] = 0 ; + strerr_warni7sys("deny ", fmtip, ":", fmtport, " count ", fmtnum, fmtlocalmaxconn) ; +} + +static void log_accept (uint32 pid, uint32 ip, uint16 port, unsigned int num) +{ + char fmtipport[IP4_FMT + UINT16_FMT + 1] ; + char fmtpid[UINT32_FMT] ; + char fmtnum[UINT_FMT] ; + register unsigned int n ; + n = ip4_fmtu32(fmtipport, ip) ; + fmtipport[n++] = ':' ; + n += uint16_fmt(fmtipport + n, port) ; + fmtipport[n] = 0 ; + fmtnum[uint_fmt(fmtnum, num)] = 0 ; + fmtpid[uint32_fmt(fmtpid, pid)] = 0 ; + strerr_warni7x("allow ", fmtipport, " pid ", fmtpid, " count ", fmtnum, fmtlocalmaxconn) ; +} + +static void log_close (uint32 pid, uint32 ip, int w) +{ + char fmtpid[UINT32_FMT] ; + char fmtip[IP4_FMT] = "?" ; + char fmtw[UINT_FMT] ; + fmtpid[uint32_fmt(fmtpid, pid)] = 0 ; + fmtip[ip4_fmtu32(fmtip, ip)] = 0 ; + fmtw[uint_fmt(fmtw, WIFSIGNALED(w) ? WTERMSIG(w) : WEXITSTATUS(w))] = 0 ; + strerr_warni6x("end pid ", fmtpid, " ip ", fmtip, WIFSIGNALED(w) ? " signal " : " exitcode ", fmtw) ; +} + + + /* Signal handling */ + +static void killthem (int sig) +{ + register unsigned int i = 0 ; + for (; i < numconn ; i++) kill(pidip[i].left, sig) ; +} + +static void wait_children (void) +{ + for (;;) + { + unsigned int i ; + int w ; + register int pid = wait_nohang(&w) ; + if (pid < 0) + if (errno != ECHILD) strerr_diefu1sys(111, "wait_nohang") ; + else break ; + else if (!pid) break ; + i = lookup_pid(pid) ; + if (i < numconn) /* it's one of ours ! */ + { + uint32 ip = pidip[i].right ; + register unsigned int j = lookup_ip(ip) ; + if (j >= iplen) X() ; + if (!--ipnum[j].right) ipnum[j] = ipnum[--iplen] ; + pidip[i] = pidip[--numconn] ; + if (verbosity >= 2) + { + log_close(pid, ip, w) ; + log_status() ; + } + } + } +} + +static void handle_signals (void) +{ + for (;;) switch (selfpipe_read()) + { + case -1 : strerr_diefu1sys(111, "read selfpipe") ; + case 0 : return ; + case SIGCHLD : wait_children() ; break ; + case SIGTERM : + { + if (verbosity >= 2) + strerr_warni3x("received ", "SIGTERM,", " quitting") ; + cont = 0 ; + break ; + } + case SIGHUP : + { + if (verbosity >= 2) + strerr_warni5x("received ", "SIGHUP,", " sending ", "SIGTERM+SIGCONT", " to all connections") ; + killthem(SIGTERM) ; + killthem(SIGCONT) ; + break ; + } + case SIGQUIT : + { + if (verbosity >= 2) + strerr_warni6x("received ", "SIGQUIT,", " sending ", "SIGTERM+SIGCONT", " to all connections", " and quitting") ; + cont = 0 ; + killthem(SIGTERM) ; + killthem(SIGCONT) ; + break ; + } + case SIGABRT : + { + if (verbosity >= 2) + strerr_warni6x("received ", "SIGABRT,", " sending ", "SIGKILL", " to all connections", " and quitting") ; + cont = 0 ; + killthem(SIGKILL) ; + break ; + } + default : X() ; + } +} + + + /* New connection handling */ + +static void run_child (int, uint32, uint16, unsigned int, char const *const *, char const *const *) gccattr_noreturn ; +static void run_child (int s, uint32 ip, uint16 port, unsigned int num, char const *const *argv, char const *const *envp) +{ + char fmt[74] ; + unsigned int n = 0 ; + PROG = "s6-tcpserver (child)" ; + if ((fd_move(0, s) < 0) || (fd_copy(1, 0) < 0)) + strerr_diefu1sys(111, "move fds") ; + byte_copy(fmt+n, 22, "PROTO=TCP\0TCPREMOTEIP=") ; n += 22 ; + n += ip4_fmtu32(fmt+n, ip) ; fmt[n++] = 0 ; + byte_copy(fmt+n, 14, "TCPREMOTEPORT=") ; n += 14 ; + n += uint16_fmt(fmt+n, port) ; fmt[n++] = 0 ; + byte_copy(fmt+n, 11, "TCPCONNNUM=") ; n += 11 ; + n += uint_fmt(fmt+n, num) ; fmt[n++] = 0 ; + pathexec_r(argv, envp, env_len(envp), fmt, n) ; + strerr_dieexec(111, argv[0]) ; +} + +static void new_connection (int s, uint32 ip, uint16 port, char const *const *argv, char const *const *envp) +{ + unsigned int i = lookup_ip(ip) ; + unsigned int num = (i < iplen) ? ipnum[i].right : 0 ; + register int pid ; + if (num >= localmaxconn) + { + log_deny(ip, port, num) ; + return ; + } + pid = fork() ; + if (pid < 0) + { + if (verbosity) strerr_warnwu1sys("fork") ; + return ; + } + else if (!pid) + { + selfpipe_finish() ; + run_child(s, ip, port, num+1, argv, envp) ; + } + + if (i < iplen) ipnum[i].right = num + 1 ; + else + { + ipnum[iplen].left = ip ; + ipnum[iplen++].right = 1 ; + } + pidip[numconn].left = (uint32)pid ; + pidip[numconn++].right = ip ; + if (verbosity >= 2) + { + log_accept((uint32)pid, ip, port, ipnum[i].right) ; + log_status() ; + } +} + + + /* And the main */ + +int main (int argc, char const *const *argv, char const *const *envp) +{ + iopause_fd x[2] = { { -1, IOPAUSE_READ, 0 }, { -1, IOPAUSE_READ | IOPAUSE_EXCEPT, 0 } } ; + PROG = "s6-tcpserver4" ; + { + subgetopt_t l = SUBGETOPT_ZERO ; + unsigned int uid = 0, gid = 0 ; + gid_t gids[NGROUPS_MAX] ; + unsigned int gidn = 0 ; + unsigned int backlog = 20 ; + char ip[4] ; + int flag1 = 0 ; + uint16 port ; + for (;;) + { + register int opt = subgetopt_r(argc, argv, "1Uc:C:b:u:g:G:v:", &l) ; + if (opt == -1) break ; + switch (opt) + { + case '1' : flag1 = 1 ; break ; + case 'c' : if (!uint0_scan(l.arg, &maxconn)) dieusage() ; break ; + case 'C' : if (!uint0_scan(l.arg, &localmaxconn)) dieusage() ; break ; + case 'b' : if (!uint0_scan(l.arg, &backlog)) dieusage() ; break ; + case 'u' : if (!uint0_scan(l.arg, &uid)) dieusage() ; break ; + case 'g' : if (!uint0_scan(l.arg, &gid)) dieusage() ; break ; + case 'G' : if (!gid_scanlist(gids, NGROUPS_MAX, l.arg, &gidn)) dieusage() ; break ; + case 'v' : if (!uint0_scan(l.arg, &verbosity)) dieusage() ; break ; + case 'U' : + { + char const *x = env_get2(envp, "UID") ; + if (!x) strerr_dienotset(100, "UID") ; + if (!uint0_scan(x, &uid)) strerr_dieinvalid(100, "UID") ; + x = env_get2(envp, "GID") ; + if (!x) strerr_dienotset(100, "GID") ; + if (!uint0_scan(x, &gid)) strerr_dieinvalid(100, "GID") ; + x = env_get2(envp, "GIDLIST") ; + if (!x) strerr_dienotset(100, "GIDLIST") ; + if (!gid_scanlist(gids, NGROUPS_MAX, x, &gidn) && *x) + strerr_dieinvalid(100, "GIDLIST") ; + break ; + } + default : dieusage() ; + } + } + argc -= l.ind ; argv += l.ind ; + if (argc < 3) dieusage() ; + if (!ip4_scan(argv[0], ip) || !uint160_scan(argv[1], &port)) dieusage() ; + fd_close(0) ; + if (!flag1) fd_close(1) ; + if (!maxconn) maxconn = 1 ; + if (maxconn > ABSOLUTE_MAXCONN) maxconn = ABSOLUTE_MAXCONN ; + if (localmaxconn > maxconn) localmaxconn = maxconn ; + x[1].fd = socket_tcp4() ; + if ((x[1].fd == -1) || (coe(x[1].fd) == -1)) + strerr_diefu1sys(111, "create socket") ; + if (socket_bind4_reuse(x[1].fd, ip, port) < 0) + strerr_diefu2sys(111, "bind to ", argv[0]) ; + if (socket_listen(x[1].fd, backlog) == -1) + strerr_diefu1sys(111, "listen") ; + if (gidn && (setgroups(gidn, gids) < 0)) strerr_diefu1sys(111, "setgroups") ; + if (gid && (setgid(gid) < 0)) strerr_diefu1sys(111, "drop gid") ; + if (uid && (setuid(uid) < 0)) strerr_diefu1sys(111, "drop uid") ; + + x[0].fd = selfpipe_init() ; + if (x[0].fd == -1) strerr_diefu1sys(111, "create selfpipe") ; + if (sig_ignore(SIGPIPE) < 0) strerr_diefu1sys(111, "ignore SIGPIPE") ; + { + sigset_t set ; + sigemptyset(&set) ; + sigaddset(&set, SIGCHLD) ; + sigaddset(&set, SIGTERM) ; + sigaddset(&set, SIGHUP) ; + sigaddset(&set, SIGQUIT) ; + sigaddset(&set, SIGABRT) ; + if (selfpipe_trapset(&set) < 0) strerr_diefu1sys(111, "trap signals") ; + } + if (flag1) + { + char fmt[UINT16_FMT] ; + unsigned int n = uint16_fmt(fmt, port) ; + fmt[n++] = '\n' ; + if (allwrite(1, fmt, n) < n) strerr_diefu1sys(111, "write to stdout") ; + fd_close(1) ; + } + fmtlocalmaxconn[1+uint_fmt(fmtlocalmaxconn+1, localmaxconn)] = 0 ; + if (verbosity >= 2) + { + fmtmaxconn[1+uint_fmt(fmtmaxconn+1, maxconn)] = 0 ; + log_start(ip, port) ; + log_status() ; + } + } + + { + diuint32 inyostack[maxconn<<1] ; + pidip = inyostack ; ipnum = inyostack + maxconn ; + while (cont) + { + if (iopause(x, 1 + (numconn < maxconn), 0, 0) == -1) + strerr_diefu1sys(111, "iopause") ; + + if (x[0].revents & IOPAUSE_EXCEPT) strerr_dief1x(111, "trouble with selfpipe") ; + if (x[0].revents & IOPAUSE_READ) handle_signals() ; + if (numconn < maxconn) + { + if (x[1].revents & IOPAUSE_EXCEPT) strerr_dief1x(111, "trouble with socket") ; + if (x[1].revents & IOPAUSE_READ) + { + char packedip[4] ; + uint16 port ; + register int fd = socket_accept4(x[1].fd, packedip, &port) ; + if (fd < 0) + { + if (verbosity) strerr_warnwu1sys("accept") ; + } + else + { + uint32 ip ; + uint32_unpack_big(packedip, &ip) ; + new_connection(fd, ip, port, argv+2, envp) ; + fd_close(fd) ; + } + } + } + } + } + if (verbosity >= 2) log_exit() ; + return 0 ; +} diff --git a/src/conn-tools/s6-tcpserver6.c b/src/conn-tools/s6-tcpserver6.c new file mode 100644 index 0000000..9b27f42 --- /dev/null +++ b/src/conn-tools/s6-tcpserver6.c @@ -0,0 +1,412 @@ +/* ISC license. */ + +#ifndef _BSD_SOURCE +#define _BSD_SOURCE +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ABSOLUTE_MAXCONN 1000 + +#define USAGE "s6-tcpserver6 [ -v verbosity ] [ -1 ] [ -c maxconn ] [ -C localmaxconn ] [ -b backlog ] [ -G gid,gid,... ] [ -g gid ] [ -u uid ] [ -U ] ip6 port prog..." + +typedef struct ipnum_s ipnum_t, *ipnum_t_ref ; +struct ipnum_s +{ + char ip[16] ; + unsigned int num ; +} ; +#define IPNUM_ZERO { "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 0 } + +static unsigned int maxconn = 40 ; +static unsigned int localmaxconn = 40 ; +static unsigned int verbosity = 1 ; +static int cont = 1 ; +static ipnum_t_ref pidip = 0 ; +static unsigned int numconn = 0 ; +static ipnum_t_ref ipnum = 0 ; +static unsigned int iplen = 0 ; + +static char fmtmaxconn[UINT_FMT+1] = "/" ; +static char fmtlocalmaxconn[UINT_FMT+1] = "/" ; + + + /* Utility functions */ + +static inline void dieusage () +{ + strerr_dieusage(100, USAGE) ; +} + +static inline void X (void) +{ + strerr_dief1x(101, "internal inconsistency. Please submit a bug-report.") ; +} + + + /* Lookup primitives */ + +static unsigned int lookup_pid (unsigned int pid) +{ + register unsigned int i = 0 ; + for (; i < numconn ; i++) if (pid == pidip[i].num) break ; + return i ; +} + +static unsigned int lookup_ip (char const *ip) +{ + register unsigned int i = 0 ; + for (; i < iplen ; i++) if (!byte_diff(ip, 16, ipnum[i].ip)) break ; + return i ; +} + + + /* Logging */ + +static void log_start (char const *ip, uint16 port) +{ + char fmtip[IP6_FMT] ; + char fmtport[UINT16_FMT] ; + fmtip[ip6_fmt(fmtip, ip)] = 0 ; + fmtport[uint16_fmt(fmtport, port)] = 0 ; + strerr_warni4x("starting - listening on ", fmtip, " port ", fmtport) ; +} + +static inline void log_exit (void) +{ + strerr_warni1x("exiting") ; +} + +static void log_status (void) +{ + char fmt[UINT_FMT] ; + fmt[uint_fmt(fmt, numconn)] = 0 ; + strerr_warni3x("status: ", fmt, fmtmaxconn) ; +} + +static void log_deny (char const *ip, uint16 port, unsigned int num) +{ + char fmtip[IP6_FMT] ; + char fmtport[UINT16_FMT] ; + char fmtnum[UINT_FMT] ; + fmtip[ip6_fmt(fmtip, ip)] = 0 ; + fmtport[uint16_fmt(fmtport, port)] = 0 ; + fmtnum[uint_fmt(fmtnum, num)] = 0 ; + strerr_warni7sys("deny ", fmtip, " port ", fmtport, " count ", fmtnum, fmtlocalmaxconn) ; +} + +static void log_accept (unsigned int pid, char const *ip, uint16 port, unsigned int num) +{ + char fmtipport[IP6_FMT + UINT16_FMT + 6] ; + char fmtpid[UINT_FMT] ; + char fmtnum[UINT_FMT] ; + register unsigned int n ; + n = ip6_fmt(fmtipport, ip) ; + byte_copy(fmtipport + n, 6, " port ") ; n += 6 ; + n += uint16_fmt(fmtipport + n, port) ; + fmtipport[n] = 0 ; + fmtnum[uint_fmt(fmtnum, num)] = 0 ; + fmtpid[uint_fmt(fmtpid, pid)] = 0 ; + strerr_warni7x("allow ", fmtipport, " pid ", fmtpid, " count ", fmtnum, fmtlocalmaxconn) ; +} + +static void log_close (unsigned int pid, char const *ip, int w) +{ + char fmtpid[UINT_FMT] ; + char fmtip[IP6_FMT] = "?" ; + char fmtw[UINT_FMT] ; + fmtpid[uint_fmt(fmtpid, pid)] = 0 ; + fmtip[ip6_fmt(fmtip, ip)] = 0 ; + fmtw[uint_fmt(fmtw, WIFSIGNALED(w) ? WTERMSIG(w) : WEXITSTATUS(w))] = 0 ; + strerr_warni6x("end pid ", fmtpid, " ip ", fmtip, WIFSIGNALED(w) ? " signal " : " exitcode ", fmtw) ; +} + + + /* Signal handling */ + +static void killthem (int sig) +{ + register unsigned int i = 0 ; + for (; i < numconn ; i++) kill(pidip[i].num, sig) ; +} + +static void wait_children (void) +{ + for (;;) + { + unsigned int i ; + int w ; + register int pid = wait_nohang(&w) ; + if (pid < 0) + if (errno != ECHILD) strerr_diefu1sys(111, "wait_nohang") ; + else break ; + else if (!pid) break ; + i = lookup_pid(pid) ; + if (i < numconn) /* it's one of ours ! */ + { + register unsigned int j = lookup_ip(pidip[i].ip) ; + if (j >= iplen) X() ; + if (!--ipnum[j].num) ipnum[j] = ipnum[--iplen] ; + --numconn ; + if (verbosity >= 2) + { + log_close(pid, pidip[i].ip, w) ; + log_status() ; + } + pidip[i] = pidip[numconn] ; + } + } +} + +static void handle_signals (void) +{ + for (;;) switch (selfpipe_read()) + { + case -1 : strerr_diefu1sys(111, "read selfpipe") ; + case 0 : return ; + case SIGCHLD : wait_children() ; break ; + case SIGTERM : + { + if (verbosity >= 2) + strerr_warni3x("received ", "SIGTERM,", " quitting") ; + cont = 0 ; + break ; + } + case SIGHUP : + { + if (verbosity >= 2) + strerr_warni5x("received ", "SIGHUP,", " sending ", "SIGTERM+SIGCONT", " to all connections") ; + killthem(SIGTERM) ; + killthem(SIGCONT) ; + break ; + } + case SIGQUIT : + { + if (verbosity >= 2) + strerr_warni6x("received ", "SIGQUIT,", " sending ", "SIGTERM+SIGCONT", " to all connections", " and quitting") ; + cont = 0 ; + killthem(SIGTERM) ; + killthem(SIGCONT) ; + break ; + } + case SIGABRT : + { + if (verbosity >= 2) + strerr_warni6x("received ", "SIGABRT,", " sending ", "SIGKILL", " to all connections", " and quitting") ; + cont = 0 ; + killthem(SIGKILL) ; + break ; + } + default : X() ; + } +} + + + /* New connection handling */ + +static void run_child (int, char const *, uint16, unsigned int, char const *const *, char const *const *) gccattr_noreturn ; +static void run_child (int s, char const *ip, uint16 port, unsigned int num, char const *const *argv, char const *const *envp) +{ + char fmt[98] ; + unsigned int n = 0 ; + PROG = "s6-tcpserver6 (child)" ; + if ((fd_move(0, s) < 0) || (fd_copy(1, 0) < 0)) + strerr_diefu1sys(111, "move fds") ; + byte_copy(fmt+n, 24, "PROTO=TCP\0TCPREMOTEIP=") ; n += 22 ; + n += ip6_fmt(fmt+n, ip) ; fmt[n++] = 0 ; + byte_copy(fmt+n, 14, "TCPREMOTEPORT=") ; n += 14 ; + n += uint16_fmt(fmt+n, port) ; fmt[n++] = 0 ; + byte_copy(fmt+n, 11, "TCPCONNNUM=") ; n += 11 ; + n += uint_fmt(fmt+n, num) ; fmt[n++] = 0 ; + pathexec_r(argv, envp, env_len(envp), fmt, n) ; + strerr_dieexec(111, argv[0]) ; +} + +static void new_connection (int s, char const *ip, uint16 port, char const *const *argv, char const *const *envp) +{ + unsigned int i = lookup_ip(ip) ; + unsigned int num = (i < iplen) ? ipnum[i].num : 0 ; + register int pid ; + if (num >= localmaxconn) + { + log_deny(ip, port, num) ; + return ; + } + pid = fork() ; + if (pid < 0) + { + if (verbosity) strerr_warnwu1sys("fork") ; + return ; + } + else if (!pid) + { + selfpipe_finish() ; + run_child(s, ip, port, num+1, argv, envp) ; + } + + if (i < iplen) ipnum[i].num = num + 1 ; + else + { + byte_copy(ipnum[iplen].ip, 16, ip) ; + ipnum[iplen++].num = 1 ; + } + pidip[numconn].num = pid ; + byte_copy(pidip[numconn++].ip, 16, ip) ; + if (verbosity >= 2) + { + log_accept(pid, ip, port, ipnum[i].num) ; + log_status() ; + } +} + + +int main (int argc, char const *const *argv, char const *const *envp) +{ + iopause_fd x[2] = { { -1, IOPAUSE_READ, 0 }, { -1, IOPAUSE_READ | IOPAUSE_EXCEPT, 0 } } ; + PROG = "s6-tcpserver6" ; + { + subgetopt_t l = SUBGETOPT_ZERO ; + unsigned int uid = 0, gid = 0 ; + gid_t gids[NGROUPS_MAX] ; + unsigned int gidn = 0 ; + unsigned int backlog = 20 ; + char ip[16] ; + int flag1 = 0 ; + uint16 port ; + for (;;) + { + register int opt = subgetopt_r(argc, argv, "1Uc:C:b:u:g:G:v:", &l) ; + if (opt == -1) break ; + switch (opt) + { + case '1' : flag1 = 1 ; break ; + case 'c' : if (!uint0_scan(l.arg, &maxconn)) dieusage() ; break ; + case 'C' : if (!uint0_scan(l.arg, &localmaxconn)) dieusage() ; break ; + case 'b' : if (!uint0_scan(l.arg, &backlog)) dieusage() ; break ; + case 'u' : if (!uint0_scan(l.arg, &uid)) dieusage() ; break ; + case 'g' : if (!uint0_scan(l.arg, &gid)) dieusage() ; break ; + case 'G' : if (!gid_scanlist(gids, NGROUPS_MAX, l.arg, &gidn)) dieusage() ; break ; + case 'v' : if (!uint0_scan(l.arg, &verbosity)) dieusage() ; break ; + case 'U' : + { + char const *x = env_get2(envp, "UID") ; + if (!x) strerr_dienotset(100, "UID") ; + if (!uint0_scan(x, &uid)) strerr_dieinvalid(100, "UID") ; + x = env_get2(envp, "GID") ; + if (!x) strerr_dienotset(100, "GID") ; + if (!uint0_scan(x, &gid)) strerr_dieinvalid(100, "GID") ; + x = env_get2(envp, "GIDLIST") ; + if (!x) strerr_dienotset(100, "GIDLIST") ; + if (!gid_scanlist(gids, NGROUPS_MAX, x, &gidn) && *x) + strerr_dieinvalid(100, "GIDLIST") ; + break ; + } + default : dieusage() ; + } + } + argc -= l.ind ; argv += l.ind ; + if (argc < 3) dieusage() ; + if (!ip6_scan(argv[0], ip) || !uint160_scan(argv[1], &port)) dieusage() ; + fd_close(0) ; + if (!flag1) fd_close(1) ; + if (!maxconn) maxconn = 1 ; + if (maxconn > ABSOLUTE_MAXCONN) maxconn = ABSOLUTE_MAXCONN ; + if (localmaxconn > maxconn) localmaxconn = maxconn ; + x[1].fd = socket_tcp6() ; + if ((x[1].fd == -1) || (coe(x[1].fd) == -1)) + strerr_diefu1sys(111, "create socket") ; + if (socket_bind6_reuse(x[1].fd, ip, port) < 0) + strerr_diefu2sys(111, "bind to ", argv[0]) ; + if (socket_listen(x[1].fd, backlog) == -1) + strerr_diefu1sys(111, "listen") ; + if (gidn && (setgroups(gidn, gids) < 0)) strerr_diefu1sys(111, "setgroups") ; + if (gid && (setgid(gid) < 0)) strerr_diefu1sys(111, "drop gid") ; + if (uid && (setuid(uid) < 0)) strerr_diefu1sys(111, "drop uid") ; + + x[0].fd = selfpipe_init() ; + if (x[0].fd == -1) strerr_diefu1sys(111, "create selfpipe") ; + if (sig_ignore(SIGPIPE) < 0) strerr_diefu1sys(111, "ignore SIGPIPE") ; + { + sigset_t set ; + sigemptyset(&set) ; + sigaddset(&set, SIGCHLD) ; + sigaddset(&set, SIGTERM) ; + sigaddset(&set, SIGHUP) ; + sigaddset(&set, SIGQUIT) ; + sigaddset(&set, SIGABRT) ; + if (selfpipe_trapset(&set) < 0) strerr_diefu1sys(111, "trap signals") ; + } + if (flag1) + { + char fmt[UINT16_FMT] ; + unsigned int n = uint16_fmt(fmt, port) ; + fmt[n++] = '\n' ; + if (allwrite(1, fmt, n) < n) strerr_diefu1sys(111, "write to stdout") ; + fd_close(1) ; + } + fmtlocalmaxconn[1+uint_fmt(fmtlocalmaxconn+1, localmaxconn)] = 0 ; + if (verbosity >= 2) + { + fmtmaxconn[1+uint_fmt(fmtmaxconn+1, maxconn)] = 0 ; + log_start(ip, port) ; + log_status() ; + } + } + + { + ipnum_t inyostack[maxconn<<1] ; + pidip = inyostack ; ipnum = inyostack + maxconn ; + while (cont) + { + if (iopause(x, 1 + (numconn < maxconn), 0, 0) == -1) + strerr_diefu1sys(111, "iopause") ; + + if (x[0].revents & IOPAUSE_EXCEPT) strerr_dief1x(111, "trouble with selfpipe") ; + if (x[0].revents & IOPAUSE_READ) handle_signals() ; + if (numconn < maxconn) + { + if (x[1].revents & IOPAUSE_EXCEPT) strerr_dief1x(111, "trouble with socket") ; + if (x[1].revents & IOPAUSE_READ) + { + char ip[16] ; + uint16 port ; + register int fd = socket_accept6(x[1].fd, ip, &port) ; + if (fd < 0) + { + if (verbosity) strerr_warnwu1sys("accept") ; + } + else + { + new_connection(fd, ip, port, argv+2, envp) ; + fd_close(fd) ; + } + } + } + } + } + if (verbosity >= 2) log_exit() ; + return 0 ; +} diff --git a/src/conn-tools/seekablepipe.c b/src/conn-tools/seekablepipe.c new file mode 100644 index 0000000..611f227 --- /dev/null +++ b/src/conn-tools/seekablepipe.c @@ -0,0 +1,41 @@ +/* ISC license. */ + +#include +#include +#include +#include + +#define USAGE "seekablepipe tempfile prog..." + +#define N 8192 + +int main (int argc, char const *const *argv, char const *const *envp) +{ + iobuffer b ; + int fdr, fdw ; + int r ; + PROG = "seekablepipe" ; + if (argc < 3) strerr_dieusage(100, USAGE) ; + fdw = open_trunc(argv[1]) ; + if (fdw < 0) + strerr_diefu2sys(111, "create temporary ", argv[1]) ; + fdr = open_readb(argv[1]) ; + if (fdr < 0) + strerr_diefu3sys(111, "open ", argv[1], " for reading") ; + if (unlink(argv[1]) < 0) + strerr_diefu2sys(111, "unlink ", argv[1]) ; + if (ndelay_off(fdw) < 0) + strerr_diefu1sys(111, "set fdw blocking") ; + if (!iobuffer_init(&b, 0, fdw)) + strerr_diefu1sys(111, "iobuffer_init") ; + while ((r = iobuffer_fill(&b)) > 0) + if (!iobuffer_flush(&b)) + strerr_diefu2sys(111, "write to ", argv[1]) ; + if (r < 0) strerr_diefu1sys(111, "read from stdin") ; + iobuffer_finish(&b) ; + fd_close(fdw) ; + if (fd_move(0, fdr) < 0) + strerr_diefu1sys(111, "move fdr to stdin") ; + pathexec_run(argv[2], argv+2, envp) ; + strerr_dieexec(111, argv[2]) ; +} diff --git a/src/include/s6-networking/accessrules.h b/src/include/s6-networking/accessrules.h new file mode 100644 index 0000000..ec7a0d5 --- /dev/null +++ b/src/include/s6-networking/accessrules.h @@ -0,0 +1,53 @@ +/* ISC license. */ + +#ifndef S6NET_ACCESSRULES_H +#define S6NET_ACCESSRULES_H + +#include +#include +#include + +typedef struct s6net_accessrules_params_s s6net_accessrules_params_t, *s6net_accessrules_params_t_ref ; +struct s6net_accessrules_params_s +{ + stralloc env ; + stralloc exec ; +} ; +#define S6NET_ACCESSRULES_PARAMS_ZERO { STRALLOC_ZERO, STRALLOC_ZERO } + +typedef enum s6net_accessrules_result_e s6net_accessrules_result_t, *s6net_accessrules_result_t_ref ; +enum s6net_accessrules_result_e +{ + S6NET_ACCESSRULES_ERROR = -1, + S6NET_ACCESSRULES_DENY = 0, + S6NET_ACCESSRULES_ALLOW = 1, + S6NET_ACCESSRULES_NOTFOUND = 2 +} ; + +typedef s6net_accessrules_result_t s6net_accessrules_backend_func_t (char const *, unsigned int, void *, s6net_accessrules_params_t *) ; +typedef s6net_accessrules_backend_func_t *s6net_accessrules_backend_func_t_ref ; + +extern s6net_accessrules_backend_func_t s6net_accessrules_backend_fs ; +extern s6net_accessrules_backend_func_t s6net_accessrules_backend_cdb ; + +typedef s6net_accessrules_result_t s6net_accessrules_keycheck_func_t (void const *, void *, s6net_accessrules_params_t *, s6net_accessrules_backend_func_t_ref) ; +typedef s6net_accessrules_keycheck_func_t *s6net_accessrules_keycheck_func_t_ref ; + +extern s6net_accessrules_keycheck_func_t s6net_accessrules_keycheck_uidgid ; +extern s6net_accessrules_keycheck_func_t s6net_accessrules_keycheck_ip4 ; +extern s6net_accessrules_keycheck_func_t s6net_accessrules_keycheck_ip6 ; +extern s6net_accessrules_keycheck_func_t s6net_accessrules_keycheck_reversedns ; +#define s6net_accessrules_keycheck_ip46(key, data, params, f) (ip46_is6((ip46_t const *)(key)) ? s6net_accessrules_keycheck_ip6(((ip46_t const *)(key))->ip, data, params, f) : s6net_accessrules_keycheck_ip4(((ip46_t const *)(key))->ip, data, params, f)) + +extern s6net_accessrules_result_t s6net_accessrules_uidgid_cdb (unsigned int, unsigned int, struct cdb *, s6net_accessrules_params_t *) ; +extern s6net_accessrules_result_t s6net_accessrules_uidgid_fs (unsigned int, unsigned int, char const *, s6net_accessrules_params_t *) ; +#define s6net_accessrules_ip4_cdb(ip4, c, params) s6net_accessrules_keycheck_ip4(ip4, c, (params), &s6net_accessrules_backend_cdb) +#define s6net_accessrules_ip4_fs(ip4, rulesdir, params) s6net_accessrules_keycheck_ip4(ip4, rulesdir, (params), &s6net_accessrules_backend_fs) +#define s6net_accessrules_ip6_cdb(ip6, c, params) s6net_accessrules_keycheck_ip6(ip6, c, (params), &s6net_accessrules_backend_cdb) +#define s6net_accessrules_ip6_fs(ip6, rulesdir, params) s6net_accessrules_keycheck_ip6(ip6, rulesdir, (params), &s6net_accessrules_backend_fs) +#define s6net_accessrules_ip46_cdb(ip, c, params) s6net_accessrules_keycheck_ip46(ip, c, (params), &s6net_accessrules_backend_cdb) +#define s6net_accessrules_ip46_fs(ip, rulesdir, params) s6net_accessrules_keycheck_ip46(ip, rulesdir, (params), &s6net_accessrules_backend_fs) +#define s6net_accessrules_reversedns_cdb(name, c, params) s6net_accessrules_keycheck_reversedns(name, c, (params), &s6net_accessrules_backend_cdb) +#define s6net_accessrules_reversedns_fs(name, c, params) s6net_accessrules_keycheck_reversedns(name, c, (params), &s6net_accessrules_backend_fs) + +#endif diff --git a/src/include/s6-networking/ident.h b/src/include/s6-networking/ident.h new file mode 100644 index 0000000..723fc89 --- /dev/null +++ b/src/include/s6-networking/ident.h @@ -0,0 +1,26 @@ +/* ISC license. */ + +#ifndef IDENT1413_H +#define IDENT1413_H + +#include +#include +#include + +#define S6NET_IDENT_ID_SIZE 512 +#define S6NET_IDENT_REPLY_SIZE 1024 + + /* High-level */ + +extern int s6net_ident_client (char *, unsigned int, ip46_t const *, uint16, ip46_t const *, uint16, tain_t const *, tain_t *) ; +#define s6net_ident_client_g(s, max, ra, rp, la, lp, deadline) s6net_ident_client(s, max, ra, rp, la, lp, (deadline), &STAMP) +extern char const *s6net_ident_error_str (int) ; + + + /* Low-level */ + +extern int s6net_ident_reply_get (char *, ip46_t const *, uint16, ip46_t const *, uint16, tain_t const *, tain_t *) ; +#define s6net_ident_reply_get_g(s, ra, rp, la, lp, deadline) s6net_ident_reply_get(s, ra, rp, la, lp, (deadline), &STAMP) +extern int s6net_ident_reply_parse (char const *, uint16, uint16) ; + +#endif diff --git a/src/include/s6-networking/s6net.h b/src/include/s6-networking/s6net.h new file mode 100644 index 0000000..81d804e --- /dev/null +++ b/src/include/s6-networking/s6net.h @@ -0,0 +1,9 @@ +/* ISC license. */ + +#ifndef S6NET_H +#define S6NET_H + +#include +#include + +#endif diff --git a/src/libs6net/deps-lib/s6net b/src/libs6net/deps-lib/s6net new file mode 100644 index 0000000..7b497ac --- /dev/null +++ b/src/libs6net/deps-lib/s6net @@ -0,0 +1,12 @@ +s6net_accessrules_backend_cdb.o +s6net_accessrules_backend_fs.o +s6net_accessrules_keycheck_ip4.o +s6net_accessrules_keycheck_ip6.o +s6net_accessrules_keycheck_reversedns.o +s6net_accessrules_keycheck_uidgid.o +s6net_accessrules_uidgid_cdb.o +s6net_accessrules_uidgid_fs.o +s6net_ident_client.o +s6net_ident_reply_get.o +s6net_ident_reply_parse.o +s6net_ident_error.o diff --git a/src/libs6net/s6net_accessrules_backend_cdb.c b/src/libs6net/s6net_accessrules_backend_cdb.c new file mode 100644 index 0000000..e75f755 --- /dev/null +++ b/src/libs6net/s6net_accessrules_backend_cdb.c @@ -0,0 +1,38 @@ +/* ISC license. */ + +#include +#include +#include +#include +#include +#include +#include + +s6net_accessrules_result_t s6net_accessrules_backend_cdb (char const *key, unsigned int keylen, void *data, s6net_accessrules_params_t *params) +{ + struct cdb *c = data ; + unsigned int execbase, n ; + uint16 envlen, execlen ; + register int r = cdb_find(c, key, keylen) ; + if (r < 0) return S6NET_ACCESSRULES_ERROR ; + else if (!r) return S6NET_ACCESSRULES_NOTFOUND ; + n = cdb_datalen(c) ; + if ((n < 5U) || (n > 8197U)) return (errno = EINVAL, S6NET_ACCESSRULES_ERROR) ; + if (!stralloc_readyplus(¶ms->exec, n)) return S6NET_ACCESSRULES_ERROR ; + execbase = params->exec.len ; + if (cdb_read(c, params->exec.s + execbase, n, cdb_datapos(c)) < 0) return S6NET_ACCESSRULES_ERROR ; + if (params->exec.s[execbase] == 'D') return S6NET_ACCESSRULES_DENY ; + else if (params->exec.s[execbase] != 'A') return S6NET_ACCESSRULES_NOTFOUND ; + uint16_unpack_big(params->exec.s + execbase + 1U, &envlen) ; + if ((envlen > 4096U) || (envlen+5U > n)) return (errno = EINVAL, S6NET_ACCESSRULES_ERROR) ; + uint16_unpack_big(params->exec.s + execbase + 3 + envlen, &execlen) ; + if ((execlen > 4096U) || (5U + envlen + execlen != n)) return (errno = EINVAL, S6NET_ACCESSRULES_ERROR) ; + if (!stralloc_catb(¶ms->env, params->exec.s + execbase + 3U, envlen)) return S6NET_ACCESSRULES_ERROR ; + byte_copy(params->exec.s + execbase, execlen, params->exec.s + execbase + 5U + envlen) ; + if (execlen) + { + params->exec.len += execlen ; + params->exec.s[params->exec.len++] = 0 ; + } + return S6NET_ACCESSRULES_ALLOW ; +} diff --git a/src/libs6net/s6net_accessrules_backend_fs.c b/src/libs6net/s6net_accessrules_backend_fs.c new file mode 100644 index 0000000..d609285 --- /dev/null +++ b/src/libs6net/s6net_accessrules_backend_fs.c @@ -0,0 +1,58 @@ +/* ISC license. */ + +#include +#include +#include +#include +#include +#include +#include + +s6net_accessrules_result_t s6net_accessrules_backend_fs (char const *key, unsigned int keylen, void *data, s6net_accessrules_params_t *params) +{ + char *dir = data ; + unsigned int dirlen = str_len(dir) ; + unsigned int envbase = params->env.len ; + int wasnull = !params->env.s ; + { + char tmp[dirlen + keylen + 10] ; + byte_copy(tmp, dirlen, dir) ; + tmp[dirlen] = '/' ; + byte_copy(tmp + dirlen + 1, keylen, key) ; + byte_copy(tmp + dirlen + keylen + 1, 7, "/allow") ; + if (access(tmp, R_OK) < 0) + { + if ((errno != EACCES) && (errno != ENOENT)) + return S6NET_ACCESSRULES_ERROR ; + byte_copy(tmp + dirlen + keylen + 2, 5, "deny") ; + return (access(tmp, R_OK) == 0) ? S6NET_ACCESSRULES_DENY : + (errno != EACCES) && (errno != ENOENT) ? S6NET_ACCESSRULES_ERROR : + S6NET_ACCESSRULES_NOTFOUND ; + } + byte_copy(tmp + dirlen + keylen + 2, 4, "env") ; + if ((envdir(tmp, ¶ms->env) < 0) && (errno != ENOENT)) + return S6NET_ACCESSRULES_ERROR ; + if (!stralloc_readyplus(¶ms->exec, 4097)) + { + if (wasnull) stralloc_free(¶ms->env) ; + else params->env.len = envbase ; + return S6NET_ACCESSRULES_ERROR ; + } + byte_copy(tmp + dirlen + keylen + 2, 5, "exec") ; + { + register int r = openreadnclose(tmp, params->exec.s + params->exec.len, 4096) ; + if ((r < 0) && (errno != EACCES) && (errno != ENOENT)) + { + if (wasnull) stralloc_free(¶ms->env) ; + else params->env.len = envbase ; + return S6NET_ACCESSRULES_ERROR ; + } + if (r > 0) + { + params->exec.len += r ; + params->exec.s[params->exec.len++] = 0 ; + } + } + } + return S6NET_ACCESSRULES_ALLOW ; +} diff --git a/src/libs6net/s6net_accessrules_keycheck_ip4.c b/src/libs6net/s6net_accessrules_keycheck_ip4.c new file mode 100644 index 0000000..1f96bd8 --- /dev/null +++ b/src/libs6net/s6net_accessrules_keycheck_ip4.c @@ -0,0 +1,24 @@ +/* ISC license. */ + +#include +#include +#include +#include + +s6net_accessrules_result_t s6net_accessrules_keycheck_ip4 (void const *key, void *data, s6net_accessrules_params_t *params, s6net_accessrules_backend_func_t_ref check1) +{ + char fmt[IP4_FMT + UINT_FMT + 6] = "ip4/" ; + uint32 ip ; + unsigned int i = 0 ; + uint32_unpack_big((char const *)key, &ip) ; + for (; i <= 32 ; i++) + { + register s6net_accessrules_result_t r ; + register unsigned int len = 4 + ip4_fmtu32(fmt+4, (i == 32) ? 0 : ip & ~((1U << i) - 1)) ; + fmt[len++] = '_' ; + len += uint_fmt(fmt + len, 32 - i) ; + r = (*check1)(fmt, len, data, params) ; + if (r != S6NET_ACCESSRULES_NOTFOUND) return r ; + } + return S6NET_ACCESSRULES_NOTFOUND ; +} diff --git a/src/libs6net/s6net_accessrules_keycheck_ip6.c b/src/libs6net/s6net_accessrules_keycheck_ip6.c new file mode 100644 index 0000000..c2ee5ae --- /dev/null +++ b/src/libs6net/s6net_accessrules_keycheck_ip6.c @@ -0,0 +1,27 @@ +/* ISC license. */ + +#include +#include +#include +#include +#include + +s6net_accessrules_result_t s6net_accessrules_keycheck_ip6 (void const *key, void *data, s6net_accessrules_params_t *params, s6net_accessrules_backend_func_t_ref check1) +{ + char fmt[IP6_FMT + UINT_FMT + 6] = "ip6/" ; + char ip6[16] ; + unsigned int i = 0 ; + byte_copy(ip6, 16, (char const *)key) ; + for (; i <= 128 ; i++) + { + unsigned int len ; + register s6net_accessrules_result_t r ; + if (i) bitarray_clear(ip6, 128 - i) ; + len = 4 + ip6_fmt(fmt+4, ip6) ; + fmt[len++] = '_' ; + len += uint_fmt(fmt + len, 128 - i) ; + r = (*check1)(fmt, len, data, params) ; + if (r != S6NET_ACCESSRULES_NOTFOUND) return r ; + } + return S6NET_ACCESSRULES_NOTFOUND ; +} diff --git a/src/libs6net/s6net_accessrules_keycheck_reversedns.c b/src/libs6net/s6net_accessrules_keycheck_reversedns.c new file mode 100644 index 0000000..f4c0213 --- /dev/null +++ b/src/libs6net/s6net_accessrules_keycheck_reversedns.c @@ -0,0 +1,27 @@ +/* ISC license. */ + +#include +#include +#include + +s6net_accessrules_result_t s6net_accessrules_keycheck_reversedns (void const *key, void *data, s6net_accessrules_params_t *params, s6net_accessrules_backend_func_t_ref check1) +{ + char const *name = key ; + unsigned int len = str_len(name) ; + if (!len) return (errno = EINVAL, S6NET_ACCESSRULES_ERROR) ; + if (name[len-1] == '.') len-- ; + { + unsigned int i = 0 ; + char tmp[len + 11] ; + byte_copy(tmp, 11, "reversedns/") ; + while (i < len) + { + register s6net_accessrules_result_t r ; + byte_copy(tmp+11, len-i, name+i) ; + r = (*check1)(tmp, 11+len-i, data, params) ; + if (r != S6NET_ACCESSRULES_NOTFOUND) return r ; + i += byte_chr(name+i, len-i, '.') + 1 ; + } + } + return (*check1)("reversedns/@", 12, data, params) ; +} diff --git a/src/libs6net/s6net_accessrules_keycheck_uidgid.c b/src/libs6net/s6net_accessrules_keycheck_uidgid.c new file mode 100644 index 0000000..a7e2200 --- /dev/null +++ b/src/libs6net/s6net_accessrules_keycheck_uidgid.c @@ -0,0 +1,16 @@ +/* ISC license. */ + +#include +#include +#include + +s6net_accessrules_result_t s6net_accessrules_keycheck_uidgid (void const *key, void *data, s6net_accessrules_params_t *params, s6net_accessrules_backend_func_t_ref check1) +{ + char fmt[4 + UINT_FMT] = "uid/" ; + register s6net_accessrules_result_t r = (*check1)(fmt, 4 + uint_fmt(fmt+4, ((diuint const *)key)->left), data, params) ; + if (r != S6NET_ACCESSRULES_NOTFOUND) return r ; + fmt[0] = 'g' ; + r = (*check1)(fmt, 4 + uint_fmt(fmt+4, ((diuint const *)key)->right), data, params) ; + return (r != S6NET_ACCESSRULES_NOTFOUND) ? r : + (*check1)("uid/default", 11, data, params) ; +} diff --git a/src/libs6net/s6net_accessrules_uidgid_cdb.c b/src/libs6net/s6net_accessrules_uidgid_cdb.c new file mode 100644 index 0000000..1836389 --- /dev/null +++ b/src/libs6net/s6net_accessrules_uidgid_cdb.c @@ -0,0 +1,11 @@ +/* ISC license. */ + +#include +#include +#include + +s6net_accessrules_result_t s6net_accessrules_uidgid_cdb (unsigned int uid, unsigned int gid, struct cdb *c, s6net_accessrules_params_t *params) +{ + diuint uidgid = { uid, gid } ; + return s6net_accessrules_keycheck_uidgid(&uidgid, c, params, &s6net_accessrules_backend_cdb) ; +} diff --git a/src/libs6net/s6net_accessrules_uidgid_fs.c b/src/libs6net/s6net_accessrules_uidgid_fs.c new file mode 100644 index 0000000..db2e909 --- /dev/null +++ b/src/libs6net/s6net_accessrules_uidgid_fs.c @@ -0,0 +1,10 @@ +/* ISC license. */ + +#include +#include + +s6net_accessrules_result_t s6net_accessrules_uidgid_fs (unsigned int uid, unsigned int gid, char const *rulesdir, s6net_accessrules_params_t *params) +{ + diuint uidgid = { uid, gid } ; + return s6net_accessrules_keycheck_uidgid(&uidgid, (void *)rulesdir, params, &s6net_accessrules_backend_fs) ; +} diff --git a/src/libs6net/s6net_ident_client.c b/src/libs6net/s6net_ident_client.c new file mode 100644 index 0000000..c6b9ac0 --- /dev/null +++ b/src/libs6net/s6net_ident_client.c @@ -0,0 +1,22 @@ +/* ISC license. */ + +#include +#include +#include +#include +#include +#include + +int s6net_ident_client (char *s, unsigned int max, ip46_t const *remoteip, uint16 remoteport, ip46_t const *localip, uint16 localport, tain_t const *deadline, tain_t *stamp) +{ + char buf[S6NET_IDENT_REPLY_SIZE] ; + unsigned int len ; + register int r = s6net_ident_reply_get(buf, remoteip, remoteport, localip, localport, deadline, stamp) ; + if (r < 0) return errno == EPIPE ? (errno = EIO, 0) : -1 ; /* the RFC says so */ + len = r ; + r = s6net_ident_reply_parse(buf, remoteport, localport) ; + if (r <= 0) return r ; + if (max + r < len + 1) return (errno = ENAMETOOLONG, -1) ; + byte_copy(s, len - r + 1, buf + r) ; + return len - r + 1 ; +} diff --git a/src/libs6net/s6net_ident_error.c b/src/libs6net/s6net_ident_error.c new file mode 100644 index 0000000..de01714 --- /dev/null +++ b/src/libs6net/s6net_ident_error.c @@ -0,0 +1,33 @@ +/* ISC license */ + +#include +#include +#include + +#define N 5 + +static char const *s6net_ident_error_strings[N+1] = +{ + "invalid port", + "no such user", + "identification denied", + "unknown error", + "X-error-token", + "(invalid error code)" +} ; + +static int const s6net_ident_error_codes[N] = +{ + EINVAL, + ESRCH, + EPERM, + EIO, + EEXIST +} ; + +char const *s6net_ident_error_str (int e) +{ + register unsigned int i = 0 ; + for (; i < N ; i++) if (e == s6net_ident_error_codes[i]) break ; + return s6net_ident_error_strings[i] ; +} diff --git a/src/libs6net/s6net_ident_reply_get.c b/src/libs6net/s6net_ident_reply_get.c new file mode 100644 index 0000000..3e11121 --- /dev/null +++ b/src/libs6net/s6net_ident_reply_get.c @@ -0,0 +1,46 @@ +/* ISC license. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int s6net_ident_reply_get (char *s, ip46_t const *remoteip, uint16 remoteport, ip46_t const *localip, uint16 localport, tain_t const *deadline, tain_t *stamp) +{ + unsigned int len ; + int fd ; + if (ip46_is6(remoteip) != ip46_is6(localip)) return (errno = EAFNOSUPPORT, -1) ; + fd = socket_tcp46(ip46_is6(remoteip)) ; + if (fd < 0) return -1 ; + if (socket_bind46(fd, localip, 0) < 0) goto err ; + if (socket_deadlineconnstamp46(fd, remoteip, 113, deadline, stamp) <= 0) goto err ; + { + char buf[S6NET_IDENT_REPLY_SIZE + 1] ; + char fmt[UINT16_FMT] ; + buffer b = BUFFER_INIT(&buffer_write, fd, buf, 256) ; + unsigned int n = uint16_fmt(fmt, remoteport) ; + buffer_putnoflush(&b, fmt, n) ; + buffer_putnoflush(&b, " , ", 3) ; + n = uint16_fmt(fmt, localport) ; + buffer_putnoflush(&b, fmt, n) ; + buffer_putnoflush(&b, "\r\n", 2) ; + if (!buffer_timed_flush(&b, deadline, stamp)) goto err ; + buffer_init(&b, &buffer_read, fd, buf, S6NET_IDENT_REPLY_SIZE + 1) ; + if (sanitize_read(timed_getlnmax(&b, s, S6NET_IDENT_REPLY_SIZE, &len, '\n', deadline, stamp)) <= 0) goto err ; + } + fd_close(fd) ; + if (len < 2) return (errno = EPROTO, -1) ; + len -= 2 ; + s[len] = 0 ; + return (int)len ; + +err: + fd_close(fd) ; + return -1 ; +} diff --git a/src/libs6net/s6net_ident_reply_parse.c b/src/libs6net/s6net_ident_reply_parse.c new file mode 100644 index 0000000..a895d60 --- /dev/null +++ b/src/libs6net/s6net_ident_reply_parse.c @@ -0,0 +1,58 @@ +/* ISC license. */ + +#include +#include +#include +#include +#include + +static unsigned int skipspace (char const *s) +{ + register unsigned int n = 0 ; + while ((s[n] == ' ') || (s[n] == '\t')) n++ ; + return n ; +} + +int s6net_ident_reply_parse (char const *s, uint16 rp, uint16 lp) +{ + unsigned int n = 0 ; + n += skipspace(s+n) ; if (!s[n]) goto err ; + { + unsigned int i ; + uint16 u ; + i = uint16_scan(s+n, &u) ; if (!i) goto err ; n += i ; + if (u != rp) goto err ; + n += skipspace(s+n) ; if (!s[n]) goto err ; + if (s[n++] != ',') goto err ; + n += skipspace(s+n) ; if (!s[n]) goto err ; + i = uint16_scan(s+n, &u) ; if (!i) goto err ; n += i ; + if (u != lp) goto err ; + } + n += skipspace(s+n) ; if (!s[n]) goto err ; + if (s[n++] != ':') goto err ; + n += skipspace(s+n) ; if (!s[n]) goto err ; + if (!str_diffn(s+n, "ERROR", 5)) goto ERROR ; + if (!str_diffn(s+n, "USERID", 6)) goto USERID ; + err: + return (errno = EPROTO, -1) ; + + ERROR: + n += 5 ; + n += skipspace(s+n) ; if (!s[n]) goto err ; + if (s[n++] != ':') goto err ; + n += skipspace(s+n) ; if (!s[n]) goto err ; + if (!str_diffn(s+n, "INVALID-PORT", 12)) return (errno = EINVAL, 0) ; + if (!str_diffn(s+n, "NO-USER", 7)) return (errno = ESRCH, 0) ; + if (!str_diffn(s+n, "HIDDEN-USER", 11)) return (errno = EPERM, 0) ; + if (!str_diffn(s+n, "UNKNOWN-ERROR", 13)) return (errno = EIO, 0) ; + if (s[n] == 'X') return (errno = EEXIST, 0) ; + goto err ; + + USERID: + n += 6 ; + n += skipspace(s+n) ; if (!s[n]) goto err ; + if (s[n++] != ':') goto err ; + n += str_chr(s+n, ':') ; if (!s[n]) goto err ; + n++ ; if ((s[n] == ' ') || (s[n] == '\t')) n++ ; + return n ; +} diff --git a/src/minidentd/deps-exe/minidentd b/src/minidentd/deps-exe/minidentd new file mode 100755 index 0000000..37c2de8 --- /dev/null +++ b/src/minidentd/deps-exe/minidentd @@ -0,0 +1,4 @@ +mgetuid.o +-lskarnet +${SOCKET_LIB} +${TAINNOW_LIB} diff --git a/src/minidentd/mgetuid-default.c b/src/minidentd/mgetuid-default.c new file mode 100644 index 0000000..6c9ae9b --- /dev/null +++ b/src/minidentd/mgetuid-default.c @@ -0,0 +1,15 @@ +/* ISC license. */ + +#include +#include +#include +#include "mgetuid.h" + +int mgetuid (ip46_t const *localaddr, uint16 localport, ip46_t const *remoteaddr, uint16 remoteport) +{ + (void)localaddr ; + (void)localport ; + (void)remoteaddr ; + (void)remoteport ; + return (errno = ENOSYS, -2) ; +} diff --git a/src/minidentd/mgetuid-linux.c b/src/minidentd/mgetuid-linux.c new file mode 100644 index 0000000..442f642 --- /dev/null +++ b/src/minidentd/mgetuid-linux.c @@ -0,0 +1,169 @@ +/* ISC license. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mgetuid.h" + +#ifdef DEBUG +#include +#define bug(a) do { strerr_warn4x("bug parsing ", a, "remaining: ", cur) ; return 0 ; } while (0) +#else +#define bug(a) return 0 +#endif + +#define LINESIZE 256 + +static int skipspace (char **s) +{ + while (**s && ((**s == ' ') || (**s == '\t'))) + (*s)++ ; + return (int)**s ; +} + +static int parseline (char *s, unsigned int len, unsigned int *u, char *la, uint16 *lp, char *ra, uint16 *rp, int is6) +{ + char *cur = s ; + unsigned int pos ; + uint32 junk ; + register unsigned int iplen = is6 ? 16 : 4 ; + + if (!skipspace(&cur)) bug("initial whitespace") ; + pos = uint32_scan(cur, &junk) ; /* sl */ + if (!pos || (cur-s+1+pos) > len) bug("sl") ; + cur += pos ; + if ((*cur++) != ':') bug("sl:") ; + if (!skipspace(&cur)) bug("sl: SPACE") ; + + if ((cur - s + 1 + iplen) > len) bug("local_address") ; + pos = ucharn_scan(cur, la, iplen) ; /* local_address */ + if (!pos) bug("local_address") ; + cur += pos ; + if ((*cur++) != ':') bug("local_address:") ; + + pos = uint16_xscan(cur, lp) ; /* :port */ + if (!pos || (cur-s+pos) > len) bug("local_port") ; + cur += pos ; + if (!skipspace(&cur)) bug("local_port SPACE") ; + + if ((cur - s + 1 + iplen) > len) bug("remote_address") ; + pos = ucharn_scan(cur, ra, iplen) ; /* remote_address */ + if (!pos) bug("remote_address") ; + cur += pos ; + if ((*cur++) != ':') bug("remote_address:") ; + + pos = uint16_xscan(cur, rp) ; /* :port */ + if (!pos || (cur-s+pos) > len) bug("remote_port") ; + cur += pos ; + if (!skipspace(&cur)) bug("remote_port SPACE"); + + pos = uint32_xscan(cur, &junk) ; /* st */ + if (!pos || (cur-s+pos) > len) bug("st") ; + cur += pos ; + if (!skipspace(&cur)) bug("st SPACE") ; + pos = uint32_xscan(cur, &junk) ; /* tx_queue */ + if (!pos || (cur-s+1+pos) > len) bug("tx_queue") ; + cur += pos ; + if ((*cur++) != ':') bug("tx_queue:") ; + pos = uint32_xscan(cur, &junk) ; /* rx_queue */ + if (!pos || (cur-s+pos) > len) bug("rx_queue") ; + cur += pos ; + if (!skipspace(&cur)) bug("rx_queue SPACE") ; + pos = uint32_xscan(cur, &junk) ; /* tr */ + if (!pos || (cur-s+1+pos) > len) bug("tr") ; + cur += pos ; + if ((*cur++) != ':') bug("tr:") ; + pos = uint32_xscan(cur, &junk) ; /* tm->when */ + if (!pos || (cur-s+pos) > len) bug("tm->when") ; + cur += pos ; + if (!skipspace(&cur)) bug("tm->when SPACE") ; + pos = uint32_xscan(cur, &junk) ; /* retrnsmt */ + if (!pos || (cur-s+pos) > len) bug("retrnsmt") ; + cur += pos ; + + if (!skipspace(&cur)) bug("retrnsmt SPACE") ; + pos = uint_scan(cur, u) ; /* uid */ + if (!pos || (cur-s+1+pos) > len) bug("uid") ; + + return 1 ; +} + +#ifdef DEBUG + +static void debuglog (uint16 a, uint16 b, unsigned int c, char const *d, char const *e, int is6) +{ + char sa[UINT16_FMT] ; + char sb[UINT16_FMT] ; + char sc[UINT_FMT] ; + char sd[IP46_FMT] ; + char se[IP46_FMT] ; + + sa[uint16_fmt(sa, a)] = 0 ; + sb[uint16_fmt(sb, b)] = 0 ; + sc[uint_fmt(sc, c)] = 0 ; + sd[is6 ? ip6_fmt(sd, d) : ip4_fmt(sd, d)] = 0 ; + se[is6 ? ip6_fmt(se, e) : ip4_fmt(se, e)] = 0 ; + + buffer_puts(buffer_2, sd) ; + buffer_puts(buffer_2, ":") ; + buffer_puts(buffer_2, sa) ; + buffer_puts(buffer_2, " , ") ; + buffer_puts(buffer_2, se) ; + buffer_puts(buffer_2, ":") ; + buffer_puts(buffer_2, sb) ; + buffer_puts(buffer_2, " -> ") ; + buffer_puts(buffer_2, sc) ; + buffer_putsflush(buffer_2, "\n") ; +} + +#endif + +int mgetuid (ip46_t const *localaddr, uint16 localport, ip46_t const *remoteaddr, uint16 remoteport) +{ + int r ; + int u = -2 ; + stralloc line = STRALLOC_ZERO ; + buffer b ; + char y[BUFFER_INSIZE] ; + register int is6 = ip46_is6(localaddr) ; + int fd = open_readb(is6 ? "/proc/net/tcp6" : "/proc/net/tcp") ; + if (fd == -1) return -2 ; + buffer_init(&b, &buffer_read, fd, y, BUFFER_INSIZE_SMALL) ; + if (skagetln(&b, &line, '\n') < 1) goto err ; +#ifdef DEBUG + line.s[line.len-1] = 0 ; + debuglog(localport, remoteport, 65535, localaddr->ip, remoteaddr->ip, is6) ; +#endif + for (;;) + { + char la[16] ; + char ra[16] ; + unsigned int nu ; + uint16 lp, rp ; + line.len = 0 ; + r = skagetln(&b, &line, '\n') ; + if (r <= 0) { u = -1 ; break ; } + line.s[line.len-1] = 0 ; + if (!parseline(line.s, line.len, &nu, la, &lp, ra, &rp, is6)) break ; +#ifdef DEBUG + debuglog(lp, rp, nu, la, ra, is6) ; +#endif + if ((lp == localport) && (rp == remoteport) + && !byte_diff(la, is6 ? 16 : 4, localaddr->ip) + && !byte_diff(ra, is6 ? 16 : 4, remoteaddr->ip)) + { + u = nu ; break ; + } + } + stralloc_free(&line) ; + err: + fd_close(fd) ; + return u ; +} diff --git a/src/minidentd/mgetuid.h b/src/minidentd/mgetuid.h new file mode 100644 index 0000000..0572385 --- /dev/null +++ b/src/minidentd/mgetuid.h @@ -0,0 +1,11 @@ +/* ISC license. */ + +#ifndef MGETUID_H +#define MGETUID_H + +#include +#include + +extern int mgetuid (ip46_t const *, uint16, ip46_t const *, uint16) ; + +#endif diff --git a/src/minidentd/minidentd.c b/src/minidentd/minidentd.c new file mode 100644 index 0000000..0bd86be --- /dev/null +++ b/src/minidentd/minidentd.c @@ -0,0 +1,273 @@ +/* ISC license. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mgetuid.h" + +#define USAGE "minidentd [ -v ] [ -n | -i | -r ] [ -y file ] [ -t timeout ]" +#define dieusage() strerr_dieusage(100, USAGE) + + +static int how = 0 ; +static int flagverbose = 0 ; +static char const *userfile = ".ident" ; + +static tain_t deadline ; +static unsigned int nquery = 0 ; +static char logfmt[UINT_FMT] ; + +#define DECIMAL "0123456789" +#define godecimal(s) while (*(s) && !DECIMAL[str_chr(DECIMAL, *(s))]) (s)++ + +static int parseline (char const *s, uint16 *localport, uint16 *remoteport) +{ + unsigned int pos = 0 ; + + godecimal(s) ; + if (!*s) return 0 ; + s += uint16_scan(s, localport) ; + if (!*s) return 0 ; + s += str_chr(s+pos, ',') ; + if (*s) s++ ; + godecimal(s) ; + if (!*s) return 0 ; + if (!uint16_scan(s, remoteport)) return 0 ; + return 1 ; +} + +static void formatlr (char *s, uint16 lp, uint16 rp) +{ + s += uint16_fmt(s, lp) ; + *s++ = ',' ; + *s++ = ' ' ; + s += uint16_fmt(s, rp) ; + *s = 0 ; +} + +static void reply (char const *s, char const *r, char const *info) +{ + buffer_puts(buffer_1small, s) ; + buffer_put(buffer_1small, " : ", 3) ; + buffer_puts(buffer_1small, r) ; + buffer_put(buffer_1small, " : ", 3) ; + buffer_puts(buffer_1small, info) ; + buffer_put(buffer_1small, "\r\n", 2) ; + if (!buffer_timed_flush_g(buffer_1small, &deadline)) + strerr_diefu1sys(111, "write to stdout") ; +} + +static void logquery (char const *s) +{ + if (!flagverbose) return ; + logfmt[uint_fmt(logfmt, ++nquery)] = 0 ; + buffer_put(buffer_2, "query ", 6) ; + buffer_puts(buffer_2, logfmt) ; + buffer_put(buffer_2, ": ", 2) ; + buffer_puts(buffer_2, s) ; + buffer_putflush(buffer_2, "\n", 1) ; +} + +static void logreply (char const *type, char const *reply1, char const *reply2) +{ + if (!flagverbose) return ; + buffer_puts(buffer_2, "reply type ") ; + buffer_puts(buffer_2, type) ; + buffer_put(buffer_2, ": ", 2) ; + buffer_puts(buffer_2, logfmt) ; + buffer_put(buffer_2, ": ", 2) ; + buffer_puts(buffer_2, reply1) ; + buffer_put(buffer_2, ": ", 2) ; + buffer_puts(buffer_2, reply2) ; + buffer_putflush(buffer_2, "\n", 1) ; +} + +static int userident (char *s, char const *home) +{ + int fd ; + int r = 1 ; + { + unsigned int homelen = str_len(home) ; + unsigned int userlen = str_len(userfile) ; + char tmp[homelen + userlen + 2] ; + byte_copy(tmp, homelen, home) ; + tmp[homelen] = '/' ; + byte_copy(tmp + homelen + 1, userlen + 1, userfile) ; + fd = open_readb(tmp) ; + } + if (fd == -1) return (errno != ENOENT) ? -1 : 0 ; + if (how == 1) + { + fd_close(fd) ; + return 1 ; + } + r = allread(fd, s, 14) ; + fd_close(fd) ; + if (r == -1) return -1 ; + if (!r) return 1 ; + s[r] = 0 ; + s[byte_chr(s, r, '\n')] = 0 ; + return 2 ; +} + + +static void doit (char const *s, ip46_t const *localaddr, ip46_t const *remoteaddr) +{ + char lr[15] ; + uint16 localport, remoteport ; + struct passwd *pw ; + int uid ; + if (!parseline(s, &localport, &remoteport)) + { + reply("0, 0", "ERROR", "INVALID-PORT") ; + return ; + } + formatlr(lr, localport, remoteport) ; + logquery(lr) ; + + uid = mgetuid(localaddr, localport, remoteaddr, remoteport) ; + if (uid == -2) + { + strerr_warnwu1sys("get uid") ; + reply(lr, "ERROR", "UNKNOWN-ERROR") ; + return ; + } + else if (uid == -1) + { + reply(lr, "ERROR", "NO-USER") ; + logreply("error", "ERROR", "NO-USER") ; + return ; + } + + if (how == 3) + { + char name[9] ; + char fmt[4 + UINT_FMT] = "uid " ; + fmt[4 + uint_fmt(fmt+4, uid)] = 0 ; + if (random_name(name, 8) < 8) + { + strerr_warnwu1sys("perform random") ; + reply(lr, "ERROR", "UNKNOWN-ERROR") ; + return ; + } + reply(lr, "UNIX", name) ; + logreply("random", fmt, name) ; + return ; + } + + pw = getpwuid(uid) ; + if (!pw) + { + char fmt[UINT_FMT] ; + fmt[uint_fmt(fmt, uid)] = 0 ; + strerr_warnw2x("unknown uid ", fmt) ; + reply(lr, "ERROR", "UNKNOWN-ERROR") ; + return ; + } + + if (how) + { + char s[15] ; + register int r = userident(s, pw->pw_dir) ; + if ((how == 1) || (r == 1)) + { + reply(lr, "ERROR", "HIDDEN-USER") ; + logreply("user", "ERROR", "HIDDEN-USER") ; + return ; + } + else if (r == 2) + { + reply(lr, "USERID : UNIX", s) ; + logreply("user", "UNIX", s) ; + return ; + } + } + + reply(lr, "USERID : UNIX", pw->pw_name) ; + logreply("user", "UNIX", pw->pw_name) ; +} + + +int main (int argc, char const *const *argv, char const *const *envp) +{ + stralloc line = STRALLOC_ZERO ; + tain_t tto ; + ip46_t localaddr, remoteaddr ; + PROG = "minidentd" ; + + { + subgetopt_t l = SUBGETOPT_ZERO ; + unsigned int t = 0 ; + for (;;) + { + register int opt = subgetopt_r(argc, argv, "vniry:t:", &l) ; + if (opt == -1) break ; + switch (opt) + { + case 'v' : flagverbose = 1 ; break ; + case 'n' : how = 1 ; break ; + case 'i' : how = 2 ; break ; + case 'r' : how = 3 ; break ; + case 'y' : userfile = l.arg ; break ; + case 't' : if (!uint0_scan(l.arg, &t)) dieusage() ; break ; + default : dieusage() ; + } + } + if (t) tain_from_millisecs(&tto, t) ; else tto = tain_infinite_relative ; + argc -= l.ind ; argv += l.ind ; + } + + { + char const *proto = env_get2(envp, "PROTO") ; + if (!proto) strerr_dienotset(100, "PROTO") ; + { + char const *x ; + unsigned int protolen = str_len(proto) ; + char tmp[protolen + 9] ; + byte_copy(tmp, protolen, proto) ; + byte_copy(tmp + protolen, 8, "LOCALIP") ; + x = env_get2(envp, tmp) ; + if (!x) strerr_dienotset(100, tmp) ; + if (!ip46_scan(x, &localaddr)) strerr_dieinvalid(100, tmp) ; + byte_copy(tmp + protolen, 9, "REMOTEIP") ; + x = env_get2(envp, tmp) ; + if (!x) strerr_dienotset(100, tmp) ; + if (!ip46_scan(x, &remoteaddr)) strerr_dieinvalid(100, tmp) ; + } + } + + if (ip46_is6(&localaddr) != ip46_is6(&remoteaddr)) + strerr_dief1x(100, "local and remote address not of the same family") ; + tain_now_g() ; + + for (;;) + { + register int r ; + line.len = 0 ; + tain_add_g(&deadline, &tto) ; + r = timed_getln_g(buffer_0small, &line, '\n', &deadline) ; + if (r == -1) + { + if (errno == ETIMEDOUT) return 1 ; + else strerr_diefu1sys(111, "read from stdin") ; + } + if (!r) break ; + line.s[line.len - 1] = 0 ; + doit(line.s, &localaddr, &remoteaddr) ; + } + return 0 ; +} diff --git a/tools/gen-deps.sh b/tools/gen-deps.sh new file mode 100755 index 0000000..af31259 --- /dev/null +++ b/tools/gen-deps.sh @@ -0,0 +1,79 @@ +#!/bin/sh -e + +. package/info + +echo '#' +echo '# This file has been generated by tools/gen-deps.sh' +echo '#' +echo + +for dir in src/include/${package} src/* ; do + for file in $(ls -1 $dir | grep -- \\.h$) ; do + { + grep -F -- "#include <${package}/" < ${dir}/$file | cut -d'<' -f2 | cut -d'>' -f1 ; + grep -- '#include ".*\.h"' < ${dir}/$file | cut -d'"' -f2 + } | sort -u | { + deps= + while read dep ; do + if echo $dep | grep -q "^${package}/" ; then + deps="$deps src/include/$dep" + elif test -f "${dir}/$dep" ; then + deps="$deps ${dir}/$dep" + else + deps="$deps src/include-local/$dep" + fi + done + if test -n "$deps" ; then + echo "${dir}/${file}:${deps}" + fi + } + done +done + +for dir in src/* ; do + for file in $(ls -1 $dir | grep -- \\.c$) ; do + { + grep -F -- "#include <${package}/" < ${dir}/$file | cut -d'<' -f2 | cut -d'>' -f1 ; + grep -- '#include ".*\.h"' < ${dir}/$file | cut -d'"' -f2 + } | sort -u | { + deps=" ${dir}/$file" + while read dep ; do + if echo $dep | grep -q "^${package}/" ; then + deps="$deps src/include/$dep" + elif test -f "${dir}/$dep" ; then + deps="$deps ${dir}/$dep" + else + deps="$deps src/include-local/$dep" + fi + done + o=$(echo $file | sed s/\\.c$/.o/) + lo=$(echo $file | sed s/\\.c$/.lo/) + echo "${dir}/${o} ${dir}/${lo}:${deps}" + } + done +done +echo + +for dir in $(ls -1 src | grep -v ^include) ; do + for file in $(ls -1 src/$dir/deps-lib) ; do + deps= + while read dep ; do + deps="$deps src/$dir/$dep" + done < src/$dir/deps-lib/$file + echo "lib$file.a: $deps" + if test -x "src/$dir/deps-lib/$file" ; then + echo "lib${file}.so: $(echo "$deps" | sed 's/\.o/.lo/g')" + fi + done + + for file in $(ls -1 src/$dir/deps-exe) ; do + deps= + while read dep ; do + if echo $dep | grep -q -- \\.o$ ; then + dep="src/$dir/$dep" + fi + deps="$deps $dep" + done < src/$dir/deps-exe/$file + echo "$file: src/$dir/$file.o$deps" + done +done diff --git a/tools/install.sh b/tools/install.sh new file mode 100755 index 0000000..89f9428 --- /dev/null +++ b/tools/install.sh @@ -0,0 +1,64 @@ +#!/bin/sh + +usage() { + echo "usage: $0 [-D] [-l] [-m mode] src dst" 1>&2 + exit 1 +} + +mkdirp=false +symlink=false +mode=0755 + +while getopts Dlm: name ; do + case "$name" in + D) mkdirp=true ;; + l) symlink=true ;; + m) mode=$OPTARG ;; + ?) usage ;; + esac +done +shift $(($OPTIND - 1)) + +test "$#" -eq 2 || usage +src=$1 +dst=$2 +tmp="$dst.tmp.$$" + +case "$dst" in + */) echo "$0: $dst ends in /" 1>&2 ; exit 1 ;; +esac + +set -C +set -e + +if $mkdirp ; then + umask 022 + case "$2" in + */*) mkdir -p "${dst%/*}" ;; + esac +fi + +trap 'rm -f "$tmp"' EXIT INT QUIT TERM HUP + +umask 077 + +if $symlink ; then + ln -s "$src" "$tmp" +else + cat < "$1" > "$tmp" + chmod "$mode" "$tmp" +fi + +mv -f "$tmp" "$dst" +if test -d "$dst" ; then + rm -f "$dst/$(basename $tmp)" + if $symlink ; then + mkdir "$tmp" + ln -s "$src" "$tmp/$(basename $dst)" + mv -f "$tmp/$(basename $dst)" "${dst%/*}" + rmdir "$tmp" + else + echo "$0: $dst is a directory" 1>&2 + exit 1 + fi +fi -- cgit v1.2.3