From d7fde65d0ed7c3a3d6b23959d8a7de22feb83032 Mon Sep 17 00:00:00 2001 From: "Pedro F. Giffuni" Date: Wed, 20 Nov 2019 22:20:11 +0000 Subject: [PATCH 1/3] Undo r354917 to correct the log: it was actually version 3.9.2 --- LICENSE | 23 ----- Makefile | 38 ++------ README | 11 +++ README.md | 64 ------------- configure | 88 ++++++++--------- dnsmasq.in | 10 +- libc.in | 24 ++--- named.in | 8 +- pdns_recursor.in | 17 ++-- pdnsd.in | 12 +-- resolvconf.conf | 2 +- resolvconf.conf.5.in | 157 ++++++++++-------------------- resolvconf.in | 223 ++++++++++++++++++------------------------- unbound.in | 2 +- 14 files changed, 244 insertions(+), 435 deletions(-) delete mode 100644 LICENSE create mode 100644 README delete mode 100644 README.md diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 758aec33805..00000000000 --- a/LICENSE +++ /dev/null @@ -1,23 +0,0 @@ -Copyright (c) 2007-2019 Roy Marples -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. -2. 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 AUTHOR 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 AUTHOR 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. diff --git a/Makefile b/Makefile index 6cfa6e4ea07..095779b95b0 100644 --- a/Makefile +++ b/Makefile @@ -10,7 +10,6 @@ SYSCONFDIR?= /etc LIBEXECDIR?= /libexec/resolvconf VARDIR?= /var/run/resolvconf -ECHO?= echo INSTALL?= install SED?= sed @@ -21,7 +20,7 @@ DOCMODE?= 0644 MANMODE?= 0444 RESOLVCONF= resolvconf resolvconf.8 resolvconf.conf.5 -SUBSCRIBERS= libc dnsmasq named pdnsd pdns_recursor unbound +SUBSCRIBERS= libc dnsmasq named pdnsd unbound TARGET= ${RESOLVCONF} ${SUBSCRIBERS} SRCS= ${TARGET:C,$,.in,} # pmake SRCS:= ${TARGET:=.in} # gmake @@ -43,7 +42,7 @@ DISTINFOSIGN= ${DISTINFO}.asc CKSUM?= cksum -a SHA256 PGP?= netpgp -GITREF?= HEAD +FOSSILID?= current .SUFFIXES: .in @@ -80,17 +79,15 @@ maninstall: install: proginstall maninstall -dist-git: - git archive --prefix=${DISTPREFIX}/ ${GITREF} | xz >${DISTFILE} - -dist-inst: - mkdir /tmp/${DISTPREFIX} - cp -RPp * /tmp/${DISTPREFIX} - (cd /tmp/${DISTPREFIX}; make clean) - tar -cvjpf ${DISTFILE} -C /tmp ${DISTPREFIX} +import: rm -rf /tmp/${DISTPREFIX} + ${INSTALL} -d /tmp/${DISTPREFIX} + cp README ${SRCS} /tmp/${DISTPREFIX} -dist: dist-git +dist: + fossil tarball --name ${DISTPREFIX} ${FOSSILID} ${DISTFILEGZ} + gunzip -c ${DISTFILEGZ} | xz >${DISTFILE} + rm ${DISTFILEGZ} distinfo: dist rm -f ${DISTINFO} ${DISTINFOSIGN} @@ -99,20 +96,3 @@ distinfo: dist ${PGP} --clearsign --output=${DISTINFOSIGN} ${DISTINFO} chmod 644 ${DISTINFOSIGN} ls -l ${DISTFILE} ${DISTINFO} ${DISTINFOSIGN} - -import: dist - rm -rf /tmp/${DISTPREFIX} - ${INSTALL} -d /tmp/${DISTPREFIX} - tar xvJpf ${DISTFILE} -C /tmp - -_import-src: - rm -rf ${DESTDIR}/* - ${INSTALL} -d ${DESTDIR} - cp LICENSE README.md ${SRCS} resolvconf.conf ${DESTDIR}; - cp resolvconf.8.in resolvconf.conf.5.in ${DESTDIR}; - @${ECHO} - @${ECHO} "=============================================================" - @${ECHO} "openresolv-${VERSION} imported to ${DESTDIR}" - -import-src: - ${MAKE} _import-src DESTDIR=`if [ -n "${DESTDIR}" ]; then echo "${DESTDIR}"; else echo /tmp/${DISTPREFIX}; fi` diff --git a/README b/README new file mode 100644 index 00000000000..b4042b0e81a --- /dev/null +++ b/README @@ -0,0 +1,11 @@ +openresolv is a resolvconf implementation which manages resolv.conf +You can find the latest version at http://roy.marples.name/projects/openresolv +It is written and maintained by Roy Marples + +This resolvconf implementation, along with its subscribers, work with a +POSIX compliant shell and userland utilities. It is designed to work without +tools such as sed as it *has* to work without /usr being available. + +On systems where resolvconf is expected to be used before /var/run is available +for writing, you can configure openresolv to write somewhere else, like say a +ramdisk. diff --git a/README.md b/README.md deleted file mode 100644 index 739cc73356c..00000000000 --- a/README.md +++ /dev/null @@ -1,64 +0,0 @@ -# openresolv - -openresolv is a [resolvconf](https://en.wikipedia.org/wiki/Resolvconf) -implementation which manages `/etc/resolv.conf`. - -`/etc/resolv.conf` is a file that holds the configuration for the local -resolution of domain names. -Normally this file is either static or maintained by a local daemon, -normally a DHCP daemon. But what happens if more than one thing wants to -control the file? -Say you have wired and wireless interfaces to different subnets and run a VPN -or two on top of that, how do you say which one controls the file? -It's also not as easy as just adding and removing the nameservers each client -knows about as different clients could add the same nameservers. - -Enter resolvconf, the middleman between the network configuration services and -`/etc/resolv.conf`. -resolvconf itself is just a script that stores, removes and lists a full -`resolv.conf` generated for the interface. It then calls all the helper scripts -it knows about so it can configure the real `/etc/resolv.conf` and optionally -any local nameservers other than libc. - -## Reasons for using openresolv - -Why openresolv over the -[Debian implementation](http://qref.sourceforge.net/Debian/reference/ch-gateway.en.html#s-dns-resolvconf)? -Here's some reasons: - * Works with - [POSIX shell and userland](http://www.opengroup.org/onlinepubs/009695399) - * Does not need awk, grep or sed which means we can work without `/usr` - mounted - * Works with other init systems than Debians' out of the box - * Available as a 2 clause - [BSD license](http://www.freebsd.org/copyright/freebsd-license.html) - * Prefer configs via IF_METRIC for dynamic ordering - * Configures zones for local resolvers other than libc - -The last point is quite important, especially when running VPN systems. -Take the following resolv.conf files which have been generated by a -[DHCP client](../dhcpcd) and sent to resolvconf: - -``` -# resolv.conf from bge0 -search foo.com -nameserver 1.2.3.4 - -# resolv.conf from tap0 -domain bar.org -nameserver 5.6.7.8 -``` - -In this instance, queries for foo.com will go to 1.2.3.4 and queries for -bar.org will go to 5.6.7.8. -This does require the resolvers to be configured to pickup the resolvconf -generated configuration for them though. -openresolv ships with helpers for: - * [unbound](http://www.unbound.net/) - * [dnsmasq](http://www.thekelleys.org.uk/dnsmasq/doc.html) - * [ISC BIND](http://www.isc.org/software/bind) - * [PowerDNS Recursor](http://wiki.powerdns.com/trac) - -See the -[configuration section](https://roy.marples.name/projects/openresolv/config) -for more details. diff --git a/configure b/configure index c9422b74b69..a890cf0dc63 100644 --- a/configure +++ b/configure @@ -44,8 +44,42 @@ 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} +: ${SYSCONFDIR:=$PREFIX/etc} +: ${SBINDIR:=$PREFIX/sbin} +: ${LIBEXECDIR:=$PREFIX/libexec/resolvconf} +: ${STATEDIR:=/var} +: ${RUNDIR:=$STATEDIR/run} +: ${MANDIR:=${PREFIX:-/usr}/share/man} + +eval SYSCONFDIR="$SYSCONFDIR" +eval SBINDIR="$SBINDIR" +eval LIBEXECDIR="$LIBEXECDIR" +eval VARDIR="$RUNDIR/resolvconf" +eval MANDIR="$MANDIR" + CONFIG_MK=config.mk if [ -z "$BUILD" ]; then @@ -87,19 +121,7 @@ rm -rf $CONFIG_MK echo "# $OS" >$CONFIG_MK case "$OS" in -dragonfly*) - # This means /usr HAS to be mounted not via dhcpcd - : ${LIBEXECDIR:=${PREFIX:-/usr}/libexec/resolvconf} - ;; -linux*) - # cksum does't support -a and netpgp is rare - echo "CKSUM= sha256sum --tag" >>$CONFIG_MK - echo "PGP= gpg2" >>$CONFIG_MK - ;; -esac - -case "$OS" in -dragonfly*|freebsd*) +freebsd*) # On FreeBSD, /etc/init.d/foo status returns 0 if foo is not enabled # regardless of if it's not running. # So we force onestatus to work around this silly bug. @@ -107,43 +129,13 @@ dragonfly*|freebsd*) STATUSARG="onestatus" fi ;; +linux*) + # cksum does't support -a and netpgp is rare + echo "CKSUM= sha256sum --tag" >>$CONFIG_MK + echo "PGP= gpg2" >>$CONFIG_MK + ;; esac - -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 - -: ${SYSCONFDIR:=$PREFIX/etc} -: ${SBINDIR:=$PREFIX/sbin} -: ${LIBEXECDIR:=$PREFIX/libexec/resolvconf} -: ${STATEDIR:=/var} -: ${RUNDIR:=$STATEDIR/run} -: ${MANDIR:=${PREFIX:-/usr}/share/man} - -eval SYSCONFDIR="$SYSCONFDIR" -eval SBINDIR="$SBINDIR" -eval LIBEXECDIR="$LIBEXECDIR" -eval VARDIR="$RUNDIR/resolvconf" -eval MANDIR="$MANDIR" - for x in SYSCONFDIR SBINDIR LIBEXECDIR VARDIR MANDIR RESTARTCMD RCDIR STATUSARG do eval v=\$$x diff --git a/dnsmasq.in b/dnsmasq.in index 8b51866dca2..b1a2c7408d9 100644 --- a/dnsmasq.in +++ b/dnsmasq.in @@ -1,5 +1,5 @@ #!/bin/sh -# Copyright (c) 2007-2019 Roy Marples +# Copyright (c) 2007-2016 Roy Marples # All rights reserved # dnsmasq subscriber for resolvconf @@ -28,7 +28,7 @@ [ -f "@SYSCONFDIR@"/resolvconf.conf ] || exit 0 . "@SYSCONFDIR@/resolvconf.conf" || exit 1 -[ -z "${dnsmasq_conf}${dnsmasq_resolv}" ] && exit 0 +[ -z "$dnsmasq_conf" -a -z "$dnsmasq_resolv" ] && exit 0 [ -z "$RESOLVCONF" ] && eval "$(@SBINDIR@/resolvconf -v)" NL=" " @@ -98,7 +98,7 @@ for d in $DOMAINS; do empty=false i=0 IFS=: set -- $n - while [ -n "$1" ] || [ -n "$2" ]; do + while [ -n "$1" -o -n "$2" ]; do addr="$1" shift if [ -z "$addr" ]; then @@ -184,7 +184,7 @@ if $changed; then eval $dnsmasq_restart elif [ -n "$RESTARTCMD" ]; then set -- ${dnsmasq_service} - eval "$RESTARTCMD" + eval $RESTARTCMD else @SBINDIR@/resolvconf -r ${dnsmasq_service} fi @@ -206,6 +206,4 @@ if $dbus; then dbus-send --system --dest=uk.org.thekelleys.dnsmasq \ /uk/org/thekelleys/dnsmasq uk.org.thekelleys.$method \ $dbusdest - dbus-send --system --dest=uk.org.thekelleys.dnsmasq \ - /uk/org/thekelleys/dnsmasq uk.org.thekelleys.ClearCache fi diff --git a/libc.in b/libc.in index f44f61bac7f..d49be65b67b 100644 --- a/libc.in +++ b/libc.in @@ -1,5 +1,5 @@ #!/bin/sh -# Copyright (c) 2007-2019 Roy Marples +# Copyright (c) 2007-2016 Roy Marples # All rights reserved # libc subscriber for resolvconf @@ -36,9 +36,9 @@ NL=" # sed may not be available, and this is faster on small files key_get_value() { - key="$1" - shift + local key="$1" x= line= + shift if [ $# -eq 0 ]; then while read -r line; do case "$line" in @@ -58,6 +58,8 @@ key_get_value() keys_remove() { + local key x line found + while read -r line; do found=false for key do @@ -77,7 +79,7 @@ local_nameservers="127.* 0.0.0.0 255.255.255.255 ::1" if [ -f "$SYSCONFDIR"/resolvconf.conf ]; then . "$SYSCONFDIR"/resolvconf.conf elif [ -d "$SYSCONFDIR"/resolvconf ]; then - SYSCONFDIR="$SYSCONFDIR/resolvconf" + SYSCONFDIR="$SYSCONFDIR/resolvconf/resolv.conf.d" base="$SYSCONFDIR/resolv.conf.d/base" if [ -f "$base" ]; then prepend_nameservers="$(key_get_value "nameserver " "$base")" @@ -96,12 +98,10 @@ fi : ${resolv_conf:=/etc/resolv.conf} : ${libc_service:=nscd} : ${list_resolv:=@SBINDIR@/resolvconf -l} -if [ "${resolv_conf_head-x}" = x ] && [ -f "$SYSCONFDIR"/resolv.conf.head ] -then +if [ "${resolv_conf_head-x}" = x -a -f "$SYSCONFDIR"/resolv.conf.head ]; then resolv_conf_head="$(cat "${SYSCONFDIR}"/resolv.conf.head)" fi -if [ "${resolv_conf_tail-x}" = x ] && [ -f "$SYSCONFDIR"/resolv.conf.tail ] -then +if [ "${resolv_conf_tail-x}" = x -a -f "$SYSCONFDIR"/resolv.conf.tail ]; then resolv_conf_tail="$(cat "$SYSCONFDIR"/resolv.conf.tail)" fi @@ -110,7 +110,7 @@ signature="# Generated by resolvconf" uniqify() { - result= + local result= while [ -n "$1" ]; do case " $result " in *" $1 "*);; @@ -126,7 +126,7 @@ case "${resolv_conf_passthrough:-NO}" in backup=false newest= for conf in "$IFACEDIR"/*; do - if [ -z "$newest" ] || [ "$conf" -nt "$newest" ]; then + if [ -z "$newest" -o "$conf" -nt "$newest" ]; then newest="$conf" fi done @@ -178,7 +178,7 @@ case "${resolv_conf_passthrough:-NO}" in fi [ -n "$domain" ] && newconf="${newconf}domain $domain$NL" - if [ -n "$newsearch" ] && [ "$newsearch" != "$domain" ]; then + if [ -n "$newsearch" -a "$newsearch" != "$domain" ]; then newconf="${newconf}search $newsearch$NL" fi for n in $newns; do @@ -232,7 +232,7 @@ if [ -n "$libc_restart" ]; then eval $libc_restart elif [ -n "$RESTARTCMD" ]; then set -- ${libc_service} - eval "$RESTARTCMD" + eval $RESTARTCMD else @SBINDIR@/resolvconf -r ${libc_service} fi diff --git a/named.in b/named.in index e5b5a91ee77..2083ea52058 100644 --- a/named.in +++ b/named.in @@ -28,14 +28,14 @@ [ -f "@SYSCONFDIR@"/resolvconf.conf ] || exit 0 . "@SYSCONFDIR@/resolvconf.conf" || exit 1 -[ -z "${named_zones}${named_options}" ] && exit 0 +[ -z "$named_zones" -a -z "$named_options" ] && exit 0 [ -z "$RESOLVCONF" ] && eval "$(@SBINDIR@/resolvconf -v)" NL=" " # Platform specific kludges -if [ -z "${named_service}${named_restart}" ] && - [ -d "$RCDIR" ] && ! [ -x "$RCDIR"/named ] +if [ -z "$named_service" -a -z "$named_restart" -a \ + -d "$RCDIR" -a ! -x "$RCDIR"/named ] then if [ -x "$RCDIR"/bind9 ]; then # Debian and derivatives @@ -111,7 +111,7 @@ if $changed; then eval $named_restart elif [ -n "$RESTARTCMD" ]; then set -- ${named_service} - eval "$RESTARTCMD" + eval $RESTARTCMD else @SBINDIR@/resolvconf -r ${named_service} fi diff --git a/pdns_recursor.in b/pdns_recursor.in index f3632e00038..ad2d922dc4d 100644 --- a/pdns_recursor.in +++ b/pdns_recursor.in @@ -1,5 +1,5 @@ #!/bin/sh -# Copyright (c) 2009-2019 Roy Marples +# Copyright (c) 2009-2011 Roy Marples # All rights reserved # PowerDNS Recursor subscriber for resolvconf @@ -33,14 +33,17 @@ NL=" " -: ${pdns_service:=pdns-recursor} +: ${pdns_service:=pdns_recursor} newzones= -for n in $NAMESERVERS; do - newzones="$newzones${newzones:+,}$n" -done -[ -n "$newzones" ] && newzones="+.=$newzones$NL" +# 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= @@ -68,7 +71,7 @@ then eval $pdns_restart elif [ -n "$RESTARTCMD" ]; then set -- ${pdns_service} - eval "$RESTARTCMD" + eval $RESTARTCMD else @SBINDIR@/resolvconf -r ${pdns_service} fi diff --git a/pdnsd.in b/pdnsd.in index 971d90d26a1..59a4755aff1 100644 --- a/pdnsd.in +++ b/pdnsd.in @@ -1,5 +1,5 @@ #!/bin/sh -# Copyright (c) 2010-2018 Roy Marples +# Copyright (c) 2010-2013 Roy Marples # All rights reserved # pdnsd subscriber for resolvconf @@ -28,7 +28,7 @@ [ -f "@SYSCONFDIR@"/resolvconf.conf ] || exit 0 . "@SYSCONFDIR@/resolvconf.conf" || exit 1 -[ -z "${pdnsd_conf}${pdnsd_resolv}" ] && exit 0 +[ -z "$pdnsd_conf" -a -z "$pdnsd_resolv" ] && exit 0 [ -z "$RESOLVCONF" ] && eval "$(@SBINDIR@/resolvconf -v)" NL=" " @@ -41,16 +41,14 @@ signature_end="# End of resolvconf" # but sed may not always be available at the time. remove_markers() { - m1="$1" - m2="$2" - in_marker=0 + local m1="$1" m2="$2" x= line= in_marker=0 shift; shift if type sed >/dev/null 2>&1; then sed "/^$m1/,/^$m2/d" $@ else - for x do - while read line; do + for x; do + while read -r line; do case "$line" in "$m1"*) in_marker=1;; "$m2"*) in_marker=0;; diff --git a/resolvconf.conf b/resolvconf.conf index 4f5f2e6f42e..607f6af4d91 100644 --- a/resolvconf.conf +++ b/resolvconf.conf @@ -4,4 +4,4 @@ resolv_conf=/etc/resolv.conf # If you run a local name server, you should uncomment the below line and # configure your subscribers configuration files below. -#name_servers=127.0.0.1 +#name_servers=127.0.0.1 \ No newline at end of file diff --git a/resolvconf.conf.5.in b/resolvconf.conf.5.in index 1b0c4716084..fcebd864e36 100644 --- a/resolvconf.conf.5.in +++ b/resolvconf.conf.5.in @@ -22,7 +22,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd September 8, 2019 +.Dd December 29, 2016 .Dt RESOLVCONF.CONF 5 .Os .Sh NAME @@ -64,25 +64,19 @@ Defaults to YES. .It Sy interface_order These interfaces will always be processed first. If unset, defaults to the following:- -.Bd -compact -literal -offset indent -lo lo[0-9]* -.Ed +.D1 lo lo[0-9]* .It Sy dynamic_order These interfaces will be processed next, unless they have a metric. If unset, defaults to the following:- -.Bd -compact -literal -offset indent -tap[0-9]* tun[0-9]* vpn vpn[0-9]* ppp[0-9]* ippp[0-9]* -.Ed +.D1 tap[0-9]* tun[0-9]* vpn vpn[0-9]* ppp[0-9]* ippp[0-9]* .It Sy inclusive_interfaces -Ignore any exclusive marking for these interfaces. +Ignore any exlcusive marking for these interfaces. This is handy when 3rd party integrations force the .Nm resolvconf -x option and you want to disable it easily. .It Sy local_nameservers If unset, defaults to the following:- -.Bd -compact -literal -offset indent -127.* 0.0.0.0 255.255.255.255 ::1 -.Ed +.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 @@ -124,24 +118,16 @@ The syntax is this: .Va $keyword Ns / Ns Va $match Ns / Ns Va $replacement .Pp Example, given this resolv.conf: -.Bd -compact -literal -offset indent -domain foo.org -search foo.org dead.beef -nameserver 1.2.3.4 -nameserver 2.3.4.5 -.Ed +.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: -.Bd -compact -literal -offset indent -replace="search/foo*/bar.com" -replace="$replace nameserver/1.2.3.4/5.6.7.8" -replace="$replace nameserver/2.3.4.5/" -.Ed +.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: -.Bd -compact -literal -offset indent -domain foo.org -search bar.com -nameserver 5.6.7.8 -.Ed +.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 @@ -152,11 +138,9 @@ Using the same example resolv.conf and changing to .Sy replace_sub , you would get this resolv.conf instead: -.Bd -compact -literal -offset indent -domain foo.org -search bar.com dead.beef -nameserver 5.6.7.8 -.Ed +.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@ . @@ -211,8 +195,7 @@ Prepend search domains to the dynamically generated list. openresolv ships with subscribers for the name servers .Xr dnsmasq 8 , .Xr named 8 , -.Xr pdnsd 8 , -.Xr pdns_recursor 8 , +.Xr pdnsd 8 and .Xr unbound 8 . Each subscriber can create configuration files which should be included in @@ -220,9 +203,7 @@ 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: -.Bd -compact -literal -offset indent -libc=NO -.Ed +.D1 libc=NO .Bl -tag -width indent .It Sy dnsmasq_conf This file tells dnsmasq which name servers to use for specific domains. @@ -230,21 +211,17 @@ This file tells dnsmasq which name servers to use for specific domains. This file tells dnsmasq which name servers to use for global lookups. .Pp Example resolvconf.conf for dnsmasq: -.Bd -compact -literal -offset indent -name_servers=127.0.0.1 -dnsmasq_conf=/etc/dnsmasq-conf.conf -dnsmasq_resolv=/etc/dnsmasq-resolv.conf -.Ed +.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: -.Bd -compact -literal -offset indent -listen-address=127.0.0.1 -# If dnsmasq is compiled for DBus then we can take -# advantage of not having to restart dnsmasq. -enable-dbus -conf-file=/etc/dnsmasq-conf.conf -resolv-file=/etc/dnsmasq-resolv.conf -.Ed +.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 name servers to use for global lookups. @@ -253,21 +230,16 @@ Include this file in the named global scope, after the options block. This file tells named which name servers to use for specific domains. .Pp Example resolvconf.conf for named: -.Bd -compact -literal -offset indent -name_servers=127.0.0.1 -named_options=/etc/named-options.conf -named_zones=/etc/named-zones.conf -.Ed +.D1 name_servers=127.0.0.1 +.D1 named_options=/etc/named-options.conf +.D1 named_zones=/etc/named-zones.conf .Pp Example named.conf: -.Bd -compact -literal -offset indent -options { - listen-on { 127.0.0.1; }; - include "/etc/named-options.conf"; -}; - -include "/etc/named-zones.conf"; -.Ed +.D1 options { +.D1 listen-on { 127.0.0.1; }; +.D1 include "/etc/named-options.conf"; +.D1 }; +.D1 include "/etc/named-zones.conf"; .It Sy pdnsd_conf This is the main pdnsd configuration file which we modify to add our forward domains to. @@ -281,54 +253,32 @@ If this variable is not set then it's written to .Pa pdnsd_conf . .Pp Example resolvconf.conf for pdnsd: -.Bd -compact -literal -offset indent -name_servers=127.0.0.1 -pdnsd_conf=/etc/pdnsd.conf -# pdnsd_resolv=/etc/pdnsd-resolv.conf -.Ed +.D1 name_servers=127.0.0.1 +.D1 pdnsd_conf=/etc/pdnsd.conf +.D1 # pdnsd_resolv=/etc/pdnsd-resolv.conf .Pp Example pdnsd.conf: -.Bd -compact -literal -offset indent -global { - server_ip = 127.0.0.1; - status_ctl = on; -} -server { - # A server definition is required, even if empty. - label="empty"; - proxy_only=on; - # file="/etc/pdnsd-resolv.conf"; -} -.Ed -.It Sy pdns_zones -This file tells pdns_recursor about specific and global name servers. -.Pp -Example resolvconf.conf for pdns_recursor: -.Bd -compact -literal -offset indent -name_servers=127.0.0.1 -pdns_zones=/etc/pdns/recursor-zones.conf -.Ed -.Pp -Example recursor.conf: -.Bd -compact -literal -offset indent -allow-from=127.0.0.0/8, ::1/128 -forward-zones-file=/etc/pdns/recursor-zones.conf -.Ed +.D1 global { +.D1 server_ip = 127.0.0.1; +.D1 status_ctl = on; +.D1 } +.D1 server { +.D1 # A server definition is required, even if emtpy. +.D1 label="empty"; +.D1 proxy_only=on; +.D1 # file="/etc/pdnsd-resolv.conf"; +.D1 } .It Sy unbound_conf 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: -.Bd -compact -literal -offset indent -name_servers=127.0.0.1 -unbound_conf=/etc/unbound-resolvconf.conf -.Ed +.D1 name_servers=127.0.0.1 +.D1 unbound_conf=/etc/unbound-resolvconf.conf .Pp Example unbound.conf: -.Bd -compact -literal -offset indent -include: /etc/unbound-resolvconf.conf -.Ed +.D1 include: /etc/unbound-resolvconf.conf .El .Sh SUBSCRIBER INTEGRATION Not all distributions store the files the subscribers need in the same @@ -342,6 +292,7 @@ Also, users could equally want to use a different version from the one installed by default, such as bind8 and bind9. To accommodate this, the subscribers have these files in configurable variables, documented below. +.Pp .Bl -tag -width indent .It Sy dnsmasq_service Name of the dnsmasq service. @@ -359,10 +310,6 @@ Name of the named service. Command to restart the named service. .It Sy pdnsd_restart Command to restart the pdnsd service. -.It Sy pdns_service -Command to restart the pdns_recursor service. -.It Sy pdns_restart -Command to restart the pdns_recursor service. .It Sy unbound_service Name of the unbound service. .It Sy unbound_restart diff --git a/resolvconf.in b/resolvconf.in index e7d38211181..7353cfc348d 100644 --- a/resolvconf.in +++ b/resolvconf.in @@ -1,5 +1,5 @@ #!/bin/sh -# Copyright (c) 2007-2019 Roy Marples +# Copyright (c) 2007-2016 Roy Marples # All rights reserved # Redistribution and use in source and binary forms, with or without @@ -25,7 +25,7 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. RESOLVCONF="$0" -OPENRESOLV_VERSION="3.9.2" +OPENRESOLV_VERSION="3.9.0" SYSCONFDIR=@SYSCONFDIR@ LIBEXECDIR=@LIBEXECDIR@ VARDIR=@VARDIR@ @@ -125,22 +125,21 @@ usage() # If you think otherwise, capture a DNS trace and you'll see libc # will strip it regardless. # This also solves setting up duplicate zones in our subscribers. -# Also strip any comments denoted by #. -resolv_strip() +strip_trailing_dots() { - space= - for word; do - case "$word" in - \#*) break;; - esac - printf "%s%s" "$space${word%.}" - space=" " + local n= d= + + for n; do + printf "$d%s" "${n%.}" + d=" " done printf "\n" } private_iface() { + local p + # Allow expansion cd "$IFACEDIR" @@ -169,15 +168,12 @@ private_iface() # for domain name servers, search name servers and global nameservers parse_resolv() { - domain= - new=true + local line= ns= ds= search= d= n= newns= + local new=true iface= private=false p= domain= l= islocal= + newns= - ns= - private=false - search= while read -r line; do - stripped_line="$(resolv_strip ${line#* })" case "$line" in "# resolv.conf from "*) if ${new}; then @@ -193,32 +189,29 @@ parse_resolv() "nameserver "*) islocal=false for l in $local_nameservers; do - case "$stripped_line" in + case "${line#* }" in $l) islocal=true + echo "LOCALNAMESERVERS=\"\$LOCALNAMESERVERS ${line#* }\"" break ;; esac done - if $islocal; then - echo "LOCALNAMESERVERS=\"\$LOCALNAMESERVERS $stripped_line\"" - else - ns="$ns$stripped_line " - fi + $islocal || ns="$ns${line#* } " ;; "domain "*) - search="$stripped_line" + search="$(strip_trailing_dots ${line#* })" if [ -z "$domain" ]; then domain="$search" echo "DOMAIN=\"$domain\"" fi ;; "search "*) - search="$stripped_line" + search="$(strip_trailing_dots ${line#* })" ;; *) [ -n "$line" ] && continue - if [ -n "$ns" ] && [ -n "$search" ]; then + if [ -n "$ns" -a -n "$search" ]; then newns= for n in $ns; do newns="$newns${newns:+,}$n" @@ -243,7 +236,7 @@ parse_resolv() uniqify() { - result= + local result= while [ -n "$1" ]; do case " $result " in *" $1 "*);; @@ -256,8 +249,8 @@ uniqify() dirname() { - OIFS="$IFS" - IFS=/ + local dir= OIFS="$IFS" + local IFS=/ set -- $@ IFS="$OIFS" if [ -n "$1" ]; then @@ -274,7 +267,7 @@ dirname() config_mkdirs() { - e=0 + local e=0 f d for f; do [ -n "$f" ] || continue d="$(dirname "$f")" @@ -302,86 +295,66 @@ detect_init() # Detect the running init system. # As systemd and OpenRC can be installed on top of legacy init # systems we try to detect them first. - status="@STATUSARG@" + local status="@STATUSARG@" : ${status:=status} - if [ -x /bin/systemctl ] && [ -S /run/systemd/private ]; then - RESTARTCMD=' - if /bin/systemctl --quiet is-active $1.service - then - /bin/systemctl restart $1.service - fi' - elif [ -x /usr/bin/systemctl ] && [ -S /run/systemd/private ]; then - RESTARTCMD=' - if /usr/bin/systemctl --quiet is-active $1.service - then - /usr/bin/systemctl restart $1.service - fi' - elif [ -x /sbin/rc-service ] && - { [ -s /libexec/rc/init.d/softlevel ] || - [ -s /run/openrc/softlevel ]; } + if [ -x /bin/systemctl -a -S /run/systemd/private ]; then + RESTARTCMD="if /bin/systemctl --quiet is-active \$1.service; then + /bin/systemctl restart \$1.service; +fi" + elif [ -x /usr/bin/systemctl -a -S /run/systemd/private ]; then + RESTARTCMD="if /usr/bin/systemctl --quiet is-active \$1.service; then + /usr/bin/systemctl restart \$1.service; +fi" + elif [ -x /sbin/rc-service -a \ + -s /libexec/rc/init.d/softlevel -o -s /run/openrc/softlevel ] then - RESTARTCMD='/sbin/rc-service -i $1 -- -Ds restart' + RESTARTCMD="/sbin/rc-service -i \$1 -- -Ds restart" elif [ -x /usr/sbin/invoke-rc.d ]; then RCDIR=/etc/init.d - RESTARTCMD=' - if /usr/sbin/invoke-rc.d --quiet $1 status >/dev/null 2>&1 - then - /usr/sbin/invoke-rc.d $1 restart - fi' + RESTARTCMD="if /usr/sbin/invoke-rc.d --quiet \$1 status 1>/dev/null 2>&1; then + /usr/sbin/invoke-rc.d \$1 restart; +fi" elif [ -x /sbin/service ]; then # Old RedHat RCDIR=/etc/init.d - RESTARTCMD=' - if /sbin/service $1; then - /sbin/service $1 restart - fi' + RESTARTCMD="if /sbin/service \$1; then + /sbin/service \$1 restart; +fi" elif [ -x /usr/sbin/service ]; then # Could be FreeBSD - RESTARTCMD=" - if /usr/sbin/service \$1 $status >/dev/null 2>&1 - then - /usr/sbin/service \$1 restart - fi" + RESTARTCMD="if /usr/sbin/service \$1 $status 1>/dev/null 2>&1; then + /usr/sbin/service \$1 restart; +fi" elif [ -x /bin/sv ]; then - RESTARTCMD='/bin/sv status $1 >/dev/null 2>&1 && - /bin/sv try-restart $1' + RESTARTCMD="/bin/sv status \$1 >/dev/null 2>&1 && /bin/sv try-restart \$1" elif [ -x /usr/bin/sv ]; then - RESTARTCMD='/usr/bin/sv status $1 >/dev/null 2>&1 && - /usr/bin/sv try-restart $1' - elif [ -e /etc/arch-release ] && [ -d /etc/rc.d ]; then + RESTARTCMD="/usr/bin/sv status \$1 >/dev/null 2>&1 && /usr/bin/sv try-restart \$1" + elif [ -e /etc/arch-release -a -d /etc/rc.d ]; then RCDIR=/etc/rc.d - RESTARTCMD=' - if [ -e /var/run/daemons/$1 ] - then - /etc/rc.d/$1 restart - fi' - elif [ -e /etc/slackware-version ] && [ -d /etc/rc.d ]; then - RESTARTCMD=' - if /etc/rc.d/rc.$1 status >/dev/null 2>&1 - then - /etc/rc.d/rc.$1 restart - fi' - elif [ -e /etc/rc.d/rc.subr ] && [ -d /etc/rc.d ]; then + RESTARTCMD="if [ -e /var/run/daemons/\$1 ]; then + /etc/rc.d/\$1 restart; +fi" + elif [ -e /etc/slackware-version -a -d /etc/rc.d ]; then + RESTARTCMD="if /etc/rc.d/rc.\$1 status 1>/dev/null 2>&1; then + /etc/rc.d/rc.\$1 restart; +fi" + elif [ -e /etc/rc.d/rc.subr -a -d /etc/rc.d ]; then # OpenBSD - RESTARTCMD=' - if /etc/rc.d/$1 check >/dev/null 2>&1 - then - /etc/rc.d/$1 restart - fi' + RESTARTCMD="if /etc/rc.d/\$1 check 1>/dev/null 2>&1; then + /etc/rc.d/\$1 restart; +fi" else for x in /etc/init.d/rc.d /etc/rc.d /etc/init.d; do [ -d $x ] || continue - RESTARTCMD=" - if $x/\$1 $status >/dev/null 2>&1 - then - $x/\$1 restart - fi" + RESTARTCMD="if $x/\$1 $status 1>/dev/null 2>&1; then + $x/\$1 restart; +fi" break done fi if [ -z "$RESTARTCMD" ]; then - if [ "$_NOINIT_WARNED" != true ]; then + if [ "$NOINIT_WARNED" != true ]; then warn "could not detect a useable init system" _NOINIT_WARNED=true fi @@ -393,9 +366,9 @@ detect_init() echo_resolv() { - OIFS="$IFS" + local line= OIFS="$IFS" - [ -n "$1" ] && [ -f "$IFACEDIR/$1" ] || return 1 + [ -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. @@ -415,16 +388,11 @@ list_resolv() { [ -d "$IFACEDIR" ] || return 0 - cmd="$1" + local report=false list= retval=0 cmd="$1" excl= shift - excl=false - list= - report=false - retval=0 case "$IF_EXCLUSIVE" in [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1) - excl=true if [ -d "$EXCLUSIVEDIR" ]; then cd "$EXCLUSIVEDIR" for i in *; do @@ -434,15 +402,19 @@ list_resolv() fi done fi + excl=true cd "$IFACEDIR" for i in $inclusive_interfaces; do - if [ -f "$i" ] && [ "$list" = "$i" ]; then + if [ -f "$i" -a "$list" = "$i" ]; then list= excl=false break fi done ;; + *) + excl=false + ;; esac # If we have an interface ordering list, then use that. @@ -459,28 +431,22 @@ list_resolv() done done for i in $dynamic_order; do - if [ -e "$i" ] && ! [ -e "$METRICDIR/"*" $i" ]; then + if [ -e "$i" -a ! -e "$METRICDIR/"*" $i" ]; then list="$list $i" fi for ii in "$i":* "$i".*; do - if [ -f "$ii" ] && ! [ -e "$METRICDIR/"*" $ii" ] - then + if [ -f "$ii" -a ! -e "$METRICDIR/"*" $ii" ]; then list="$list $ii" fi done done - # Interfaces have an implicit metric of 0 if not specified. - for i in *; do - if [ -f "$i" ] && ! [ -e "$METRICDIR/"*" $i" ]; then - list="$list $i" - fi - done if [ -d "$METRICDIR" ]; then cd "$METRICDIR" for i in *; do [ -f "$i" ] && list="$list ${i#* }" done fi + list="$list *" fi cd "$IFACEDIR" @@ -495,24 +461,23 @@ list_resolv() continue fi - if [ "$cmd" = i ] || [ "$cmd" = "-i" ]; then + if [ "$cmd" = i -o "$cmd" = "-i" ]; then printf %s "$i " else echo_resolv "$i" && echo fi - [ $? = 0 ] && [ "$retval" = 1 ] && retval=0 + [ $? = 0 -a "$retval" = 1 ] && retval=0 done - [ "$cmd" = i ] || [ "$cmd" = "-i" ] && echo + [ "$cmd" = i -o "$cmd" = "-i" ] && echo return $retval } -list_remove() -{ +list_remove() { + local list= e= l= result= found= retval=0 + [ -z "$2" ] && return 0 eval list=\"\$$1\" shift - result= - retval=0 set -f for e; do @@ -560,6 +525,8 @@ echo_append() replace() { + local r= k= f= v= val= sub= + while read -r keyword value; do for r in $replace; do k="${r%%/*}" @@ -599,6 +566,8 @@ replace() make_vars() { + local newdomains= d= dn= newns= ns= + # Clear variables DOMAIN= DOMAINS= @@ -606,7 +575,7 @@ make_vars() NAMESERVERS= LOCALNAMESERVERS= - if [ -n "${name_servers}${search_domains}" ]; then + if [ -n "$name_servers" -o -n "$search_domains" ]; then eval "$(echo_prepend | parse_resolv)" fi if [ -z "$VFLAG" ]; then @@ -614,12 +583,11 @@ make_vars() list_resolv -i "$@" >/dev/null || IF_EXCLUSIVE=0 eval "$(list_resolv -l "$@" | replace | parse_resolv)" fi - if [ -n "${name_servers_append}${search_domains_append}" ]; then + 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 @@ -699,37 +667,36 @@ if [ "$cmd" = D ]; then fi # -l lists our resolv files, optionally for a specific interface -if [ "$cmd" = l ] || [ "$cmd" = i ]; then +if [ "$cmd" = l -o "$cmd" = i ]; then list_resolv "$cmd" "$args" exit $? fi # Restart a service or echo the command to restart a service -if [ "$cmd" = r ] || [ "$cmd" = R ]; then +if [ "$cmd" = r -o "$cmd" = R ]; then detect_init || exit 1 if [ "$cmd" = r ]; then set -- $args - eval "$RESTARTCMD" + eval $RESTARTCMD else - echo "$RESTARTCMD" | - sed -e '/^$/d' -e 's/^ //g' + echo "$RESTARTCMD" fi exit $? fi # Not normally needed, but subscribers should be able to run independently -if [ "$cmd" = v ] || [ -n "$VFLAG" ]; then +if [ "$cmd" = v -o -n "$VFLAG" ]; then make_vars "$iface" exit $? fi # Test that we have valid options -if [ "$cmd" = a ] || [ "$cmd" = d ]; then +if [ "$cmd" = a -o "$cmd" = d ]; then if [ -z "$iface" ]; then usage "Interface not specified" fi elif [ "$cmd" != u ]; then - [ -n "$cmd" ] && [ "$cmd" != h ] && usage "Unknown option $cmd" + [ -n "$cmd" -a "$cmd" != h ] && usage "Unknown option $cmd" usage fi @@ -745,7 +712,7 @@ if [ "$cmd" = a ]; then "$x not allowed at start of interface name";; esac done - [ "$cmd" = a ] && [ -t 0 ] && error_exit "No file given via stdin" + [ "$cmd" = a -a -t 0 ] && error_exit "No file given via stdin" fi if [ ! -d "$VARDIR" ]; then @@ -841,8 +808,8 @@ a) newmetric="$METRICDIR/$IF_METRIC $iface" fi rm -f "$METRICDIR/"*" $iface" - [ "$oldmetric" != "$newmetric" ] && - [ "$oldmetric" != "$METRICDIR/* $iface" ] && + [ "$oldmetric" != "$newmetric" -a \ + "$oldmetric" != "$METRICDIR/* $iface" ] && changed=true [ -n "$newmetric" ] && echo " " >"$newmetric" diff --git a/unbound.in b/unbound.in index 34cb401f57c..46d45246ff2 100644 --- a/unbound.in +++ b/unbound.in @@ -76,7 +76,7 @@ restart_unbound() eval $unbound_restart elif [ -n "$RESTARTCMD" ]; then set -- ${unbound_service} - eval "$RESTARTCMD" + eval $RESTARTCMD else @SBINDIR@/resolvconf -r ${unbound_service} fi From a1f03c7e4d33421baac0a5f3dc5947c7b379ea0c Mon Sep 17 00:00:00 2001 From: "Pedro F. Giffuni" Date: Wed, 20 Nov 2019 22:23:50 +0000 Subject: [PATCH 2/3] Import openresolv 3.9.2 More information at: https://roy.marples.name/projects/openresolv --- LICENSE | 23 +++++ Makefile | 40 ++++++-- README | 11 --- README.md | 64 +++++++++++++ configure | 90 +++++++++-------- dnsmasq.in | 10 +- libc.in | 24 ++--- named.in | 8 +- pdns_recursor.in | 17 ++-- pdnsd.in | 12 ++- resolvconf.conf | 2 +- resolvconf.conf.5.in | 157 ++++++++++++++++++++---------- resolvconf.in | 223 +++++++++++++++++++++++++------------------ unbound.in | 2 +- 14 files changed, 437 insertions(+), 246 deletions(-) create mode 100644 LICENSE delete mode 100644 README create mode 100644 README.md diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000000..758aec33805 --- /dev/null +++ b/LICENSE @@ -0,0 +1,23 @@ +Copyright (c) 2007-2019 Roy Marples +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. 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 AUTHOR 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 AUTHOR 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. diff --git a/Makefile b/Makefile index 095779b95b0..6cfa6e4ea07 100644 --- a/Makefile +++ b/Makefile @@ -10,6 +10,7 @@ SYSCONFDIR?= /etc LIBEXECDIR?= /libexec/resolvconf VARDIR?= /var/run/resolvconf +ECHO?= echo INSTALL?= install SED?= sed @@ -20,7 +21,7 @@ DOCMODE?= 0644 MANMODE?= 0444 RESOLVCONF= resolvconf resolvconf.8 resolvconf.conf.5 -SUBSCRIBERS= libc dnsmasq named pdnsd unbound +SUBSCRIBERS= libc dnsmasq named pdnsd pdns_recursor unbound TARGET= ${RESOLVCONF} ${SUBSCRIBERS} SRCS= ${TARGET:C,$,.in,} # pmake SRCS:= ${TARGET:=.in} # gmake @@ -42,7 +43,7 @@ DISTINFOSIGN= ${DISTINFO}.asc CKSUM?= cksum -a SHA256 PGP?= netpgp -FOSSILID?= current +GITREF?= HEAD .SUFFIXES: .in @@ -79,15 +80,17 @@ maninstall: install: proginstall maninstall -import: - rm -rf /tmp/${DISTPREFIX} - ${INSTALL} -d /tmp/${DISTPREFIX} - cp README ${SRCS} /tmp/${DISTPREFIX} +dist-git: + git archive --prefix=${DISTPREFIX}/ ${GITREF} | xz >${DISTFILE} -dist: - fossil tarball --name ${DISTPREFIX} ${FOSSILID} ${DISTFILEGZ} - gunzip -c ${DISTFILEGZ} | xz >${DISTFILE} - rm ${DISTFILEGZ} +dist-inst: + mkdir /tmp/${DISTPREFIX} + cp -RPp * /tmp/${DISTPREFIX} + (cd /tmp/${DISTPREFIX}; make clean) + tar -cvjpf ${DISTFILE} -C /tmp ${DISTPREFIX} + rm -rf /tmp/${DISTPREFIX} + +dist: dist-git distinfo: dist rm -f ${DISTINFO} ${DISTINFOSIGN} @@ -96,3 +99,20 @@ distinfo: dist ${PGP} --clearsign --output=${DISTINFOSIGN} ${DISTINFO} chmod 644 ${DISTINFOSIGN} ls -l ${DISTFILE} ${DISTINFO} ${DISTINFOSIGN} + +import: dist + rm -rf /tmp/${DISTPREFIX} + ${INSTALL} -d /tmp/${DISTPREFIX} + tar xvJpf ${DISTFILE} -C /tmp + +_import-src: + rm -rf ${DESTDIR}/* + ${INSTALL} -d ${DESTDIR} + cp LICENSE README.md ${SRCS} resolvconf.conf ${DESTDIR}; + cp resolvconf.8.in resolvconf.conf.5.in ${DESTDIR}; + @${ECHO} + @${ECHO} "=============================================================" + @${ECHO} "openresolv-${VERSION} imported to ${DESTDIR}" + +import-src: + ${MAKE} _import-src DESTDIR=`if [ -n "${DESTDIR}" ]; then echo "${DESTDIR}"; else echo /tmp/${DISTPREFIX}; fi` diff --git a/README b/README deleted file mode 100644 index b4042b0e81a..00000000000 --- a/README +++ /dev/null @@ -1,11 +0,0 @@ -openresolv is a resolvconf implementation which manages resolv.conf -You can find the latest version at http://roy.marples.name/projects/openresolv -It is written and maintained by Roy Marples - -This resolvconf implementation, along with its subscribers, work with a -POSIX compliant shell and userland utilities. It is designed to work without -tools such as sed as it *has* to work without /usr being available. - -On systems where resolvconf is expected to be used before /var/run is available -for writing, you can configure openresolv to write somewhere else, like say a -ramdisk. diff --git a/README.md b/README.md new file mode 100644 index 00000000000..739cc73356c --- /dev/null +++ b/README.md @@ -0,0 +1,64 @@ +# openresolv + +openresolv is a [resolvconf](https://en.wikipedia.org/wiki/Resolvconf) +implementation which manages `/etc/resolv.conf`. + +`/etc/resolv.conf` is a file that holds the configuration for the local +resolution of domain names. +Normally this file is either static or maintained by a local daemon, +normally a DHCP daemon. But what happens if more than one thing wants to +control the file? +Say you have wired and wireless interfaces to different subnets and run a VPN +or two on top of that, how do you say which one controls the file? +It's also not as easy as just adding and removing the nameservers each client +knows about as different clients could add the same nameservers. + +Enter resolvconf, the middleman between the network configuration services and +`/etc/resolv.conf`. +resolvconf itself is just a script that stores, removes and lists a full +`resolv.conf` generated for the interface. It then calls all the helper scripts +it knows about so it can configure the real `/etc/resolv.conf` and optionally +any local nameservers other than libc. + +## Reasons for using openresolv + +Why openresolv over the +[Debian implementation](http://qref.sourceforge.net/Debian/reference/ch-gateway.en.html#s-dns-resolvconf)? +Here's some reasons: + * Works with + [POSIX shell and userland](http://www.opengroup.org/onlinepubs/009695399) + * Does not need awk, grep or sed which means we can work without `/usr` + mounted + * Works with other init systems than Debians' out of the box + * Available as a 2 clause + [BSD license](http://www.freebsd.org/copyright/freebsd-license.html) + * Prefer configs via IF_METRIC for dynamic ordering + * Configures zones for local resolvers other than libc + +The last point is quite important, especially when running VPN systems. +Take the following resolv.conf files which have been generated by a +[DHCP client](../dhcpcd) and sent to resolvconf: + +``` +# resolv.conf from bge0 +search foo.com +nameserver 1.2.3.4 + +# resolv.conf from tap0 +domain bar.org +nameserver 5.6.7.8 +``` + +In this instance, queries for foo.com will go to 1.2.3.4 and queries for +bar.org will go to 5.6.7.8. +This does require the resolvers to be configured to pickup the resolvconf +generated configuration for them though. +openresolv ships with helpers for: + * [unbound](http://www.unbound.net/) + * [dnsmasq](http://www.thekelleys.org.uk/dnsmasq/doc.html) + * [ISC BIND](http://www.isc.org/software/bind) + * [PowerDNS Recursor](http://wiki.powerdns.com/trac) + +See the +[configuration section](https://roy.marples.name/projects/openresolv/config) +for more details. diff --git a/configure b/configure index a890cf0dc63..c9422b74b69 100644 --- a/configure +++ b/configure @@ -44,42 +44,8 @@ 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} -: ${SYSCONFDIR:=$PREFIX/etc} -: ${SBINDIR:=$PREFIX/sbin} -: ${LIBEXECDIR:=$PREFIX/libexec/resolvconf} -: ${STATEDIR:=/var} -: ${RUNDIR:=$STATEDIR/run} -: ${MANDIR:=${PREFIX:-/usr}/share/man} - -eval SYSCONFDIR="$SYSCONFDIR" -eval SBINDIR="$SBINDIR" -eval LIBEXECDIR="$LIBEXECDIR" -eval VARDIR="$RUNDIR/resolvconf" -eval MANDIR="$MANDIR" - CONFIG_MK=config.mk if [ -z "$BUILD" ]; then @@ -121,13 +87,9 @@ rm -rf $CONFIG_MK echo "# $OS" >$CONFIG_MK case "$OS" in -freebsd*) - # On FreeBSD, /etc/init.d/foo status returns 0 if foo is not enabled - # regardless of if it's not running. - # So we force onestatus to work around this silly bug. - if [ -z "$STATUSARG" ]; then - STATUSARG="onestatus" - fi +dragonfly*) + # This means /usr HAS to be mounted not via dhcpcd + : ${LIBEXECDIR:=${PREFIX:-/usr}/libexec/resolvconf} ;; linux*) # cksum does't support -a and netpgp is rare @@ -136,6 +98,52 @@ linux*) ;; esac +case "$OS" in +dragonfly*|freebsd*) + # On FreeBSD, /etc/init.d/foo status returns 0 if foo is not enabled + # regardless of if it's not running. + # So we force onestatus to work around this silly bug. + if [ -z "$STATUSARG" ]; then + STATUSARG="onestatus" + fi + ;; +esac + + +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 + +: ${SYSCONFDIR:=$PREFIX/etc} +: ${SBINDIR:=$PREFIX/sbin} +: ${LIBEXECDIR:=$PREFIX/libexec/resolvconf} +: ${STATEDIR:=/var} +: ${RUNDIR:=$STATEDIR/run} +: ${MANDIR:=${PREFIX:-/usr}/share/man} + +eval SYSCONFDIR="$SYSCONFDIR" +eval SBINDIR="$SBINDIR" +eval LIBEXECDIR="$LIBEXECDIR" +eval VARDIR="$RUNDIR/resolvconf" +eval MANDIR="$MANDIR" + for x in SYSCONFDIR SBINDIR LIBEXECDIR VARDIR MANDIR RESTARTCMD RCDIR STATUSARG do eval v=\$$x diff --git a/dnsmasq.in b/dnsmasq.in index b1a2c7408d9..8b51866dca2 100644 --- a/dnsmasq.in +++ b/dnsmasq.in @@ -1,5 +1,5 @@ #!/bin/sh -# Copyright (c) 2007-2016 Roy Marples +# Copyright (c) 2007-2019 Roy Marples # All rights reserved # dnsmasq subscriber for resolvconf @@ -28,7 +28,7 @@ [ -f "@SYSCONFDIR@"/resolvconf.conf ] || exit 0 . "@SYSCONFDIR@/resolvconf.conf" || exit 1 -[ -z "$dnsmasq_conf" -a -z "$dnsmasq_resolv" ] && exit 0 +[ -z "${dnsmasq_conf}${dnsmasq_resolv}" ] && exit 0 [ -z "$RESOLVCONF" ] && eval "$(@SBINDIR@/resolvconf -v)" NL=" " @@ -98,7 +98,7 @@ for d in $DOMAINS; do empty=false i=0 IFS=: set -- $n - while [ -n "$1" -o -n "$2" ]; do + while [ -n "$1" ] || [ -n "$2" ]; do addr="$1" shift if [ -z "$addr" ]; then @@ -184,7 +184,7 @@ if $changed; then eval $dnsmasq_restart elif [ -n "$RESTARTCMD" ]; then set -- ${dnsmasq_service} - eval $RESTARTCMD + eval "$RESTARTCMD" else @SBINDIR@/resolvconf -r ${dnsmasq_service} fi @@ -206,4 +206,6 @@ if $dbus; then dbus-send --system --dest=uk.org.thekelleys.dnsmasq \ /uk/org/thekelleys/dnsmasq uk.org.thekelleys.$method \ $dbusdest + dbus-send --system --dest=uk.org.thekelleys.dnsmasq \ + /uk/org/thekelleys/dnsmasq uk.org.thekelleys.ClearCache fi diff --git a/libc.in b/libc.in index d49be65b67b..f44f61bac7f 100644 --- a/libc.in +++ b/libc.in @@ -1,5 +1,5 @@ #!/bin/sh -# Copyright (c) 2007-2016 Roy Marples +# Copyright (c) 2007-2019 Roy Marples # All rights reserved # libc subscriber for resolvconf @@ -36,9 +36,9 @@ NL=" # sed may not be available, and this is faster on small files key_get_value() { - local key="$1" x= line= - + key="$1" shift + if [ $# -eq 0 ]; then while read -r line; do case "$line" in @@ -58,8 +58,6 @@ key_get_value() keys_remove() { - local key x line found - while read -r line; do found=false for key do @@ -79,7 +77,7 @@ local_nameservers="127.* 0.0.0.0 255.255.255.255 ::1" if [ -f "$SYSCONFDIR"/resolvconf.conf ]; then . "$SYSCONFDIR"/resolvconf.conf elif [ -d "$SYSCONFDIR"/resolvconf ]; then - SYSCONFDIR="$SYSCONFDIR/resolvconf/resolv.conf.d" + SYSCONFDIR="$SYSCONFDIR/resolvconf" base="$SYSCONFDIR/resolv.conf.d/base" if [ -f "$base" ]; then prepend_nameservers="$(key_get_value "nameserver " "$base")" @@ -98,10 +96,12 @@ fi : ${resolv_conf:=/etc/resolv.conf} : ${libc_service:=nscd} : ${list_resolv:=@SBINDIR@/resolvconf -l} -if [ "${resolv_conf_head-x}" = x -a -f "$SYSCONFDIR"/resolv.conf.head ]; then +if [ "${resolv_conf_head-x}" = x ] && [ -f "$SYSCONFDIR"/resolv.conf.head ] +then resolv_conf_head="$(cat "${SYSCONFDIR}"/resolv.conf.head)" fi -if [ "${resolv_conf_tail-x}" = x -a -f "$SYSCONFDIR"/resolv.conf.tail ]; then +if [ "${resolv_conf_tail-x}" = x ] && [ -f "$SYSCONFDIR"/resolv.conf.tail ] +then resolv_conf_tail="$(cat "$SYSCONFDIR"/resolv.conf.tail)" fi @@ -110,7 +110,7 @@ signature="# Generated by resolvconf" uniqify() { - local result= + result= while [ -n "$1" ]; do case " $result " in *" $1 "*);; @@ -126,7 +126,7 @@ case "${resolv_conf_passthrough:-NO}" in backup=false newest= for conf in "$IFACEDIR"/*; do - if [ -z "$newest" -o "$conf" -nt "$newest" ]; then + if [ -z "$newest" ] || [ "$conf" -nt "$newest" ]; then newest="$conf" fi done @@ -178,7 +178,7 @@ case "${resolv_conf_passthrough:-NO}" in fi [ -n "$domain" ] && newconf="${newconf}domain $domain$NL" - if [ -n "$newsearch" -a "$newsearch" != "$domain" ]; then + if [ -n "$newsearch" ] && [ "$newsearch" != "$domain" ]; then newconf="${newconf}search $newsearch$NL" fi for n in $newns; do @@ -232,7 +232,7 @@ if [ -n "$libc_restart" ]; then eval $libc_restart elif [ -n "$RESTARTCMD" ]; then set -- ${libc_service} - eval $RESTARTCMD + eval "$RESTARTCMD" else @SBINDIR@/resolvconf -r ${libc_service} fi diff --git a/named.in b/named.in index 2083ea52058..e5b5a91ee77 100644 --- a/named.in +++ b/named.in @@ -28,14 +28,14 @@ [ -f "@SYSCONFDIR@"/resolvconf.conf ] || exit 0 . "@SYSCONFDIR@/resolvconf.conf" || exit 1 -[ -z "$named_zones" -a -z "$named_options" ] && exit 0 +[ -z "${named_zones}${named_options}" ] && exit 0 [ -z "$RESOLVCONF" ] && eval "$(@SBINDIR@/resolvconf -v)" NL=" " # Platform specific kludges -if [ -z "$named_service" -a -z "$named_restart" -a \ - -d "$RCDIR" -a ! -x "$RCDIR"/named ] +if [ -z "${named_service}${named_restart}" ] && + [ -d "$RCDIR" ] && ! [ -x "$RCDIR"/named ] then if [ -x "$RCDIR"/bind9 ]; then # Debian and derivatives @@ -111,7 +111,7 @@ if $changed; then eval $named_restart elif [ -n "$RESTARTCMD" ]; then set -- ${named_service} - eval $RESTARTCMD + eval "$RESTARTCMD" else @SBINDIR@/resolvconf -r ${named_service} fi diff --git a/pdns_recursor.in b/pdns_recursor.in index ad2d922dc4d..f3632e00038 100644 --- a/pdns_recursor.in +++ b/pdns_recursor.in @@ -1,5 +1,5 @@ #!/bin/sh -# Copyright (c) 2009-2011 Roy Marples +# Copyright (c) 2009-2019 Roy Marples # All rights reserved # PowerDNS Recursor subscriber for resolvconf @@ -33,17 +33,14 @@ NL=" " -: ${pdns_service:=pdns_recursor} +: ${pdns_service:=pdns-recursor} 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 n in $NAMESERVERS; do + newzones="$newzones${newzones:+,}$n" +done +[ -n "$newzones" ] && newzones="+.=$newzones$NL" for d in $DOMAINS; do newns= @@ -71,7 +68,7 @@ then eval $pdns_restart elif [ -n "$RESTARTCMD" ]; then set -- ${pdns_service} - eval $RESTARTCMD + eval "$RESTARTCMD" else @SBINDIR@/resolvconf -r ${pdns_service} fi diff --git a/pdnsd.in b/pdnsd.in index 59a4755aff1..971d90d26a1 100644 --- a/pdnsd.in +++ b/pdnsd.in @@ -1,5 +1,5 @@ #!/bin/sh -# Copyright (c) 2010-2013 Roy Marples +# Copyright (c) 2010-2018 Roy Marples # All rights reserved # pdnsd subscriber for resolvconf @@ -28,7 +28,7 @@ [ -f "@SYSCONFDIR@"/resolvconf.conf ] || exit 0 . "@SYSCONFDIR@/resolvconf.conf" || exit 1 -[ -z "$pdnsd_conf" -a -z "$pdnsd_resolv" ] && exit 0 +[ -z "${pdnsd_conf}${pdnsd_resolv}" ] && exit 0 [ -z "$RESOLVCONF" ] && eval "$(@SBINDIR@/resolvconf -v)" NL=" " @@ -41,14 +41,16 @@ signature_end="# End of resolvconf" # but sed may not always be available at the time. remove_markers() { - local m1="$1" m2="$2" x= line= in_marker=0 + m1="$1" + m2="$2" + in_marker=0 shift; shift if type sed >/dev/null 2>&1; then sed "/^$m1/,/^$m2/d" $@ else - for x; do - while read -r line; do + for x do + while read line; do case "$line" in "$m1"*) in_marker=1;; "$m2"*) in_marker=0;; diff --git a/resolvconf.conf b/resolvconf.conf index 607f6af4d91..4f5f2e6f42e 100644 --- a/resolvconf.conf +++ b/resolvconf.conf @@ -4,4 +4,4 @@ resolv_conf=/etc/resolv.conf # If you run a local name server, you should uncomment the below line and # configure your subscribers configuration files below. -#name_servers=127.0.0.1 \ No newline at end of file +#name_servers=127.0.0.1 diff --git a/resolvconf.conf.5.in b/resolvconf.conf.5.in index fcebd864e36..1b0c4716084 100644 --- a/resolvconf.conf.5.in +++ b/resolvconf.conf.5.in @@ -22,7 +22,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd December 29, 2016 +.Dd September 8, 2019 .Dt RESOLVCONF.CONF 5 .Os .Sh NAME @@ -64,19 +64,25 @@ Defaults to YES. .It Sy interface_order These interfaces will always be processed first. If unset, defaults to the following:- -.D1 lo lo[0-9]* +.Bd -compact -literal -offset indent +lo lo[0-9]* +.Ed .It Sy dynamic_order 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]* +.Bd -compact -literal -offset indent +tap[0-9]* tun[0-9]* vpn vpn[0-9]* ppp[0-9]* ippp[0-9]* +.Ed .It Sy inclusive_interfaces -Ignore any exlcusive marking for these interfaces. +Ignore any exclusive marking for these interfaces. This is handy when 3rd party integrations force the .Nm resolvconf -x option and you want to disable it easily. .It Sy local_nameservers If unset, defaults to the following:- -.D1 127.* 0.0.0.0 255.255.255.255 ::1 +.Bd -compact -literal -offset indent +127.* 0.0.0.0 255.255.255.255 ::1 +.Ed .It Sy search_domains Prepend search domains to the dynamically generated list. .It Sy search_domains_append @@ -118,16 +124,24 @@ 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 +.Bd -compact -literal -offset indent +domain foo.org +search foo.org dead.beef +nameserver 1.2.3.4 +nameserver 2.3.4.5 +.Ed and this configuaration: -.D1 replace="search/foo*/bar.com nameserver/1.2.3.4/5.6.7.8 nameserver/2.3.4.5/" +.Bd -compact -literal -offset indent +replace="search/foo*/bar.com" +replace="$replace nameserver/1.2.3.4/5.6.7.8" +replace="$replace nameserver/2.3.4.5/" +.Ed you would get this resolv.conf instead: -.D1 domain foo.org -.D1 search bar.com -.D1 nameserver 5.6.7.8 +.Bd -compact -literal -offset indent +domain foo.org +search bar.com +nameserver 5.6.7.8 +.Ed .It Sy replace_sub Works the same way as .Sy replace @@ -138,9 +152,11 @@ Using the same example resolv.conf and changing 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 +.Bd -compact -literal -offset indent +domain foo.org +search bar.com dead.beef +nameserver 5.6.7.8 +.Ed .It Sy state_dir Override the default state directory of .Pa @VARDIR@ . @@ -195,7 +211,8 @@ Prepend search domains to the dynamically generated list. openresolv ships with subscribers for the name servers .Xr dnsmasq 8 , .Xr named 8 , -.Xr pdnsd 8 +.Xr pdnsd 8 , +.Xr pdns_recursor 8 , and .Xr unbound 8 . Each subscriber can create configuration files which should be included in @@ -203,7 +220,9 @@ 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 +.Bd -compact -literal -offset indent +libc=NO +.Ed .Bl -tag -width indent .It Sy dnsmasq_conf This file tells dnsmasq which name servers to use for specific domains. @@ -211,17 +230,21 @@ This file tells dnsmasq which name servers to use for specific domains. This file tells dnsmasq which name servers to use for global lookups. .Pp Example resolvconf.conf for dnsmasq: -.D1 name_servers=127.0.0.1 -.D1 dnsmasq_conf=/etc/dnsmasq-conf.conf -.D1 dnsmasq_resolv=/etc/dnsmasq-resolv.conf +.Bd -compact -literal -offset indent +name_servers=127.0.0.1 +dnsmasq_conf=/etc/dnsmasq-conf.conf +dnsmasq_resolv=/etc/dnsmasq-resolv.conf +.Ed .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 +.Bd -compact -literal -offset indent +listen-address=127.0.0.1 +# If dnsmasq is compiled for DBus then we can take +# advantage of not having to restart dnsmasq. +enable-dbus +conf-file=/etc/dnsmasq-conf.conf +resolv-file=/etc/dnsmasq-resolv.conf +.Ed .It Sy named_options Include this file in the named options block. This file tells named which name servers to use for global lookups. @@ -230,16 +253,21 @@ Include this file in the named global scope, after the options block. This file tells named which name servers to use for specific domains. .Pp Example resolvconf.conf for named: -.D1 name_servers=127.0.0.1 -.D1 named_options=/etc/named-options.conf -.D1 named_zones=/etc/named-zones.conf +.Bd -compact -literal -offset indent +name_servers=127.0.0.1 +named_options=/etc/named-options.conf +named_zones=/etc/named-zones.conf +.Ed .Pp Example named.conf: -.D1 options { -.D1 listen-on { 127.0.0.1; }; -.D1 include "/etc/named-options.conf"; -.D1 }; -.D1 include "/etc/named-zones.conf"; +.Bd -compact -literal -offset indent +options { + listen-on { 127.0.0.1; }; + include "/etc/named-options.conf"; +}; + +include "/etc/named-zones.conf"; +.Ed .It Sy pdnsd_conf This is the main pdnsd configuration file which we modify to add our forward domains to. @@ -253,32 +281,54 @@ If this variable is not set then it's written to .Pa pdnsd_conf . .Pp Example resolvconf.conf for pdnsd: -.D1 name_servers=127.0.0.1 -.D1 pdnsd_conf=/etc/pdnsd.conf -.D1 # pdnsd_resolv=/etc/pdnsd-resolv.conf +.Bd -compact -literal -offset indent +name_servers=127.0.0.1 +pdnsd_conf=/etc/pdnsd.conf +# pdnsd_resolv=/etc/pdnsd-resolv.conf +.Ed .Pp Example pdnsd.conf: -.D1 global { -.D1 server_ip = 127.0.0.1; -.D1 status_ctl = on; -.D1 } -.D1 server { -.D1 # A server definition is required, even if emtpy. -.D1 label="empty"; -.D1 proxy_only=on; -.D1 # file="/etc/pdnsd-resolv.conf"; -.D1 } +.Bd -compact -literal -offset indent +global { + server_ip = 127.0.0.1; + status_ctl = on; +} +server { + # A server definition is required, even if empty. + label="empty"; + proxy_only=on; + # file="/etc/pdnsd-resolv.conf"; +} +.Ed +.It Sy pdns_zones +This file tells pdns_recursor about specific and global name servers. +.Pp +Example resolvconf.conf for pdns_recursor: +.Bd -compact -literal -offset indent +name_servers=127.0.0.1 +pdns_zones=/etc/pdns/recursor-zones.conf +.Ed +.Pp +Example recursor.conf: +.Bd -compact -literal -offset indent +allow-from=127.0.0.0/8, ::1/128 +forward-zones-file=/etc/pdns/recursor-zones.conf +.Ed .It Sy unbound_conf 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 name_servers=127.0.0.1 -.D1 unbound_conf=/etc/unbound-resolvconf.conf +.Bd -compact -literal -offset indent +name_servers=127.0.0.1 +unbound_conf=/etc/unbound-resolvconf.conf +.Ed .Pp Example unbound.conf: -.D1 include: /etc/unbound-resolvconf.conf +.Bd -compact -literal -offset indent +include: /etc/unbound-resolvconf.conf +.Ed .El .Sh SUBSCRIBER INTEGRATION Not all distributions store the files the subscribers need in the same @@ -292,7 +342,6 @@ Also, users could equally want to use a different version from the one installed by default, such as bind8 and bind9. To accommodate this, the subscribers have these files in configurable variables, documented below. -.Pp .Bl -tag -width indent .It Sy dnsmasq_service Name of the dnsmasq service. @@ -310,6 +359,10 @@ Name of the named service. Command to restart the named service. .It Sy pdnsd_restart Command to restart the pdnsd service. +.It Sy pdns_service +Command to restart the pdns_recursor service. +.It Sy pdns_restart +Command to restart the pdns_recursor service. .It Sy unbound_service Name of the unbound service. .It Sy unbound_restart diff --git a/resolvconf.in b/resolvconf.in index 7353cfc348d..e7d38211181 100644 --- a/resolvconf.in +++ b/resolvconf.in @@ -1,5 +1,5 @@ #!/bin/sh -# Copyright (c) 2007-2016 Roy Marples +# Copyright (c) 2007-2019 Roy Marples # All rights reserved # Redistribution and use in source and binary forms, with or without @@ -25,7 +25,7 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. RESOLVCONF="$0" -OPENRESOLV_VERSION="3.9.0" +OPENRESOLV_VERSION="3.9.2" SYSCONFDIR=@SYSCONFDIR@ LIBEXECDIR=@LIBEXECDIR@ VARDIR=@VARDIR@ @@ -125,21 +125,22 @@ usage() # If you think otherwise, capture a DNS trace and you'll see libc # will strip it regardless. # This also solves setting up duplicate zones in our subscribers. -strip_trailing_dots() +# Also strip any comments denoted by #. +resolv_strip() { - local n= d= - - for n; do - printf "$d%s" "${n%.}" - d=" " + space= + for word; do + case "$word" in + \#*) break;; + esac + printf "%s%s" "$space${word%.}" + space=" " done printf "\n" } private_iface() { - local p - # Allow expansion cd "$IFACEDIR" @@ -168,12 +169,15 @@ private_iface() # for domain name servers, search name servers and global nameservers parse_resolv() { - local line= ns= ds= search= d= n= newns= - local new=true iface= private=false p= domain= l= islocal= - + domain= + new=true newns= + ns= + private=false + search= while read -r line; do + stripped_line="$(resolv_strip ${line#* })" case "$line" in "# resolv.conf from "*) if ${new}; then @@ -189,29 +193,32 @@ parse_resolv() "nameserver "*) islocal=false for l in $local_nameservers; do - case "${line#* }" in + case "$stripped_line" in $l) islocal=true - echo "LOCALNAMESERVERS=\"\$LOCALNAMESERVERS ${line#* }\"" break ;; esac done - $islocal || ns="$ns${line#* } " + if $islocal; then + echo "LOCALNAMESERVERS=\"\$LOCALNAMESERVERS $stripped_line\"" + else + ns="$ns$stripped_line " + fi ;; "domain "*) - search="$(strip_trailing_dots ${line#* })" + search="$stripped_line" if [ -z "$domain" ]; then domain="$search" echo "DOMAIN=\"$domain\"" fi ;; "search "*) - search="$(strip_trailing_dots ${line#* })" + search="$stripped_line" ;; *) [ -n "$line" ] && continue - if [ -n "$ns" -a -n "$search" ]; then + if [ -n "$ns" ] && [ -n "$search" ]; then newns= for n in $ns; do newns="$newns${newns:+,}$n" @@ -236,7 +243,7 @@ parse_resolv() uniqify() { - local result= + result= while [ -n "$1" ]; do case " $result " in *" $1 "*);; @@ -249,8 +256,8 @@ uniqify() dirname() { - local dir= OIFS="$IFS" - local IFS=/ + OIFS="$IFS" + IFS=/ set -- $@ IFS="$OIFS" if [ -n "$1" ]; then @@ -267,7 +274,7 @@ dirname() config_mkdirs() { - local e=0 f d + e=0 for f; do [ -n "$f" ] || continue d="$(dirname "$f")" @@ -295,66 +302,86 @@ detect_init() # Detect the running init system. # As systemd and OpenRC can be installed on top of legacy init # systems we try to detect them first. - local status="@STATUSARG@" + status="@STATUSARG@" : ${status:=status} - if [ -x /bin/systemctl -a -S /run/systemd/private ]; then - RESTARTCMD="if /bin/systemctl --quiet is-active \$1.service; then - /bin/systemctl restart \$1.service; -fi" - elif [ -x /usr/bin/systemctl -a -S /run/systemd/private ]; then - RESTARTCMD="if /usr/bin/systemctl --quiet is-active \$1.service; then - /usr/bin/systemctl restart \$1.service; -fi" - elif [ -x /sbin/rc-service -a \ - -s /libexec/rc/init.d/softlevel -o -s /run/openrc/softlevel ] + if [ -x /bin/systemctl ] && [ -S /run/systemd/private ]; then + RESTARTCMD=' + if /bin/systemctl --quiet is-active $1.service + then + /bin/systemctl restart $1.service + fi' + elif [ -x /usr/bin/systemctl ] && [ -S /run/systemd/private ]; then + RESTARTCMD=' + if /usr/bin/systemctl --quiet is-active $1.service + then + /usr/bin/systemctl restart $1.service + fi' + elif [ -x /sbin/rc-service ] && + { [ -s /libexec/rc/init.d/softlevel ] || + [ -s /run/openrc/softlevel ]; } then - RESTARTCMD="/sbin/rc-service -i \$1 -- -Ds restart" + RESTARTCMD='/sbin/rc-service -i $1 -- -Ds restart' elif [ -x /usr/sbin/invoke-rc.d ]; then RCDIR=/etc/init.d - RESTARTCMD="if /usr/sbin/invoke-rc.d --quiet \$1 status 1>/dev/null 2>&1; then - /usr/sbin/invoke-rc.d \$1 restart; -fi" + RESTARTCMD=' + if /usr/sbin/invoke-rc.d --quiet $1 status >/dev/null 2>&1 + then + /usr/sbin/invoke-rc.d $1 restart + fi' elif [ -x /sbin/service ]; then # Old RedHat RCDIR=/etc/init.d - RESTARTCMD="if /sbin/service \$1; then - /sbin/service \$1 restart; -fi" + RESTARTCMD=' + if /sbin/service $1; then + /sbin/service $1 restart + fi' elif [ -x /usr/sbin/service ]; then # Could be FreeBSD - RESTARTCMD="if /usr/sbin/service \$1 $status 1>/dev/null 2>&1; then - /usr/sbin/service \$1 restart; -fi" + RESTARTCMD=" + if /usr/sbin/service \$1 $status >/dev/null 2>&1 + then + /usr/sbin/service \$1 restart + fi" elif [ -x /bin/sv ]; then - RESTARTCMD="/bin/sv status \$1 >/dev/null 2>&1 && /bin/sv try-restart \$1" + RESTARTCMD='/bin/sv status $1 >/dev/null 2>&1 && + /bin/sv try-restart $1' elif [ -x /usr/bin/sv ]; then - RESTARTCMD="/usr/bin/sv status \$1 >/dev/null 2>&1 && /usr/bin/sv try-restart \$1" - elif [ -e /etc/arch-release -a -d /etc/rc.d ]; then + RESTARTCMD='/usr/bin/sv status $1 >/dev/null 2>&1 && + /usr/bin/sv try-restart $1' + elif [ -e /etc/arch-release ] && [ -d /etc/rc.d ]; then RCDIR=/etc/rc.d - RESTARTCMD="if [ -e /var/run/daemons/\$1 ]; then - /etc/rc.d/\$1 restart; -fi" - elif [ -e /etc/slackware-version -a -d /etc/rc.d ]; then - RESTARTCMD="if /etc/rc.d/rc.\$1 status 1>/dev/null 2>&1; then - /etc/rc.d/rc.\$1 restart; -fi" - elif [ -e /etc/rc.d/rc.subr -a -d /etc/rc.d ]; then + RESTARTCMD=' + if [ -e /var/run/daemons/$1 ] + then + /etc/rc.d/$1 restart + fi' + elif [ -e /etc/slackware-version ] && [ -d /etc/rc.d ]; then + RESTARTCMD=' + if /etc/rc.d/rc.$1 status >/dev/null 2>&1 + then + /etc/rc.d/rc.$1 restart + fi' + elif [ -e /etc/rc.d/rc.subr ] && [ -d /etc/rc.d ]; then # OpenBSD - RESTARTCMD="if /etc/rc.d/\$1 check 1>/dev/null 2>&1; then - /etc/rc.d/\$1 restart; -fi" + RESTARTCMD=' + if /etc/rc.d/$1 check >/dev/null 2>&1 + then + /etc/rc.d/$1 restart + fi' else for x in /etc/init.d/rc.d /etc/rc.d /etc/init.d; do [ -d $x ] || continue - RESTARTCMD="if $x/\$1 $status 1>/dev/null 2>&1; then - $x/\$1 restart; -fi" + RESTARTCMD=" + if $x/\$1 $status >/dev/null 2>&1 + then + $x/\$1 restart + fi" break done fi if [ -z "$RESTARTCMD" ]; then - if [ "$NOINIT_WARNED" != true ]; then + if [ "$_NOINIT_WARNED" != true ]; then warn "could not detect a useable init system" _NOINIT_WARNED=true fi @@ -366,9 +393,9 @@ fi" echo_resolv() { - local line= OIFS="$IFS" + OIFS="$IFS" - [ -n "$1" -a -f "$IFACEDIR/$1" ] || return 1 + [ -n "$1" ] && [ -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. @@ -388,11 +415,16 @@ list_resolv() { [ -d "$IFACEDIR" ] || return 0 - local report=false list= retval=0 cmd="$1" excl= + cmd="$1" shift + excl=false + list= + report=false + retval=0 case "$IF_EXCLUSIVE" in [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1) + excl=true if [ -d "$EXCLUSIVEDIR" ]; then cd "$EXCLUSIVEDIR" for i in *; do @@ -402,19 +434,15 @@ list_resolv() fi done fi - excl=true cd "$IFACEDIR" for i in $inclusive_interfaces; do - if [ -f "$i" -a "$list" = "$i" ]; then + if [ -f "$i" ] && [ "$list" = "$i" ]; then list= excl=false break fi done ;; - *) - excl=false - ;; esac # If we have an interface ordering list, then use that. @@ -431,22 +459,28 @@ list_resolv() done done for i in $dynamic_order; do - if [ -e "$i" -a ! -e "$METRICDIR/"*" $i" ]; then + if [ -e "$i" ] && ! [ -e "$METRICDIR/"*" $i" ]; then list="$list $i" fi for ii in "$i":* "$i".*; do - if [ -f "$ii" -a ! -e "$METRICDIR/"*" $ii" ]; then + if [ -f "$ii" ] && ! [ -e "$METRICDIR/"*" $ii" ] + then list="$list $ii" fi done done + # Interfaces have an implicit metric of 0 if not specified. + for i in *; do + if [ -f "$i" ] && ! [ -e "$METRICDIR/"*" $i" ]; then + list="$list $i" + fi + done if [ -d "$METRICDIR" ]; then cd "$METRICDIR" for i in *; do [ -f "$i" ] && list="$list ${i#* }" done fi - list="$list *" fi cd "$IFACEDIR" @@ -461,23 +495,24 @@ list_resolv() continue fi - if [ "$cmd" = i -o "$cmd" = "-i" ]; then + if [ "$cmd" = i ] || [ "$cmd" = "-i" ]; then printf %s "$i " else echo_resolv "$i" && echo fi - [ $? = 0 -a "$retval" = 1 ] && retval=0 + [ $? = 0 ] && [ "$retval" = 1 ] && retval=0 done - [ "$cmd" = i -o "$cmd" = "-i" ] && echo + [ "$cmd" = i ] || [ "$cmd" = "-i" ] && echo return $retval } -list_remove() { - local list= e= l= result= found= retval=0 - +list_remove() +{ [ -z "$2" ] && return 0 eval list=\"\$$1\" shift + result= + retval=0 set -f for e; do @@ -525,8 +560,6 @@ echo_append() replace() { - local r= k= f= v= val= sub= - while read -r keyword value; do for r in $replace; do k="${r%%/*}" @@ -566,8 +599,6 @@ replace() make_vars() { - local newdomains= d= dn= newns= ns= - # Clear variables DOMAIN= DOMAINS= @@ -575,7 +606,7 @@ make_vars() NAMESERVERS= LOCALNAMESERVERS= - if [ -n "$name_servers" -o -n "$search_domains" ]; then + if [ -n "${name_servers}${search_domains}" ]; then eval "$(echo_prepend | parse_resolv)" fi if [ -z "$VFLAG" ]; then @@ -583,11 +614,12 @@ make_vars() 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 + if [ -n "${name_servers_append}${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 @@ -667,36 +699,37 @@ if [ "$cmd" = D ]; then fi # -l lists our resolv files, optionally for a specific interface -if [ "$cmd" = l -o "$cmd" = i ]; then +if [ "$cmd" = l ] || [ "$cmd" = i ]; then list_resolv "$cmd" "$args" exit $? fi # Restart a service or echo the command to restart a service -if [ "$cmd" = r -o "$cmd" = R ]; then +if [ "$cmd" = r ] || [ "$cmd" = R ]; then detect_init || exit 1 if [ "$cmd" = r ]; then set -- $args - eval $RESTARTCMD + eval "$RESTARTCMD" else - echo "$RESTARTCMD" + echo "$RESTARTCMD" | + sed -e '/^$/d' -e 's/^ //g' fi exit $? fi # Not normally needed, but subscribers should be able to run independently -if [ "$cmd" = v -o -n "$VFLAG" ]; then +if [ "$cmd" = v ] || [ -n "$VFLAG" ]; then make_vars "$iface" exit $? fi # Test that we have valid options -if [ "$cmd" = a -o "$cmd" = d ]; then +if [ "$cmd" = a ] || [ "$cmd" = d ]; then if [ -z "$iface" ]; then usage "Interface not specified" fi elif [ "$cmd" != u ]; then - [ -n "$cmd" -a "$cmd" != h ] && usage "Unknown option $cmd" + [ -n "$cmd" ] && [ "$cmd" != h ] && usage "Unknown option $cmd" usage fi @@ -712,7 +745,7 @@ if [ "$cmd" = a ]; then "$x not allowed at start of interface name";; esac done - [ "$cmd" = a -a -t 0 ] && error_exit "No file given via stdin" + [ "$cmd" = a ] && [ -t 0 ] && error_exit "No file given via stdin" fi if [ ! -d "$VARDIR" ]; then @@ -808,8 +841,8 @@ a) newmetric="$METRICDIR/$IF_METRIC $iface" fi rm -f "$METRICDIR/"*" $iface" - [ "$oldmetric" != "$newmetric" -a \ - "$oldmetric" != "$METRICDIR/* $iface" ] && + [ "$oldmetric" != "$newmetric" ] && + [ "$oldmetric" != "$METRICDIR/* $iface" ] && changed=true [ -n "$newmetric" ] && echo " " >"$newmetric" diff --git a/unbound.in b/unbound.in index 46d45246ff2..34cb401f57c 100644 --- a/unbound.in +++ b/unbound.in @@ -76,7 +76,7 @@ restart_unbound() eval $unbound_restart elif [ -n "$RESTARTCMD" ]; then set -- ${unbound_service} - eval $RESTARTCMD + eval "$RESTARTCMD" else @SBINDIR@/resolvconf -r ${unbound_service} fi From 43ade7d9e9170654b2866b20cb35d5743beda925 Mon Sep 17 00:00:00 2001 From: Siva Mahadevan Date: Sun, 5 Apr 2026 14:40:56 -0400 Subject: [PATCH 3/3] Import openresolv 3.17.4 More information at: https://roy.marples.name/projects/openresolv --- LICENSE | 2 +- Makefile | 54 ++- README.md | 9 +- avahi-daemon.in | 32 ++ configure | 4 +- dnsmasq.in | 8 +- libc.in | 82 ++-- mdnsd.in | 32 ++ named.in | 4 +- pdns_recursor.in | 4 +- pdnsd.in | 10 +- resolvconf.8.in | 226 ++++++---- resolvconf.conf.5.in | 182 ++++++-- resolvconf.in | 1007 ++++++++++++++++++++++++++++++++---------- resolvectl.in | 159 +++++++ systemd-resolved.in | 96 ++++ unbound.in | 30 +- 17 files changed, 1514 insertions(+), 427 deletions(-) create mode 100644 avahi-daemon.in mode change 100644 => 100755 configure create mode 100644 mdnsd.in create mode 100644 resolvectl.in create mode 100644 systemd-resolved.in diff --git a/LICENSE b/LICENSE index 758aec33805..19785ddb1e0 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2007-2019 Roy Marples +Copyright (c) 2007-2020 Roy Marples All rights reserved. Redistribution and use in source and binary forms, with or without diff --git a/Makefile b/Makefile index 6cfa6e4ea07..ba9a75d35f2 100644 --- a/Makefile +++ b/Makefile @@ -5,6 +5,9 @@ _CONFIG_MK!= test -e config.mk && echo config.mk || echo config-null.mk CONFIG_MK?= ${_CONFIG_MK} include ${CONFIG_MK} +DIST!= if test -d .git; then echo "dist-git"; \ + else echo "dist-inst"; fi + SBINDIR?= /sbin SYSCONFDIR?= /etc LIBEXECDIR?= /libexec/resolvconf @@ -20,9 +23,11 @@ BINMODE?= 0755 DOCMODE?= 0644 MANMODE?= 0444 -RESOLVCONF= resolvconf resolvconf.8 resolvconf.conf.5 -SUBSCRIBERS= libc dnsmasq named pdnsd pdns_recursor unbound -TARGET= ${RESOLVCONF} ${SUBSCRIBERS} +RESOLVCONF= resolvconf resolvconf.8 resolvconf.conf.5 +SUBSCRIBERS= libc dnsmasq named pdnsd pdns_recursor unbound +SUBSCRIBERS+= systemd-resolved resolvectl +LIBC_SUBSCRIBERS= avahi-daemon mdnsd +TARGET= ${RESOLVCONF} ${SUBSCRIBERS} ${LIBC_SUBSCRIBERS} SRCS= ${TARGET:C,$,.in,} # pmake SRCS:= ${TARGET:=.in} # gmake @@ -36,12 +41,12 @@ SED_RCDIR= -e 's:@RCDIR@:${RCDIR}:g' SED_STATUSARG= -e 's:@STATUSARG@:${STATUSARG}:g' DISTPREFIX?= ${PKG}-${VERSION} -DISTFILEGZ?= ${DISTPREFIX}.tar.gz DISTFILE?= ${DISTPREFIX}.tar.xz DISTINFO= ${DISTFILE}.distinfo -DISTINFOSIGN= ${DISTINFO}.asc -CKSUM?= cksum -a SHA256 -PGP?= netpgp +DISTINFOMD= ${DISTINFO}.md +DISTSIGN= ${DISTFILE}.asc +SHA256?= sha256 +PGP?= gpg2 GITREF?= HEAD @@ -59,7 +64,7 @@ clean: rm -f ${TARGET} distclean: clean - rm -f config.mk ${DISTFILE} ${DISTINFO} ${DISTINFOSIGN} + rm -f config.mk ${DISTFILE} ${DISTINFO} ${DISTINFOMD} ${DISTSIGN} installdirs: @@ -71,6 +76,9 @@ proginstall: ${TARGET} ${INSTALL} -m ${DOCMODE} resolvconf.conf ${DESTDIR}${SYSCONFDIR} ${INSTALL} -d ${DESTDIR}${LIBEXECDIR} ${INSTALL} -m ${DOCMODE} ${SUBSCRIBERS} ${DESTDIR}${LIBEXECDIR} + ${INSTALL} -d ${DESTDIR}${LIBEXECDIR}/libc.d + ${INSTALL} -m ${DOCMODE} ${LIBC_SUBSCRIBERS} \ + ${DESTDIR}${LIBEXECDIR}/libc.d maninstall: ${INSTALL} -d ${DESTDIR}${MANDIR}/man8 @@ -87,18 +95,30 @@ dist-inst: mkdir /tmp/${DISTPREFIX} cp -RPp * /tmp/${DISTPREFIX} (cd /tmp/${DISTPREFIX}; make clean) - tar -cvjpf ${DISTFILE} -C /tmp ${DISTPREFIX} + tar -cvJpf ${DISTFILE} -C /tmp ${DISTPREFIX} rm -rf /tmp/${DISTPREFIX} -dist: dist-git +dist: ${DIST} distinfo: dist - rm -f ${DISTINFO} ${DISTINFOSIGN} - ${CKSUM} ${DISTFILE} >${DISTINFO} - #printf "SIZE (${DISTFILE}) = %s\n" $$(wc -c <${DISTFILE}) >>${DISTINFO} - ${PGP} --clearsign --output=${DISTINFOSIGN} ${DISTINFO} - chmod 644 ${DISTINFOSIGN} - ls -l ${DISTFILE} ${DISTINFO} ${DISTINFOSIGN} + rm -f ${DISTINFO} ${DISTSIGN} + ${SHA256} ${DISTFILE} >${DISTINFO} + wc -c <${DISTFILE} \ + | xargs printf 'Size (${DISTFILE}) = %s\n' >>${DISTINFO} + ${PGP} --sign --armour --detach ${DISTFILE} + chmod 644 ${DISTSIGN} + ls -l ${DISTFILE} ${DISTINFO} ${DISTSIGN} + +${DISTINFOMD}: ${DISTINFO} + echo '```' >${DISTINFOMD} + cat ${DISTINFO} >>${DISTINFOMD} + echo '```' >>${DISTINFOMD} + +release: distinfo ${DISTINFOMD} + gh release create v${VERSION} \ + --title "openresolv ${VERSION}" --draft --generate-notes \ + --notes-file ${DISTINFOMD} \ + ${DISTFILE} ${DISTSIGN} import: dist rm -rf /tmp/${DISTPREFIX} @@ -115,4 +135,4 @@ _import-src: @${ECHO} "openresolv-${VERSION} imported to ${DESTDIR}" import-src: - ${MAKE} _import-src DESTDIR=`if [ -n "${DESTDIR}" ]; then echo "${DESTDIR}"; else echo /tmp/${DISTPREFIX}; fi` + ${MAKE} _import-src DESTDIR=`if [ -n "${DESTDIR}" ]; then echo "${DESTDIR}"; else echo /tmp/${DISTPREFIX}; fi` diff --git a/README.md b/README.md index 739cc73356c..50e54ced93e 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ Here's some reasons: The last point is quite important, especially when running VPN systems. Take the following resolv.conf files which have been generated by a -[DHCP client](../dhcpcd) and sent to resolvconf: +[DHCP client](https://github.com/NetworkConfiguration/dhcpcd) and sent to resolvconf: ``` # resolv.conf from bge0 @@ -58,7 +58,12 @@ openresolv ships with helpers for: * [dnsmasq](http://www.thekelleys.org.uk/dnsmasq/doc.html) * [ISC BIND](http://www.isc.org/software/bind) * [PowerDNS Recursor](http://wiki.powerdns.com/trac) + * [systemd-resolved](https://www.freedesktop.org/software/systemd/man/latest/systemd-resolved.service.html) See the -[configuration section](https://roy.marples.name/projects/openresolv/config) +[configuration section](https://roy.marples.name/projects/openresolv/configuration) for more details. + +If openresolv updates `/etc/resolv.conf` it can notify the following of this: + * [Bonjour (mdnsd)](https://developer.apple.com/bonjour/) + * [avahi](http://www.avahi.org/) diff --git a/avahi-daemon.in b/avahi-daemon.in new file mode 100644 index 00000000000..cdac44fe823 --- /dev/null +++ b/avahi-daemon.in @@ -0,0 +1,32 @@ +#!/bin/sh +# Copyright (c) 2007-2023 Roy Marples +# All rights reserved + +# avahi-daemon notifier for resolvconf libc subscriber + +# 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. + +: ${avahi_daemon_pidfile:=/var/run/avahi-daemon/pid} +if [ -s "$avahi_daemon_pidfile" ]; then + kill -HUP $(cat "$avahi_daemon_pidfile") +fi diff --git a/configure b/configure old mode 100644 new mode 100755 index c9422b74b69..50fe74da8ba --- a/configure +++ b/configure @@ -39,7 +39,7 @@ for x do --includedir) eval INCLUDEDIR="$INCLUDEDIR${INCLUDEDIR:+ }$var";; --datadir|--infodir) ;; # ignore autotools --disable-maintainer-mode|--disable-dependency-tracking) ;; - --help) echo "See the README file for available options"; exit 0;; + --help) echo "See the source for available options"; exit 0;; *) echo "$0: WARNING: unknown option $opt" >&2;; esac done @@ -92,7 +92,7 @@ dragonfly*) : ${LIBEXECDIR:=${PREFIX:-/usr}/libexec/resolvconf} ;; linux*) - # cksum does't support -a and netpgp is rare + # cksum doesn't support -a and netpgp is rare echo "CKSUM= sha256sum --tag" >>$CONFIG_MK echo "PGP= gpg2" >>$CONFIG_MK ;; diff --git a/dnsmasq.in b/dnsmasq.in index 8b51866dca2..ae33f094b64 100644 --- a/dnsmasq.in +++ b/dnsmasq.in @@ -1,5 +1,5 @@ #!/bin/sh -# Copyright (c) 2007-2019 Roy Marples +# Copyright (c) 2007-2023 Roy Marples # All rights reserved # dnsmasq subscriber for resolvconf @@ -105,7 +105,7 @@ for d in $DOMAINS; do empty=true continue fi - i=$(($i + 1)) + i=$((i + 1)) while [ ${#addr} -lt 4 ]; do addr="0${addr}" done @@ -118,7 +118,7 @@ for d in $DOMAINS; do fi done while [ $i != 8 ]; do - i=$(($i + 1)) + i=$((i + 1)) front="$front byte:0 byte:0" done front="${front}$back" @@ -151,7 +151,7 @@ else fi # Try to ensure that config dirs exist -if type config_mkdirs >/dev/null 2>&1; then +if command -v config_mkdirs >/dev/null 2>&1; then config_mkdirs "$dnsmasq_conf" "$dnsmasq_resolv" else @SBINDIR@/resolvconf -D "$dnsmasq_conf" "$dnsmasq_resolv" diff --git a/libc.in b/libc.in index f44f61bac7f..78fe86ce00c 100644 --- a/libc.in +++ b/libc.in @@ -1,5 +1,5 @@ #!/bin/sh -# Copyright (c) 2007-2019 Roy Marples +# Copyright (c) 2007-2025 Roy Marples # All rights reserved # libc subscriber for resolvconf @@ -29,10 +29,23 @@ SYSCONFDIR=@SYSCONFDIR@ LIBEXECDIR=@LIBEXECDIR@ VARDIR=@VARDIR@ -IFACEDIR="$VARDIR/interfaces" +KEYDIR="$VARDIR/keys" +# Compat +if [ ! -d "$KEYDIR" ] && [ -d "$VARDIR/interfaces" ]; then + KEYDIR="$VARDIR/interfaces" +fi + +CMD="$1" +KEY="$2" + NL=" " +warn() +{ + echo "${0##*/}: $*" >&2 +} + # sed may not be available, and this is faster on small files key_get_value() { @@ -94,8 +107,12 @@ elif [ -d "$SYSCONFDIR"/resolvconf ]; then fi fi : ${resolv_conf:=/etc/resolv.conf} +if [ "$resolv_conf" = "/dev/null" ]; then + exit 0 +fi +: ${resolv_conf_tmp:="$resolv_conf.$$.openresolv"} : ${libc_service:=nscd} -: ${list_resolv:=@SBINDIR@/resolvconf -l} +: ${list_resolv:=@SBINDIR@/resolvconf -L} if [ "${resolv_conf_head-x}" = x ] && [ -f "$SYSCONFDIR"/resolv.conf.head ] then resolv_conf_head="$(cat "${SYSCONFDIR}"/resolv.conf.head)" @@ -105,9 +122,8 @@ then resolv_conf_tail="$(cat "$SYSCONFDIR"/resolv.conf.tail)" fi -backup=true signature="# Generated by resolvconf" - + uniqify() { result= @@ -123,15 +139,14 @@ 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 + for conf in "$KEYDIR"/*; do if [ -z "$newest" ] || [ "$conf" -nt "$newest" ]; then newest="$conf" fi done [ -z "$newest" ] && exit 0 - newconf="$(cat "$newest")$NL" + newconf="$signature$NL$(cat "$newest")$NL" ;; /dev/null|[Nn][Uu][Ll][Ll]) : ${resolv_conf_local_only:=NO} @@ -207,27 +222,44 @@ esac # Check if the file has actually changed or not 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")$NL" - fi - elif [ -e "$resolv_conf" ]; then - read line <"$resolv_conf" - if [ "$line" != "$signature" ]; then - cp "$resolv_conf" "$resolv_conf.bak" + if [ "$CMD" != u ] && \ + [ "$(cat "$resolv_conf")" = "$(printf %s "$newconf")" ] + then + exit 0 + fi + read line <"$resolv_conf" + if [ "$line" != "$signature" ]; then + if [ "$CMD" != u ]; then + warn "signature mismatch: $resolv_conf" + warn "run \`resolvconf -u\` to update" + exit 1 fi + cp "$resolv_conf" "$resolv_conf.bak" fi fi -# Create our resolv.conf now -(umask 022; printf %s "$newconf" >"$resolv_conf") +# There are pros and cons for writing directly to resolv.conf +# instead of a temporary file and then moving it over. +# The default is to write to resolv.conf as it has the least +# issues and has been the long standing default behaviour. +# resolv.conf could also be bind mounted for network namespaces +# so we cannot move in this instance. +case "${resolv_conf_mv:-NO}" in +[Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1) + # Protect against symlink attack, ensure new file does not exist + rm -f "$resolv_conf_tmp" + # Keep original file owner, group and mode + [ -r "$resolv_conf" ] && cp -p "$resolv_conf" "$resolv_conf_tmp" + # Create our resolv.conf now + if (umask 022; printf %s "$newconf" >"$resolv_conf_tmp"); then + mv "$resolv_conf_tmp" "$resolv_conf" + fi + ;; +*) + (umask 022; printf %s "$newconf" >"$resolv_conf") + ;; +esac + if [ -n "$libc_restart" ]; then eval $libc_restart elif [ -n "$RESTARTCMD" ]; then diff --git a/mdnsd.in b/mdnsd.in new file mode 100644 index 00000000000..3b01cd459f1 --- /dev/null +++ b/mdnsd.in @@ -0,0 +1,32 @@ +#!/bin/sh +# Copyright (c) 2007-2023 Roy Marples +# All rights reserved + +# mdnsd notifier for resolvconf libc subscriber + +# 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. + +: ${mdnsd_pidfile:=/var/run/mdnsd/mdnsd.pid} +if [ -s "$mdnsd_pidfile" ]; then + kill -HUP $(cat "$mdnsd_pidfile") +fi diff --git a/named.in b/named.in index e5b5a91ee77..978eb98eafa 100644 --- a/named.in +++ b/named.in @@ -1,5 +1,5 @@ #!/bin/sh -# Copyright (c) 2007-2016 Roy Marples +# Copyright (c) 2007-2023 Roy Marples # All rights reserved # named subscriber for resolvconf @@ -79,7 +79,7 @@ for d in $DOMAINS; do done # Try to ensure that config dirs exist -if type config_mkdirs >/dev/null 2>&1; then +if command -v config_mkdirs >/dev/null 2>&1; then config_mkdirs "$named_options" "$named_zones" else @SBINDIR@/resolvconf -D "$named_options" "$named_zones" diff --git a/pdns_recursor.in b/pdns_recursor.in index f3632e00038..71045689465 100644 --- a/pdns_recursor.in +++ b/pdns_recursor.in @@ -1,5 +1,5 @@ #!/bin/sh -# Copyright (c) 2009-2019 Roy Marples +# Copyright (c) 2009-2023 Roy Marples # All rights reserved # PowerDNS Recursor subscriber for resolvconf @@ -54,7 +54,7 @@ for d in $DOMAINS; do done # Try to ensure that config dirs exist -if type config_mkdirs >/dev/null 2>&1; then +if command -v config_mkdirs >/dev/null 2>&1; then config_mkdirs "$pdnsd_zones" else @SBINDIR@/resolvconf -D "$pdnsd_zones" diff --git a/pdnsd.in b/pdnsd.in index 971d90d26a1..149808c0966 100644 --- a/pdnsd.in +++ b/pdnsd.in @@ -1,5 +1,5 @@ #!/bin/sh -# Copyright (c) 2010-2018 Roy Marples +# Copyright (c) 2010-2023 Roy Marples # All rights reserved # pdnsd subscriber for resolvconf @@ -46,7 +46,7 @@ remove_markers() in_marker=0 shift; shift - if type sed >/dev/null 2>&1; then + if command -v sed >/dev/null 2>&1; then sed "/^$m1/,/^$m2/d" $@ else for x do @@ -66,9 +66,9 @@ remove_markers() change_file() { if [ -e "$1" ]; then - if type cmp >/dev/null 2>&1; then + if command -v cmp >/dev/null 2>&1; then cmp -s "$1" "$2" - elif type diff >/dev/null 2>&1; then + elif command -v diff >/dev/null 2>&1; then diff -q "$1" "$2" >/dev/null else # Hopefully we're only working on small text files ... @@ -88,7 +88,7 @@ newresolv="# Generated by resolvconf$NL" changed=false # Try to ensure that config dirs exist -if type config_mkdirs >/dev/null 2>&1; then +if command -v config_mkdirs >/dev/null 2>&1; then config_mkdirs "$pdnsd_resolv" "$pdnsd_conf" else @SBINDIR@/resolvconf -D "$pdnsd_resolv" "$pdnsd_conf" diff --git a/resolvconf.8.in b/resolvconf.8.in index fa00582976d..f29d272d71e 100644 --- a/resolvconf.8.in +++ b/resolvconf.8.in @@ -1,4 +1,4 @@ -.\" Copyright (c) 2007-2016 Roy Marples +.\" Copyright (c) 2007-2025 Roy Marples .\" All rights reserved .\" .\" Redistribution and use in source and binary forms, with or without @@ -22,7 +22,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd November 29, 2016 +.Dd June 26, 2025 .Dt RESOLVCONF 8 .Os .Sh NAME @@ -35,14 +35,18 @@ .Op Fl m Ar metric .Op Fl p .Op Fl x -.Fl a Ar interface Ns Op Ar .protocol +.Fl a Ar key .No < Ns Pa file .Nm +.Fl C Ar pattern +.Nm +.Fl c Ar pattern +.Nm .Op Fl f -.Fl d Ar interface Ns Op Ar .protocol +.Fl d Ar key .Nm .Op Fl x -.Fl il Ar pattern +.Fl iLlp Ar pattern .Nm .Fl u .Nm @@ -68,12 +72,20 @@ file to via .Xr stdin 4 with the argument -.Fl a Ar interface Ns Op Ar .protocol +.Fl a Ar key instead of the filesystem. .Nm then updates .Pa /etc/resolv.conf as it thinks best. +If +.Pa /etc/resolv.conf +already exists and the top line does not match the expected signature, +then +.Nm +will refuse to update it unless the +.Fl u +update command is given. When a local resolver other than libc is installed, such as .Xr dnsmasq 8 or @@ -82,27 +94,40 @@ then .Nm will supply files that the resolver should be configured to include. .Pp +At it's heart, .Nm -assumes it has a job to do. -In some situations +is a key/value store for +.Pa resolv.conf +files. +Each entry must have a unique +.Ar key +and should be expressed as +.Sy interface.protocol +so that it's easy to tell from where the +.Pa resolv.conf +file came from. +This also allows using pattern matching such as +.Sy interface.* +to match all protocols running on the interface. +For example, a modern system will likely run DHCP, RA and DHCPv6 +which could be from separate programs or one program running +many protocols. +However, this is not a fixed requirement, .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 . +will work with any key name and it should be treated as an opaque value +outside of +.Nm . .Pp .Nm -can mark an interfaces +can mark a .Pa resolv.conf -as private. +as private and optionally non-searchable. This means that the name servers listed in that .Pa resolv.conf -are only used for queries against the domain/search listed in the same file. +are only used for queries against the domain/search listed in the same file +and if non-searchable then the domain/search listed are +excluded from the global search list defined in +.Pa /etc/resolv.conf . This only works when a local resolver other than libc is installed. See .Xr resolvconf.conf 5 @@ -111,57 +136,97 @@ for how to configure to use a local name server and how to remove the private marking. .Pp .Nm -can mark an interfaces +can mark a .Pa resolv.conf as exclusive. -Only the latest exclusive interface is used for processing, otherwise all are. +Only the latest exclusive key is used for processing, otherwise all are. .Pp -When an interface goes down, it should then call +When a configuration source goes away, +such as an interface going down or a VPN stopping, +it should then call .Nm with -.Fl d Ar interface.* -arguments to delete the +.Fl d Ar key +arguments to clean up the .Pa resolv.conf -file(s) for all the -.Ar protocols -on the -.Ar interface . +it added previously. +For systems that support the concept of persisting configuration when +the source is suspended, +such as the carrier going down, +then it should instead call +.Nm +with +.Fl C Ar key +arguments to deprecate the entry +.Fl c Ar key +to activate the entry when it comes back again. +This only affects the order in which the +.Pa resolv.conf +entries are processed. .Pp -Here are some options for the above commands:- +Here are some options for the above commands: .Bl -tag -width pattern_opt .It Fl f -Ignore non existent interfaces. -Only really useful for deleting interfaces. +Ignore non existent +.Pa resolv.conf +entries. +Only really useful for deleting. .It Fl m Ar metric -Set the metric of the interface when adding it, default of 0. +Set the metric of the +.Pa resolv.conf +entry when adding it, default of 0. Lower metrics take precedence. -This affects the default order of interfaces when listed. -.It Fl p -Marks the interface +This affects the default order of entires when listed. +.It Fl p Op Ar pattern +Marks the .Pa resolv.conf -as private. +as private if the +.Fl a +command is given, otherwise +.Pa resolv.conf +entries having their key matching +.Ar pattern +are listed. +If an extra +.Fl p +is given then the +.Pa resolv.conf +is marked as non-searchable as well. .It Fl x -Mark the interface +Mark the .Pa resolv.conf -as exclusive when adding, otherwise only use the latest exclusive interface. +as exclusive when adding, otherwise only use the latest exclusive key. .El .Pp .Nm -has some more commands for general usage:- +has some more commands for general usage: .Bl -tag -width pattern_opt -.It Fl i Ar pattern -List the interfaces and protocols, optionally matching +.It Fl i Op Ar pattern +List the keys stored, optionally matching .Ar pattern , we have .Pa resolv.conf files for. -.It Fl l Ar pattern +If the +.Fl L +option is given first, then the keys will be list post-processed. +.It Fl L Op Ar pattern +List the +.Pa resolv.conf +files we have, +post-processed by the +.Xr resolvconf.conf 5 +configuration. +If +.Ar pattern +is specified then we list the files for the keys which match it. +.It Fl l Op Ar pattern List the .Pa resolv.conf files we have. If .Ar pattern -is specified then we list the files for the interfaces and protocols +is specified then we list the files for the keys which match it. that match it. .It Fl u Force @@ -169,27 +234,23 @@ Force 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. +what it already has for that key. .It Fl Fl version Echo the resolvconf version to .Em stdout . .El .Pp .Nm -also has some commands designed to be used by it's subscribers and -system startup:- +also has some commands designed to be used by its subscribers and +system startup: .Bl -tag -width pattern_opt .It Fl I Initialise the state directory .Pa @VARDIR@ . -This only needs to be called if the initial system boot sequence does not -automatically clean it out; for example the state directory is moved -somewhere other than -.Pa /var/run . -If used, it should only be called once as early in the system boot sequence -as possible and before -.Nm -is used to add interfaces. +This should be called after the base state directory has either been +cleaned out or mounted as a memory backed filesystem during the +initial boot sequence before any daemon has the chance to call +.Nm . .It Fl R Echo the command used to restart a service. .It Fl r Ar service @@ -208,17 +269,18 @@ except that only the information configured in .Xr resolvconf.conf 5 is set. .El -.Sh INTERFACE ORDERING +.Sh RESOLV.CONF ORDERING For .Nm -to work effectively, it has to process the resolv.confs for the interfaces -in the correct order. +to work effectively, it has to process the +.Pa resolv.conf +entries in the correct order. .Nm -first processes interfaces from the -.Sy interface_order -list, then interfaces without a metic and that match the +first processes keys from the +.Sy key_order +list, then entries without a metric and that match the .Sy dynamic_order -list, then interfaces with a metric in order and finally the rest in +list, then entries with a metric in order and finally the rest in the operating systems lexical order. See .Xr resolvconf.conf 5 @@ -226,19 +288,9 @@ 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 pattern_opt .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 @@ -270,11 +322,15 @@ option is not present then we use .Va IF_METRIC for the metric. .It Va IF_PRIVATE -Marks the interface +Marks the .Pa resolv.conf as private. +.It Va IF_NOSEARCH +Marks the +.Pa resolv.conf +as non-searchable. .It Va IF_EXCLUSIVE -Marks the interface +Marks the .Pa resolv.conf as exclusive. .El @@ -295,6 +351,17 @@ Directory of subscribers which are run after the libc subscriber is run. State directory for .Nm . .El +.Sh NOTES +Domain labels are assumed to be in ASCII and are converted to lower case +to avoid duplicate zones when given differing case from different sources. +.Pp +When running a local resolver other than libc, you will need to configure it +to include files that +.Nm +will generate. +You should consult +.Xr resolvconf.conf 5 +for instructions on how to configure your resolver. .Sh SEE ALSO .Xr resolver 3 , .Xr stdin 4 , @@ -310,14 +377,3 @@ resolvconf, as written by Thomas Hood. .Sh BUGS Please report them to .Lk http://roy.marples.name/projects/openresolv -.Pp -.Nm -does not validate any of the files given to it. -.Pp -When running a local resolver other than libc, you will need to configure it -to include files that -.Nm -will generate. -You should consult -.Xr resolvconf.conf 5 -for instructions on how to configure your resolver. diff --git a/resolvconf.conf.5.in b/resolvconf.conf.5.in index 1b0c4716084..4501221be84 100644 --- a/resolvconf.conf.5.in +++ b/resolvconf.conf.5.in @@ -1,4 +1,4 @@ -.\" Copyright (c) 2009-2016 Roy Marples +.\" Copyright (c) 2009-2025 Roy Marples .\" All rights reserved .\" .\" Redistribution and use in source and binary forms, with or without @@ -22,7 +22,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd September 8, 2019 +.Dd May 15, 2025 .Dt RESOLVCONF.CONF 5 .Os .Sh NAME @@ -61,26 +61,44 @@ 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:- -.Bd -compact -literal -offset indent +.It Sy allow_keys +If set, only these keys will be processed. +.It Sy deny_keys +If set, these keys will not be processed. +.It Sy exclude +Is a space separated list of key/value pairs to match. +If all key/value pairs in one element can be found in the file, +then the whole file will be excluded from processing. +The syntax is this: +.Va $keyword Ns / Ns Va $match Ns Op / Ns Va $keyword Ns / Ns Va $match +.Pp +For example given this configuration: +.Bd -literal -compact -offset indent +exclude="search/foo*/nameserver/1.2.3.4 search/bar.org" +.Ed +.Pp +Then any resolv.conf with both a search option starting with foo with a nameserver of 1.2.3.4 +OR a search option of bar.org would be excluded. +.It Sy key_order +These keys will always be processed first. +If unset, defaults to the following: +.Bd -literal -compact -offset indent lo lo[0-9]* .Ed .It Sy dynamic_order -These interfaces will be processed next, unless they have a metric. -If unset, defaults to the following:- -.Bd -compact -literal -offset indent -tap[0-9]* tun[0-9]* vpn vpn[0-9]* ppp[0-9]* ippp[0-9]* +These keys will be processed next, unless they have a metric. +If unset, defaults to the following: +.Bd -literal -compact -offset indent +tap[0-9]* tun[0-9]* vpn vpn[0-9]* wg[0-9]* ppp[0-9]* ippp[0-9]* .Ed -.It Sy inclusive_interfaces -Ignore any exclusive marking for these interfaces. +.It Sy inclusive_keys +Ignore any exclusive marking for these keys. This is handy when 3rd party integrations force the .Nm resolvconf -x option and you want to disable it easily. .It Sy local_nameservers -If unset, defaults to the following:- -.Bd -compact -literal -offset indent +If unset, defaults to the following: +.Bd -literal -compact -offset indent 127.* 0.0.0.0 255.255.255.255 ::1 .Ed .It Sy search_domains @@ -101,20 +119,25 @@ Append name servers to the dynamically generated list. 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 +.It Sy private_keys +These keys name servers will only be queried for the domains listed in their resolv.conf. Useful for VPN domains. Setting -.Sy private_interfaces Ns ="*" +.Sy private_keys Ns ="*" will stop the forwarding of the root zone and allows the local resolver to recursively query the root servers directly. Requires a local nameserver other than libc. This is equivalent to the .Nm resolvconf -p option. -.It Sy public_interfaces -Force these interface to be public, overriding the private marking. +.It Sy nosearch_keys +These keys domains/search won't be added to the global search list +in +.Pa /etc/resolv.conf . +.It Sy public_keys +Force these keys to be public, overriding the private and nosearch +markings. This is handy when 3rd party integrations force the .Nm resolvconf -p option and you want to disable it easily. @@ -124,24 +147,27 @@ The syntax is this: .Va $keyword Ns / Ns Va $match Ns / Ns Va $replacement .Pp Example, given this resolv.conf: -.Bd -compact -literal -offset indent +.Bd -literal -compact -offset indent domain foo.org search foo.org dead.beef nameserver 1.2.3.4 nameserver 2.3.4.5 .Ed -and this configuaration: -.Bd -compact -literal -offset indent +.Pp +and this configuration: +.Bd -literal -compact -offset indent replace="search/foo*/bar.com" replace="$replace nameserver/1.2.3.4/5.6.7.8" replace="$replace nameserver/2.3.4.5/" .Ed +.Pp you would get this resolv.conf instead: -.Bd -compact -literal -offset indent +.Bd -literal -compact -offset indent domain foo.org search bar.com nameserver 5.6.7.8 .Ed +.Pp .It Sy replace_sub Works the same way as .Sy replace @@ -152,11 +178,12 @@ Using the same example resolv.conf and changing to .Sy replace_sub , you would get this resolv.conf instead: -.Bd -compact -literal -offset indent +.Bd -literal -compact -offset indent domain foo.org search bar.com dead.beef nameserver 5.6.7.8 .Ed +.Pp .It Sy state_dir Override the default state directory of .Pa @VARDIR@ . @@ -167,12 +194,17 @@ is in use unless the old directory is copied to the new one. .Sh LIBC OPTIONS The following variables affect .Xr resolv.conf 5 -directly:- +directly: .Bl -tag -width indent .It Sy resolv_conf Defaults to .Pa /etc/resolv.conf if not set. +Set to +.Pa /dev/null +to stop +.Xr resolvconf 8 +from changing it. .It Sy resolv_conf_options A list of libc resolver options, as specified in .Xr resolv.conf 5 . @@ -188,6 +220,14 @@ is unset unless overridden and only the information set in .Nm is written to .Sy resolv_conf . +.It Sy resolv_conf_restore +When set to YES and +and an empty +.Pa resolv.conf +would be written, restore +.Pa resolv.conf.bak +instead if it exists. +Defaults to YES if not set. .It Sy resolv_conf_sortlist A libc resolver sortlist, as specified in .Xr resolv.conf 5 . @@ -206,21 +246,28 @@ Prepend name servers to the dynamically generated list. Append search domains to the dynamically generated list. .It Sy prepend_search Prepend search domains to the dynamically generated list. +.It Sy resolv_conf_mv +Defaults to NO. +Defines if +.Pa /etc/resolv.conf +is updated by writing to a temporary file and then moving it +vs writing directly to it. .El .Sh SUBSCRIBER OPTIONS openresolv ships with subscribers for the name servers .Xr dnsmasq 8 , .Xr named 8 , .Xr pdnsd 8 , -.Xr pdns_recursor 8 , +.Xr pdns_recursor 1 , and .Xr unbound 8 . Each subscriber can create configuration files which should be included in -in the subscribers main configuration file. +the subscribers main configuration file. .Pp -To disable a subscriber, simply set it's name to NO. +To disable a subscriber, simply set its name to NO. +If the subscriber name has a dash in it, then replace it with an underscore. For example, to disable the libc subscriber you would set: -.Bd -compact -literal -offset indent +.Bd -literal -compact -offset indent libc=NO .Ed .Bl -tag -width indent @@ -230,14 +277,14 @@ This file tells dnsmasq which name servers to use for specific domains. This file tells dnsmasq which name servers to use for global lookups. .Pp Example resolvconf.conf for dnsmasq: -.Bd -compact -literal -offset indent +.Bd -literal -compact -offset indent name_servers=127.0.0.1 dnsmasq_conf=/etc/dnsmasq-conf.conf dnsmasq_resolv=/etc/dnsmasq-resolv.conf .Ed .Pp Example dnsmasq.conf: -.Bd -compact -literal -offset indent +.Bd -literal -compact -offset indent listen-address=127.0.0.1 # If dnsmasq is compiled for DBus then we can take # advantage of not having to restart dnsmasq. @@ -253,14 +300,14 @@ Include this file in the named global scope, after the options block. This file tells named which name servers to use for specific domains. .Pp Example resolvconf.conf for named: -.Bd -compact -literal -offset indent +.Bd -literal -compact -offset indent name_servers=127.0.0.1 named_options=/etc/named-options.conf named_zones=/etc/named-zones.conf .Ed .Pp Example named.conf: -.Bd -compact -literal -offset indent +.Bd -literal -compact -offset indent options { listen-on { 127.0.0.1; }; include "/etc/named-options.conf"; @@ -281,14 +328,14 @@ If this variable is not set then it's written to .Pa pdnsd_conf . .Pp Example resolvconf.conf for pdnsd: -.Bd -compact -literal -offset indent +.Bd -literal -compact -offset indent name_servers=127.0.0.1 pdnsd_conf=/etc/pdnsd.conf # pdnsd_resolv=/etc/pdnsd-resolv.conf .Ed .Pp Example pdnsd.conf: -.Bd -compact -literal -offset indent +.Bd -literal -compact -offset indent global { server_ip = 127.0.0.1; status_ctl = on; @@ -304,29 +351,76 @@ server { This file tells pdns_recursor about specific and global name servers. .Pp Example resolvconf.conf for pdns_recursor: -.Bd -compact -literal -offset indent +.Bd -literal -compact -offset indent name_servers=127.0.0.1 pdns_zones=/etc/pdns/recursor-zones.conf .Ed .Pp Example recursor.conf: -.Bd -compact -literal -offset indent +.Bd -literal -compact -offset indent allow-from=127.0.0.0/8, ::1/128 forward-zones-file=/etc/pdns/recursor-zones.conf .Ed +.It Sy resolvectl +When set to YES, +.Xr resolvectl 1 +will be used to write per interface entries from +.Xr resolvconf 8 +to +.Xr systemd-resolved 8 . +A warning is emitted for any entry that cannot be matched to an +interface. +.Pp +This subscriber should only be used if your systemd-resolved does +not support DNS delegates and you need private or non searchable +.Xr resolvconf 8 +entries, or you're really beholden to seeing DNS setup per interface via +.Xr resolvectl 1 . +The systemd-resolved subscriber documented below is the better option. +.Pp +Example resolvconf.conf for resolvectl: +.Bd -literal -compact -offset indent +# Keep /etc/resolv.conf as systemd-resolved wants it +libc=NO +resolvectl=YES +.Ed +.It Sy systemd_resolved +When set to YES, global DNS will be written to the +.Sy systemd_resolved_conf +configuration file and DNS delegates will be written to the +.Sy systemd_delegate_dir +directory. +.It Sy systemd_resolved_conf +Defaults to +.Pa /run/systemd/resolved.conf.d/60-resolvconf.conf . +.It Sy systemd_delegate_dir +Defaults to +.Pa /run/systemd/dns-delegate.d . +.Pp +Example resolvconf.conf for systemd-resolved: +.Bd -literal -compact -offset indent +# Keep /etc/resolv.conf as systemd-resolved wants it +libc=NO +systemd_resolved=YES +.Ed .It Sy unbound_conf 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. +.It Sy unbound_private +When set to YES, unbound marks the domains as private, allowing it and its subdomains to contain private addresses. +.It Sy unbound_forward_zone_options +Options appended to each forward zone. +Each option should be separated by an embedded new line. .Pp Example resolvconf.conf for unbound: -.Bd -compact -literal -offset indent +.Bd -literal -compact -offset indent name_servers=127.0.0.1 unbound_conf=/etc/unbound-resolvconf.conf .Ed .Pp Example unbound.conf: -.Bd -compact -literal -offset indent +.Bd -literal -compact -offset indent include: /etc/unbound-resolvconf.conf .Ed .El @@ -380,5 +474,13 @@ Location of the unbound pidfile. Each distribution is a special snowflake and likes to name the same thing differently, namely the named service script. .Pp +Swapping between resolvectl and systemd-resolved subscribers at runtime +is not supported. +Files referenced by systemd_resolved_conf and systemd_delegate_dir +need to be removed by hand. +A reboot is recommended so that stale data is removed. +While you could run them both at the same time, only using one is the +recommended approach. +.Pp Please report them to -.Lk http://roy.marples.name/projects/openresolv +.Lk https://roy.marples.name/projects/openresolv diff --git a/resolvconf.in b/resolvconf.in index e7d38211181..2f55a9de0ed 100644 --- a/resolvconf.in +++ b/resolvconf.in @@ -1,5 +1,5 @@ #!/bin/sh -# Copyright (c) 2007-2019 Roy Marples +# Copyright (c) 2007-2025 Roy Marples # All rights reserved # Redistribution and use in source and binary forms, with or without @@ -25,7 +25,7 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. RESOLVCONF="$0" -OPENRESOLV_VERSION="3.9.2" +OPENRESOLV_VERSION="3.17.4" SYSCONFDIR=@SYSCONFDIR@ LIBEXECDIR=@LIBEXECDIR@ VARDIR=@VARDIR@ @@ -34,7 +34,7 @@ RESTARTCMD=@RESTARTCMD@ if [ "$1" = "--version" ]; then echo "openresolv $OPENRESOLV_VERSION" - echo "Copyright (c) 2007-2016 Roy Marples" + echo "Copyright (c) 2007-2025 Roy Marples" exit 0 fi @@ -44,10 +44,31 @@ 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]*" +dynamic_order="tap[0-9]* tun[0-9]* vpn vpn[0-9]* wg[0-9]* ppp[0-9]* ippp[0-9]*" interface_order="lo lo[0-9]*" name_server_blacklist="0.0.0.0" +# Poor mans cat +# /usr might not be available +cat() +{ + OIFS="$IFS" + IFS='' + if [ -n "$1" ]; then + while read -r line; do + printf "%s\n" "$line" + done < "$1" + else + while read -r line; do + printf "%s\n" "$line" + done + fi + retval=$? + IFS="$OIFS" + return $retval +} + + # Support original resolvconf configuration layout # as well as the openresolv config file if [ -f "$SYSCONFDIR"/resolvconf.conf ]; then @@ -59,21 +80,36 @@ elif [ -d "$SYSCONFDIR/resolvconf" ]; then interface_order="$(cat "$SYSCONFDIR"/interface-order)" fi fi -IFACEDIR="$VARDIR/interfaces" + +KEYDIR="$VARDIR/keys" METRICDIR="$VARDIR/metrics" PRIVATEDIR="$VARDIR/private" +NOSEARCHDIR="$VARDIR/nosearch" EXCLUSIVEDIR="$VARDIR/exclusive" +DEPRECATEDDIR="$VARDIR/deprecated" LOCKDIR="$VARDIR/lock" _PWD="$PWD" +# Compat +if [ ! -d "$KEYDIR" ] && [ -d "$VARDIR/interfaces" ]; then + KEYDIR="$VARDIR/interfaces" +fi +: ${allow_keys:="$allow_interfaces"} +: ${deny_keys:="$deny_interfaces"} +: ${key_order:="$interface_order"} +: ${inclusive_keys:="$inclusive_interfaces"} +: ${exclusive_keys:="$exclusive_interfaces"} +: ${private_keys:="$private_interfaces"} +: ${public_keys:="$public_interfaces"} + warn() { - echo "$*" >&2 + echo "${RESOLVCONF##*/}: $*" >&2 } error_exit() { - echo "$*" >&2 + warn "$*" exit 1 } @@ -85,24 +121,27 @@ usage() Inform the system about any DNS updates. Commands: - -a \$INTERFACE Add DNS information to the specified interface + -a \$KEY Add DNS information to the specified key (DNS supplied via stdin in resolv.conf format) - -d \$INTERFACE Delete DNS information from the specified interface + -C \$PATTERN Deprecate DNS information for matched key + -c \$PATTERN Configure DNS information for matched key + -d \$PATTERN Delete DNS information from the matched key -h Show this help cruft - -i [\$PATTERN] Show interfaces that have supplied DNS information - optionally from interfaces that match the specified + -i [\$PATTERN] Show keys that have supplied DNS information + optionally from keys that match the specified pattern - -l [\$PATTERN] Show DNS information, optionally from interfaces + -l [\$PATTERN] Show DNS information, optionally from keys that match the specified pattern + -L [\$PATTERN] Same as -l, but adjusted by our config -u Run updates from our current DNS information --version Echo the ${RESOLVCONF##*/} version Options: - -f Ignore non existent interfaces + -f Ignore non existent keys -m metric Give the added DNS information a metric - -p Mark the interface as private - -x Mark the interface as exclusive + -p Mark the resolv.conf as private + -x Mark the resolv.conf as exclusive Subscriber and System Init Commands: -I Init the state dir @@ -117,56 +156,111 @@ usage() EOF [ -z "$1" ] && exit 0 echo - error_exit "$*" + error_exit "$@" } -# Strip any trailing dot from each name as a FQDN does not belong -# in resolv.conf(5) -# If you think otherwise, capture a DNS trace and you'll see libc -# will strip it regardless. -# This also solves setting up duplicate zones in our subscribers. -# Also strip any comments denoted by #. -resolv_strip() -{ - space= - for word; do - case "$word" in - \#*) break;; - esac - printf "%s%s" "$space${word%.}" - space=" " - done - printf "\n" -} +public_key() { + key="$1" -private_iface() -{ # Allow expansion - cd "$IFACEDIR" + cd "$KEYDIR" - # Public interfaces override private ones. - for p in $public_interfaces; do - case "$iface" in - "$p"|"$p":*) return 1;; - esac - done - - if [ -e "$PRIVATEDIR/$iface" ]; then - return 0 - fi - - for p in $private_interfaces; do - case "$iface" in + # Public keys override private ones. + for p in $public_keys; do + case "$key" in "$p"|"$p":*) return 0;; esac done - # Not a private interface return 1 } +private_key() +{ + key="$1" + + if public_key "$key"; then + return 1 + fi + + if [ -e "$PRIVATEDIR/$key" ]; then + return 0 + fi + + for p in $private_keys; do + case "$key" in + "$p"|"$p":*) return 0;; + esac + done + + # Not a private key + return 1 +} + +nosearch_key() +{ + key="$1" + + if public_key "$key"; then + return 1 + fi + + if [ -e "$NOSEARCHDIR/$key" ]; then + return 0 + fi + + for p in $nosearch_keys; do + case "$key" in + "$p"|"$p":*) return 0;; + esac + done + + # Not a non searchable key + return 1 +} + +exclusive_key() +{ + key="$1" + + for x in "$EXCLUSIVEDIR/"*" $key"; do + if [ -f "$x" ]; then + return 0 + fi + done + + # Not an exclusive key + return 1 +} + +# Quote input so it can be safely used for variable assignment via eval +quote() +{ + if [ -z "$1" ]; then + R="''" + else + R= + for W; do + while [ -n "$W" ]; do + case "$W" in + \'*) R="$R\\'"; W=${W#?};; + ?\'*) R="$R\\${W%%\'*}"; W="${W#?}";; + *\'*) R="$R'${W%%\'*}'"; W="'${W#*\'}";; + ?) R="$R\\$W"; W=;; + *) R="$R'$W'"; W=;; + esac + done + done + fi + + printf '%s\n' "$R" + return 0 +} + # Parse resolv.conf's and make variables # for domain name servers, search name servers and global nameservers +# Important! Each printf here should use the above quote function +# to ensure that user input is quoted for eval. parse_resolv() { domain= @@ -174,26 +268,32 @@ parse_resolv() newns= ns= private=false + nosearch=false search= while read -r line; do - stripped_line="$(resolv_strip ${line#* })" + value="${line#* }" case "$line" in "# resolv.conf from "*) if ${new}; then - iface="${line#\# resolv.conf from *}" + key="${line#\# resolv.conf from *}" new=false - if private_iface "$iface"; then + if nosearch_key "$key"; then private=true + nosearch=true + elif private_key "$key"; then + private=true + nosearch=false else private=false + nosearch=false fi fi ;; "nameserver "*) islocal=false for l in $local_nameservers; do - case "$stripped_line" in + case "$value" in $l) islocal=true break @@ -201,20 +301,22 @@ parse_resolv() esac done if $islocal; then - echo "LOCALNAMESERVERS=\"\$LOCALNAMESERVERS $stripped_line\"" + printf 'LOCALNAMESERVERS="$LOCALNAMESERVERS "%s\n' "$(quote "$value")" else - ns="$ns$stripped_line " + ns="$ns${ns:+ }$value" fi ;; "domain "*) - search="$stripped_line" + search="$value" if [ -z "$domain" ]; then domain="$search" - echo "DOMAIN=\"$domain\"" + if ! $nosearch; then + printf 'DOMAIN=%s\n' "$(quote "$domain")" + fi fi ;; "search "*) - search="$stripped_line" + search="$value" ;; *) [ -n "$line" ] && continue @@ -227,11 +329,13 @@ parse_resolv() for d in $search; do ds="$ds${ds:+ }$d:$newns" done - echo "DOMAINS=\"\$DOMAINS $ds\"" + printf 'DOMAINS="$DOMAINS "%s\n' "$(quote "$ds")" + fi + if ! $nosearch; then + printf 'SEARCH="$SEARCH "%s\n' "$(quote "$search")" fi - echo "SEARCH=\"\$SEARCH $search\"" if ! $private; then - echo "NAMESERVERS=\"\$NAMESERVERS $ns\"" + printf 'NAMESERVERS="$NAMESERVERS "%s\n' "$(quote "$ns")" fi ns= search= @@ -274,26 +378,21 @@ dirname() config_mkdirs() { - e=0 for f; do [ -n "$f" ] || continue d="$(dirname "$f")" if [ ! -d "$d" ]; then - if type install >/dev/null 2>&1; then - install -d "$d" || e=$? - else - mkdir "$d" || e=$? - fi + mkdir -p "$d" || return $? fi done - return $e + return 0 } # With the advent of alternative init systems, it's possible to have # more than one installed. So we need to try and guess what one we're -# using unless overriden by configure. +# using unless overridden by configure. # Note that restarting a service is a last resort - the subscribers -# should make a reasonable attempt to reconfigre the service via some +# should make a reasonable attempt to reconfigure the service via some # method, normally SIGHUP. detect_init() { @@ -328,6 +427,12 @@ detect_init() then /usr/sbin/invoke-rc.d $1 restart fi' + elif [ -x /usr/bin/s6-rc ] && [ -x /usr/bin/s6-svc ]; then + RESTARTCMD=' + if s6-rc -a list 2>/dev/null | grep -qFx $1-srv + then + s6-svc -r /run/service/$1-srv + fi' elif [ -x /sbin/service ]; then # Old RedHat RCDIR=/etc/init.d @@ -368,6 +473,8 @@ detect_init() then /etc/rc.d/$1 restart fi' + elif [ -d /etc/dinit.d ] && command -v dinitctl >/dev/null 2>&1; then + RESTARTCMD='dinitctl --quiet restart --ignore-unstarted $1' else for x in /etc/init.d/rc.d /etc/rc.d /etc/init.d; do [ -d $x ] || continue @@ -395,9 +502,9 @@ echo_resolv() { OIFS="$IFS" - [ -n "$1" ] && [ -f "$IFACEDIR/$1" ] || return 1 + [ -n "$1" ] && [ -f "$KEYDIR/$1" ] || return 1 echo "# resolv.conf from $1" - # Our variable maker works of the fact each resolv.conf per interface + # Our variable maker works of the fact each resolv.conf per key # is separated by blank lines. # So we remove them when echoing them. while read -r line; do @@ -407,103 +514,296 @@ echo_resolv() IFS='' printf "%s\n" "$line" fi - done < "$IFACEDIR/$1" + done < "$KEYDIR/$1" IFS="$OIFS" } +deprecated_key() +{ + [ -d "$DEPRECATEDDIR" ] || return 1 + + cd "$DEPRECATEDDIR" + for da; do + for daf in *; do + [ -f "$daf" ] || continue + case "$da" in + $daf) return 0;; + esac + done + done + return 1 +} + +match() +{ + match="$1" + file="$2" + retval=1 + count=0 + + while read -r keyword value; do + new_match= + for om in $match; do + m="$om" + keep= + while [ -n "$m" ]; do + k="${m%%/*}" + r="${m#*/}" + f="${r%%/*}" + r="${r#*/}" + # If the length of m is the same as k/f then + # we know that we are done + if [ ${#m} = $((${#k} + 1 + ${#f})) ]; then + r= + fi + m="$r" + matched=false + case "$keyword" in + $k) + case "$value" in + $f) + matched=true + ;; + esac + ;; + esac + if ! $matched; then + keep="$keep${keep:+/}$k/$f" + fi + done + if [ -n "$om" ] && [ -z "$keep" ]; then + retval=0 + break 2 + fi + new_match="${new_match}${new_match:+ }${keep}" + done + match="${new_match}" + done < "$file" + return $retval +} + +list_keys() { + list_cmd="$1" + shift + + [ -d "$KEYDIR" ] || return 0 + cd "$KEYDIR" + + [ -n "$1" ] || set -- "*" + list= + retval=0 + if [ "$list_cmd" = -i ] || [ "$list_cmd" = -l ]; then + for i in $@; do + if [ ! -f "$i" ]; then + if ! $force && [ "$i" != "*" ]; then + echo "No resolv.conf for key $i" >&2 + fi + retval=2 + continue + fi + list="$list $i" + done + [ -z "$list" ] || uniqify $list + return $retval + fi + + if [ "$list_cmd" != -I ] && [ "$list_cmd" != -L ]; then + echo "list_keys: unknown command $list_cmd" >&2 + return 1 + fi + + if [ -d "$EXCLUSIVEDIR" ]; then + cd "$EXCLUSIVEDIR" + for i in $EXCLUSIVEDIR/*; do + if [ -f "$i" ]; then + cd "$KEYDIR" + for ii in $inclusive_keys; do + if [ -f "$ii" ] && [ "${i#* }" = "$ii" ]; then + continue 2 + fi + done + list="${i#* }" + break + fi + done + cd "$KEYDIR" + if [ -n "$list" ]; then + for i in $@; do + # list will be one item due to the above + if [ -f "$i" ] && [ "$i" = "$list" ]; then + echo "$i" + return 0 + fi + done + return 0 + fi + fi + + for i in $key_order; do + for ii in "$i" "$i":* "$i".*; do + [ -f "$ii" ] && list="$list $ii" + done + done + + for i in $dynamic_order; do + for ii in "$i" "$i":* "$i".*; do + if [ -f "$ii" ] && ! [ -e "$METRICDIR/"*" $ii" ] + then + list="$list $ii" + fi + done + done + + # Interfaces have an implicit metric of 0 if not specified. + for i in *; do + if [ -f "$i" ] && ! [ -e "$METRICDIR/"*" $i" ]; then + list="$list $i" + fi + done + + if [ -d "$METRICDIR" ]; then + cd "$METRICDIR" + for i in *; do + [ -f "$i" ] && list="$list ${i#* }" + done + cd "$KEYDIR" + fi + + # Move deprecated keys to the back + active= + deprecated= + for i in $list; do + if deprecated_key "$i"; then + deprecated="$deprecated $i" + else + active="$active $i" + fi + done + list="$active $deprecated" + + retval=0 + if [ "$1" != "*" ]; then + cd "$KEYDIR" + matched= + for i in $@; do + if ! [ -f "$i" ]; then + if ! $force; then + echo "No resolv.conf for key $i" >&2 + fi + retval=2 + continue + fi + for ii in $list; do + if [ "$i" = "$ii" ]; then + matched="$matched${matched:+ }$i" + break + fi + done + done + if [ -z "$matched" ]; then + return $retval + fi + list="$matched" + fi + + allowed= + for i in $(uniqify $list); do + if [ -n "$allow_keys" ]; then + x=false + for ii in $allow_keys; do + if [ "$i" = "$ii" ]; then + x=true + break + fi + done + $x || continue + fi + for ii in $deny_keys; do + if [ "$i" = "$ii" ]; then + continue 2 + fi + done + + if [ -n "$exclude" ] && match "$exclude" "$i"; then + continue + fi + allowed="$allowed${allowed:+ }$i" + done + + cd "$KEYDIR" + for i in $exclusive_keys; do + for ii in $allowed; do + if [ "$i" = "$ii" ]; then + echo "$i" + return + fi + done + done + [ -z "$allowed" ] || echo "$allowed" +} + list_resolv() { - [ -d "$IFACEDIR" ] || return 0 - - cmd="$1" - shift - excl=false - list= - report=false - retval=0 - - case "$IF_EXCLUSIVE" in - [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1) - excl=true - if [ -d "$EXCLUSIVEDIR" ]; then - cd "$EXCLUSIVEDIR" - for i in *; do - if [ -f "$i" ]; then - list="${i#* }" - break - fi - done - fi - cd "$IFACEDIR" - for i in $inclusive_interfaces; do - if [ -f "$i" ] && [ "$list" = "$i" ]; then - list= - excl=false - break - fi - done - ;; - 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 - elif ! $excl; then - cd "$IFACEDIR" - for i in $interface_order; do - [ -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" ] && ! [ -e "$METRICDIR/"*" $i" ]; then - list="$list $i" - fi - for ii in "$i":* "$i".*; do - if [ -f "$ii" ] && ! [ -e "$METRICDIR/"*" $ii" ] - then - list="$list $ii" - fi - done - done - # Interfaces have an implicit metric of 0 if not specified. - for i in *; do - if [ -f "$i" ] && ! [ -e "$METRICDIR/"*" $i" ]; then - list="$list $i" - fi - done - if [ -d "$METRICDIR" ]; then - cd "$METRICDIR" - for i in *; do - [ -f "$i" ] && list="$list ${i#* }" - done - fi + keys="$(list_keys "$@")" + retval=$? + if [ "$retval" != 0 ]; then + return $retval fi - - cd "$IFACEDIR" - retval=1 - for i in $(uniqify $list); do - # Only list interfaces which we really have - if ! [ -f "$i" ]; then - if $report; then - echo "No resolv.conf for interface $i" >&2 - retval=2 - fi - continue - fi - - if [ "$cmd" = i ] || [ "$cmd" = "-i" ]; then - printf %s "$i " - else - echo_resolv "$i" && echo - fi - [ $? = 0 ] && [ "$retval" = 1 ] && retval=0 + for i in $keys; do + echo_resolv "$i" && echo done - [ "$cmd" = i ] || [ "$cmd" = "-i" ] && echo - return $retval +} + +list_private() +{ + KEYS= + cd "$KEYDIR" + if [ -z "$1" ]; then + set -- "*" + fi + for i in $@; do + if private_key "$i"; then + KEYS="${KEYS}${KEYS:+ }$i" + fi + done + if [ -n "$KEYS" ]; then + echo "$KEYS" + fi +} + +list_nosearch() +{ + + KEYS= + cd "$KEYDIR" + if [ -z "$1" ]; then + set -- "*" + fi + for i in $@; do + if nosearch_key "$i"; then + KEYS="${KEYS}${KEYS:+ }$i" + fi + done + if [ -n "$KEYS" ]; then + echo "$KEYS" + fi +} + +list_exclusive() +{ + KEYS= + cd "$KEYDIR" + if [ -z "$1" ]; then + set -- "*" + fi + for i in $@; do + if exclusive_key "$i"; then + KEYS="${KEYS}${KEYS:+ }$i" + fi + done + if [ -n "$KEYS" ]; then + echo "$KEYS" + fi } list_remove() @@ -558,7 +858,81 @@ echo_append() echo } -replace() +tolower() { + # There is no good way of doing this portably in shell :( + # Luckily we are only doing this for domain names which we + # know have to be ASCII. + # Non ASCII domains *should* be translated to ASCII *before* + # we get to this stage. + # We could use echo "$@" | tr '[:upper:]' '[:lower:]' but + # tr is in /usr/bin and may not be available when data is fed + # to resolvconf. + # So it's the cost of a pipe + fork vs this slow loop + # + for word; do + # Check if we have any upper to avoid looping per char + case "$word" in + *[A-Z]*) ;; + *) printf "%s " "$word"; continue;; + esac + + while [ -n "$word" ]; do + # Remove everything except the first character + afterchar="${word#?}" + # Remove the afterchar to get the first character + char="${word%%$afterchar}" + # Assign afterchar back to word for looping + word="$afterchar" + + # Now enforce lowercase a-z + case "$char" in + A) char=a;; + B) char=b;; + C) char=c;; + D) char=d;; + E) char=e;; + F) char=f;; + G) char=g;; + H) char=h;; + I) char=i;; + J) char=j;; + K) char=k;; + L) char=l;; + M) char=m;; + N) char=n;; + O) char=o;; + P) char=p;; + Q) char=q;; + R) char=r;; + S) char=s;; + T) char=t;; + U) char=u;; + V) char=v;; + W) char=w;; + X) char=x;; + Y) char=y;; + Z) char=z;; + esac + printf %s "$char" + done + printf " " + done + printf "\n" +} + +# Strip any trailing dot from each name as a FQDN does not belong +# in resolv.conf(5). +# While DNS is not case sensitive, our labels for building the zones +# are, so ensure it's lower case. +process_domain() +{ + for word in $(tolower "$@"); do + printf "%s " "${word%.}" + done + printf "\n" +} + +process_resolv() { while read -r keyword value; do for r in $replace; do @@ -593,6 +967,18 @@ replace() done val="$val${val:+ }$sub" done + case "$keyword" in + \#) + case "$val" in + "resolv.conf from "*) ;; + *) continue;; + esac + ;; + \#*) continue;; + esac + case "$keyword" in + domain|search) val="$(process_domain $val)";; + esac printf "%s %s\n" "$keyword" "$val" done } @@ -605,14 +991,12 @@ make_vars() SEARCH= NAMESERVERS= LOCALNAMESERVERS= - + if [ -n "${name_servers}${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)" + eval "$(list_resolv -L "$@" | process_resolv | parse_resolv)" fi if [ -n "${name_servers_append}${search_domains_append}" ]; then eval "$(echo_append | parse_resolv)" @@ -646,6 +1030,7 @@ make_vars() newdomains="$newdomains${newdomains:+ }$dn:$newns" fi done + DOMAIN="$(list_remove domain_blacklist $DOMAIN)" SEARCH="$(uniqify $SEARCH)" SEARCH="$(list_remove domain_blacklist $SEARCH)" @@ -653,21 +1038,30 @@ make_vars() 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'" + + # Ensure output is quoted for eval + printf 'DOMAIN=%s\n' "$(quote "$DOMAIN")" + printf 'SEARCH=%s\n' "$(quote "$SEARCH")" + printf 'NAMESERVERS=%s\n' "$(quote "$NAMESERVERS")" + printf 'LOCALNAMESERVERS=%s\n' "$(quote "$LOCALNAMESERVERS")" + printf 'DOMAINS=%s\n' "$(quote "$newdomains")" } force=false +LFLAG= VFLAG= -while getopts a:Dd:fhIilm:pRruvVx OPT; do +while getopts a:C:c:Dd:fhIiLlm:pRruvVx OPT; do case "$OPT" in f) force=true;; h) usage;; m) IF_METRIC="$OPTARG";; - p) IF_PRIVATE=1;; + p) + if [ "$IF_PRIVATE" = 1 ]; then + IF_NOSEARCH=1 + else + IF_PRIVATE=1 + fi + ;; V) VFLAG=1 if [ "$local_nameservers" = \ @@ -677,19 +1071,23 @@ while getopts a:Dd:fhIilm:pRruvVx OPT; do fi ;; x) IF_EXCLUSIVE=1;; - '?') ;; - *) cmd="$OPT"; iface="$OPTARG";; + '?') exit 1;; + *) + [ "$OPT" != L ] || LFLAG=1 + cmd="$OPT"; key="$OPTARG";; esac done shift $(($OPTIND - 1)) -args="$iface${iface:+ }$*" +if [ -n "$key" ]; then + set -- "$key" "$@" +fi -# -I inits the state dir -if [ "$cmd" = I ]; then - if [ -d "$VARDIR" ]; then - rm -rf "$VARDIR"/* +if [ -z "$cmd" ]; then + if [ "$IF_PRIVATE" = 1 ]; then + cmd=p + elif [ "$IF_EXCLUSIVE" = 1 ]; then + cmd=x fi - exit $? fi # -D ensures that the listed config file base dirs exist @@ -698,9 +1096,38 @@ if [ "$cmd" = D ]; then exit $? fi -# -l lists our resolv files, optionally for a specific interface -if [ "$cmd" = l ] || [ "$cmd" = i ]; then - list_resolv "$cmd" "$args" +# -i lists which keys have a resolv file +if [ "$cmd" = i ]; then + # If the -L modifier is given, the list is post-processed + if [ "$LFLAG" = 1 ]; then + cmd="L" + fi + list_keys "-$cmd" "$@" + exit $? +fi + +# -l lists our resolv files, optionally for a specific key +if [ "$cmd" = l ]; then + list_resolv "-$cmd" "$@" + exit $? +fi +# -L is the same as -l, but post-processed from our config +if [ "$cmd" = L ]; then + list_resolv "-$cmd" "$@" | process_resolv + exit $? +fi + +if [ "$cmd" = p ]; then + if [ "$IF_NOSEARCH" = 1 ]; then + list_nosearch "$@" + else + list_private "$@" + fi + exit $? +fi + +if [ "$cmd" = x ]; then + list_exclusive "$@" exit $? fi @@ -708,7 +1135,6 @@ fi if [ "$cmd" = r ] || [ "$cmd" = R ]; then detect_init || exit 1 if [ "$cmd" = r ]; then - set -- $args eval "$RESTARTCMD" else echo "$RESTARTCMD" | @@ -719,30 +1145,36 @@ fi # Not normally needed, but subscribers should be able to run independently if [ "$cmd" = v ] || [ -n "$VFLAG" ]; then - make_vars "$iface" + make_vars "$@" exit $? fi # Test that we have valid options -if [ "$cmd" = a ] || [ "$cmd" = d ]; then - if [ -z "$iface" ]; then - usage "Interface not specified" +case "$cmd" in +a|d|C|c) + if [ -z "$key" ]; then + error_exit "Key not specified" + fi + ;; +I|u) ;; +*) + if [ -n "$cmd" ] && [ "$cmd" != h ]; then + error_exit "Unknown option $cmd" fi -elif [ "$cmd" != u ]; then - [ -n "$cmd" ] && [ "$cmd" != h ] && usage "Unknown option $cmd" usage -fi + ;; +esac if [ "$cmd" = a ]; then - for x in '/' \\ ' ' '*'; do - case "$iface" in - *[$x]*) error_exit "$x not allowed in interface name";; + for x in '/' '\' ' ' '*'; do + case "$key" in + "$x"|"$x"*|*"$x"|*"$x"*) error_exit "$x not allowed in key name";; esac done for x in '.' '-' '~'; do - case "$iface" in - [$x]*) error_exit \ - "$x not allowed at start of interface name";; + case "$key" in + "$x"*) error_exit \ + "$x not allowed at start of key name";; esac done [ "$cmd" = a ] && [ -t 0 ] && error_exit "No file given via stdin" @@ -765,15 +1197,15 @@ if [ ! -d "$VARDIR" ]; then fi fi -if [ ! -d "$IFACEDIR" ]; then - mkdir -m 0755 -p "$IFACEDIR" || \ - error_exit "Failed to create needed directory $IFACEDIR" +if [ ! -d "$KEYDIR" ]; then + mkdir -m 0755 -p "$KEYDIR" || \ + error_exit "Failed to create needed directory $KEYDIR" 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" + cd "$KEYDIR" + for i in $@; do + warn "No resolv.conf for key $i" done fi ${force} @@ -781,7 +1213,7 @@ if [ ! -d "$IFACEDIR" ]; then fi fi -# An interface was added, changed, deleted or a general update was called. +# A key 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. @@ -789,6 +1221,9 @@ fi # in /usr which we do our very best to operate without. [ -w "$VARDIR" ] || error_exit "Cannot write to $LOCKDIR" : ${lock_timeout:=10} +: ${clear_nopids:=5} +have_pid=false +had_pid=false while true; do if mkdir "$LOCKDIR" 2>/dev/null; then trap 'rm -rf "$LOCKDIR";' EXIT @@ -796,18 +1231,43 @@ while true; do echo $$ >"$LOCKDIR/pid" break fi - pid=$(cat "$LOCKDIR/pid") - if ! kill -0 "$pid"; then + pid=$(cat "$LOCKDIR/pid" 2>/dev/null) + if [ "$pid" -gt 0 ] 2>/dev/null; then + have_pid=true + had_pid=true + else + have_pid=false + clear_nopids=$(($clear_nopids - 1)) + if [ "$clear_nopids" -le 0 ]; then + warn "not seen a pid, clearing lock directory" + rm -rf "$LOCKDIR" + else + lock_timeout=$(($lock_timeout - 1)) + sleep 1 + fi + continue + fi + if $have_pid && ! 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" + if $have_pid; then + error_exit "timed out waiting for lock from pid $pid" + else + if $had_pid; then + error_exit "timed out waiting for lock" \ + "from some pids" + else + error_exit "timed out waiting for lock" + fi + fi fi sleep 1 done +unset have_pid had_pid clear_nopids case "$cmd" in a) @@ -816,9 +1276,9 @@ a) changed=false changedfile=false # If what we are given matches what we have, then do nothing - if [ -e "$IFACEDIR/$iface" ]; then + if [ -e "$KEYDIR/$key" ]; then if [ "$(echo "$resolv")" != \ - "$(cat "$IFACEDIR/$iface")" ] + "$(cat "$KEYDIR/$key")" ] then changed=true changedfile=true @@ -828,21 +1288,21 @@ a) changedfile=true fi - # Set metric and private before creating the interface resolv.conf file + # Set metric and private before creating the resolv.conf file # to ensure that it will have the correct flags [ ! -d "$METRICDIR" ] && mkdir "$METRICDIR" - oldmetric="$METRICDIR/"*" $iface" + oldmetric="$METRICDIR/"*" $key" 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 - newmetric="$METRICDIR/$IF_METRIC $iface" + newmetric="$METRICDIR/$IF_METRIC $key" fi - rm -f "$METRICDIR/"*" $iface" + rm -f "$METRICDIR/"*" $key" [ "$oldmetric" != "$newmetric" ] && - [ "$oldmetric" != "$METRICDIR/* $iface" ] && + [ "$oldmetric" != "$METRICDIR/* $key" ] && changed=true [ -n "$newmetric" ] && echo " " >"$newmetric" @@ -852,19 +1312,37 @@ a) [ -e "$PRIVATEDIR" ] && rm "$PRIVATEDIR" mkdir "$PRIVATEDIR" fi - [ -e "$PRIVATEDIR/$iface" ] || changed=true - [ -d "$PRIVATEDIR" ] && echo " " >"$PRIVATEDIR/$iface" + [ -e "$PRIVATEDIR/$key" ] || changed=true + [ -d "$PRIVATEDIR" ] && echo " " >"$PRIVATEDIR/$key" ;; *) - if [ -e "$PRIVATEDIR/$iface" ]; then - rm -f "$PRIVATEDIR/$iface" + if [ -e "$PRIVATEDIR/$key" ]; then + rm -f "$PRIVATEDIR/$key" changed=true fi ;; esac + case "$IF_NOSEARCH" in + [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1) + if [ ! -d "$NOSEARCHDIR" ]; then + [ -e "$NOSEARCHDIR" ] && rm "$NOSEARCHDIR" + mkdir "$NOSEARCHDIR" + fi + [ -e "$NOSEARCHDIR/$key" ] || changed=true + [ -d "$NOSEARCHDIR" ] && echo " " >"$NOSEARCHDIR/$key" + ;; + *) + if [ -e "$NOSEARCHDIR/$key" ]; then + rm -f "$NOSEARCHDIR/$key" + changed=true + fi + ;; + esac + set +x + oldexcl= - for x in "$EXCLUSIVEDIR/"*" $iface"; do + for x in "$EXCLUSIVEDIR/"*" $key"; do if [ -f "$x" ]; then oldexcl="$x" break @@ -880,7 +1358,7 @@ a) for x in *; do [ -f "$x" ] && break done - if [ "${x#* }" != "$iface" ]; then + if [ "${x#* }" != "$key" ]; then if [ "$x" = "${x% *}" ]; then x=10000000 else @@ -892,7 +1370,7 @@ a) x=$(($x - 1)) fi if [ -d "$EXCLUSIVEDIR" ]; then - echo " " >"$EXCLUSIVEDIR/$x $iface" + echo " " >"$EXCLUSIVEDIR/$x $key" fi changed=true fi @@ -906,34 +1384,80 @@ a) esac if $changedfile; then - printf "%s\n" "$resolv" >"$IFACEDIR/$iface" || exit $? - elif ! $changed; then + printf "%s\n" "$resolv" >"$KEYDIR/$key" || exit $? + elif ! $changed && [ ! -e "$VARDIR"/error ]; then exit 0 fi unset changed changedfile oldmetric newmetric x oldexcl ;; d) - # Delete any existing information about the interface - cd "$IFACEDIR" + # Delete any existing information about the key + cd "$KEYDIR" changed=false - for i in $args; do + for i in $@; do if [ -e "$i" ]; then changed=true elif ! ${force}; then - warn "No resolv.conf for interface $i" + warn "No resolv.conf for key $i" fi rm -f "$i" "$METRICDIR/"*" $i" \ "$PRIVATEDIR/$i" \ "$EXCLUSIVEDIR/"*" $i" || exit $? done - if ! ${changed}; then + + if ! $changed && [ ! -e "$VARDIR"/error ]; then # Set the return code based on the forced flag - ${force} + $force exit $? fi unset changed i ;; + +C) + # Mark key as deprecated + [ ! -d "$DEPRECATEDDIR" ] && mkdir "$DEPRECATEDDIR" + cd "$DEPRECATEDDIR" + changed=false + for i in $@; do + if [ ! -e "$i" ]; then + changed=true + echo " " >"$i" || exit $? + fi + done + if ! $changed && [ ! -e "$VARDIR"/error ]; then + exit 0 + fi + unset changed i + ;; + +c) + # Mark key as active + if [ -d "$DEPRECATEDDIR" ]; then + cd "$DEPRECATEDDIR" + changed=false + for i in $@; do + if [ -e "$i" ]; then + changed=true + rm "$i" || exit $? + fi + done + if ! $changed && [ ! -e "$VARDIR"/error ]; then + exit 0 + fi + unset changed i + fi + ;; +I) + # Init the state dir, keeping our lock and key directories only + for i in "$VARDIR"/*; do + case "$i" in + "$LOCKDIR") ;; + "$KEYDIR") rm -rf "$KEYDIR"/*;; + *) rm -rf "$i";; + esac + done + ;; esac case "${resolvconf:-YES}" in @@ -947,7 +1471,7 @@ export RESTARTCMD RCDIR _NOINIT_WARNED eval "$(make_vars)" export RESOLVCONF DOMAINS SEARCH NAMESERVERS LOCALNAMESERVERS -: ${list_resolv:=list_resolv -l} +: ${list_resolv:=list_resolv -L} retval=0 # Run scripts in the same directory resolvconf is run from @@ -955,17 +1479,26 @@ retval=0 cd "$_PWD" for script in "$LIBEXECDIR"/*; do if [ -f "$script" ]; then - eval script_enabled="\$${script##*/}" + script_var="${script##*/}" + while [ "${script_var%%-*}" != "$script_var" ]; do + script_var="${script_var%%-*}_${script_var#*-}" + done + eval script_enabled="\$$script_var" case "${script_enabled:-YES}" in [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1) ;; *) continue;; esac if [ -x "$script" ]; then - "$script" "$cmd" "$iface" + "$script" "$cmd" "$key" else - (set -- "$cmd" "$iface"; . "$script") + (set -- "$cmd" "$key"; . "$script") fi retval=$(($retval + $?)) fi done +if [ "$retval" = 0 ]; then + rm -f "$VARDIR"/error +else + echo "$retval" >"$VARDIR"/error +fi exit $retval diff --git a/resolvectl.in b/resolvectl.in new file mode 100644 index 00000000000..167aac62ba5 --- /dev/null +++ b/resolvectl.in @@ -0,0 +1,159 @@ +#!/bin/sh +# Copyright (c) 2025 Roy Marples +# All rights reserved + +# resolvectl 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 + +case "${resolvectl:-NO}" in +[Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1) ;; +*) exit 0;; +esac + +# If we don't have resolvectl or systemd-resolved isn't running then +# we can't do much. +# We can't persist our data in /run/systemd/resolve/netif/$ifindex +# because systemd-resolved keeps it somehow, ie we can't change it +# once we have inserted it +if ! [ -d /sys/class/net ] || \ + ! type resolvectl >/dev/null 2>&1 || \ + ! pidof systemd-resolved >/dev/null +then + exit 1 +fi + +# resolvectl only accepts resolv.conf setup per physical interface +# although resolvconf has always hinted that the named configuration +# should be $interface.$protocol, this has never been a fixed requirement. +# Because resolvectl only accepts one configuration per interface we need +# to try and merge the resolv.conf's together. +# Luckily resolvconf makes this easy for us. + +# Returns a list of resolvconf entries for a real interface +get_resolvconf_interfaces() { + IFACE="$1" + [ -d /sys/class/net/"$IFACE" ] || return 1 + + IFACES= + for IFACE_PROTO in $(@SBINDIR@/resolvconf -Li "$IFACE" "$IFACE.*" 2>/dev/null); do + # ens5 will work with ens5.dhcp and ens5.ra, + # but not ens5.5 or ens5.5.dhcp + if [ "$IFACE_PROTO" != "$IFACE" ]; then + # Ensure that ens5.5.dhcp doesn't work for ens5 + if [ "${IFACE_PROTO%.*}" != "$IFACE" ]; then + continue + fi + # Ensure that ens5.dhcp isn't a real interface + # as ens5.5 likely is and the .5 matches the .dhcp + if [ -d /sys/class/net/"$IFACE_PROTO" ]; then + continue + fi + fi + IFACES="$IFACES${IFACES:+ }$IFACE_PROTO" + done + echo "$IFACES" +} + +# For the given interface, apply a list of resolvconf entries +apply_resolvconf() { + IFACE="$1" + shift + + if [ -z "$1" ]; then + resolvectl revert "$IFACE" + return + fi + + # Set the default-route property first to avoid leakage. + # If any entry is private, the whole interface has to be private. + # If a more granular approach is needed, consider using the + # systemd-resolved subscriber instead which supports DNS delegates. + if [ -n "$(@SBINDIR@/resolvconf -p $@)" ]; then + resolvectl default-route "$IFACE" false + else + resolvectl default-route "$IFACE" true + fi + + # Now set domain and dns + DOMAIN=$(@SBINDIR@/resolvconf -L $@ 2>/dev/null | sed -n -e "s/domain //p" -e "s/search //p") + NS=$(@SBINDIR@/resolvconf -L $@ 2>/dev/null | sed -n -e "s/nameserver //p") + if [ -n "$DOMAIN" ]; then + # If any entry is marked as not searchable, we mark all the + # domains as non searchable. + # If a more granular approach is needed, consider using the + # systemd-resolved subscriber instead which supports DNS delegates. + if [ -n "$(@SBINDIR@/resolvconf -pp $@)" ]; then + ND= + for d in $DOMAIN; do + ND="$ND${ND:+ }~$d" + done + DOMAIN="$ND" + fi + resolvectl domain "$IFACE" $DOMAIN + else + resolvectl domain "$IFACE" "" + fi + if [ -n "$NS" ]; then + resolvectl dns "$IFACE" $NS + else + resolvectl dns "$IFACE" "" + fi +} + +# To get the full features of resolvconf, we need to work out each interface +# for every resolvconf addition and deletion +# This is because resolvconf.conf might have changed OR an exclusive +# interface deleted which makes other interfaces visible. +cd /sys/class/net +for IFACE in *; do + if [ "$IFACE" = lo ]; then + # systemd-resolved doesn't work with lo + continue + fi + + IFACES=$(get_resolvconf_interfaces "$IFACE") + apply_resolvconf "$IFACE" $IFACES +done + +# warn about resolv.conf with no matching interface +FAILED= +for IFACE_PROTO in $(@SBINDIR@/resolvconf -Li); do + IFACE="${IFACE_PROTO%.*}" + if [ "$IFACE" = lo ]; then + # Don't warn about loopback interface as that is typically + # used to configure libc for a nameserver on it and the libc + # subscriber will process that just fine. + continue + fi + + if ! [ -d "/sys/class/net/$IFACE" ]; then + FAILED="$FAILED${FAILED:+ }$IFACE_PROTO" + fi +done +if [ -n "$FAILED" ]; then + echo "Could not apply resolv.conf to resolvectl: $FAILED" >&2 +fi diff --git a/systemd-resolved.in b/systemd-resolved.in new file mode 100644 index 00000000000..0190a73723e --- /dev/null +++ b/systemd-resolved.in @@ -0,0 +1,96 @@ +#!/bin/sh +# Copyright (c) 2025 Roy Marples +# All rights reserved + +# systemd-resolved 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 + +case "${systemd_resolved:-NO}" in +[Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1) ;; +*) exit 0;; +esac + +[ -n "$RESOLVCONF" ] || eval "$(@SBINDIR@/resolvconf -v)" +NL=" +" + +: ${systemd_resolved_conf:=/run/systemd/resolved.conf.d/60-resolvconf.conf} +: ${systemd_delegate_dir:=/run/systemd/dns-delegate.d} + +# Try to ensure that config dirs exist +if command -v config_mkdirs >/dev/null 2>&1; then + config_mkdirs "$systemd_resolved_conf" "$systemd_delegate_dir/x" +else + @SBINDIR@/resolvconf -D "$systemd_resolved_conf" "$systemd_delegate_dir/x" +fi + +header="# Generated by resolvconf$NL" +header="${header}$NL" +header="${header}[Resolve]$NL" + +conf="$header" +# We emit blank values to force them to reset on SIGHUP +conf="${conf}DNS=$NAMESERVERS$NL" +# Indicate these nameservers are for all domain lookups by using ~. +conf="${conf}Domains=$SEARCH${NAMESERVERS:+ ~.}$NL" + +printf %s "$conf" >"$systemd_resolved_conf" + +# DNS Delegates requires https://github.com/systemd/systemd/pull/34368 +rm -f "$systemd_delegate_dir/resolvconf-"*".dns-delegate" +header="# Generated by resolvconf$NL" +header="${header}$NL" +header="${header}[Delegate]$NL" +for d in $DOMAINS; do + dn="${d%%:*}" + ns="${d#*:}" + dconf="${header}Domains=" + search=false + for sd in $SEARCH; do + if [ "$sd" = "$dn" ]; then + search=true + break + fi + done + if ! $search; then + dconf="${dconf}~" + fi + dconf="${dconf}$dn$NL" + dconf="${dconf}DNS=" + while [ -n "$ns" ]; do + dconf="$dconf${ns%%,*} " + [ "$ns" = "${ns#*,}" ] && break + ns="${ns#*,}" + done + dconf="$dconf$NL" + printf %s "$dconf" >"$systemd_delegate_dir/resolvconf-$dn.dns-delegate" +done + +pid=$(pidof systemd-resolved) +if [ -n "$pid" ]; then + kill -HUP $pid +fi diff --git a/unbound.in b/unbound.in index 34cb401f57c..a61a09d1935 100644 --- a/unbound.in +++ b/unbound.in @@ -1,5 +1,5 @@ #!/bin/sh -# Copyright (c) 2009-2016 Roy Marples +# Copyright (c) 2009-2023 Roy Marples # All rights reserved # unbound subscriber for resolvconf @@ -27,6 +27,7 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. unbound_insecure= +unbound_private= [ -f "@SYSCONFDIR@"/resolvconf.conf ] || exit 0 . "@SYSCONFDIR@/resolvconf.conf" || exit 1 @@ -42,13 +43,29 @@ newconf="# Generated by resolvconf$NL" for d in $DOMAINS; do dn="${d%%:*}" ns="${d#*:}" + create_unbound_insecure=false + create_unbound_private=false case "$unbound_insecure" in [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1) - newconf="$newconf${NL}server:$NL" - newconf="$newconf domain-insecure: \"$dn\"$NL" - ;; + create_unbound_insecure=true ;; esac + case "$unbound_private" in + [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1) + create_unbound_private=true ;; + esac + if $create_unbound_insecure || $create_unbound_private; then + newconf="$newconf${NL}server:$NL" + if $create_unbound_insecure; then + newconf="$newconf domain-insecure: \"$dn\"$NL" + fi + if $create_unbound_private; then + newconf="$newconf private-domain: \"$dn\"$NL" + fi + fi newconf="$newconf${NL}forward-zone:$NL name: \"$dn\"$NL" + if [ -n "$unbound_forward_zone_options" ]; then + newconf="$newconf $unbound_forward_zone_options${NL}" + fi while [ -n "$ns" ]; do newconf="$newconf forward-addr: ${ns%%,*}$NL" [ "$ns" = "${ns#*,}" ] && break @@ -58,13 +75,16 @@ done if [ -n "$NAMESERVERS" ]; then newconf="$newconf${NL}forward-zone:$NL name: \".\"$NL" + if [ -n "$unbound_forward_zone_options" ]; then + newconf="$newconf $unbound_forward_zone_options${NL}" + fi for n in $NAMESERVERS; do newconf="$newconf forward-addr: $n$NL" done fi # Try to ensure that config dirs exist -if type config_mkdirs >/dev/null 2>&1; then +if command -v config_mkdirs >/dev/null 2>&1; then config_mkdirs "$unbound_conf" else @SBINDIR@/resolvconf -D "$unbound_conf"