diff --git a/GNUmakefile b/GNUmakefile new file mode 100644 index 000000000000..4bc845ab3c89 --- /dev/null +++ b/GNUmakefile @@ -0,0 +1,4 @@ +# Nasty hack so that make clean works without configure being run +CONFIG_MK?=$(shell test -e config.mk && echo config.mk || echo config-null.mk) + +include Makefile diff --git a/Makefile b/Makefile index 3ab015fc28bf..aca2cb531769 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,19 @@ -include config.mk +PKG= openresolv +VERSION= 3.7.0 -NAME= openresolv -VERSION= 3.4.4 -PKG= ${NAME}-${VERSION} +# Nasty hack so that make clean works without configure being run +_CONFIG_MK!= test -e config.mk && echo config.mk || echo config-null.mk +CONFIG_MK?= ${_CONFIG_MK} +include ${CONFIG_MK} + +SBINDIR?= /sbin +SYSCONFDIR?= /etc +LIBEXECDIR?= /libexec/resolvconf +VARDIR?= /var/run/resolvconf +RCDIR?= /etc/rc.d +RESTARTCMD?= if ${RCDIR}/\1 status >/dev/null 2>\&1; then \ + ${RCDIR}/\1 restart; \ + fi INSTALL?= install SED?= sed @@ -17,31 +28,36 @@ TARGET= ${RESOLVCONF} ${SUBSCRIBERS} SRCS= ${TARGET:C,$,.in,} # pmake SRCS:= ${TARGET:=.in} # gmake -SED_PREFIX= -e 's:@PREFIX@:${PREFIX}:g' +SED_SBINDIR= -e 's:@SBINDIR@:${SBINDIR}:g' SED_SYSCONFDIR= -e 's:@SYSCONFDIR@:${SYSCONFDIR}:g' SED_LIBEXECDIR= -e 's:@LIBEXECDIR@:${LIBEXECDIR}:g' SED_VARDIR= -e 's:@VARDIR@:${VARDIR}:g' SED_RCDIR= -e 's:@RCDIR@:${RCDIR}:g' SED_RESTARTCMD= -e 's:@RESTARTCMD \(.*\)@:${RESTARTCMD}:g' +DISTPREFIX?= ${PKG}-${VERSION} +DISTFILEGZ?= ${DISTPREFIX}.tar.gz +DISTFILE?= ${DISTPREFIX}.tar.bz2 +FOSSILID?= current + .SUFFIXES: .in all: ${TARGET} .in: - ${SED} ${SED_PREFIX} ${SED_SYSCONFDIR} ${SED_LIBEXECDIR} \ + ${SED} ${SED_SBINDIR} ${SED_SYSCONFDIR} ${SED_LIBEXECDIR} \ ${SED_VARDIR} ${SED_RCDIR} ${SED_RESTARTCMD} \ $< > $@ clean: - rm -f ${TARGET} openresolv-${VERSION}.tar.bz2 + rm -f ${TARGET} distclean: clean - rm -f config.mk + rm -f config.mk ${DISTFILE} installdirs: -install: ${TARGET} +proginstall: ${TARGET} ${INSTALL} -d ${DESTDIR}${SBINDIR} ${INSTALL} -m ${BINMODE} resolvconf ${DESTDIR}${SBINDIR} ${INSTALL} -d ${DESTDIR}${SYSCONFDIR} @@ -49,18 +65,21 @@ install: ${TARGET} ${INSTALL} -m ${DOCMODE} resolvconf.conf ${DESTDIR}${SYSCONFDIR} ${INSTALL} -d ${DESTDIR}${LIBEXECDIR} ${INSTALL} -m ${DOCMODE} ${SUBSCRIBERS} ${DESTDIR}${LIBEXECDIR} + +maninstall: ${INSTALL} -d ${DESTDIR}${MANDIR}/man8 ${INSTALL} -m ${MANMODE} resolvconf.8 ${DESTDIR}${MANDIR}/man8 ${INSTALL} -d ${DESTDIR}${MANDIR}/man5 ${INSTALL} -m ${MANMODE} resolvconf.conf.5 ${DESTDIR}${MANDIR}/man5 -import: - rm -rf /tmp/${PKG} - ${INSTALL} -d /tmp/${PKG} - cp README ${SRCS} /tmp/${PKG} +install: proginstall maninstall -dist: import - cp configure Makefile resolvconf.conf /tmp/${PKG} - tar cvjpf ${PKG}.tar.bz2 -C /tmp ${PKG} - rm -rf /tmp/${PKG} - ls -l ${PKG}.tar.bz2 +import: + rm -rf /tmp/${DISTPREFIX} + ${INSTALL} -d /tmp/${DISTPREFIX} + cp README ${SRCS} /tmp/${DISPREFIX} + +dist: + fossil tarball --name ${DISTPREFIX} ${FOSSILID} ${DISTFILEGZ} + gunzip -c ${DISTFILEGZ} | bzip2 >${DISTFILE} + rm ${DISTFILEGZ} diff --git a/config-null.mk b/config-null.mk new file mode 100644 index 000000000000..6bf701d5aa6b --- /dev/null +++ b/config-null.mk @@ -0,0 +1 @@ +# This space left intentionally blank diff --git a/configure b/configure index 13b68e674180..190ee71a9f92 100644 --- a/configure +++ b/configure @@ -18,7 +18,7 @@ for x do --debug) DEBUG=$var;; --disable-debug) DEBUG=no;; --enable-debug) DEBUG=yes;; - --prefix) prefix=$var;; + --prefix) PREFIX=$var;; --sysconfdir) SYSCONFDIR=$var;; --bindir|--sbindir) SBINDIR=$var;; --libexecdir) LIBEXECDIR=$var;; @@ -41,68 +41,76 @@ for x do esac done +if [ -z "$LIBEXECDIR" ]; then + printf "Checking for directory /libexec ... " + if [ -d /libexec ]; then + echo "yes" + LIBEXECDIR=$PREFIX/libexec/resolvconf + else + echo "no" + LIBEXECDIR=$PREFIX/lib/resolvconf + fi +fi +if [ -z "$RUNDIR" ]; then + printf "Checking for directory /run ... " + if [ -d /run ]; then + echo "yes" + RUNDIR=/run + else + echo "no" + RUNDIR=/var/run + fi +fi + : ${SED:=sed} -: ${PREFIX:=$prefix} : ${SYSCONFDIR:=$PREFIX/etc} : ${SBINDIR:=$PREFIX/sbin} -: ${LIBEXECDIR:=$PREFIX/libexec} +: ${LIBEXECDIR:=$PREFIX/libexec/resolvconf} : ${STATEDIR:=/var} : ${RUNDIR:=$STATEDIR/run} : ${MANDIR:=${PREFIX:-/usr}/share/man} eval SYSCONFDIR="$SYSCONFDIR" eval SBINDIR="$SBINDIR" -eval LIBEXECDIR="$LIBEXECDIR/resolvconf" +eval LIBEXECDIR="$LIBEXECDIR" eval VARDIR="$RUNDIR/resolvconf" eval MANDIR="$MANDIR" CONFIG_MK=config.mk if [ -z "$BUILD" ]; then - BUILD=`uname -m`-`uname -s | tr '[:upper:]' '[:lower:]'` -fi -if [ -z "$HOST" ]; then - [ -z "$TARGET" ] && TARGET=$BUILD - HOST=$TARGET -fi -if [ -z "$TARGET" ]; then - [ -z "$HOST" ] && HOST=$BUILD - TARGET=$HOST -fi - -# Debian and Slackware have linux in different places when dealing with -# autoconf, so we deal with that here. -if [ -z "$OS" ]; then - case "$TARGET" in - *-linux-*|linux-*|*-linux|linux) OS=linux;; - esac + # autoconf target triplet: cpu-vendor-os + BUILD=$(uname -m)-unknown-$(uname -s | tr '[:upper:]' '[:lower:]') fi +: ${HOST:=$BUILD} if [ -z "$OS" ]; then - # Derive OS from cpu-manufacturer-os-kernel - CPU=${TARGET%%-*} - REST=${TARGET#*-} + echo "Deriving operating system from ... $HOST" + # Derive OS from cpu-vendor-[kernel-]os + CPU=${HOST%%-*} + REST=${HOST#*-} if [ "$CPU" != "$REST" ]; then - MANU=${REST%%-*} + VENDOR=${REST%%-*} REST=${REST#*-} - if [ "$MANU" != "$REST" ]; then + if [ "$VENDOR" != "$REST" ]; then + # Use kernel if given, otherwise os OS=${REST%%-*} - REST=${REST#*-} - if [ "$OS" != "$REST" ]; then - KERNEL=${REST%%-*} - else - # 3 tupple - KERNEL=$OS - OS=$MANU - MANU= - fi else # 2 tupple - OS=$MANU - MANU= + OS=$VENDOR + VENDOR= fi fi + + # Work with cpu-kernel-os, ie Debian + case "$VENDOR" in + linux*|kfreebsd*) OS=$VENDOR; VENDOR= ;; + esac + # Special case + case "$OS" in + gnu*) OS=hurd;; # No HURD support as yet + esac fi echo "Configuring openresolv for ... $OS" @@ -118,17 +126,36 @@ for x in SYSCONFDIR SBINDIR LIBEXECDIR VARDIR MANDIR; do echo "$x=$t $v" >>$CONFIG_MK done -if [ -e /etc/arch-release -a -d /etc/rc.d ]; then - echo "Overriding service status check for Arch Linux" - RCDIR=/etc/rc.d - RESTARTCMD="[ -e /var/run/daemons/\1 ] \&\& /etc/rc.d/\1 restart" - echo "yes" +if [ -z "$RESTARTCMD" ]; then + printf "Checking for systemd ... " + if [ -x /bin/systemctl ]; then + RESTARTCMD="/bin/systemctl try-restart \1" + echo "yes" + elif [ -x /usr/bin/systemctl ]; then + RESTARTCMD="/usr/bin/systemctl try-restart \1" + echo "yes" + else + echo "no" + fi +fi + +# Arch upgraded to systemd, so this check has to be just after systemd +# but higher than the others +if [ -z "$RESTARTCMD" ]; then + printf "Checking for Arch ... " + if [ -e /etc/arch-release -a -d /etc/rc.d ]; then + RCDIR=/etc/rc.d + RESTARTCMD="[ -e /var/run/daemons/\1 ] \&\& /etc/rc.d/\1 restart" + echo "yes" + else + echo "no" + fi fi if [ -z "$RESTARTCMD" ]; then printf "Checking for OpenRC ... " if [ -x /sbin/rc-service ]; then - RESTARTCMD="/sbin/rc-service -e \1 \&\& /sbin/rc-service \1 -- -Ds restart" + RESTARTCMD="if /sbin/rc-service -e \1; then /sbin/rc-service \1 -- -Ds restart; fi" echo "yes" else echo "no" @@ -138,7 +165,7 @@ if [ -z "$RESTARTCMD" ]; then printf "Checking for invoke-rc.d ... " if [ -x /usr/sbin/invoke-rc.d ]; then RCDIR=/etc/init.d - RESTARTCMD="/usr/sbin/invoke-rc.d --quiet \1 status >/dev/null 2>\&1 \&\& /usr/sbin/invoke-rc.d \1 restart" + RESTARTCMD="if /usr/sbin/invoke-rc.d --quiet \1 status >/dev/null 2>\&1; then /usr/sbin/invoke-rc.d \1 restart; fi" echo "yes" else echo "no" @@ -148,7 +175,19 @@ if [ -z "$RESTARTCMD" ]; then printf "Checking for service ... " if [ -x /sbin/service ]; then RCDIR=/etc/init.d - RESTARTCMD="/sbin/service \1 \&\& /sbin/service \1 restart" + RESTARTCMD="if /sbin/service \1; then /sbin/service \1 restart; fi" + echo "yes" + else + echo "no" + fi +fi +if [ -z "$RESTARTCMD" ]; then + printf "Checking for runit... " + if [ -x /bin/sv ]; then + RESTARTCMD="/bin/sv try-restart \1" + echo "yes" + elif [ -x /usr/bin/sv ]; then + RESTARTCMD="/usr/bin/sv try-restart \1" echo "yes" else echo "no" @@ -159,7 +198,7 @@ if [ -z "$RESTARTCMD" ]; then printf "Checking for $x ... " if [ -d $x ]; then RCDIR=$x - RESTARTCMD="$x/\1 status >/dev/null 2>\&1 \&\& $x/\1 restart" + RESTARTCMD="if $x/\1 status >/dev/null 2>\&1; then $x/\1 restart; fi" echo "yes" break else @@ -169,7 +208,7 @@ if [ -z "$RESTARTCMD" ]; then fi if [ -z "$RESTARTCMD" ]; then - echo "WARNING! No means of interacting with system services detected!" + echo "$0: WARNING: No means of interacting with system services detected!" exit 1 fi diff --git a/dnsmasq.in b/dnsmasq.in index 9c7fdeef9670..1b6ad16640c7 100644 --- a/dnsmasq.in +++ b/dnsmasq.in @@ -1,5 +1,5 @@ #!/bin/sh -# Copyright (c) 2007-2011 Roy Marples +# Copyright (c) 2007-2012 Roy Marples # All rights reserved # dnsmasq subscriber for resolvconf @@ -29,12 +29,13 @@ [ -f "@SYSCONFDIR@"/resolvconf.conf ] || exit 0 . "@SYSCONFDIR@/resolvconf.conf" || exit 1 [ -z "$dnsmasq_conf" -a -z "$dnsmasq_resolv" ] && exit 0 -[ -z "$RESOLVCONF" ] && eval "$(@PREFIX@/sbin/resolvconf -v)" +[ -z "$RESOLVCONF" ] && eval "$(@SBINDIR@/resolvconf -v)" NL=" " : ${dnsmasq_pid:=/var/run/dnsmasq.pid} [ -s "$dnsmasq_pid" ] || dnsmasq_pid=/var/run/dnsmasq/dnsmasq.pid +[ -s "$dnsmasq_pid" ] || unset dnsmasq_pid : ${dnsmasq_service:=dnsmasq} : ${dnsmasq_restart:=@RESTARTCMD ${dnsmasq_service}@} newconf="# Generated by resolvconf$NL" @@ -46,21 +47,18 @@ newresolv="$newconf" # so we need to validate a few things first. # Check for DBus support in the binary dbus=false -: ${dbus_pid:=/var/run/dbus/dbus.pid} -[ -s "$dbus_pid" ] || dbus_pid=/var/run/dbus.pid -[ -s "$dbus_pid" ] || dbus_pid=/var/run/dbus/pid -if [ -s "$dbus_pid" -a -s "$dnsmasq_pid" ]; then - if dnsmasq --version 2>/dev/null | \ - grep -q "^Compile time options.*[[:space:]]DBus[[:space:]]" +dbus_ex=false +dbus_introspect=$(dbus-send --print-reply --system \ + --dest=uk.org.thekelleys.dnsmasq \ + /uk/org/thekelleys/dnsmasq \ + org.freedesktop.DBus.Introspectable.Introspect \ + 2>/dev/null) +if [ $? = 0 ]; then + dbus=true + if printf %s "$dbus_introspect" | \ + grep -q '' then - # Sanity - check that dnsmasq and dbus are running - if kill -0 $(cat "$dbus_pid") 2>/dev/null && \ - kill -0 $(cat "$dnsmasq_pid") 2>/dev/null - then - dbus=true - newconf="$newconf$NL# Domain specific servers will" - newconf="$newconf be sent over dbus${NL}enable-dbus$NL" - fi + dbus_ex=true fi fi @@ -69,35 +67,95 @@ for n in $NAMESERVERS; do done dbusdest= +dbusdest_ex= +conf= for d in $DOMAINS; do dn="${d%%:*}" ns="${d#*:}" while [ -n "$ns" ]; do - if $dbus; then - SIFS=${IFS-y} OIFS=$IFS - IFS=. - set -- ${ns%%,*} - num="0x$(printf %02x $1 $2 $3 $4)" - if [ "$SIFS" = yi ]; then - unset IFS - else - IFS=$OIFS - fi - dbusdest="$dbusdest uint32:$(printf %u $num)" - dbusdest="$dbusdest string:$dn" - else - newconf="${newconf}server=/$dn/${ns%%,*}$NL" + n="${ns%%,*}" + if $dbus && ! $dbus_ex; then + case "$n" in + *.*.*.*) + SIFS=${IFS-y} OIFS=$IFS + IFS=. + set -- $n + num="0x$(printf %02x $1 $2 $3 $4)" + if [ "$SIFS" = y ]; then + unset IFS + else + IFS=$OIFS + fi + dbusdest="$dbusdest uint32:$(printf %u $num)" + dbusdest="$dbusdest string:$dn" + ;; + *:*%*) + # This version of dnsmasq won't accept + # scoped IPv6 addresses + dbus=false + ;; + *:*) + SIFS=${IFS-y} OIFS=$IFS bytes= front= back= + empty=false i=0 + IFS=: + set -- $n + while [ -n "$1" -o -n "$2" ]; do + addr="$1" + shift + if [ -z "$addr" ]; then + empty=true + continue + fi + i=$(($i + 1)) + while [ ${#addr} -lt 4 ]; do + addr="0${addr}" + done + byte1="$(printf %d 0x${addr%??})" + byte2="$(printf %d 0x${addr#??})" + if $empty; then + back="$back byte:$byte1 byte:$byte2" + else + front="$front byte:$byte1 byte:$byte2" + fi + done + while [ $i != 8 ]; do + i=$(($i + 1)) + front="$front byte:0 byte:0" + done + front="${front}$back" + if [ "$SIFS" = y ]; then + unset IFS + else + IFS=$OIFS + fi + dbusdest="${dbusdest}$front string:$dn" + ;; + *) + if ! $dbus_ex; then + dbus=false + fi + ;; + esac fi + dbusdest_ex="$dbusdest_ex${dbusdest_ex:+,}/$dn/$n" + conf="${conf}server=/$dn/$n$NL" [ "$ns" = "${ns#*,}" ] && break ns="${ns#*,}" done done +if $dbus; then + newconf="$newconf$NL# Domain specific servers will" + newconf="$newconf be sent over dbus${NL}" +else + newconf="$newconf$conf" +fi + # Try to ensure that config dirs exist if type config_mkdirs >/dev/null 2>&1; then config_mkdirs "$dnsmasq_conf" "$dnsmasq_resolv" else - @PREFIX@/sbin/resolvconf -D "$dnsmasq_conf" "$dnsmasq_resolv" + @SBINDIR@/resolvconf -D "$dnsmasq_conf" "$dnsmasq_resolv" fi changed=false @@ -110,14 +168,13 @@ if [ -n "$dnsmasq_conf" ]; then fi fi if [ -n "$dnsmasq_resolv" ]; then + # dnsmasq polls this file so no need to set changed=true if [ -f "$dnsmasq_resolv" ]; then if [ "$(cat "$dnsmasq_resolv")" != "$(printf %s "$newresolv")" ] then - changed=true printf %s "$newresolv" >"$dnsmasq_resolv" fi else - # dnsmasq polls this file so no need to set changed=true printf %s "$newresolv" >"$dnsmasq_resolv" fi fi @@ -126,9 +183,20 @@ if $changed; then eval $dnsmasq_restart fi if $dbus; then - $changed || kill -HUP $(cat "$dnsmasq_pid") + if [ -s "$dnsmasq_pid" ]; then + $changed || kill -HUP $(cat "$dnsmasq_pid") + fi # Send even if empty so old servers are cleared + if $dbus_ex; then + method=SetDomainServers + if [ -n "$dbusdest_ex" ]; then + dbusdest_ex="array:string:$dbusdest_ex" + fi + dbusdest="$dbusdest_ex" + else + method=SetServers + fi dbus-send --system --dest=uk.org.thekelleys.dnsmasq \ - /uk/org/thekelleys/dnsmasq uk.org.thekelleys.SetServers \ + /uk/org/thekelleys/dnsmasq uk.org.thekelleys.$method \ $dbusdest fi diff --git a/libc.in b/libc.in index 4c797e9067a9..3be134bfc83f 100644 --- a/libc.in +++ b/libc.in @@ -1,5 +1,5 @@ #!/bin/sh -# Copyright (c) 2007-2011 Roy Marples +# Copyright (c) 2007-2014 Roy Marples # All rights reserved # libc subscriber for resolvconf @@ -36,18 +36,18 @@ NL=" # sed may not be available, and this is faster on small files key_get_value() { - local key="$1" value= x= line= + local key="$1" x= line= shift if [ $# -eq 0 ]; then - while read line; do + while read -r line; do case "$line" in "$key"*) echo "${line##$key}";; esac done else - for x; do - while read line; do + for x do + while read -r line; do case "$line" in "$key"*) echo "${line##$key}";; esac @@ -56,6 +56,24 @@ key_get_value() fi } +keys_remove() +{ + local key x line found + + while read -r line; do + found=false + for key do + case "$line" in + "$key"*|"#"*|" "*|" "*|"") found=true;; + esac + $found && break + done + $found || echo "$line" + done +} + +local_nameservers="127.* 0.0.0.0 255.255.255.255 ::1" + # Support original resolvconf configuration layout # as well as the openresolv config file if [ -f "$SYSCONFDIR"/resolvconf.conf ]; then @@ -64,12 +82,11 @@ elif [ -d "$SYSCONFDIR"/resolvconf ]; then SYSCONFDIR="$SYSCONFDIR/resolvconf/resolv.conf.d" base="$SYSCONFDIR/resolv.conf.d/base" if [ -f "$base" ]; then - name_servers="$(key_get_value "nameserver " "$base")" - search_domains="$(key_get_value "search " "$base")" - if [ -z "$search_domains" ]; then - search_domains="$(key_get_value "domain " "$base")" - fi + prepend_nameservers="$(key_get_value "nameserver " "$base")" + domain="$(key_get_value "domain " "$base")" + prepend_search="$(key_get_value "search " "$base")" resolv_conf_options="$(key_get_value "options " "$base")" + resolv_conf_sortlist="$(key_get_value "sortlist " "$base")" fi if [ -f "$SYSCONFDIR"/resolv.conf.d/head ]; then resolv_conf_head="$(cat "${SYSCONFDIR}"/resolv.conf.d/head)" @@ -81,7 +98,7 @@ fi : ${resolv_conf:=/etc/resolv.conf} : ${libc_service:=nscd} : ${libc_restart:=@RESTARTCMD ${libc_service}@} -: ${list_resolv:=@PREFIX@/sbin/resolvconf -l} +: ${list_resolv:=@SBINDIR@/resolvconf -l} if [ "${resolv_conf_head-x}" = x -a -f "$SYSCONFDIR"/resolv.conf.head ]; then resolv_conf_head="$(cat "${SYSCONFDIR}"/resolv.conf.head)" fi @@ -89,6 +106,9 @@ if [ "${resolv_conf_tail-x}" = x -a -f "$SYSCONFDIR"/resolv.conf.tail ]; then resolv_conf_tail="$(cat "$SYSCONFDIR"/resolv.conf.tail)" fi +backup=true +signature="# Generated by resolvconf" + uniqify() { local result= @@ -104,6 +124,7 @@ uniqify() case "${resolv_conf_passthrough:-NO}" in [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1) + backup=false newest= for conf in "$IFACEDIR"/*; do if [ -z "$newest" -o "$conf" -nt "$newest" ]; then @@ -113,31 +134,70 @@ case "${resolv_conf_passthrough:-NO}" in [ -z "$newest" ] && exit 0 newconf="$(cat "$newest")$NL" ;; +/dev/null|[Nn][Uu][Ll][Ll]) + : ${resolv_conf_local_only:=NO} + if [ "$local_nameservers" = "127.* 0.0.0.0 255.255.255.255 ::1" ]; then + local_nameservers= + fi + # Need to overwrite our variables. + eval "$(@SBINDIR@/resolvconf -V)" + ;; + *) - [ -z "$RESOLVCONF" ] && eval "$(@PREFIX@/sbin/resolvconf -v)" - newsearch="$(uniqify $search_domains $SEARCH $search_domains_append)" + [ -z "$RESOLVCONF" ] && eval "$(@SBINDIR@/resolvconf -v)" + ;; +esac +case "${resolv_conf_passthrough:-NO}" in +[Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1) ;; +*) + : ${domain:=$DOMAIN} + newsearch="$(uniqify $prepend_search $SEARCH $append_search)" NS="$LOCALNAMESERVERS $NAMESERVERS" - newns="$(uniqify $name_servers $NS $name_servers_append)" + newns= + gotlocal=false + for n in $(uniqify $prepend_nameservers $NS $append_nameservers); do + add=true + islocal=false + for l in $local_nameservers; do + case "$n" in + $l) islocal=true; gotlocal=true; break;; + esac + done + if ! $islocal; then + case "${resolv_conf_local_only:-YES}" in + [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1) + $gotlocal && add=false;; + esac + fi + $add && newns="$newns $n" + done # Hold our new resolv.conf in a variable to save on temporary files - newconf="# Generated by resolvconf$NL" + newconf="$signature$NL" if [ -n "$resolv_conf_head" ]; then newconf="$newconf$resolv_conf_head$NL" fi - [ -n "$newsearch" ] && newconf="${newconf}search $newsearch$NL" + + [ -n "$domain" ] && newconf="${newconf}domain $domain$NL" + if [ -n "$newsearch" -a "$newsearch" != "$domain" ]; then + newconf="${newconf}search $newsearch$NL" + fi for n in $newns; do newconf="${newconf}nameserver $n$NL" done - # Now get any configured options - opts="$resolv_conf_options${resolv_conf_options:+ }" - opts="$opts$($list_resolv | key_get_value "options ")" - if [ -n "$opts" ]; then - newconf="${newconf}options" - for opt in $(uniqify $opts); do - newconf="${newconf} $opt" - done - newconf="$newconf$NL" + # Now add anything we don't care about such as sortlist and options + stuff="$($list_resolv | keys_remove nameserver domain search)" + if [ -n "$stuff" ]; then + newconf="$newconf$stuff$NL" + fi + + # Append any user defined ones + if [ -n "$resolv_conf_options" ]; then + newconf="${newconf}options $resolv_conf_options$NL" + fi + if [ -n "$resolv_conf_sortlist" ]; then + newconf="${newconf}sortlist $resolv_conf_sortlist$NL" fi if [ -n "$resolv_conf_tail" ]; then @@ -151,6 +211,22 @@ if [ -e "$resolv_conf" ]; then [ "$(cat "$resolv_conf")" = "$(printf %s "$newconf")" ] && exit 0 fi +# Change is good. +# If the old file does not have our signature, back it up. +# If the new file just has our signature, restore the backup. +if $backup; then + if [ "$newconf" = "$signature$NL" ]; then + if [ -e "$resolv_conf.bak" ]; then + newconf="$(cat "$resolv_conf.bak")" + fi + elif [ -e "$resolv_conf" ]; then + read line <"$resolv_conf" + if [ "$line" != "$signature" ]; then + cp "$resolv_conf" "$resolv_conf.bak" + fi + fi +fi + # Create our resolv.conf now (umask 022; printf %s "$newconf" >"$resolv_conf") eval $libc_restart diff --git a/named.in b/named.in index a89deb2693bc..43ceabb5e972 100644 --- a/named.in +++ b/named.in @@ -1,5 +1,5 @@ #!/bin/sh -# Copyright (c) 2007-2011 Roy Marples +# Copyright (c) 2007-2012 Roy Marples # All rights reserved # named subscriber for resolvconf @@ -29,7 +29,7 @@ [ -f "@SYSCONFDIR@"/resolvconf.conf ] || exit 0 . "@SYSCONFDIR@/resolvconf.conf" || exit 1 [ -z "$named_zones" -a -z "$named_options" ] && exit 0 -[ -z "$RESOLVCONF" ] && eval "$(@PREFIX@/sbin/resolvconf -v)" +[ -z "$RESOLVCONF" ] && eval "$(@SBINDIR@/resolvconf -v)" NL=" " @@ -40,6 +40,9 @@ then if [ -x "@RCDIR@"/bind9 ]; then # Debian and derivatives named_service=bind9 + elif [ -x "@RCDIR@"/rc.bind ]; then + # Slackware + named_service=rc.bind fi fi : ${named_service:=named} @@ -75,7 +78,7 @@ done if type config_mkdirs >/dev/null 2>&1; then config_mkdirs "$named_options" "$named_zones" else - @PREFIX@/sbin/resolvconf -D "$named_options" "$named_zones" + @SBINDIR@/resolvconf -D "$named_options" "$named_zones" fi # No point in changing files or reloading bind if the end result has not diff --git a/pdns_recursor.in b/pdns_recursor.in new file mode 100644 index 000000000000..2919e7d0ff37 --- /dev/null +++ b/pdns_recursor.in @@ -0,0 +1,72 @@ +#!/bin/sh +# Copyright (c) 2009-2011 Roy Marples +# All rights reserved + +# PowerDNS Recursor subscriber for resolvconf + +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided +# with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +[ -f "@SYSCONFDIR@"/resolvconf.conf ] || exit 0 +. "@SYSCONFDIR@/resolvconf.conf" || exit 1 +[ -z "$pdns_zones" ] && exit 0 +[ -z "$RESOLVCONF" ] && eval "$(@SBINDIR@/resolvconf -v)" +NL=" +" + +: ${pdns_service:=pdns_recursor} +: ${pdns_restart:=@RESTARTCMD ${pdns_service}@} + +newzones= + +# pds_recursor does not present support global forward servers, which +# does limit it's usefulness somewhat. +# If it did, the below code can be enabled, or something like it. +#for n in $NAMESERVERS; do +# newzones="$newzones${newzones:+,}$n" +#done +#[ -n "$newzones" ] && newzones=".=$newzones$NL" + +for d in $DOMAINS; do + newns= + ns="${d#*:}" + while [ -n "$ns" ]; do + newns="$newns${newns:+,}${ns%%,*}" + [ "$ns" = "${ns#*,}" ] && break + ns="${ns#*,}" + done + [ -n "$newns" ] && newzones="$newzones${d%%:*}=$newns$NL" +done + +# Try to ensure that config dirs exist +if type config_mkdirs >/dev/null 2>&1; then + config_mkdirs "$pdnsd_zones" +else + @SBINDIR@/resolvconf -D "$pdnsd_zones" +fi + +if [ ! -f "$pdns_zones" ] || \ + [ "$(cat "$pdns_zones")" != "$(printf %s "$newzones")" ] +then + printf %s "$newzones" >"$pdns_zones" + eval $pdns_restart +fi diff --git a/pdnsd.in b/pdnsd.in index 8be0f9a0d006..59a4755aff16 100644 --- a/pdnsd.in +++ b/pdnsd.in @@ -1,5 +1,5 @@ #!/bin/sh -# Copyright (c) 2010-2011 Roy Marples +# Copyright (c) 2010-2013 Roy Marples # All rights reserved # pdnsd subscriber for resolvconf @@ -29,7 +29,7 @@ [ -f "@SYSCONFDIR@"/resolvconf.conf ] || exit 0 . "@SYSCONFDIR@/resolvconf.conf" || exit 1 [ -z "$pdnsd_conf" -a -z "$pdnsd_resolv" ] && exit 0 -[ -z "$RESOLVCONF" ] && eval "$(@PREFIX@/sbin/resolvconf -v)" +[ -z "$RESOLVCONF" ] && eval "$(@SBINDIR@/resolvconf -v)" NL=" " @@ -48,7 +48,7 @@ remove_markers() sed "/^$m1/,/^$m2/d" $@ else for x; do - while read line; do + while read -r line; do case "$line" in "$m1"*) in_marker=1;; "$m2"*) in_marker=0;; @@ -82,19 +82,19 @@ change_file() return 0 } -newresolv="# Generated by resolvconf\n" +newresolv="# Generated by resolvconf$NL" changed=false # Try to ensure that config dirs exist if type config_mkdirs >/dev/null 2>&1; then config_mkdirs "$pdnsd_resolv" "$pdnsd_conf" else - @PREFIX@/sbin/resolvconf -D "$pdnsd_resolv" "$pdnsd_conf" + @SBINDIR@/resolvconf -D "$pdnsd_resolv" "$pdnsd_conf" fi if [ -n "$pdnsd_resolv" ]; then for n in $NAMESERVERS; do - newresolv="${newresolv}nameserver $n\n" + newresolv="${newresolv}nameserver $n$NL" done fi @@ -146,7 +146,7 @@ if [ -w "$pdnsd_conf" ]; then fi if change_file "$pdnsd_conf" "$cf"; then changed=true - fi + fi fi if [ -n "$pdnsd_resolv" ]; then diff --git a/resolvconf.8.in b/resolvconf.8.in index fff07014bfea..10dcf5dd7399 100644 --- a/resolvconf.8.in +++ b/resolvconf.8.in @@ -1,4 +1,4 @@ -.\" Copyright (c) 2007-2011 Roy Marples +.\" Copyright (c) 2007-2015 Roy Marples .\" All rights reserved .\" .\" Redistribution and use in source and binary forms, with or without @@ -22,8 +22,8 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd May 13, 2011 -.Dt RESOLVCONF 8 SMM +.Dd April 27, 2014 +.Dt RESOLVCONF 8 .Os .Sh NAME .Nm resolvconf @@ -34,11 +34,14 @@ .Nm .Op Fl m Ar metric .Op Fl p -.Fl a Ar interface No < Ns Pa file +.Op Fl x +.Fl a Ar interface Ns Op Ar .protocol +.No < Ns Pa file .Nm .Op Fl f -.Fl d Ar interface +.Fl d Ar interface Ns Op Ar .protocol .Nm +.Op Fl x .Fl il Ar pattern .Nm .Fl u @@ -63,7 +66,7 @@ file to via .Xr stdin 3 with the argument -.Fl a Ar interface +.Fl a Ar interface Ns Op Ar .protocol instead of the filesystem. .Nm then updates @@ -78,6 +81,20 @@ then will supply files that the resolver should be configured to include. .Pp .Nm +assumes it has a job to do. +In some situations +.Nm +needs to act as a deterrent to writing to +.Pa /etc/resolv.conf . +Where this file cannot be made immutable or you just need to toggle this +behaviour, +.Nm +can be disabled by adding +.Sy resolvconf Ns = Ns NO +to +.Xr resolvconf.conf 5 . +.Pp +.Nm can mark an interfaces .Pa resolv.conf as private. @@ -91,13 +108,21 @@ for how to configure .Nm to use a local name server. .Pp +.Nm +can mark an interfaces +.Pa resolv.conf +as exclusive. +Only the latest exclusive interface is used for processing, otherwise all are. +.Pp When an interface goes down, it should then call .Nm with -.Fl d Ar interface +.Fl d Ar interface.* arguments to delete the .Pa resolv.conf -file for the +file(s) for all the +.Ar protocols +on the .Ar interface . .Pp Here are some more options that @@ -119,7 +144,7 @@ is used to add interfaces. Ignore non existant interfaces. Only really useful for deleting interfaces. .It Fl i Ar pattern -List the interfaces, optionally matching +List the interfaces and protocols, optionally matching .Ar pattern , we have .Pa resolv.conf @@ -130,7 +155,8 @@ List the files we have. If .Ar pattern -is specified then we list the files for the interfaces that match it. +is specified then we list the files for the interfaces and protocols +that match it. .It Fl m Ar metric Set the metric of the interface when adding it, default of 0. Lower metrics take precedence. @@ -142,18 +168,28 @@ as private. .It Fl u Force .Nm -to update all it's subscribers. +to update all its subscribers. .Nm does not update the subscribers when adding a resolv.conf that matches what it already has for that interface. +.It Fl x +Mark the interface +.Pa resolv.conf +as exclusive when adding, otherwise only use the latest exclusive interface. .El .Pp .Nm -also has some options designed to be used by it's subscribers:- +also has some options designed to be used by its subscribers:- .Bl -tag -width indent .It Fl v Echo variables DOMAINS, SEARCH and NAMESERVERS so that the subscriber can configure the resolver easily. +.It Fl V +Same as +.Fl v +except that only the information configured in +.Xr resolvconf.conf 5 +is set. .El .Sh INTERFACE ORDERING For @@ -170,6 +206,29 @@ the operating systems lexical order. See .Xr resolvconf.conf 5 for details on these lists. +.Sh PROTOCOLS +Here are some suggested protocol tags to use for each +.Pa resolv.conf +file registered on an +.Ar interface Ns No :- +.Bl -tag -width indent +.It dhcp +Dynamic Host Configuration Protocol. +Initial versions of +.Nm +did not recommend a +.Ar protocol +tag be appended to the +.Ar interface +name. +When the protocol is absent, it is assumed to be the DHCP protocol. +.It ppp +Point-to-Point Protocol. +.It ra +IPv6 Router Advertisement. +.It dhcp6 +Dynamic Host Configuration Protocol, version 6. +.El .Sh IMPLEMENTATION NOTES If a subscriber has the executable bit then it is executed otherwise it is assumed to be a shell script and sourced into the current environment in a @@ -197,9 +256,15 @@ for the metric. Marks the interface .Pa resolv.conf as private. +.It Va IF_EXCLUSIVE +Marks the interface +.Pa resolv.conf +as exclusive. .El .Sh FILES .Bl -ohang +.It Pa /etc/resolv.conf.bak +Backup file of the original resolv.conf. .It Pa @SYSCONFDIR@/resolvconf.conf Configuration file for .Nm . @@ -224,9 +289,10 @@ resolvconf, as written by Thomas Hood. .Xr resolver 3 , .Xr stdin 3 .Sh AUTHORS -.An Roy Marples Aq roy@marples.name +.An Roy Marples Aq Mt roy@marples.name .Sh BUGS -Please report them to http://roy.marples.name/projects/openresolv +Please report them to +.Lk http://roy.marples.name/projects/openresolv .Pp .Nm does not validate any of the files given to it. diff --git a/resolvconf.conf.5.in b/resolvconf.conf.5.in index 0abbfb13c997..d4f654308e9e 100644 --- a/resolvconf.conf.5.in +++ b/resolvconf.conf.5.in @@ -1,4 +1,4 @@ -.\" Copyright (c) 2009-2011 Roy Marples +.\" Copyright (c) 2009-2015 Roy Marples .\" All rights reserved .\" .\" Redistribution and use in source and binary forms, with or without @@ -22,8 +22,8 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd April 21, 2011 -.Dt RESOLVCONF.CONF 5 SMM +.Dd March 20, 2015 +.Dt RESOLVCONF.CONF 5 .Os .Sh NAME .Nm resolvconf.conf @@ -42,12 +42,22 @@ must contain valid shell commands. Listed below are the standard .Nm variables that may be set. +If the values contain white space for special shell characters, +ensure they are quoted and escaped correctly. .Pp After updating this file, you may wish to run .Nm resolvconf -u to apply the new configuration. +.Pp +When a dynamically generated list is appended or prepended to, the whole +is made unique where left-most wins. .Sh RESOLVCONF OPTIONS .Bl -tag -width indent +.It Sy resolvconf +Set to NO to disable +.Nm resolvconf +from running any subscribers. +Defaults to YES. .It Sy interface_order These interfaces will always be processed first. If unset, defaults to the following:- @@ -56,16 +66,27 @@ If unset, defaults to the following:- These interfaces will be processed next, unless they have a metric. If unset, defaults to the following:- .D1 tap[0-9]* tun[0-9]* vpn vpn[0-9]* ppp[0-9]* ippp[0-9]* +.It Sy local_nameservers +If unset, defaults to the following:- +.D1 127.* 0.0.0.0 255.255.255.255 ::1 .It Sy search_domains Prepend search domains to the dynamically generated list. .It Sy search_domains_append Append search domains to the dynamically generated list. +.It Sy domain_blacklist +A list of domains to be removed from consideration. +To remove a domain, you can use foo.* +To remove a sub domain, you can use *.bar .It Sy name_servers Prepend name servers to the dynamically generated list. You should set this to 127.0.0.1 if you use a local name server other than libc. .It Sy name_servers_append Append name servers to the dynamically generated list. +.It Sy name_server_blacklist +A list of name servers to be removed from consideration. +The default is 0.0.0.0 as some faulty routers send it via DHCP. +To remove a block, you can use 192.168.* .It Sy private_interfaces These interfaces name servers will only be queried for the domains listed in their resolv.conf. @@ -73,6 +94,34 @@ Useful for VPN domains. This is equivalent to the .Nm resolvconf -p option. +.It Sy replace +Is a space separated list of replacement keywords. The syntax is this: +.Va $keyword Ns / Ns Va $match Ns / Ns Va $replacement +.Pp +Example, given this resolv.conf: +.D1 domain foo.org +.D1 search foo.org dead.beef +.D1 nameserver 1.2.3.4 +.D1 nameserver 2.3.4.5 +and this configuaration: +.D1 replace="search/foo*/bar.com nameserver/1.2.3.4/5.6.7.8 nameserver/2.3.4.5/" +you would get this resolv.conf instead: +.D1 domain foo.org +.D1 search bar.com +.D1 nameserver 5.6.7.8 +.It Sy replace_sub +Works the same way as +.Sy replace +except it works on each space separated value rather than the whole line, +so it's useful for the replacing a single domain within the search directive. +Using the same example resolv.conf and changing +.Sy replace +to +.Sy replace_sub , +you would get this resolv.conf instead: +.D1 domain foo.org +.D1 search bar.com dead.beef +.D1 nameserver 5.6.7.8 .It Sy state_dir Override the default state directory of .Pa @VARDIR@ . @@ -96,6 +145,32 @@ A list of libc resolver options, as specified in When set to YES the latest resolv.conf is written to .Sy resolv_conf without any alteration. +When set to /dev/null or NULL, +.Sy resolv_conf_local_only +is defaulted to NO, +.Sy local_nameservers +is unset unless overriden and only the information set in +.Nm +is written to +.Sy resolv_conf . +.It Sy resolv_conf_sortlist +A libc resolver sortlist, as specified in +.Xr resolv.conf 5 . +.It Sy resolv_conf_local_only +If a local name server is configured then the default is just to specify that +and ignore all other entries as they will be configured for the local +name server. +Set this to NO to also list non-local nameservers. +This will give you working DNS even if the local nameserver stops functioning +at the expense of duplicated server queries. +.It Sy append_nameservers +Append name servers to the dynamically generated list. +.It Sy prepend_nameservers +Prepend name servers to the dynamically generated list. +.It Sy append_search +Append search domains to the dynamically generated list. +.It Sy prepend_search +Prepend search domains to the dynamically generated list. .El .Sh SUBSCRIBER OPTIONS openresolv ships with subscribers for the name servers @@ -106,30 +181,37 @@ and .Xr unbound 8 . Each subscriber can create configuration files which should be included in in the subscribers main configuration file. +.Pp +To disable a subscriber, simply set it's name to NO. +For example, to disable the libc subscriber you would set: +.D1 libc=NO .Bl -tag -width indent .It Sy dnsmasq_conf -This file tells dnsmasq which nameservers to use for specific domains. +This file tells dnsmasq which name servers to use for specific domains. .It Sy dnsmasq_resolv -This file tells dnsmasq which nameservers to use for global lookups. +This file tells dnsmasq which name servers to use for global lookups. .Pp Example resolvconf.conf for dnsmasq: -.D1 nameservers=127.0.0.1 +.D1 name_servers=127.0.0.1 .D1 dnsmasq_conf=/etc/dnsmasq-conf.conf .D1 dnsmasq_resolv=/etc/dnsmasq-resolv.conf .Pp Example dnsmasq.conf: .D1 listen-address=127.0.0.1 +.D1 # If dnsmasq is compiled for DBus then we can take +.D1 # advantage of not having to restart dnsmasq. +.D1 enable-dbus .D1 conf-file=/etc/dnsmasq-conf.conf .D1 resolv-file=/etc/dnsmasq-resolv.conf .It Sy named_options Include this file in the named options block. -This file tells named which nameservers to use for global lookups. +This file tells named which name servers to use for global lookups. .It Sy named_zones Include this file in the named global scope, after the options block. -This file tells named which nameservers to use for specific domains. +This file tells named which name servers to use for specific domains. .Pp Example resolvconf.conf for named: -.D1 nameservers=127.0.0.1 +.D1 name_servers=127.0.0.1 .D1 named_options=/etc/named-options.conf .D1 named_zones=/etc/named-zones.conf .Pp @@ -147,12 +229,12 @@ setup to read .Pa pdnsd_resolv as documented below. .It Sy pdnsd_resolv -This file tells pdnsd about global nameservers. +This file tells pdnsd about global name servers. If this variable is not set then it's written to .Pa pdnsd_conf . .Pp Example resolvconf.conf for pdnsd: -.D1 nameservers=127.0.0.1 +.D1 name_servers=127.0.0.1 .D1 pdnsd_conf=/etc/pdnsd.conf .D1 # pdnsd_resolv=/etc/pdnsd-resolv.conf .Pp @@ -168,20 +250,64 @@ Example pdnsd.conf: .D1 # file="/etc/pdnsd-resolv.conf"; .D1 } .It Sy unbound_conf -This file tells unbound about specific and global nameservers. +This file tells unbound about specific and global name servers. +.It Sy unbound_insecure +When set to YES, unbound marks the domains as insecure, thus ignoring DNSSEC. .Pp Example resolvconf.conf for unbound: -.D1 nameservers=127.0.0.1 +.D1 name_servers=127.0.0.1 .D1 unbound_conf=/etc/unbound-resolvconf.conf .Pp Example unbound.conf: .D1 include: /etc/unbound-resolvconf.conf .El +.Sh SUBSCRIBER INTEGRATION +Not all distributions store the files the subscribers need in the same +locations. +For example, named service scripts have been called named, bind and rc.bind +and they could be located in a directory called /etc/rc.d, /etc/init.d or +similar. +Each subscriber attempts to automatically configure itself, but not every +distribution has been catered for. +Also, users could equally want to use a different version from the one +installed by default, such as bind8 and bind9. +To accomodate this, the subscribers have these files in configurable +variables, documented below. +.Pp +.Bl -tag -width indent +.It Sy dnsmasq_service +Location of the dnsmasq service. +.It Sy dnsmasq_restart +Command to restart the dnsmasq service. +.It Sy dnsmasq_pid +Location of the dnsmasq pidfile. +.It Sy libc_service +Location of the libc service. +.It Sy libc_restart +Command to restart the libc service. +.It Sy named_service +Location of the named service. +.It Sy named_restart +Command to restart the named service. +.It Sy pdnsd_restart +Command to restart the pdnsd service. +.It Sy unbound_service +Location of the unbound service. +.It Sy unbound_restart +Command to restart the unbound service. +.It Sy unbound_pid +Location of the unbound pidfile. +.El .Sh SEE ALSO -.Xr resolv.conf 5 +.Xr resolv.conf 5 , +.Xr resolvconf 8 and -.Xr resolvconf 8 . +.Xr sh 1 . .Sh AUTHORS -.An Roy Marples Aq roy@marples.name +.An Roy Marples Aq Mt roy@marples.name .Sh BUGS -Please report them to http://roy.marples.name/projects/openresolv +Each distribution is a special snowflake and likes to name the same thing +differently, namely the named service script. +.Pp +Please report them to +.Lk http://roy.marples.name/projects/openresolv diff --git a/resolvconf.in b/resolvconf.in index e24a90b9cb65..3b2b0f53fd83 100644 --- a/resolvconf.in +++ b/resolvconf.in @@ -1,5 +1,5 @@ #!/bin/sh -# Copyright (c) 2007-2011 Roy Marples +# Copyright (c) 2007-2015 Roy Marples # All rights reserved # Redistribution and use in source and binary forms, with or without @@ -28,6 +28,17 @@ RESOLVCONF="$0" SYSCONFDIR=@SYSCONFDIR@ LIBEXECDIR=@LIBEXECDIR@ VARDIR=@VARDIR@ + +# Disregard dhcpcd setting +unset interface_order state_dir + +# If you change this, change the test in VFLAG and libc.in as well +local_nameservers="127.* 0.0.0.0 255.255.255.255 ::1" + +dynamic_order="tap[0-9]* tun[0-9]* vpn vpn[0-9]* ppp[0-9]* ippp[0-9]*" +interface_order="lo lo[0-9]*" +name_server_blacklist="0.0.0.0" + # Support original resolvconf configuration layout # as well as the openresolv config file if [ -f "$SYSCONFDIR"/resolvconf.conf ]; then @@ -39,12 +50,17 @@ elif [ -d "$SYSCONFDIR/resolvconf" ]; then interface_order="$(cat "$SYSCONFDIR"/interface-order)" fi fi +TMPDIR="$VARDIR/tmp" IFACEDIR="$VARDIR/interfaces" METRICDIR="$VARDIR/metrics" PRIVATEDIR="$VARDIR/private" +EXCLUSIVEDIR="$VARDIR/exclusive" +LOCKDIR="$VARDIR/lock" -: ${dynamic_order:=tap[0-9]* tun[0-9]* vpn vpn[0-9]* ppp[0-9]* ippp[0-9]*} -: ${interface_order:=lo lo[0-9]*} +warn() +{ + echo "$*" >&2 +} error_exit() { @@ -64,6 +80,7 @@ usage() (DNS supplied via stdin in resolv.conf format) -m metric Give the added DNS information a metric -p Mark the interface as private + -x Mark the interface as exclusive -d \$INTERFACE Delete DNS information from the specified interface -f Ignore non existant interfaces -I Init the state dir @@ -84,16 +101,23 @@ usage() echo_resolv() { - local line= - [ -n "$1" -a -e "$IFACEDIR/$1" ] || return 1 + local line= OIFS="$IFS" + + [ -n "$1" -a -f "$IFACEDIR/$1" ] || return 1 echo "# resolv.conf from $1" # Our variable maker works of the fact each resolv.conf per interface # is separated by blank lines. # So we remove them when echoing them. - while read line; do - [ -n "$line" ] && echo "$line" + while read -r line; do + IFS="$OIFS" + if [ -n "$line" ]; then + # We need to set IFS here to preserve any whitespace + IFS='' + printf "%s\n" "$line" + fi done < "$IFACEDIR/$1" echo + IFS="$OIFS" } # Parse resolv.conf's and make variables @@ -101,22 +125,11 @@ echo_resolv() parse_resolv() { local line= ns= ds= search= d= n= newns= - local new=true iface= private=false p= + local new=true iface= private=false p= domain= l= islocal= - echo "DOMAINS=" - echo "SEARCH=\"$search_domains\"" - # let our subscribers know about global nameservers - for n in $name_servers; do - case "$n" in - 127.*|0.0.0.0|255.255.255.255|::1) :;; - *) newns="$newns${newns:+ }$n";; - esac - done - echo "NAMESERVERS=\"$newns\"" - echo "LOCALNAMESERVERS=" newns= - while read line; do + while read -r line; do case "$line" in "# resolv.conf from "*) if ${new}; then @@ -129,24 +142,34 @@ parse_resolv() cd "$IFACEDIR" private=false for p in $private_interfaces; do - if [ "$p" = "$iface" ]; then - private=true - break - fi + case "$iface" in + "$p"|"$p":*) private=true; break;; + esac done fi fi ;; "nameserver "*) - case "${line#* }" in - 127.*|0.0.0.0|255.255.255.255|::1) - echo "LOCALNAMESERVERS=\"\$LOCALNAMESERVERS ${line#* }\"" - continue - ;; - esac - ns="$ns${line#* } " + islocal=false + for l in $local_nameservers; do + case "${line#* }" in + $l) + islocal=true + echo "LOCALNAMESERVERS=\"\$LOCALNAMESERVERS ${line#* }\"" + break + ;; + esac + done + $islocal || ns="$ns${line#* } " ;; - "domain "*|"search "*) + "domain "*) + if [ -z "$domain" ]; then + domain="${line#* }" + echo "DOMAIN=\"$domain\"" + fi + search="${line#* }" + ;; + "search "*) search="${line#* }" ;; *) @@ -226,40 +249,67 @@ list_resolv() { [ -d "$IFACEDIR" ] || return 0 - local report=false list= retval=0 cmd="$1" + local report=false list= retval=0 cmd="$1" excl= shift + case "$IF_EXCLUSIVE" in + [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1) + if [ -d "$EXCLUSIVEDIR" ]; then + cd "$EXCLUSIVEDIR" + for i in *; do + if [ -f "$i" ]; then + list="${i#* }" + break + fi + done + fi + excl=true + ;; + *) + excl=false + ;; + esac + # If we have an interface ordering list, then use that. # It works by just using pathname expansion in the interface directory. if [ -n "$1" ]; then list="$*" $force || report=true - else + elif ! $excl; then cd "$IFACEDIR" for i in $interface_order; do - [ -e "$i" ] && list="$list $i" + [ -f "$i" ] && list="$list $i" + for ii in "$i":* "$i".*; do + [ -f "$ii" ] && list="$list $ii" + done done for i in $dynamic_order; do if [ -e "$i" -a ! -e "$METRICDIR/"*" $i" ]; then list="$list $i" fi + for ii in "$i":* "$i".*; do + if [ -f "$ii" -a ! -e "$METRICDIR/"*" $ii" ]; then + list="$list $ii" + fi + done done if [ -d "$METRICDIR" ]; then cd "$METRICDIR" for i in *; do - list="$list ${i#* }" + [ -f "$i" ] && list="$list ${i#* }" done fi list="$list *" fi cd "$IFACEDIR" + retval=1 for i in $(uniqify $list); do # Only list interfaces which we really have - if ! [ -e "$i" ]; then + if ! [ -f "$i" ]; then if $report; then echo "No resolv.conf for interface $i" >&2 - retval=$(($retval + 1)) + retval=2 fi continue fi @@ -269,23 +319,134 @@ list_resolv() else echo_resolv "$i" fi + [ $? = 0 -a "$retval" = 1 ] && retval=0 done [ "$cmd" = i -o "$cmd" = "-i" ] && echo return $retval } +list_remove() { + local list= e= l= result= found= retval=0 + + [ -z "$2" ] && return 0 + eval list=\"\$$1\" + shift + + set -f + for e; do + found=false + for l in $list; do + case "$e" in + $l) found=true;; + esac + $found && break + done + if $found; then + retval=$(($retval + 1)) + else + result="$result $e" + fi + done + set +f + echo "${result# *}" + return $retval +} + +echo_prepend() +{ + echo "# Generated by resolvconf" + if [ -n "$search_domains" ]; then + echo "search $search_domains" + fi + for n in $name_servers; do + echo "nameserver $n" + done + echo +} + +echo_append() +{ + echo "# Generated by resolvconf" + if [ -n "$search_domains_append" ]; then + echo "search $search_domains_append" + fi + for n in $name_servers_append; do + echo "nameserver $n" + done + echo +} + +replace() +{ + local r= k= f= v= val= sub= + + while read -r keyword value; do + for r in $replace; do + k="${r%%/*}" + r="${r#*/}" + f="${r%%/*}" + r="${r#*/}" + v="${r%%/*}" + case "$keyword" in + $k) + case "$value" in + $f) value="$v";; + esac + ;; + esac + done + val= + for sub in $value; do + for r in $replace_sub; do + k="${r%%/*}" + r="${r#*/}" + f="${r%%/*}" + r="${r#*/}" + v="${r%%/*}" + case "$keyword" in + $k) + case "$sub" in + $f) sub="$v";; + esac + ;; + esac + done + val="$val${val:+ }$sub" + done + printf "%s %s\n" "$keyword" "$val" + done +} + make_vars() { - eval "$(list_resolv -l "$@" | parse_resolv)" + local newdomains= d= dn= newns= ns= + + # Clear variables + DOMAIN= + DOMAINS= + SEARCH= + NAMESERVERS= + LOCALNAMESERVERS= + + if [ -n "$name_servers" -o -n "$search_domains" ]; then + eval "$(echo_prepend | parse_resolv)" + fi + if [ -z "$VFLAG" ]; then + IF_EXCLUSIVE=1 + list_resolv -i "$@" >/dev/null || IF_EXCLUSIVE=0 + eval "$(list_resolv -l "$@" | replace | parse_resolv)" + fi + if [ -n "$name_servers_append" -o -n "$search_domains_append" ]; then + eval "$(echo_append | parse_resolv)" + fi # Ensure that we only list each domain once - newdomains= for d in $DOMAINS; do dn="${d%%:*}" + list_remove domain_blacklist "$dn" >/dev/null || continue case " $newdomains" in *" ${dn}:"*) continue;; esac - newdomains="$newdomains${newdomains:+ }$dn:" newns= for nd in $DOMAINS; do if [ "$dn" = "${nd%%:*}" ]; then @@ -293,28 +454,50 @@ make_vars() while [ -n "$ns" ]; do case ",$newns," in *,${ns%%,*},*) ;; - *) newns="$newns${newns:+,}${ns%%,*}";; + *) list_remove name_server_blacklist \ + "${ns%%,*}" >/dev/null \ + && newns="$newns${newns:+,}${ns%%,*}";; esac [ "$ns" = "${ns#*,}" ] && break ns="${ns#*,}" done fi done - newdomains="$newdomains$newns" + if [ -n "$newns" ]; then + newdomains="$newdomains${newdomains:+ }$dn:$newns" + fi done + DOMAIN="$(list_remove domain_blacklist $DOMAIN)" + SEARCH="$(uniqify $SEARCH)" + SEARCH="$(list_remove domain_blacklist $SEARCH)" + NAMESERVERS="$(uniqify $NAMESERVERS)" + NAMESERVERS="$(list_remove name_server_blacklist $NAMESERVERS)" + LOCALNAMESERVERS="$(uniqify $LOCALNAMESERVERS)" + LOCALNAMESERVERS="$(list_remove name_server_blacklist $LOCALNAMESERVERS)" + echo "DOMAIN='$DOMAIN'" + echo "SEARCH='$SEARCH'" + echo "NAMESERVERS='$NAMESERVERS'" + echo "LOCALNAMESERVERS='$LOCALNAMESERVERS'" echo "DOMAINS='$newdomains'" - echo "SEARCH='$(uniqify $SEARCH)'" - echo "NAMESERVERS='$(uniqify $NAMESERVERS)'" - echo "LOCALNAMESERVERS='$(uniqify $LOCALNAMESERVERS)'" } force=false -while getopts a:Dd:fhIilm:puv OPT; do +VFLAG= +while getopts a:Dd:fhIilm:puvVx OPT; do case "$OPT" in f) force=true;; h) usage;; m) IF_METRIC="$OPTARG";; p) IF_PRIVATE=1;; + V) + VFLAG=1 + if [ "$local_nameservers" = \ + "127.* 0.0.0.0 255.255.255.255 ::1" ] + then + local_nameservers= + fi + ;; + x) IF_EXCLUSIVE=1;; '?') ;; *) cmd="$OPT"; iface="$OPTARG";; esac @@ -343,7 +526,7 @@ if [ "$cmd" = l -o "$cmd" = i ]; then fi # Not normally needed, but subscribers should be able to run independently -if [ "$cmd" = v ]; then +if [ "$cmd" = v -o -n "$VFLAG" ]; then make_vars "$iface" exit $? fi @@ -357,6 +540,7 @@ elif [ "$cmd" != u ]; then [ -n "$cmd" -a "$cmd" != h ] && usage "Unknown option $cmd" usage fi + if [ "$cmd" = a ]; then for x in '/' \\ ' ' '*'; do case "$iface" in @@ -372,78 +556,198 @@ if [ "$cmd" = a ]; then [ "$cmd" = a -a -t 0 ] && error_exit "No file given via stdin" fi -if [ ! -d "$IFACEDIR" ]; then - if [ ! -d "$VARDIR" ]; then - if [ -L "$VARDIR" ]; then - dir="$(readlink "$VARDIR")" - # link maybe relative - cd "${VARDIR%/*}" - if ! mkdir -m 0755 -p "$dir"; then - error_exit "Failed to create needed" \ - "directory $dir" - fi - else - if ! mkdir -m 0755 -p "$VARDIR"; then - error_exit "Failed to create needed" \ - "directory $VARDIR" - fi +if [ ! -d "$VARDIR" ]; then + if [ -L "$VARDIR" ]; then + dir="$(readlink "$VARDIR")" + # link maybe relative + cd "${VARDIR%/*}" + if ! mkdir -m 0755 -p "$dir"; then + error_exit "Failed to create needed" \ + "directory $dir" + fi + else + if ! mkdir -m 0755 -p "$VARDIR"; then + error_exit "Failed to create needed" \ + "directory $VARDIR" fi - fi - mkdir -m 0755 -p "$IFACEDIR" || \ - error_exit "Failed to create needed directory $IFACEDIR" -else - # Delete any existing information about the interface - if [ "$cmd" = d ]; then - cd "$IFACEDIR" - for i in $args; do - if [ "$cmd" = d -a ! -e "$i" ]; then - $force && continue - error_exit "No resolv.conf for" \ - "interface $i" - fi - rm -f "$i" "$METRICDIR/"*" $i" \ - "$PRIVATEDIR/$i" || exit $? - done fi fi -if [ "$cmd" = a ]; then +if [ ! -d "$IFACEDIR" ]; then + mkdir -m 0755 -p "$IFACEDIR" || \ + error_exit "Failed to create needed directory $IFACEDIR" + if [ "$cmd" = d ]; then + # Provide the same error messages as below + if ! ${force}; then + cd "$IFACEDIR" + for i in $args; do + warn "No resolv.conf for interface $i" + done + fi + ${force} + exit $? + fi +fi + +# An interface was added, changed, deleted or a general update was called. +# Due to exclusivity we need to ensure that this is an atomic operation. +# Our subscribers *may* need this as well if the init system is sub par. +# As such we spinlock at this point as best we can. +# We don't use flock(1) because it's not widely available and normally resides +# in /usr which we do our very best to operate without. +[ -w "$VARDIR" ] || error_exit "Cannot write to $LOCKDIR" +: ${lock_timeout:=10} +while true; do + if mkdir "$LOCKDIR" 2>/dev/null; then + trap 'rm -rf "$LOCKDIR";' EXIT + trap 'rm -rf "$LOCKDIR"; exit 1' INT QUIT ABRT SEGV ALRM TERM + echo $$ >"$LOCKDIR/pid" + break + fi + pid=$(cat "$LOCKDIR/pid") + if ! kill -0 "$pid"; then + warn "clearing stale lock pid $pid" + rm -rf "$LOCKDIR" + continue + fi + lock_timeout=$(($lock_timeout - 1)) + if [ "$lock_timeout" -le 0 ]; then + error_exit "timed out waiting for lock from pid $pid" + fi + sleep 1 +done + +case "$cmd" in +a) # Read resolv.conf from stdin resolv="$(cat)" + changed=false + changedfile=false # If what we are given matches what we have, then do nothing if [ -e "$IFACEDIR/$iface" ]; then - if [ "$(echo "$resolv")" = \ + if [ "$(echo "$resolv")" != \ "$(cat "$IFACEDIR/$iface")" ] then - exit 0 + changed=true + changedfile=true fi - rm "$IFACEDIR/$iface" + else + changed=true + changedfile=true fi - echo "$resolv" >"$IFACEDIR/$iface" || exit $? + + # Set metric and private before creating the interface resolv.conf file + # to ensure that it will have the correct flags [ ! -d "$METRICDIR" ] && mkdir "$METRICDIR" - rm -f "$METRICDIR/"*" $iface" + oldmetric="$METRICDIR/"*" $iface" + newmetric= if [ -n "$IF_METRIC" ]; then # Pad metric to 6 characters, so 5 is less than 10 while [ ${#IF_METRIC} -le 6 ]; do IF_METRIC="0$IF_METRIC" done - echo " " >"$METRICDIR/$IF_METRIC $iface" + newmetric="$METRICDIR/$IF_METRIC $iface" fi + rm -f "$METRICDIR/"*" $iface" + [ "$oldmetric" != "$newmetric" -a \ + "$oldmetric" != "$METRICDIR/* $iface" ] && + changed=true + [ -n "$newmetric" ] && echo " " >"$newmetric" + case "$IF_PRIVATE" in [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1) if [ ! -d "$PRIVATEDIR" ]; then [ -e "$PRIVATEDIR" ] && rm "$PRIVATEDIR" mkdir "$PRIVATEDIR" fi + [ -e "$PRIVATEDIR/$iface" ] || changed=true [ -d "$PRIVATEDIR" ] && echo " " >"$PRIVATEDIR/$iface" ;; *) if [ -e "$PRIVATEDIR/$iface" ]; then rm -f "$PRIVATEDIR/$iface" + changed=true fi ;; esac -fi + + oldexcl= + for x in "$EXCLUSIVEDIR/"*" $iface"; do + if [ -f "$x" ]; then + oldexcl="$x" + break + fi + done + case "$IF_EXCLUSIVE" in + [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1) + if [ ! -d "$EXCLUSIVEDIR" ]; then + [ -e "$EXCLUSIVEDIR" ] && rm "$EXCLUSIVEDIR" + mkdir "$EXCLUSIVEDIR" + fi + cd "$EXCLUSIVEDIR" + for x in *; do + [ -f "$x" ] && break + done + if [ "${x#* }" != "$iface" ]; then + if [ "$x" = "${x% *}" ]; then + x=10000000 + else + x="${x% *}" + fi + if [ "$x" = "0000000" ]; then + warn "exclusive underflow" + else + x=$(($x - 1)) + fi + if [ -d "$EXCLUSIVEDIR" ]; then + echo " " >"$EXCLUSIVEDIR/$x $iface" + fi + changed=true + fi + ;; + *) + if [ -f "$oldexcl" ]; then + rm -f "$oldexcl" + changed=true + fi + ;; + esac + + if $changedfile; then + printf "%s\n" "$resolv" >"$IFACEDIR/$iface" || exit $? + elif ! $changed; then + exit 0 + fi + unset changed changedfile oldmetric newmetric x oldexcl + ;; + +d) + # Delete any existing information about the interface + cd "$IFACEDIR" + changed=false + for i in $args; do + if [ -e "$i" ]; then + changed=true + elif ! ${force}; then + warn "No resolv.conf for interface $i" + fi + rm -f "$i" "$METRICDIR/"*" $i" \ + "$PRIVATEDIR/$i" \ + "$EXCLUSIVEDIR/"*" $i" || exit $? + done + if ! ${changed}; then + # Set the return code based on the forced flag + ${force} + exit $? + fi + unset changed i + ;; +esac + +case "${resolvconf:-YES}" in +[Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1) ;; +*) exit 0;; +esac eval "$(make_vars)" export RESOLVCONF DOMAINS SEARCH NAMESERVERS LOCALNAMESERVERS @@ -451,6 +755,11 @@ export RESOLVCONF DOMAINS SEARCH NAMESERVERS LOCALNAMESERVERS retval=0 for script in "$LIBEXECDIR"/*; do if [ -f "$script" ]; then + eval script_enabled="\$${script##*/}" + case "${script_enabled:-YES}" in + [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1) ;; + *) continue;; + esac if [ -x "$script" ]; then "$script" "$cmd" "$iface" else diff --git a/unbound.in b/unbound.in index 4ee0da720263..5752e6f2c412 100644 --- a/unbound.in +++ b/unbound.in @@ -1,5 +1,5 @@ #!/bin/sh -# Copyright (c) 2009-2011 Roy Marples +# Copyright (c) 2009-2014 Roy Marples # All rights reserved # unbound subscriber for resolvconf @@ -26,10 +26,12 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +unbound_insecure= + [ -f "@SYSCONFDIR@"/resolvconf.conf ] || exit 0 . "@SYSCONFDIR@/resolvconf.conf" || exit 1 [ -z "$unbound_conf" ] && exit 0 -[ -z "$RESOLVCONF" ] && eval "$(@PREFIX@/sbin/resolvconf -v)" +[ -z "$RESOLVCONF" ] && eval "$(@SBINDIR@/resolvconf -v)" NL=" " @@ -41,6 +43,11 @@ newconf="# Generated by resolvconf$NL" for d in $DOMAINS; do dn="${d%%:*}" ns="${d#*:}" + case "$unbound_insecure" in + [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1) + newconf="$newconf${NL}domain-insecure: \"$dn\"" + ;; + esac newconf="$newconf${NL}forward-zone:$NL name: \"$dn\"$NL" while [ -n "$ns" ]; do newconf="$newconf forward-addr: ${ns%%,*}$NL" @@ -60,7 +67,7 @@ fi if type config_mkdirs >/dev/null 2>&1; then config_mkdirs "$unbound_conf" else - @PREFIX@/sbin/resolvconf -D "$unbound_conf" + @SBINDIR@/resolvconf -D "$unbound_conf" fi if [ ! -f "$unbound_conf" ] || \