From fab29b78d87d0114ecbdfbcac3b0e7c0edb463ba Mon Sep 17 00:00:00 2001 From: Laurent Bercot Date: Mon, 28 Dec 2020 22:09:00 +0000 Subject: Initial commit --- .gitignore | 10 + AUTHORS | 2 + COPYING | 13 + INSTALL | 167 ++++++++++ Makefile | 149 +++++++++ NEWS | 6 + README | 27 ++ configure | 475 +++++++++++++++++++++++++++++ doc/dnsfunnel-daemon.html | 112 +++++++ doc/dnsfunnel-translate.html | 70 +++++ doc/dnsfunneld.html | 160 ++++++++++ doc/index.html | 116 +++++++ doc/upgrade.html | 28 ++ package/deps-build | 2 + package/deps.mak | 16 + package/info | 4 + package/modes | 3 + package/targets.mak | 7 + src/dnsfunnel/deps-exe/dnsfunnel-daemon | 1 + src/dnsfunnel/deps-exe/dnsfunnel-translate | 1 + src/dnsfunnel/deps-exe/dnsfunneld | 4 + src/dnsfunnel/dnsfunnel-daemon.c | 150 +++++++++ src/dnsfunnel/dnsfunnel-translate.c | 93 ++++++ src/dnsfunnel/dnsfunneld.c | 305 ++++++++++++++++++ src/dnsfunnel/dnsfunneld.h | 44 +++ src/dnsfunnel/dnsfunneld_answer.c | 132 ++++++++ src/dnsfunnel/dnsfunneld_process.c | 136 +++++++++ tools/gen-deps.sh | 93 ++++++ tools/install.sh | 64 ++++ 29 files changed, 2390 insertions(+) create mode 100644 .gitignore create mode 100644 AUTHORS create mode 100644 COPYING create mode 100644 INSTALL create mode 100644 Makefile create mode 100644 NEWS create mode 100644 README create mode 100644 configure create mode 100644 doc/dnsfunnel-daemon.html create mode 100644 doc/dnsfunnel-translate.html create mode 100644 doc/dnsfunneld.html create mode 100644 doc/index.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 100644 src/dnsfunnel/deps-exe/dnsfunnel-daemon create mode 100644 src/dnsfunnel/deps-exe/dnsfunnel-translate create mode 100644 src/dnsfunnel/deps-exe/dnsfunneld create mode 100644 src/dnsfunnel/dnsfunnel-daemon.c create mode 100644 src/dnsfunnel/dnsfunnel-translate.c create mode 100644 src/dnsfunnel/dnsfunneld.c create mode 100644 src/dnsfunnel/dnsfunneld.h create mode 100644 src/dnsfunnel/dnsfunneld_answer.c create mode 100644 src/dnsfunnel/dnsfunneld_process.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..8c7ef95 --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +*.o +*.a +*.lo +*.so +*.so.* +/config.mak +/src/include/dnsfunnel/config.h +/dnsfunnel-daemon +/dnsfunneld +/dnsfunnel-translate diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..7a708a1 --- /dev/null +++ b/AUTHORS @@ -0,0 +1,2 @@ +Main author: + Laurent Bercot diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..6b16aad --- /dev/null +++ b/COPYING @@ -0,0 +1,13 @@ +Copyright (c) 2020 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..2c3583d --- /dev/null +++ b/INSTALL @@ -0,0 +1,167 @@ +Build Instructions +------------------ + +* Requirements + ------------ + + - A POSIX-compliant C development environment + - GNU make version 3.81 or later + - skalibs version 2.10.0.0 or later: https://skarnet.org/software/skalibs/ + - s6-dns version 2.3.3.0 or later: https://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. + + 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, a few standard environment variables are recognized. + + If the CC environment variable is set, its value will override compiler +detection by configure. The --host=HOST option will still add a HOST- +prefix to the value of CC. + + The values of CFLAGS, CPPFLAGS and LDFLAGS will be appended to flags +auto-detected by configure. To entirely override the flags set by +configure instead, use make variables. + + +* Make variables + -------------- + + You can invoke make with a few variables for more configuration. + + CC, CFLAGS, CPPFLAGS, LDFLAGS, LDLIBS, AR, RANLIB, STRIP, INSTALL and +CROSS_COMPILE can all be overridden on the make command line. This is +an even bigger hammer than running ./configure with environment +variables, so it is advised to only do this when it is the only way of +obtaining the behaviour you want. + + DESTDIR can be given on the "make install" command line in order to +install to a staging directory. + + +* 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. + + 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 --host=HOST option to configure, HOST being the triplet +for your target. + * Make sure your cross-toolchain binaries (i.e. prefixed with HOST-) +are accessible via your PATH environment variable. + * 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. +If $package_home is the home of the package, defined as +DIR/package/$category/$package-$version with the variables +read from the package/info file, then: + + --dynlibdir is set to $package_home/library.so + --bindir is set to $package_home/command + --sbindir is also set to $package_home/command (slashpackage +differentiates root-only binaries by their Unix rights, not their +location in the filesystem) + --libexecdir is also set to $package_home/command (slashpackage +does not need a specific directory for internal binaries) + --libdir is set to $package_home/library + --includedir is set to $package_home/include + + --prefix is pretty much ignored when you use --enable-slashpackage. +You should probably not use both --enable-slashpackage and --prefix. + + 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. + + +* Absolute pathnames + ------------------ + + You may want to use fixed absolute pathnames even if you're not +following the slashpackage convention: for instance, the Nix packaging +system prefers calling binaries with immutable paths rather than rely on +PATH resolution. If you are in that case, use the --enable-absolute-paths +option to configure. This will ensure that programs calling binaries from +this package will call them with their full installation path (in bindir) +without relying on a PATH search. + + +* 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..94a820f --- /dev/null +++ b/Makefile @@ -0,0 +1,149 @@ +# +# This Makefile requires GNU make. +# +# Do not make changes here. +# Use the included .mak files. +# + +it: all + +make_need := 3.81 +ifeq "" "$(strip $(filter $(make_need), $(firstword $(sort $(make_need) $(MAKE_VERSION)))))" +fail := $(error Your make ($(MAKE_VERSION)) is too old. You need $(make_need) or newer) +endif + +CC = $(error Please use ./configure first) + +STATIC_LIBS := +SHARED_LIBS := +INTERNAL_LIBS := +EXTRA_TARGETS := +LIB_DEFS := + +define library_definition +LIB$(firstword $(subst =, ,$(1))) := lib$(lastword $(subst =, ,$(1))).$(if $(DO_ALLSTATIC),a,so).xyzzy +ifdef DO_SHARED +SHARED_LIBS += lib$(lastword $(subst =, ,$(1))).so.xyzzy +endif +ifdef DO_STATIC +STATIC_LIBS += lib$(lastword $(subst =, ,$(1))).a.xyzzy +endif +endef + +-include config.mak +include package/targets.mak + +$(foreach var,$(LIB_DEFS),$(eval $(call library_definition,$(var)))) + +include package/deps.mak + +version_m := $(basename $(version)) +version_M := $(basename $(version_m)) +version_l := $(basename $(version_M)) +CPPFLAGS_ALL := $(CPPFLAGS_AUTO) $(CPPFLAGS) +CFLAGS_ALL := $(CFLAGS_AUTO) $(CFLAGS) +ifeq ($(strip $(STATIC_LIBS_ARE_PIC)),) +CFLAGS_SHARED := -fPIC +else +CFLAGS_SHARED := +endif +LDFLAGS_ALL := $(LDFLAGS_AUTO) $(LDFLAGS) +AR := $(CROSS_COMPILE)ar +RANLIB := $(CROSS_COMPILE)ranlib +STRIP := $(CROSS_COMPILE)strip +INSTALL := ./tools/install.sh + +ALL_BINS := $(LIBEXEC_TARGETS) $(BIN_TARGETS) +ALL_LIBS := $(SHARED_LIBS) $(STATIC_LIBS) $(INTERNAL_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) $(EXTRA_TARGETS) + +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 $(STATIC_LIBS)),) + exec $(STRIP) -x -R .note -R .comment -R .note.GNU-stack $(STATIC_LIBS) +endif +ifneq ($(strip $(ALL_BINS)$(SHARED_LIBS)),) + exec $(STRIP) -R .note -R .comment -R .note.GNU-stack $(ALL_BINS) $(SHARED_LIBS) +endif + +install: install-dynlib install-libexec install-bin install-lib install-include +install-dynlib: $(SHARED_LIBS:lib%.so.xyzzy=$(DESTDIR)$(dynlibdir)/lib%.so) +install-libexec: $(LIBEXEC_TARGETS:%=$(DESTDIR)$(libexecdir)/%) +install-bin: $(BIN_TARGETS:%=$(DESTDIR)$(bindir)/%) +install-lib: $(STATIC_LIBS:lib%.a.xyzzy=$(DESTDIR)$(libdir)/lib%.a) +install-include: $(ALL_INCLUDES:src/include/$(package)/%.h=$(DESTDIR)$(includedir)/$(package)/%.h) +install-data: $(ALL_DATA:src/etc/%=$(DESTDIR)$(datadir)/%) + +ifneq ($(exthome),) + +$(DESTDIR)$(exthome): $(DESTDIR)$(home) + exec $(INSTALL) -l $(notdir $(home)) $(DESTDIR)$(exthome) + +update: $(DESTDIR)$(exthome) + +global-links: $(DESTDIR)$(exthome) $(SHARED_LIBS:lib%.so.xyzzy=$(DESTDIR)$(sproot)/library.so/lib%.so.$(version_M)) $(BIN_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 dnsfunnel. diff --git a/configure b/configure new file mode 100644 index 0000000..664a43f --- /dev/null +++ b/configure @@ -0,0 +1,475 @@ +#!/bin/sh + +cd `dirname "$0"` +. package/info + +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="$*" +} + +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 $CPPFLAGS $CPPFLAGS_POST $CFLAGS_AUTO $CFLAGS $CFLAGS_POST "$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 $CFLAGS $CFLAGS_POST $LDFLAGS_AUTO $LDFLAGS $LDFLAGS_POST -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 + +CC_AUTO= +CPPFLAGS_AUTO="-D_POSIX_C_SOURCE=200809L -D_XOPEN_SOURCE=700 -iquote src/include-local -Isrc/include" +CPPFLAGS_POST="$CPPFLAGS" +CPPFLAGS= +CFLAGS_AUTO="-pipe -Wall" +CFLAGS_POST="$CFLAGS" +CFLAGS=-O2 +LDFLAGS_AUTO= +LDFLAGS_POST="$LDFLAGS" +LDFLAGS= +LDFLAGS_NOSHARED= +LDFLAGS_SHARED=-shared +prefix= +exec_prefix='$prefix' +dynlibdir='$prefix/lib' +libexecdir='$exec_prefix/libexec' +bindir='$exec_prefix/bin' +libdir='$prefix/lib/$package' +includedir='$prefix/include' +sysdeps='$prefix/lib/skalibs/sysdeps' +manualsysdeps=false +shared=false +static=true +allpic=true +slashpackage=false +abspath=false +sproot= +home= +exthome= +allstatic=true +evenmorestatic=false +addincpath='' +addlibspath='' +addlibdpath='' +vpaths='' +vpathd='' +build= +cachelist=/run/dnsfunnel-caches + +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#*=} ;; + --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 ; evenmorestatic=false ;; + --enable-static-libc|--enable-static-libc=yes) allstatic=true ; evenmorestatic=true ;; + --disable-static-libc|--enable-static-libc=no) evenmorestatic=false ;; + --enable-all-pic|--enable-all-pic=yes) allpic=true ;; + --disable-all-pic|--enable-all-pic=no) allpic=false ;; + --enable-slashpackage=*) sproot=${arg#*=} ; slashpackage=true ; ;; + --enable-slashpackage) sproot= ; slashpackage=true ;; + --disable-slashpackage) sproot= ; slashpackage=false ;; + --enable-absolute-paths|--enable-absolute-paths=yes) abspath=true ;; + --disable-absolute-paths|--enable-absolute-paths=no) abspath=false ;; + --with-cachelist=*) cachelist=${arg#*=} ;; + --enable-*|--disable-*|--with-*|--without-*|--*dir=*) ;; + --host=*|--target=*) target=${arg#*=} ;; + --build=*) build=${arg#*=} ;; + -* ) echo "$0: unknown option $arg" ;; + *=*) ;; + *) target=$arg ;; + esac +done + +# Add /usr in the default default case +if test -z "$prefix" ; then + if test "$libdir" = '$prefix/lib/$package' ; then + libdir=/usr/lib/$package + fi + if test "$includedir" = '$prefix/include' ; then + includedir=/usr/include + fi + if test "$sysdeps" = '$prefix/lib/skalibs/sysdeps' ; then + sysdeps=/usr/lib/skalibs/sysdeps + fi +fi + +# Expand installation directories +stripdir prefix +for i in exec_prefix dynlibdir libexecdir bindir libdir includedir sysdeps sproot ; 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 + extbinprefix=${exthome}/command + dynlibdir=${home}/library.so + bindir=${home}/command + libdir=${home}/library + libexecdir=$bindir + includedir=${home}/include + while read dep condvar ; do + if test -n "$condvar" ; then + eval "cond=$condvar" + else + cond=true + fi + if $cond ; then + addincpath="$addincpath -I${sproot}${dep}/include" + vpaths="$vpaths ${sproot}${dep}/library" + addlibspath="$addlibspath -L${sproot}${dep}/library" + 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 +if test -n "$target" && test x${build} != x${target} ; then + cross=${target}- +else + cross= +fi +echo "Checking for C compiler..." +trycc ${CC} +if test -n "$CC_AUTO" ; then + b=`basename "$CC"` + adjust_cross=false + if test "$b" != "$CC" ; then + adjust_cross=true + echo "$0: warning: compiler $CC is declared with its own path. If it's not accessible via PATH, you will need to pass AR, RANLIB and STRIP make variables to the make invocation." 1>&2 + fi + if test -n "$cross" ; then + if test "$b" = "${b##$cross}" ; then + echo "$0: warning: compiler $CC is declared as a cross-compiler for target $target but does not start with prefix ${cross}" 1>&2 + elif $adjust_cross ; then + cross=`dirname "$CC"`/"$cross" + fi + fi +fi +trycc ${cross}gcc +trycc ${cross}clang +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 $CPPFLAGS $CPPFLAGS_POST $CFLAGS_AUTO $CFLAGS $CFLAGS_POST -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..." +if test -z "$target" ; then + if test -n "$build" ; then + target=$build ; + else + target=$($CC_AUTO -dumpmachine 2>/dev/null) || target=unknown + fi +fi +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 + +spawn_lib=$(cat $sysdeps/spawn.lib) +socket_lib=$(cat $sysdeps/socket.lib) +sysclock_lib=$(cat $sysdeps/sysclock.lib) +timer_lib=$(cat $sysdeps/timer.lib) +util_lib=$(cat $sysdeps/util.lib) + +if $allpic ; then + tryflag CPPFLAGS_AUTO -fPIC +fi +tryflag CFLAGS_AUTO -std=c99 +tryflag CFLAGS -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 CPPFLAGS_AUTO -Werror=implicit-function-declaration +tryflag CPPFLAGS_AUTO -Werror=implicit-int +tryflag CPPFLAGS_AUTO -Werror=pointer-sign +tryflag CPPFLAGS_AUTO -Werror=pointer-arith +tryflag CFLAGS_AUTO -ffunction-sections +tryflag CFLAGS_AUTO -fdata-sections + +tryldflag LDFLAGS_AUTO -Wl,--sort-section=alignment +tryldflag LDFLAGS_AUTO -Wl,--sort-common + +CPPFLAGS_AUTO="${CPPFLAGS_AUTO}${addincpath}" + +if $evenmorestatic ; then + LDFLAGS_NOSHARED=-static +fi + +if $shared ; then + tryldflag LDFLAGS -Wl,--hash-style=both +fi + +LDFLAGS_SHARED="${LDFLAGS_SHARED}${addlibdpath}" + +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 + +if $allstatic ; then + LDFLAGS_NOSHARED="${LDFLAGS_NOSHARED}${addlibspath}" + tryldflag LDFLAGS_NOSHARED -Wl,--gc-sections +else + LDFLAGS_NOSHARED="${LDFLAGS_NOSHARED}${addlibdpath}" +fi + +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 +libdir := $libdir +includedir := $includedir +sysdeps := $sysdeps +slashpackage := $slashpackage +sproot := $sproot +version := $version +home := $home +exthome := $exthome +SPAWN_LIB := ${spawn_lib} +SOCKET_LIB := ${socket_lib} +SYSCLOCK_LIB := ${sysclock_lib} +TIMER_LIB := ${timer_lib} +UTIL_LIB := ${util_lib} + +CC := $CC_AUTO +CPPFLAGS_AUTO := $CPPFLAGS_AUTO +CPPFLAGS := $CPPFLAGS $CPPFLAGS_POST +CFLAGS_AUTO := $CFLAGS_AUTO +CFLAGS := $CFLAGS $CFLAGS_POST +LDFLAGS_AUTO := $LDFLAGS_AUTO +LDFLAGS := $LDFLAGS $LDFLAGS_POST +LDFLAGS_SHARED := $LDFLAGS_SHARED +LDFLAGS_NOSHARED := $LDFLAGS_NOSHARED +CROSS_COMPILE := $cross + +vpath lib%.a$vpaths +vpath lib%.so$vpathd +EOF +if $allstatic ; then + echo ".LIBPATTERNS := lib%.a" + echo "DO_ALLSTATIC := 1" +else + echo ".LIBPATTERNS := lib%.so" +fi +if $static ; then + echo "DO_STATIC := 1" +else + echo "DO_STATIC :=" +fi +if $shared ; then + echo "DO_SHARED := 1" +else + echo "DO_SHARED :=" +fi +if $allpic ; then + echo "STATIC_LIBS_ARE_PIC := 1" +else + echo "STATIC_LIBS_ARE_PIC :=" +fi + +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/dnsfunnel-daemon.html b/doc/dnsfunnel-daemon.html new file mode 100644 index 0000000..d93d463 --- /dev/null +++ b/doc/dnsfunnel-daemon.html @@ -0,0 +1,112 @@ + + + + + + dnsfunnel: the dnsfunnel-daemon program + + + + + + +

+dnsfunnel
+Software
+skarnet.org +

+ +

The dnsfunnel-daemon program

+ +

+dnsfunnel-daemon binds to a local UDP socket, drops its +privileges, then executes into dnsfunneld. +

+ +

Interface

+ +
+     dnsfunnel-daemon [ -v verbosity ] [ -d notif ] [ -U | -u uid -g gid ] [ -i ip:port ] [ -R root ] [ -b bufsize ] [ -f cachelist ] [ -T | -t ] [ -N | -n ]
+
+ +
    +
  • dnsfunnel-daemon creates a UDP inet domain socket and binds it +to IPv4 address ip (normally 127.0.0.1) and port port +(normally 53).
  • +
  • Depending on the options it has been given, it may chroot and lose +privileges on its gid and uid.
  • +
  • It execs into dnsfunneld with the +UDP socket as its standard input.
  • +
+ +

+ The point of dnsfunnel-daemon is to separate the administrative +operations of starting a daemon from the actual serving part, which is +handled by dnsfunneld. +

+ +

Exit codes

+ +
    +
  • 100: wrong usage
  • +
  • 111: system call failed
  • +
  • 126: failed to exec dnsfunneld
  • +
  • 127: could not find the dnsfunneld executable
  • +
+ +

Options

+ +
    +
  • -v verbosity : verbosity of the +dnsfunneld program. This option is passed as is +to dnsfunneld. Default is 1. 0 suppresses warning +messages. Higher values may give more informational messages.
  • +
  • -d notif : readiness notification. This option +is passed as is to dnsfunneld, which will print a +newline to descriptor notif when it is ready. Default is no readiness +notification.
  • +
  • -U : read an uid in the UID environment variable and a gid +in the GID environment variable, and drop privileges to that uid/gid.
  • +
  • -u uid : drop privileges to numerical uid +uid.
  • +
  • -g gid : drop privileges to numerical gid +gid.
  • +
  • -i ip:port : bind the socket to +IPv4 ip and port port. Default for ip is +127.0.0.1; default for port is 53.
  • +
  • -R root : chroot to root. Note that +this option only increases security if you also drop privileges.
  • +
  • -b bufsize : try and reserve a kernel buffer +size of bufsize bytes for the socket. Default is 131072. If the given +bufsize is 0, then dnsfunnel-daemon will use whatever the +default is for your kernel.
  • +
  • -f cachelist : Use cachelist as the +file that dnsfunneld reads its cache addresses +from. Default is /run/dnsfunnel-caches, or file +if the --with-cachelist=file option has been given to the +configure script at build time.
  • +
+ +

+ The other options control the activation or deactivation of various +dnsfunneld features: +

+
  • -T : Do not activate truncation of responses. This is +the default.
  • +
  • -t : If a DNS response is bigger than 510 bytes, +truncate its last resource records until it fits into 510 bytes and can +be sent in a UDP packet.
  • +
  • -N : Do not activate nxdomain workaround. This is the +default.
  • +
  • -n : Activate nxdomain workaround. When receiving an A +(resp. AAAA) query to forward, also make an AAAA (resp. A) query, and adjust +the response accordingly. Some DNS servers incorrectly answer NXDOMAIN when +they should just answer NODATA, and querying for another, existing, record +type for the same domain allows dnsfunneld to tell the difference between a +real NXDOMAIN (in which case that response is forwarded to the client) and +an incorrect one (in which case NODATA is answered to the client instead).
  • +
  • Other options may be added in the future.
  • + + + + diff --git a/doc/dnsfunnel-translate.html b/doc/dnsfunnel-translate.html new file mode 100644 index 0000000..9dde27d --- /dev/null +++ b/doc/dnsfunnel-translate.html @@ -0,0 +1,70 @@ + + + + + + dnsfunnel: the dnsfunnel-translate program + + + + + + +

    +dnsfunnel
    +Software
    +skarnet.org +

    + +

    The dnsfunnel-translate program

    + +

    +dnsfunnel-translate translates a file in resolv.conf +format to a file in dnsfunneld format. +

    + +

    Interface

    + +
    +     dnsfunnel-translate [ -i inputfile ] [ -o outputfile ] [ -x ignoredip ]
    +
    + +
      +
    • dnsfunnel-translate opens inputfile and parses it. It reads +and processes lines beginning with nameserver followed by an IP +address (v4 or v6). It ignores other lines.
    • +
    • It writes the IP addresses, without the nameserver keyword, +to outputfile, one per line.
    • +
    • If a nameserver address is ignoredip, it does not +get printed to outputfile +
    • dnsfunnel-translate exits 0 when it has finished processing +inputfile.
    • +
    + +

    Exit codes

    + +
      +
    • 0: success
    • +
    • 1: no suitable IP found in the input file
    • +
    • 100: wrong usage
    • +
    • 111: system call failed
    • +
    + +

    Options

    + +
      +
    • -i inputfile : process inputfile. +Default is /etc/resolv.conf.
    • +
    • -o outputfile : write the result to +outputfile. Default is /run/dnsfunnel-caches, or file +if the --with-cachelist=file option has been given to the +configure script at build time.
    • +
    • -x ignoredip : ignore the ignoredip +IPv4 address if it shows up as a nameserver in inputfile. +Default is 127.0.0.1. The point of this option is to avoid copying +to outputfile the IPv4 address that the +dnsfunnel-daemon daemon will be bound to.
    • +
    + + + diff --git a/doc/dnsfunneld.html b/doc/dnsfunneld.html new file mode 100644 index 0000000..006a6d6 --- /dev/null +++ b/doc/dnsfunneld.html @@ -0,0 +1,160 @@ + + + + + + dnsfunnel: the dnsfunnel-daemon program + + + + + + +

    +dnsfunnel
    +Software
    +skarnet.org +

    + +

    The dnsfunneld program

    + +

    +dnsfunneld is a small DNS forwarder daemon. It receives +DNS queries from clients, then forwards them to one or more DNS caches. +It collects the responses and forwards them back to the clients. Depending +on the options it is given, it may perform light processing on the +queries, the responses, or both. +

    + +

    Interface

    + +
    +     dnsfunneld [ -v verbosity ] [ -d notif ] [ -o ops ] cachelist
    +
    + +
      +
    • dnsfunneld reads the cachelist file, expecting to find +a list of IP (v4 or v6) addresses, one per line. These addresses are the +DNS caches it will forward the queries to.
    • +
    • dnsfunneld expects to have a bound UDP inet domain socket as +its standard input. It expects to receive packets no more than 512 +bytes long, only containing DNS normal queries (QUERY) for the IN +class.
    • +
    • Depending on ops, dnsfunneld may send additional queries +to the caches listed in cachelist. It handles the answers +internally: the additional queries are invisible to clients.
    • +
    • dnsfunneld is a long-lived process.
    • +
    + +

    Signals

    + +
      +
    • SIGHUP: read the cachelist file again, updating its +in-memory cache list. In-flight queries are still handled by the old +list; the new list will only apply for queries arriving after the SIGHUP.
    • +
    • SIGTERM: enter lame-duck mode, do not accept any more queries. When +all in-flight queries have been answered, exit 0. +
    + +

    Exit codes

    + +
      +
    • 0: SIGTERM received and all in-flight queries have been answered
    • +
    • 100: wrong usage
    • +
    • 111: system call failed
    • +
    + +

    Options

    + +
      +
    • -v verbosity : verbosity. +Default is 1. 0 suppresses warning messages. Higher values may give more +informational messages in the future.
    • +
    • -d notif : readiness notification. When +dnsfunneld is ready to process queries, write a newline to file descriptor +notif. notif must be 3 or greater. Default is no notification +at all.
    • +
    • -o ops : perform various operations on +queries. ops is a decimal integer that is treated as a bitfield. +Default is 0. Operations are listed below.
    • +
    + +

    DNS forwarding behaviour

    + +
      +
    • When it receives a query, dnsfunneld forwards it to the first DNS cache +in the list it has read from the cachelist file.
    • +
    • If it receives a response with the TC bit, it resends the query over TCP.
    • +
    • If it receives a suitable response within a given time frame, it forwards +it to the client.
    • +
    • On SERVFAIL, or after a timeout of 1 second, it gives up and sends the +query to the next DNS cache in its list. (If the first cache answers after the time +frame, the answer is dropped.) +
    • If dnsfunneld reaches the end of its cache list, it retries the whole +procedure starting at the beginning of the list, but with a timeout of 3 seconds. +Caches that returned a SERVFAIL are crossed off the list for that query.
    • +
    • If the second pass fails again, dnsfunneld tries again with a timeout of +11 seconds, then with a timeout of 45 seconds. If all of this fails, it returns +a SERVFAIL to the client.
    • +
    • A machine should not use a DNS cache that is too far away. In normal operation, +a timeout of 1 second should be more than enough for a cache to answer, if it already +has the answer. If the answer is absent from all caches and it takes them more than +1 second to resolve the query, the answer will be obtained by dnsfunneld in the second +pass. Realistically, the only cases when caches that are not at the top of the list +are used are: +
        +
      • obscure DNS queries, not likely to be in the caches, and that will take +time to resolve;
      • +
      • or the first cache has really gone to lunch.
      • +
      +
    + +

    dnsfunneld operations

    + +

    + ops is an integer used as a bitfield. Depending on which bits are set, +various operations are performed on queries or answers, slightly modifying the +behaviour described above. +

    + +
      +
    • bit 0: activate truncation. If a DNS response is more than 510 bytes +long, dnsfunneld will truncate the last resource records in the response, +until it fits into 510 bytes and can be given to the client in a UDP packet. +The structure of a DNS packet makes it so the RRs are listed in order of +decreasing importance, so keeping as many RRs as will fit in 510 bytes +without reordering them is the natural way of truncating a response.
    • +
    • bit 1: activate workaround for some servers that incorrectly report +NXDOMAIN when they're asked for an AAAA record, and no such record exists +for the domain but an A record exists. When that bit is set in ops, +for every A or AAAA query dnsfunneld receives and forwards, it also sends +an additional AAAA or A query for the same domain. If the main query returns +NXDOMAIN, dnsfunneld waits for the response to the auxiliary query: if this +response is not NXDOMAIN, then dnsfunneld answers NODATA to the client instead +of NXDOMAIN. Be aware that activating this workaround can practically double +the number of queries sent to the DNS caches, and may cause additional delays +before the clients get their answers.
    • +
    + +

    Notes

    + +
      +
    • The point of dnsfunneld is to work around ill-designed or unreliable +client setups with several motley nameserver entries in +/etc/resolv.conf. By converting those entries to a cache list +instead (via the dnsfunnel-translate +program), running dnsfunneld on 127.0.0.1, and enforcing a policy of one +single nameserver 127.0.0.1 entry in /etc/resolv.conf, +the setup can be made more reliable and more consistent.
    • +
    • Such a policy can be automated, for instance, by listening to +changes on the /etc/resolv.conf file (via inotify or kqueue, +depending on your system) and immediately calling +dnsfunnel-translate, sending +a SIGHUP to dnsfunneld, and forcefully overwriting /etc/resolv.conf.
    • +
    • It is easy to send a SIGHUP to dnsfunneld even without knowing its +pid, if it is run under a process supervision system such as +s6.
    • +
    + + + diff --git a/doc/index.html b/doc/index.html new file mode 100644 index 0000000..fe8e8d4 --- /dev/null +++ b/doc/index.html @@ -0,0 +1,116 @@ + + + + + + dnsfunnel - A small local DNS cache daemon + + + + + + +

    +Software
    +skarnet.org +

    + +

    dnsfunnel

    + +

    What is it ?

    + +

    + dnsfunnel is a small daemon listening to DNS client requests over UDP +(typically from the libc's +getaddrinfo() +function) and forwards them to a list of DNS caches. It provides the +client with the first answer it gets, trimming the answer so it fits +in an UDP packet. +

    + +

    + dnsfunnel is especially useful for distributions using the +musl libc, which does not support +TCP DNS transport. It was originally written to be used in the +Alpine Linux distribution. +

    + +
    + +

    Installation

    + +

    Requirements

    + +
      +
    • A POSIX-compliant system with a standard C development environment. +The system must also support chroot()
    • +
    • GNU make, version 3.81 or later
    • +
    • skalibs version +2.10.0.0 or later. It's a build-time requirement. It's also a run-time +requirement if you link against the shared version of the skalibs +library.
    • +
    • s6-dns version +2.3.3.0 or later. It's a build-time requirement. It's also a run-time +requirement if you link against the shared version of the s6dns +library.
    • + +
    + +

    Licensing

    + +

    + dnsfunnel is free software. It is available under the +ISC license. +

    + +

    Download

    + +
      +
    • The current released version of dnsfunnel is +0.0.1.0. + (This is a lie. +dnsfunnel is in alpha development at the moment, and only +available through git.)
    • +
    • Alternatively, you can checkout a copy of the +dnsfunnel +git repository: +
       git clone git://git.skarnet.org/dnsfunnel 
    • +
    • There's also a +GitHub mirror +of the dnsfunnel git repository.
    • +
    + +

    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 dnsfunnel and the current one.
    • +
    + +
    + +

    Reference

    + +

    Commands

    + + + +

    Related resources

    + +
      +
    • dnsfunnel is discussed on the +skaware mailing-list.
    • +
    + + + diff --git a/doc/upgrade.html b/doc/upgrade.html new file mode 100644 index 0000000..6b46bde --- /dev/null +++ b/doc/upgrade.html @@ -0,0 +1,28 @@ + + + + + + dnsfunnel: how to upgrade + + + + + + +

    +dnsfunnel
    +Software
    +skarnet.org +

    + +

    What has changed in dnsfunnel

    + +

    in 0.0.1.0

    + +
      +
    • Initial release.
    • +
    + + + diff --git a/package/deps-build b/package/deps-build new file mode 100644 index 0000000..6b9f75d --- /dev/null +++ b/package/deps-build @@ -0,0 +1,2 @@ +/package/prog/skalibs +/package/web/s6-dns diff --git a/package/deps.mak b/package/deps.mak new file mode 100644 index 0000000..29eaab1 --- /dev/null +++ b/package/deps.mak @@ -0,0 +1,16 @@ +# +# This file has been generated by tools/gen-deps.sh +# + +src/dnsfunnel/dnsfunnel-daemon.o src/dnsfunnel/dnsfunnel-daemon.lo: src/dnsfunnel/dnsfunnel-daemon.c src/include/dnsfunnel/config.h +src/dnsfunnel/dnsfunnel-translate.o src/dnsfunnel/dnsfunnel-translate.lo: src/dnsfunnel/dnsfunnel-translate.c src/include/dnsfunnel/config.h +src/dnsfunnel/dnsfunneld.o src/dnsfunnel/dnsfunneld.lo: src/dnsfunnel/dnsfunneld.c src/dnsfunnel/dnsfunneld.h +src/dnsfunnel/dnsfunneld_answer.o src/dnsfunnel/dnsfunneld_answer.lo: src/dnsfunnel/dnsfunneld_answer.c src/dnsfunnel/dnsfunneld.h +src/dnsfunnel/dnsfunneld_process.o src/dnsfunnel/dnsfunneld_process.lo: src/dnsfunnel/dnsfunneld_process.c src/dnsfunnel/dnsfunneld.h + +dnsfunnel-daemon: EXTRA_LIBS := -lskarnet +dnsfunnel-daemon: src/dnsfunnel/dnsfunnel-daemon.o +dnsfunnel-translate: EXTRA_LIBS := -lskarnet +dnsfunnel-translate: src/dnsfunnel/dnsfunnel-translate.o +dnsfunneld: EXTRA_LIBS := -ls6dns -lskarnet +dnsfunneld: src/dnsfunnel/dnsfunneld.o src/dnsfunnel/dnsfunneld_answer.o src/dnsfunnel/dnsfunneld_process.o diff --git a/package/info b/package/info new file mode 100644 index 0000000..0705998 --- /dev/null +++ b/package/info @@ -0,0 +1,4 @@ +package=dnsfunnel +version=0.0.1.0 +category=web +package_macro_name=DNSFUNNEL diff --git a/package/modes b/package/modes new file mode 100644 index 0000000..d42c9a1 --- /dev/null +++ b/package/modes @@ -0,0 +1,3 @@ +dnsfunnel-daemon 0755 +dnsfunneld 0755 +dnsfunnel-translate 0755 diff --git a/package/targets.mak b/package/targets.mak new file mode 100644 index 0000000..1a65a56 --- /dev/null +++ b/package/targets.mak @@ -0,0 +1,7 @@ +BIN_TARGETS := \ +dnsfunnel-daemon \ +dnsfunneld \ +dnsfunnel-translate + +LIBEXEC_TARGETS := + diff --git a/src/dnsfunnel/deps-exe/dnsfunnel-daemon b/src/dnsfunnel/deps-exe/dnsfunnel-daemon new file mode 100644 index 0000000..e7187fe --- /dev/null +++ b/src/dnsfunnel/deps-exe/dnsfunnel-daemon @@ -0,0 +1 @@ +-lskarnet diff --git a/src/dnsfunnel/deps-exe/dnsfunnel-translate b/src/dnsfunnel/deps-exe/dnsfunnel-translate new file mode 100644 index 0000000..e7187fe --- /dev/null +++ b/src/dnsfunnel/deps-exe/dnsfunnel-translate @@ -0,0 +1 @@ +-lskarnet diff --git a/src/dnsfunnel/deps-exe/dnsfunneld b/src/dnsfunnel/deps-exe/dnsfunneld new file mode 100644 index 0000000..90302a1 --- /dev/null +++ b/src/dnsfunnel/deps-exe/dnsfunneld @@ -0,0 +1,4 @@ +dnsfunneld_answer.o +dnsfunneld_process.o +-ls6dns +-lskarnet diff --git a/src/dnsfunnel/dnsfunnel-daemon.c b/src/dnsfunnel/dnsfunnel-daemon.c new file mode 100644 index 0000000..1df6a38 --- /dev/null +++ b/src/dnsfunnel/dnsfunnel-daemon.c @@ -0,0 +1,150 @@ +/* ISC license. */ + +#include + +#ifndef SKALIBS_HASCHROOT +# error "this program can only be built on systems that provide a chroot() function" +#endif + +#include /* chroot */ +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define USAGE "dnsfunnel-daemon [ -v verbosity ] [ -d notif ] [ -U | -u uid -g gid ] [ -i ip:port ] [ -R root ] [ -b bufsize ] [ -f cachelist ] [ -T | -t ] [ -N | -n ] " +#define dieusage() strerr_dieusage(100, USAGE) + +int main (int argc, char const *const *argv) +{ + int notif = 0 ; + unsigned int verbosity = 1 ; + unsigned int bufsize = 131072 ; + int flagU = 0 ; + uid_t uid = -1 ; + gid_t gid = -1 ; + char const *ipport = "127.0.0.1:53" ; + char const *newroot = 0 ; + char const *cachelist = DNSFUNNEL_DEFAULT_CACHELIST ; + uint32_t ops = 0 ; + PROG = "dnsfunnel-daemon" ; + { + subgetopt_t l = SUBGETOPT_ZERO ; + for (;;) + { + int opt = subgetopt_r(argc, argv, "v:d:Uu:g:i:R:b:f:TtNn", &l) ; + if (opt == -1) break ; + switch (opt) + { + case 'v' : if (!uint0_scan(l.arg, &verbosity)) dieusage() ; break ; + case 'd' : if (!uint0_scan(l.arg, (unsigned int *)¬if)) dieusage() ; break ; + case 'U' : flagU = 1 ; break ; + case 'u' : if (!uid0_scan(l.arg, &uid)) dieusage() ; break ; + case 'g' : if (!gid0_scan(l.arg, &gid)) dieusage() ; break ; + case 'i' : ipport = l.arg ; break ; + case 'R' : newroot = l.arg ; break ; + case 'b' : if (!uint0_scan(l.arg, &bufsize)) dieusage() ; break ; + case 'f' : cachelist = l.arg ; break ; + case 'T' : ops &= ~1 ; break ; + case 't' : ops |= 1 ; break ; + case 'N' : ops &= ~2 ; break ; + case 'n' : ops |= 2 ; break ; + default : dieusage() ; + } + } + argc -= l.ind ; argv += l.ind ; + } + + { + int fd ; + char ip[4] ; + uint16_t port ; + size_t pos = ip4_scan(ipport, ip) ; + if (!pos) dieusage() ; + if (ipport[pos] != ':') dieusage() ; + if (!uint160_scan(ipport + pos + 1, &port)) dieusage() ; + fd = socket_udp4() ; + if (fd < 0) strerr_diefu1sys(111, "create UDP socket") ; + if (socket_bind4_reuse(fd, ip, port) < 0) + { + char fmti[IP4_FMT] ; + char fmtp[UINT16_FMT] ; + fmti[ip4_fmt(fmti, ip)] = 0 ; + fmtp[uint16_fmt(fmtp, port)] = 0 ; + strerr_diefu4sys(111, "bind on ip ", fmti, " port ", fmtp) ; + } + if (bufsize) socket_tryreservein(fd, bufsize) ; + if (fd_move(0, fd) < 0) + strerr_diefu1sys(111, "move file descriptors") ; + } + + if (newroot) + { + if (chdir(newroot) < 0 || chroot(".") < 0) + strerr_diefu2sys(111, "chroot to ", newroot) ; + } + + if (flagU) + { + char const *x = getenv("UID") ; + if (x && !uid0_scan(x, &uid)) + strerr_dieinvalid(100, "UID") ; + x = getenv("GID") ; + if (x && !gid0_scan(x, &gid)) + strerr_dieinvalid(100, "GID") ; + } + if (gid != (gid_t)-1 && setgid(gid) < 0) + { + char fmt[GID_FMT] ; + fmt[gid_fmt(fmt, gid)] = 0 ; + strerr_diefu2sys(111, "setgid to ", fmt) ; + } + if (uid != (uid_t)-1 && setuid(uid) < 0) + { + char fmt[UID_FMT] ; + fmt[uid_fmt(fmt, uid)] = 0 ; + strerr_diefu2sys(111, "setuid to ", fmt) ; + } + + { + char const *newargv[10] = { "dnsfunneld" } ; + char const *newenvp[1] = { 0 } ; + unsigned int m = 1 ; + char fmtv[UINT_FMT] ; + char fmtn[UINT_FMT] ; + char fmto[UINT_FMT] ; + if (verbosity != 1) + { + fmtv[uint_fmt(fmtv, verbosity)] = 0 ; + newargv[m++] = "-v" ; + newargv[m++] = fmtv ; + } + if (notif) + { + fmtn[uint_fmt(fmtn, notif)] = 0 ; + newargv[m++] = "-d" ; + newargv[m++] = fmtn ; + } + if (ops) + { + fmto[uint_fmt(fmto, ops)] = 0 ; + newargv[m++] = "-o" ; + newargv[m++] = fmto ; + } + newargv[m++] = "--" ; + newargv[m++] = cachelist ; + newargv[m++] = 0 ; + xexec_ae(DNSFUNNEL_BINPREFIX "dnsfunneld", newargv, newenvp) ; + } +} diff --git a/src/dnsfunnel/dnsfunnel-translate.c b/src/dnsfunnel/dnsfunnel-translate.c new file mode 100644 index 0000000..70610b8 --- /dev/null +++ b/src/dnsfunnel/dnsfunnel-translate.c @@ -0,0 +1,93 @@ +/* ISC license. */ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#define USAGE "dnsfunnel-translate [ -i resolvconf ] [ -o cachelist ] [ -x ignoredip ]" +#define dieusage() strerr_dieusage(100, USAGE) + + +static size_t parse_nameservers (ip46_t *list, char const *file, char const *ignore) +{ + static char const zero[SKALIBS_IP_SIZE] = { 0 } ; + char buf[4096] ; + size_t n = 0, i = 0 ; + ssize_t len = openreadnclose(file, buf, 4095) ; + if (len < 0) strerr_diefu2sys(111, "open ", file) ; + buf[len++] = '\n' ; + while ((i < len) && (n < S6DNS_MAX_SERVERS)) + { + size_t j = byte_chr(buf + i, len - i, '\n') ; + if ((i + j < len) && (j > 13U) && !memcmp("nameserver", buf + i, 10)) + { + size_t k = 0 ; + while ((buf[i+10+k] == ' ') || (buf[i+10+k] == '\t')) k++ ; + if (k && ip46_scan(buf+i+10+k, list + n) + && memcmp(list[n].ip, zero, SKALIBS_IP_SIZE) + && (ip46_is6(list + n) || memcmp(list[n].ip, ignore, 4)) + ) n++ ; + } + i += j + 1 ; + } + return n ; +} + + +int main (int argc, char const *const *argv) +{ + ip46_t list[S6DNS_MAX_SERVERS] = { IP46_ZERO } ; + char const *resolvconf = "/etc/resolv.conf" ; + char const *cachelist = DNSFUNNEL_DEFAULT_CACHELIST ; + char ignore[4] = "\177\0\0\1" ; + size_t n ; + PROG = "dnsfunnel-translate" ; + { + subgetopt_t l = SUBGETOPT_ZERO ; + for (;;) + { + int opt = subgetopt_r(argc, argv, "i:o:x:", &l) ; + if (opt == -1) break ; + switch (opt) + { + case 'i' : resolvconf = l.arg ; break ; + case 'o' : cachelist = l.arg ; break ; + case 'x' : if (!ip4_scan(l.arg, ignore)) dieusage() ; break ; + default : dieusage() ; + } + } + argc -= l.ind ; argv += l.ind ; + } + + n = parse_nameservers(list, resolvconf, ignore) ; + if (!n) strerr_dief2x(1, "no suitable cache address in ", resolvconf) ; + + { + char buf[4096] ; + buffer b ; + int fd = openc_trunc(cachelist) ; + if (fd < 0) strerr_diefu2sys(111, "open ", cachelist) ; + buffer_init(&b, &buffer_write, fd, buf, 4096) ; + for (size_t i = 0 ; i < n ; i++) + { + char fmt[IP46_FMT] ; + size_t len = ip46_fmt(fmt, list + i) ; + fmt[len++] = '\n' ; + if (buffer_put(&b, fmt, len) < len) + strerr_diefu2sys(111, "write to ", cachelist) ; + } + if (!buffer_flush(&b)) + strerr_diefu2sys(111, "write to ", cachelist) ; + } + return 0 ; +} diff --git a/src/dnsfunnel/dnsfunneld.c b/src/dnsfunnel/dnsfunneld.c new file mode 100644 index 0000000..bd4dc89 --- /dev/null +++ b/src/dnsfunnel/dnsfunneld.c @@ -0,0 +1,305 @@ +/* ISC license. */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "dnsfunneld.h" + +#define USAGE "dnsfunneld [ -v verbosity ] [ -d notif ] [ -o operations ] cachelist" +#define dieusage() strerr_dieusage(100, USAGE) + +#define DNSFUNNELD_INPUT_MAX 64 + +unsigned int verbosity = 1 ; +static tain_t globaltto = TAIN_INFINITE_RELATIVE ; +static int cont = 1 ; +static char const *cachelistfile = 0 ; +static s6dns_ip46list_t cachelist ; +static uint32_t ops = 0 ; + +static inline void X (void) +{ + strerr_dief1x(101, "internal inconsistency. Please submit a bug-report.") ; +} + +static inline void s6dns_ip46list_copy (s6dns_ip46list_t *dst, ip46full_t const *src, size_t n) +{ + if (n >= S6DNS_MAX_SERVERS) n = S6DNS_MAX_SERVERS - 1 ; + for (size_t i = 0 ; i < n ; i++) + { + memcpy(dst->ip + i * SKALIBS_IP_SIZE, src[i].ip, SKALIBS_IP_SIZE) ; +#ifdef SKALIBS_IPV6_ENABLED + bitarray_poke(dst->is6, i, ip46_is6(src + i)) ; +#endif + } + memset(dst->ip + n * SKALIBS_IP_SIZE, 0, SKALIBS_IP_SIZE) ; +} + +static int load_cachelist (int initial) +{ + char buf[4096] ; + ip46full_t list[S6DNS_MAX_SERVERS] ; + size_t n ; + ssize_t r = openreadnclose_nb(cachelistfile, buf, 4095) ; + if (r < 0) return -1 ; + buf[r++] = 0 ; + ip46_scanlist(list, S6DNS_MAX_SERVERS, buf, &n) ; + if (!n) return -2 ; + s6dns_ip46list_copy(&cachelist, list, n) ; + return 0 ; +} + +static inline void handle_signals (void) +{ + for (;;) + { + switch (selfpipe_read()) + { + case -1 : strerr_diefu1sys(111, "read from selfpipe") ; + case 0 : return ; + case SIGTERM : cont = 0 ; break ; + case SIGHUP : + { + switch (load_cachelist(0)) + { + case 0 : query_process_reload() ; break ; + case -1 : strerr_warnwu2sys("read ", cachelistfile) ; break ; + case -2 : strerr_warnw2x("invalid cache list in ", cachelistfile) ; break ; + default : X() ; + } + break ; + } + default : X() ; + } + } +} + +static dfquery_t const dfquery_zero = DFQUERY_ZERO ; +static gensetdyn queries = GENSETDYN_INIT(dfquery_t, 16, 3, 8) ; +static uint32_t sentinel ; +#define inflight (gensetdyn_n(&queries) - 1) +#define QUERY(i) GENSETDYN_P(dfquery_t, &queries, i) + +void query_new (s6dns_domain_t const *d, uint16_t qtype, uint16_t id, uint32_t ip, uint16_t port, uint32_t procid) +{ + dfquery_t q = + { + .next = QUERY(sentinel)->next, + .xindex = 0, + .procid = procid, + .ip = ip, + .port = port, + .id = id, + .dt = S6DNS_ENGINE_ZERO + } ; + tain_t deadline ; + uint32_t i ; + if (!gensetdyn_new(&queries, &i)) + strerr_diefu1sys(111, "create new query") ; + tain_add_g(&deadline, &globaltto) ; + if (!s6dns_engine_init_g(&q.dt, &cachelist, S6DNS_O_RECURSIVE, d->s, d->len, qtype, &deadline)) + strerr_diefu1sys(111, "start new query") ; + *QUERY(i) = q ; + QUERY(sentinel)->next = i ; +} + +static inline void sanitize_and_new (char const *buf, unsigned int len, char const *ippack, uint16_t port) +{ + s6dns_domain_t d ; + uint32_t ip ; + unsigned int pos ; + s6dns_message_header_t hdr ; + s6dns_message_counts_t counts ; + uint16_t qtype ; + if (!s6dns_message_parse_init(&hdr, &counts, buf, len, &pos) + || hdr.qr + || hdr.opcode + || !hdr.rd + || hdr.counts.qd != 1 || hdr.counts.an || hdr.counts.ns || hdr.counts.nr + || !s6dns_message_parse_question(&counts, &d, &qtype, buf, len, &pos)) + return ; + uint32_unpack_big(ippack, &ip) ; + if (ops) query_process_question(ops, &d, qtype, hdr.id, ip, port) ; + else query_new(&d, qtype, hdr.id, ip, port, 0) ; +} + +int main (int argc, char const *const *argv) +{ + int spfd = -1 ; + int notif = -1 ; + PROG = "dnsfunneld" ; + { + subgetopt_t l = SUBGETOPT_ZERO ; + for (;;) + { + int opt = subgetopt_r(argc, argv, "v:d:o:", &l) ; + if (opt == -1) break ; + switch (opt) + { + case 'v' : if (!uint0_scan(l.arg, &verbosity)) dieusage() ; break ; + case 'd' : if (!uint0_scan(l.arg, (unsigned int *)¬if)) dieusage() ; break ; + case 'o' : if (!uint320_scan(l.arg, &ops)) dieusage() ; break ; + default : dieusage() ; + } + } + argc -= l.ind ; argv += l.ind ; + if (!argc) dieusage() ; + } + if (notif >= 0) + { + if (notif < 3) strerr_dief1x(100, "notification fd must be 3 or more") ; + if (fcntl(notif, F_GETFD) < 0) strerr_dief1sys(100, "invalid notification fd") ; + } + + if (ndelay_on(0) < 0) strerr_diefu1sys(111, "turn stdin non-blocking") ; + if (sig_ignore(SIGPIPE) < 0) strerr_diefu1sys(111, "ignore SIGPIPE") ; + cachelistfile = argv[0] ; + switch (load_cachelist(1)) + { + case 0 : break ; + case -1 : strerr_diefu2sys(111, "read ", cachelistfile) ; + case -2 : strerr_dief2x(100, "invalid cache list in ", cachelistfile) ; + default : X() ; + } + if (!s6dns_init()) strerr_diefu1sys(111, "s6dns_init") ; + spfd = selfpipe_init() ; + if (spfd < 0) strerr_diefu1sys(111, "init selfpipe") ; + { + sigset_t set ; + sigemptyset(&set) ; + sigaddset(&set, SIGTERM) ; + sigaddset(&set, SIGHUP) ; + if (selfpipe_trapset(&set) < 0) strerr_diefu1sys(111, "trap signals") ; + } + if (!gensetdyn_new(&queries, &sentinel)) + strerr_diefu1sys(111, "initialize query structure") ; + *QUERY(sentinel) = dfquery_zero ; + QUERY(sentinel)->next = sentinel ; + if (!query_process_init()) + strerr_diefu1sys(111, "initialize query processing") ; + tain_now_set_stopwatch_g() ; + + if (notif >= 0) + { + fd_write(notif, "\n", 1) ; + fd_close(notif) ; + } + + for (;;) + { + tain_t deadline = TAIN_INFINITE ; + uint32_t i = QUERY(sentinel)->next ; + uint32_t j = 2 ; + int r ; + iopause_fd x[2 + inflight] ; + + x[0].fd = spfd ; + x[0].events = IOPAUSE_READ ; + x[1].fd = 0 ; + x[1].events = (cont ? IOPAUSE_READ : 0) | (dfanswer_pending() ? IOPAUSE_WRITE : 0) ; + if (!x[1].events && !inflight) break ; + + while (i != sentinel) + { + dfquery_t *q = QUERY(i) ; + s6dns_engine_nextdeadline(&q->dt, &deadline) ; + x[j].fd = q->dt.fd ; + x[j].events = 0 ; + if (s6dns_engine_isreadable(&q->dt)) x[j].events |= IOPAUSE_READ ; + if (s6dns_engine_iswritable(&q->dt)) x[j].events |= IOPAUSE_WRITE ; + q->xindex = j++ ; + i = q->next ; + } + + r = iopause_g(x, j, &deadline) ; + if (r < 0) strerr_diefu1sys(111, "iopause") ; + + if (!r) + { + i = QUERY(sentinel)->next ; + j = sentinel ; + while (i != sentinel) + { + dfquery_t *q = QUERY(i) ; + uint32_t k = q->next ; + if (s6dns_engine_timeout_g(&q->dt)) + { + query_process_response_failure(ops, q) ; + QUERY(j)->next = k ; + stralloc_free(&q->dt.sa) ; + gensetdyn_delete(&queries, i) ; + } + else j = i ; + i = k ; + } + continue ; + } + + if (x[0].revents & IOPAUSE_READ) handle_signals() ; + + if (x[1].revents & IOPAUSE_WRITE) + { + int r = dfanswer_flush() ; + if (r < 0) strerr_diefu1sys(111, "send DNS answer to client") ; + } + + i = QUERY(sentinel)->next ; + j = sentinel ; + while (i != sentinel) + { + dfquery_t *q = QUERY(i) ; + uint32_t k = q->next ; + int r = s6dns_engine_event_g(&q->dt) ; + if (r) + { + if (r > 0) query_process_response_success(ops, q) ; + else query_process_response_failure(ops, q) ; + QUERY(j)->next = k ; + if (r > 0) s6dns_engine_free(&q->dt) ; + else stralloc_free(&q->dt.sa) ; + gensetdyn_delete(&queries, i) ; + } + else j = i ; + i = k ; + } + + if (x[0].revents & IOPAUSE_READ) + { + uint32_t n = DNSFUNNELD_INPUT_MAX ; + while (n--) + { + char ip[4] ; + uint16_t port ; + char buf[512] ; + ssize_t r = socket_recv4(0, buf, 512, ip, &port) ; + if (r < 0) + if (error_isagain(errno)) break ; + else strerr_diefu1sys(111, "socket_recv") ; + else if (!r) continue ; + else sanitize_and_new(buf, r, ip, port) ; + } + } + } + return 0 ; +} diff --git a/src/dnsfunnel/dnsfunneld.h b/src/dnsfunnel/dnsfunneld.h new file mode 100644 index 0000000..9fc0bbf --- /dev/null +++ b/src/dnsfunnel/dnsfunneld.h @@ -0,0 +1,44 @@ +/* ISC license. */ + +#ifndef DNSFUNNELD_H +#define DNSFUNNELD_H + +#include +#include + +#include + +#include +#include + +typedef struct dfquery_s dfquery_t, *dfquery_t_ref ; +struct dfquery_s +{ + uint32_t next ; + uint32_t xindex ; + uint32_t procid ; + uint32_t ip ; + uint16_t port ; + uint16_t id ; + s6dns_engine_t dt ; +} ; +#define DFQUERY_ZERO { .next = 0, .xindex = 0, .procid = 0, .ip = 0, .port = 0, .id = 0, .dt = S6DNS_ENGINE_ZERO } + +extern unsigned int verbosity ; +extern size_t dfanswer_pending (void) ; +extern int dfanswer_flush (void) ; +extern void dfanswer_fail (dfquery_t const *) ; +extern void dfanswer_nxdomain (dfquery_t const *) ; +extern void dfanswer_nodata (dfquery_t const *) ; +extern void dfanswer_pass (dfquery_t const *, char *, unsigned int) ; + + +extern void query_new (s6dns_domain_t const *, uint16_t, uint16_t, uint32_t, uint16_t, uint32_t) ; + +extern int query_process_init (void) ; +extern void query_process_reload (void) ; +extern void query_process_question (uint32_t, s6dns_domain_t const *, uint16_t, uint16_t, uint32_t, uint16_t) ; +extern void query_process_response_failure (uint32_t, dfquery_t const *) ; +extern void query_process_response_success (uint32_t, dfquery_t const *) ; + +#endif diff --git a/src/dnsfunnel/dnsfunneld_answer.c b/src/dnsfunnel/dnsfunneld_answer.c new file mode 100644 index 0000000..a6ee526 --- /dev/null +++ b/src/dnsfunnel/dnsfunneld_answer.c @@ -0,0 +1,132 @@ +/* ISC license. */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include "dnsfunneld.h" + +typedef struct dfanswer_s dfanswer_t, *dfanswer_t_ref ; +struct dfanswer_s +{ + char buf[512] ; + char ip[4] ; + uint16_t len ; + uint16_t port ; +} ; +#define DFANSWER_ZERO { .buf = { 0 }, .ip = "\0\0\0", .len = 0, .port = 0 } + +static genqdyn dfanswers = GENQDYN_INIT(dfanswer_t, 1, 8) ; + +size_t dfanswer_pending () +{ + return genqdyn_n(&dfanswers) ; +} + +static void dfanswer_push (char const *s, size_t len, uint32_t ip, uint16_t port) +{ + if (len > 510) + { + if (verbosity) + strerr_warnw1x("answer too big, dropping - enable truncation to avoid this") ; + } + else + { + dfanswer_t ans = { .len = len, .port = port } ; + uint16_pack_big(ans.buf, ans.len) ; + memcpy(ans.buf, s+2, len) ; + uint32_pack_big(ans.ip, ip) ; + if (!genqdyn_push(&dfanswers, &ans)) + strerr_diefu1sys(111, "queue answer to client") ; + } +} + +int dfanswer_flush () +{ + while (dfanswer_pending()) + { + dfanswer_t *ans = GENQDYN_PEEK(dfanswer_t, &dfanswers) ; + if (socket_send4(0, ans->buf, ans->len, ans->ip, ans->port) < 0) + return error_isagain(errno) ? (errno = 0, 0) : -1 ; + genqdyn_pop(&dfanswers) ; + } + return 1 ; +} + +void dfanswer_fail (dfquery_t const *q) +{ + char buf[510] ; + uint16_t len ; + s6dns_message_header_t hdr ; + uint16_unpack_big(q->dt.sa.s, &len) ; + memcpy(buf, q->dt.sa.s + 2, len) ; + s6dns_message_header_unpack(buf, &hdr) ; + hdr.id = q->id ; + hdr.qr = 1 ; + hdr.aa = 0 ; + hdr.tc = 0 ; + hdr.rd = 1 ; + hdr.ra = 1 ; + hdr.z = 0 ; + hdr.rcode = 2 ; /* servfail */ + s6dns_message_header_pack(buf, &hdr) ; + dfanswer_push(buf, len, q->ip, q->port) ; +} + +void dfanswer_nxdomain (dfquery_t const *q) +{ + char buf[510] ; + uint16_t len ; + s6dns_message_header_t hdr ; + uint16_unpack_big(q->dt.sa.s, &len) ; + memcpy(buf, q->dt.sa.s + 2, len) ; + s6dns_message_header_unpack(buf, &hdr) ; + hdr.id = q->id ; + hdr.qr = 1 ; + hdr.aa = 1 ; + hdr.tc = 0 ; + hdr.rd = 1 ; + hdr.ra = 1 ; + hdr.z = 0 ; + hdr.rcode = 3 ; /* nxdomain */ + s6dns_message_header_pack(buf, &hdr) ; + dfanswer_push(buf, len, q->ip, q->port) ; +} + +void dfanswer_nodata (dfquery_t const *q) +{ + char buf[510] ; + uint16_t len ; + s6dns_message_header_t hdr ; + uint16_unpack_big(q->dt.sa.s, &len) ; + memcpy(buf, q->dt.sa.s + 2, len) ; + s6dns_message_header_unpack(buf, &hdr) ; + hdr.id = q->id ; + hdr.qr = 1 ; + hdr.aa = 1 ; + hdr.tc = 0 ; + hdr.rd = 1 ; + hdr.ra = 1 ; + hdr.z = 0 ; + hdr.rcode = 0 ; /* success */ + s6dns_message_header_pack(buf, &hdr) ; + dfanswer_push(buf, len, q->ip, q->port) ; +} + +void dfanswer_pass (dfquery_t const *q, char *s, unsigned int len) +{ + s6dns_message_header_t hdr ; + s6dns_message_header_unpack(s, &hdr) ; + hdr.id = q->id ; + s6dns_message_header_pack(s, &hdr) ; + dfanswer_push(s, len, q->ip, q->port) ; +} diff --git a/src/dnsfunnel/dnsfunneld_process.c b/src/dnsfunnel/dnsfunneld_process.c new file mode 100644 index 0000000..9d90289 --- /dev/null +++ b/src/dnsfunnel/dnsfunneld_process.c @@ -0,0 +1,136 @@ +/* ISC license. */ + +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include "dnsfunneld.h" + +static gensetdyn rinfo = GENSETDYN_INIT(uint8_t, 16, 3, 8) ; +#define RINFO(i) GENSETDYN_P(uint8_t, &rinfo, i) + +int query_process_init () +{ + return 1 ; +} + +void query_process_reload () +{ +} + +void query_process_question (uint32_t ops, s6dns_domain_t const *d, uint16_t qtype, uint16_t id, uint32_t ip, uint16_t port) +{ + if (ops & 2 && (qtype == S6DNS_T_A || qtype == S6DNS_T_AAAA)) + { + uint32_t i ; + if (!gensetdyn_new(&rinfo, &i)) strerr_diefu1sys(111, "process query") ; + *RINFO(i) = (qtype == S6DNS_T_AAAA) << 7 ; + query_new(d, S6DNS_T_A, id, ip, port, i+1) ; + query_new(d, S6DNS_T_AAAA, id, ip, port, i+1) ; + } + else query_new(d, qtype, id, ip, port, 0) ; +} + +static inline unsigned int truncate_packet (char *s, unsigned int olen) +{ + s6dns_message_header_t hdr ; + s6dns_message_counts_t counts ; + unsigned int section ; + unsigned int pos ; + if (!s6dns_message_parse_init(&hdr, &counts, s, olen, &pos)) return 0 ; + if (hdr.rcode) return 0 ; + section = s6dns_message_parse_skipqd(&counts, s, olen, &pos) ; + while (section) + { + s6dns_message_rr_t rr ; + s6dns_message_counts_t newcounts = counts ; + unsigned int tmp = pos ; + if (!s6dns_message_parse_getrr(&rr, s, olen, &tmp)) return 0 ; + section = s6dns_message_parse_next(&newcounts, &rr, s, olen, &tmp) ; + if (tmp > 510) + { + hdr.counts.qd -= counts.qd ; + hdr.counts.an -= counts.an ; + hdr.counts.ns -= counts.ns ; + hdr.counts.nr -= counts.nr ; + s6dns_message_header_pack(s, &hdr) ; + return pos ; + } + pos = tmp ; + counts = newcounts ; + } + return olen ; +} + +static inline uint16_t extract_qtype (dfquery_t const *q) +{ + s6dns_domain_t name ; + uint16_t qtype ; + uint16_t len ; + s6dns_message_header_t hdr ; + s6dns_message_counts_t counts ; + unsigned int pos ; + uint16_unpack_big(q->dt.sa.s, &len) ; + if (!s6dns_message_parse_init(&hdr, &counts, q->dt.sa.s + 2, len, &pos)) return 0 ; + if (!s6dns_message_parse_question(&counts, &name, &qtype, q->dt.sa.s + 2, len, &pos)) return 0 ; + return qtype ; +} + +static int isnxdomain (dfquery_t const *q) +{ + s6dns_message_header_t hdr ; + s6dns_message_counts_t counts ; + unsigned int pos ; + if (!s6dns_message_parse_init(&hdr, &counts, s6dns_engine_packet(&q->dt), s6dns_engine_packetlen(&q->dt), &pos)) return 0 ; + return hdr.rcode == 3 ; +} + +static int input_event (dfquery_t const *q, unsigned int ev) +{ + static uint8_t const table[5][6] = + { + { 0x11, 0x03, 0x81, 0x02, 0x02, 0x04 }, + { 0x06, 0x06, 0x06, 0x05, 0x05, 0x05 }, + { 0x15, 0x25, 0x85, 0x06, 0x06, 0x06 }, + { 0x06, 0x06, 0x06, 0x25, 0x25, 0x45 }, + { 0x15, 0x45, 0x85, 0x06, 0x06, 0x06 } + } ; + uint8_t b = *RINFO(q->procid - 1) ; + uint8_t isaux = 3 * (b >> 7 != (extract_qtype(q) == S6DNS_T_AAAA)) ; + uint8_t state = (b >> isaux) & 7 ; + uint8_t c = table[state][ev + isaux] ; + state = c & 7 ; + *RINFO(q->procid - 1) = (b & ~(7 << isaux)) | (state << isaux) ; + if (c & 0x10) dfanswer_fail(q) ; + if (c & 0x20) dfanswer_nxdomain(q) ; + if (c & 0x40) dfanswer_nodata(q) ; + if (c & 0x80) dfanswer_pass(q, s6dns_engine_packet(&q->dt), s6dns_engine_packetlen(&q->dt)) ; + if (state >= 6) strerr_dief1x(101, "problem in main/aux transition table; please submit a bug-report.") ; + if (state == 5) gensetdyn_delete(&rinfo, q->procid - 1) ; + return !!(c & 0xf0) ; +} + +void query_process_response_failure (uint32_t ops, dfquery_t const *q) +{ + if (ops & 2 && q->procid && input_event(q, 0)) return ; + else dfanswer_fail(q) ; +} + +void query_process_response_success (uint32_t ops, dfquery_t const *q) +{ + if (ops & 2 && q->procid && input_event(q, 1 + !isnxdomain(q))) return ; + if (ops & 1 && s6dns_engine_packetlen(&q->dt) > 510) + { + unsigned int len = truncate_packet(s6dns_engine_packet(&q->dt), s6dns_engine_packetlen(&q->dt)) ; + if (!len) dfanswer_fail(q) ; + else dfanswer_pass(q, s6dns_engine_packet(&q->dt), len) ; + } + else dfanswer_pass(q, s6dns_engine_packet(&q->dt), s6dns_engine_packetlen(&q->dt)) ; +} diff --git a/tools/gen-deps.sh b/tools/gen-deps.sh new file mode 100755 index 0000000..27e5b3e --- /dev/null +++ b/tools/gen-deps.sh @@ -0,0 +1,93 @@ +#!/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= + libs= + while read dep ; do + if echo $dep | grep -q -e ^-l -e '^\${.*_LIB}' ; then + libs="$libs $dep" + else + deps="$deps src/$dir/$dep" + fi + done < src/$dir/deps-lib/$file + echo 'ifeq ($(strip $(STATIC_LIBS_ARE_PIC)),)' + echo "lib${file}.a.xyzzy:$deps" + echo else + echo "lib${file}.a.xyzzy:$(echo "$deps" | sed 's/\.o/.lo/g')" + echo endif + echo "lib${file}.so.xyzzy: EXTRA_LIBS :=$libs" + echo "lib${file}.so.xyzzy:$(echo "$deps" | sed 's/\.o/.lo/g')" + done + + for file in $(ls -1 src/$dir/deps-exe) ; do + deps= + libs= + while read dep ; do + if echo $dep | grep -q -- \\.o$ ; then + dep="src/$dir/$dep" + fi + if echo $dep | grep -q -e ^-l -e '^\${.*_LIB}' ; then + libs="$libs $dep" + else + deps="$deps $dep" + fi + done < src/$dir/deps-exe/$file + echo "$file: EXTRA_LIBS :=$libs" + 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 1.4.1