├── config-null.mk ├── GNUmakefile ├── .gitignore ├── resolvconf.conf ├── LICENSE ├── mdnsd.in ├── avahi-daemon.in ├── pdns_recursor.in ├── README.md ├── systemd-resolved.in ├── unbound.in ├── named.in ├── configure ├── Makefile ├── pdnsd.in ├── resolvectl.in ├── dnsmasq.in ├── libc.in ├── resolvconf.8.in ├── resolvconf.conf.5.in └── resolvconf.in /config-null.mk: -------------------------------------------------------------------------------- 1 | # This space left intentionally blank 2 | -------------------------------------------------------------------------------- /GNUmakefile: -------------------------------------------------------------------------------- 1 | # Nasty hack so that make clean works without configure being run 2 | CONFIG_MK?=$(shell test -e config.mk && echo config.mk || echo config-null.mk) 3 | 4 | include Makefile 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | config.mk 2 | 3 | resolvconf 4 | resolvconf.8 5 | resolvconf.conf.5 6 | 7 | dnsmasq 8 | libc 9 | named 10 | pdnsd 11 | pdns_recursor 12 | resolvectl 13 | systemd-resolved 14 | unbound 15 | 16 | avahi-daemon 17 | mdnsd 18 | -------------------------------------------------------------------------------- /resolvconf.conf: -------------------------------------------------------------------------------- 1 | # Configuration for resolvconf(8) 2 | # See resolvconf.conf(5) for details 3 | 4 | resolv_conf=/etc/resolv.conf 5 | # If you run a local name server, you should uncomment the below line and 6 | # configure your subscribers configuration files below. 7 | #name_servers=127.0.0.1 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2007-2020 Roy Marples 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions 6 | are met: 7 | 1. Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | 2. Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 17 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 | OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 | SUCH DAMAGE. 24 | -------------------------------------------------------------------------------- /mdnsd.in: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Copyright (c) 2007-2023 Roy Marples 3 | # All rights reserved 4 | 5 | # mdnsd notifier for resolvconf libc subscriber 6 | 7 | # Redistribution and use in source and binary forms, with or without 8 | # modification, are permitted provided that the following conditions 9 | # are met: 10 | # * Redistributions of source code must retain the above copyright 11 | # notice, this list of conditions and the following disclaimer. 12 | # * Redistributions in binary form must reproduce the above 13 | # copyright notice, this list of conditions and the following 14 | # disclaimer in the documentation and/or other materials provided 15 | # with the distribution. 16 | # 17 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | : ${mdnsd_pidfile:=/var/run/mdnsd/mdnsd.pid} 30 | if [ -s "$mdnsd_pidfile" ]; then 31 | kill -HUP $(cat "$mdnsd_pidfile") 32 | fi 33 | -------------------------------------------------------------------------------- /avahi-daemon.in: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Copyright (c) 2007-2023 Roy Marples 3 | # All rights reserved 4 | 5 | # avahi-daemon notifier for resolvconf libc subscriber 6 | 7 | # Redistribution and use in source and binary forms, with or without 8 | # modification, are permitted provided that the following conditions 9 | # are met: 10 | # * Redistributions of source code must retain the above copyright 11 | # notice, this list of conditions and the following disclaimer. 12 | # * Redistributions in binary form must reproduce the above 13 | # copyright notice, this list of conditions and the following 14 | # disclaimer in the documentation and/or other materials provided 15 | # with the distribution. 16 | # 17 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | : ${avahi_daemon_pidfile:=/var/run/avahi-daemon/pid} 30 | if [ -s "$avahi_daemon_pidfile" ]; then 31 | kill -HUP $(cat "$avahi_daemon_pidfile") 32 | fi 33 | -------------------------------------------------------------------------------- /pdns_recursor.in: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Copyright (c) 2009-2023 Roy Marples 3 | # All rights reserved 4 | 5 | # PowerDNS Recursor subscriber for resolvconf 6 | 7 | # Redistribution and use in source and binary forms, with or without 8 | # modification, are permitted provided that the following conditions 9 | # are met: 10 | # * Redistributions of source code must retain the above copyright 11 | # notice, this list of conditions and the following disclaimer. 12 | # * Redistributions in binary form must reproduce the above 13 | # copyright notice, this list of conditions and the following 14 | # disclaimer in the documentation and/or other materials provided 15 | # with the distribution. 16 | # 17 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | [ -f "@SYSCONFDIR@"/resolvconf.conf ] || exit 0 30 | . "@SYSCONFDIR@/resolvconf.conf" || exit 1 31 | [ -z "$pdns_zones" ] && exit 0 32 | [ -z "$RESOLVCONF" ] && eval "$(@SBINDIR@/resolvconf -v)" 33 | NL=" 34 | " 35 | 36 | : ${pdns_service:=pdns-recursor} 37 | 38 | newzones= 39 | 40 | for n in $NAMESERVERS; do 41 | newzones="$newzones${newzones:+,}$n" 42 | done 43 | [ -n "$newzones" ] && newzones="+.=$newzones$NL" 44 | 45 | for d in $DOMAINS; do 46 | newns= 47 | ns="${d#*:}" 48 | while [ -n "$ns" ]; do 49 | newns="$newns${newns:+,}${ns%%,*}" 50 | [ "$ns" = "${ns#*,}" ] && break 51 | ns="${ns#*,}" 52 | done 53 | [ -n "$newns" ] && newzones="$newzones${d%%:*}=$newns$NL" 54 | done 55 | 56 | # Try to ensure that config dirs exist 57 | if command -v config_mkdirs >/dev/null 2>&1; then 58 | config_mkdirs "$pdnsd_zones" 59 | else 60 | @SBINDIR@/resolvconf -D "$pdnsd_zones" 61 | fi 62 | 63 | if [ ! -f "$pdns_zones" ] || \ 64 | [ "$(cat "$pdns_zones")" != "$(printf %s "$newzones")" ] 65 | then 66 | printf %s "$newzones" >"$pdns_zones" 67 | if [ -n "$pdns_restart" ]; then 68 | eval $pdns_restart 69 | elif [ -n "$RESTARTCMD" ]; then 70 | set -- ${pdns_service} 71 | eval "$RESTARTCMD" 72 | else 73 | @SBINDIR@/resolvconf -r ${pdns_service} 74 | fi 75 | fi 76 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # openresolv 2 | 3 | openresolv is a [resolvconf](https://en.wikipedia.org/wiki/Resolvconf) 4 | implementation which manages `/etc/resolv.conf`. 5 | 6 | `/etc/resolv.conf` is a file that holds the configuration for the local 7 | resolution of domain names. 8 | Normally this file is either static or maintained by a local daemon, 9 | normally a DHCP daemon. But what happens if more than one thing wants to 10 | control the file? 11 | Say you have wired and wireless interfaces to different subnets and run a VPN 12 | or two on top of that, how do you say which one controls the file? 13 | It's also not as easy as just adding and removing the nameservers each client 14 | knows about as different clients could add the same nameservers. 15 | 16 | Enter resolvconf, the middleman between the network configuration services and 17 | `/etc/resolv.conf`. 18 | resolvconf itself is just a script that stores, removes and lists a full 19 | `resolv.conf` generated for the interface. It then calls all the helper scripts 20 | it knows about so it can configure the real `/etc/resolv.conf` and optionally 21 | any local nameservers other than libc. 22 | 23 | ## Reasons for using openresolv 24 | 25 | Why openresolv over the 26 | [Debian implementation](http://qref.sourceforge.net/Debian/reference/ch-gateway.en.html#s-dns-resolvconf)? 27 | Here's some reasons: 28 | * Works with 29 | [POSIX shell and userland](http://www.opengroup.org/onlinepubs/009695399) 30 | * Does not need awk, grep or sed which means we can work without `/usr` 31 | mounted 32 | * Works with other init systems than Debians' out of the box 33 | * Available as a 2 clause 34 | [BSD license](http://www.freebsd.org/copyright/freebsd-license.html) 35 | * Prefer configs via IF_METRIC for dynamic ordering 36 | * Configures zones for local resolvers other than libc 37 | 38 | The last point is quite important, especially when running VPN systems. 39 | Take the following resolv.conf files which have been generated by a 40 | [DHCP client](https://github.com/NetworkConfiguration/dhcpcd) and sent to resolvconf: 41 | 42 | ``` 43 | # resolv.conf from bge0 44 | search foo.com 45 | nameserver 1.2.3.4 46 | 47 | # resolv.conf from tap0 48 | domain bar.org 49 | nameserver 5.6.7.8 50 | ``` 51 | 52 | In this instance, queries for foo.com will go to 1.2.3.4 and queries for 53 | bar.org will go to 5.6.7.8. 54 | This does require the resolvers to be configured to pickup the resolvconf 55 | generated configuration for them though. 56 | openresolv ships with helpers for: 57 | * [unbound](http://www.unbound.net/) 58 | * [dnsmasq](http://www.thekelleys.org.uk/dnsmasq/doc.html) 59 | * [ISC BIND](http://www.isc.org/software/bind) 60 | * [PowerDNS Recursor](http://wiki.powerdns.com/trac) 61 | * [systemd-resolved](https://www.freedesktop.org/software/systemd/man/latest/systemd-resolved.service.html) 62 | 63 | See the 64 | [configuration section](https://roy.marples.name/projects/openresolv/configuration) 65 | for more details. 66 | 67 | If openresolv updates `/etc/resolv.conf` it can notify the following of this: 68 | * [Bonjour (mdnsd)](https://developer.apple.com/bonjour/) 69 | * [avahi](http://www.avahi.org/) 70 | -------------------------------------------------------------------------------- /systemd-resolved.in: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Copyright (c) 2025 Roy Marples 3 | # All rights reserved 4 | 5 | # systemd-resolved subscriber for resolvconf 6 | 7 | # Redistribution and use in source and binary forms, with or without 8 | # modification, are permitted provided that the following conditions 9 | # are met: 10 | # * Redistributions of source code must retain the above copyright 11 | # notice, this list of conditions and the following disclaimer. 12 | # * Redistributions in binary form must reproduce the above 13 | # copyright notice, this list of conditions and the following 14 | # disclaimer in the documentation and/or other materials provided 15 | # with the distribution. 16 | # 17 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | [ -f "@SYSCONFDIR@"/resolvconf.conf ] || exit 0 30 | . "@SYSCONFDIR@/resolvconf.conf" || exit 1 31 | 32 | case "${systemd_resolved:-NO}" in 33 | [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1) ;; 34 | *) exit 0;; 35 | esac 36 | 37 | [ -n "$RESOLVCONF" ] || eval "$(@SBINDIR@/resolvconf -v)" 38 | NL=" 39 | " 40 | 41 | : ${systemd_resolved_conf:=/run/systemd/resolved.conf.d/60-resolvconf.conf} 42 | : ${systemd_delegate_dir:=/run/systemd/dns-delegate.d} 43 | 44 | # Try to ensure that config dirs exist 45 | if command -v config_mkdirs >/dev/null 2>&1; then 46 | config_mkdirs "$systemd_resolved_conf" "$systemd_delegate_dir/x" 47 | else 48 | @SBINDIR@/resolvconf -D "$systemd_resolved_conf" "$systemd_delegate_dir/x" 49 | fi 50 | 51 | header="# Generated by resolvconf$NL" 52 | header="${header}$NL" 53 | header="${header}[Resolve]$NL" 54 | 55 | conf="$header" 56 | # We emit blank values to force them to reset on SIGHUP 57 | conf="${conf}DNS=$NAMESERVERS$NL" 58 | # Indicate these nameservers are for all domain lookups by using ~. 59 | conf="${conf}Domains=$SEARCH${NAMESERVERS:+ ~.}$NL" 60 | 61 | printf %s "$conf" >"$systemd_resolved_conf" 62 | 63 | # DNS Delegates requires https://github.com/systemd/systemd/pull/34368 64 | rm -f "$systemd_delegate_dir/resolvconf-"*".dns-delegate" 65 | header="# Generated by resolvconf$NL" 66 | header="${header}$NL" 67 | header="${header}[Delegate]$NL" 68 | for d in $DOMAINS; do 69 | dn="${d%%:*}" 70 | ns="${d#*:}" 71 | dconf="${header}Domains=" 72 | search=false 73 | for sd in $SEARCH; do 74 | if [ "$sd" = "$dn" ]; then 75 | search=true 76 | break 77 | fi 78 | done 79 | if ! $search; then 80 | dconf="${dconf}~" 81 | fi 82 | dconf="${dconf}$dn$NL" 83 | dconf="${dconf}DNS=" 84 | while [ -n "$ns" ]; do 85 | dconf="$dconf${ns%%,*} " 86 | [ "$ns" = "${ns#*,}" ] && break 87 | ns="${ns#*,}" 88 | done 89 | dconf="$dconf$NL" 90 | printf %s "$dconf" >"$systemd_delegate_dir/resolvconf-$dn.dns-delegate" 91 | done 92 | 93 | pid=$(pidof systemd-resolved) 94 | if [ -n "$pid" ]; then 95 | kill -HUP $pid 96 | fi 97 | -------------------------------------------------------------------------------- /unbound.in: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Copyright (c) 2009-2023 Roy Marples 3 | # All rights reserved 4 | 5 | # unbound subscriber for resolvconf 6 | 7 | # Redistribution and use in source and binary forms, with or without 8 | # modification, are permitted provided that the following conditions 9 | # are met: 10 | # * Redistributions of source code must retain the above copyright 11 | # notice, this list of conditions and the following disclaimer. 12 | # * Redistributions in binary form must reproduce the above 13 | # copyright notice, this list of conditions and the following 14 | # disclaimer in the documentation and/or other materials provided 15 | # with the distribution. 16 | # 17 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | unbound_insecure= 30 | unbound_private= 31 | 32 | [ -f "@SYSCONFDIR@"/resolvconf.conf ] || exit 0 33 | . "@SYSCONFDIR@/resolvconf.conf" || exit 1 34 | [ -z "$unbound_conf" ] && exit 0 35 | [ -z "$RESOLVCONF" ] && eval "$(@SBINDIR@/resolvconf -v)" 36 | NL=" 37 | " 38 | 39 | : ${unbound_pid:=/var/run/unbound.pid} 40 | : ${unbound_service:=unbound} 41 | newconf="# Generated by resolvconf$NL" 42 | 43 | for d in $DOMAINS; do 44 | dn="${d%%:*}" 45 | ns="${d#*:}" 46 | create_unbound_insecure=false 47 | create_unbound_private=false 48 | case "$unbound_insecure" in 49 | [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1) 50 | create_unbound_insecure=true ;; 51 | esac 52 | case "$unbound_private" in 53 | [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1) 54 | create_unbound_private=true ;; 55 | esac 56 | if $create_unbound_insecure || $create_unbound_private; then 57 | newconf="$newconf${NL}server:$NL" 58 | if $create_unbound_insecure; then 59 | newconf="$newconf domain-insecure: \"$dn\"$NL" 60 | fi 61 | if $create_unbound_private; then 62 | newconf="$newconf private-domain: \"$dn\"$NL" 63 | fi 64 | fi 65 | newconf="$newconf${NL}forward-zone:$NL name: \"$dn\"$NL" 66 | if [ -n "$unbound_forward_zone_options" ]; then 67 | newconf="$newconf $unbound_forward_zone_options${NL}" 68 | fi 69 | while [ -n "$ns" ]; do 70 | newconf="$newconf forward-addr: ${ns%%,*}$NL" 71 | [ "$ns" = "${ns#*,}" ] && break 72 | ns="${ns#*,}" 73 | done 74 | done 75 | 76 | if [ -n "$NAMESERVERS" ]; then 77 | newconf="$newconf${NL}forward-zone:$NL name: \".\"$NL" 78 | if [ -n "$unbound_forward_zone_options" ]; then 79 | newconf="$newconf $unbound_forward_zone_options${NL}" 80 | fi 81 | for n in $NAMESERVERS; do 82 | newconf="$newconf forward-addr: $n$NL" 83 | done 84 | fi 85 | 86 | # Try to ensure that config dirs exist 87 | if command -v config_mkdirs >/dev/null 2>&1; then 88 | config_mkdirs "$unbound_conf" 89 | else 90 | @SBINDIR@/resolvconf -D "$unbound_conf" 91 | fi 92 | 93 | restart_unbound() 94 | { 95 | if [ -n "$unbound_restart" ]; then 96 | eval $unbound_restart 97 | elif [ -n "$RESTARTCMD" ]; then 98 | set -- ${unbound_service} 99 | eval "$RESTARTCMD" 100 | else 101 | @SBINDIR@/resolvconf -r ${unbound_service} 102 | fi 103 | } 104 | 105 | if [ ! -f "$unbound_conf" ] || \ 106 | [ "$(cat "$unbound_conf")" != "$(printf %s "$newconf")" ] 107 | then 108 | printf %s "$newconf" >"$unbound_conf" 109 | # If we can't sent a HUP then force a restart 110 | if [ -s "$unbound_pid" ]; then 111 | if ! kill -HUP $(cat "$unbound_pid") 2>/dev/null; then 112 | restart_unbound 113 | fi 114 | else 115 | restart_unbound 116 | fi 117 | fi 118 | -------------------------------------------------------------------------------- /named.in: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Copyright (c) 2007-2023 Roy Marples 3 | # All rights reserved 4 | 5 | # named subscriber for resolvconf 6 | 7 | # Redistribution and use in source and binary forms, with or without 8 | # modification, are permitted provided that the following conditions 9 | # are met: 10 | # * Redistributions of source code must retain the above copyright 11 | # notice, this list of conditions and the following disclaimer. 12 | # * Redistributions in binary form must reproduce the above 13 | # copyright notice, this list of conditions and the following 14 | # disclaimer in the documentation and/or other materials provided 15 | # with the distribution. 16 | # 17 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | [ -f "@SYSCONFDIR@"/resolvconf.conf ] || exit 0 30 | . "@SYSCONFDIR@/resolvconf.conf" || exit 1 31 | [ -z "${named_zones}${named_options}" ] && exit 0 32 | [ -z "$RESOLVCONF" ] && eval "$(@SBINDIR@/resolvconf -v)" 33 | NL=" 34 | " 35 | 36 | # Platform specific kludges 37 | if [ -z "${named_service}${named_restart}" ] && 38 | [ -d "$RCDIR" ] && ! [ -x "$RCDIR"/named ] 39 | then 40 | if [ -x "$RCDIR"/bind9 ]; then 41 | # Debian and derivatives 42 | named_service=bind9 43 | elif [ -x "$RCDIR"/rc.bind ]; then 44 | # Slackware 45 | named_service=rc.bind 46 | fi 47 | fi 48 | : ${named_service:=named} 49 | 50 | : ${named_pid:=/var/run/$named_service.pid} 51 | [ -s "$named_pid" ] || named_pid=/var/run/$named_service/$named_service.pid 52 | [ -s "$named_pid" ] || unset named_pid 53 | 54 | newoptions="# Generated by resolvconf$NL" 55 | newzones="$newoptions" 56 | 57 | forward= 58 | for n in $NAMESERVERS; do 59 | case "$forward" in 60 | *"$NL $n;"*);; 61 | *) forward="$forward$NL $n;";; 62 | esac 63 | done 64 | if [ -n "$forward" ]; then 65 | newoptions="${newoptions}forward first;${NL}forwarders {$forward${NL}};$NL" 66 | fi 67 | 68 | for d in $DOMAINS; do 69 | newzones="${newzones}zone \"${d%%:*}\" {$NL" 70 | newzones="$newzones type forward;$NL" 71 | newzones="$newzones forward first;$NL forwarders {$NL" 72 | ns="${d#*:}" 73 | while [ -n "$ns" ]; do 74 | newzones="$newzones ${ns%%,*};$NL" 75 | [ "$ns" = "${ns#*,}" ] && break 76 | ns="${ns#*,}" 77 | done 78 | newzones="$newzones };$NL};$NL" 79 | done 80 | 81 | # Try to ensure that config dirs exist 82 | if command -v config_mkdirs >/dev/null 2>&1; then 83 | config_mkdirs "$named_options" "$named_zones" 84 | else 85 | @SBINDIR@/resolvconf -D "$named_options" "$named_zones" 86 | fi 87 | 88 | # No point in changing files or reloading bind if the end result has not 89 | # changed 90 | changed=false 91 | if [ -n "$named_options" ]; then 92 | if [ ! -f "$named_options" ] || \ 93 | [ "$(cat "$named_options")" != "$(printf %s "$newoptions")" ] 94 | then 95 | printf %s "$newoptions" >"$named_options" 96 | changed=true 97 | fi 98 | fi 99 | if [ -n "$named_zones" ]; then 100 | if [ ! -f "$named_zones" ] || \ 101 | [ "$(cat "$named_zones")" != "$(printf %s "$newzones")" ] 102 | then 103 | printf %s "$newzones" >"$named_zones" 104 | changed=true 105 | fi 106 | fi 107 | 108 | # named does not seem to work with SIGHUP which is a same 109 | if $changed; then 110 | if [ -n "$named_restart" ]; then 111 | eval $named_restart 112 | elif [ -n "$RESTARTCMD" ]; then 113 | set -- ${named_service} 114 | eval "$RESTARTCMD" 115 | else 116 | @SBINDIR@/resolvconf -r ${named_service} 117 | fi 118 | fi 119 | -------------------------------------------------------------------------------- /configure: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Try and be like autotools configure, but without autotools 3 | 4 | # Ensure that we do not inherit these from env 5 | OS= 6 | BUILD= 7 | HOST= 8 | TARGET= 9 | RESTARTCMD= 10 | RCDIR= 11 | STATUSARG= 12 | 13 | for x do 14 | opt=${x%%=*} 15 | var=${x#*=} 16 | case "$opt" in 17 | --os|OS) OS=$var;; 18 | --with-cc|CC) CC=$var;; 19 | --debug) DEBUG=$var;; 20 | --disable-debug) DEBUG=no;; 21 | --enable-debug) DEBUG=yes;; 22 | --prefix) PREFIX=$var;; 23 | --sysconfdir) SYSCONFDIR=$var;; 24 | --bindir|--sbindir) SBINDIR=$var;; 25 | --libexecdir) LIBEXECDIR=$var;; 26 | --statedir|--localstatedir) STATEDIR=$var;; 27 | --dbdir) DBDIR=$var;; 28 | --rundir) RUNDIR=$var;; 29 | --mandir) MANDIR=$var;; 30 | --with-ccopts|CFLAGS) CFLAGS=$var;; 31 | CPPFLAGS) CPPFLAGS=$var;; 32 | --build) BUILD=$var;; 33 | --host) HOST=$var;; 34 | --target) TARGET=$var;; 35 | --libdir) LIBDIR=$var;; 36 | --restartcmd) RESTARTCMD=$var;; 37 | --rcdir) RCDIR=$var;; 38 | --statusarg) STATUSARG=$var;; 39 | --includedir) eval INCLUDEDIR="$INCLUDEDIR${INCLUDEDIR:+ }$var";; 40 | --datadir|--infodir) ;; # ignore autotools 41 | --disable-maintainer-mode|--disable-dependency-tracking) ;; 42 | --help) echo "See the source for available options"; exit 0;; 43 | *) echo "$0: WARNING: unknown option $opt" >&2;; 44 | esac 45 | done 46 | 47 | : ${SED:=sed} 48 | 49 | CONFIG_MK=config.mk 50 | 51 | if [ -z "$BUILD" ]; then 52 | # autoconf target triplet: cpu-vendor-os 53 | BUILD=$(uname -m)-unknown-$(uname -s | tr '[:upper:]' '[:lower:]') 54 | fi 55 | : ${HOST:=$BUILD} 56 | 57 | if [ -z "$OS" ]; then 58 | echo "Deriving operating system from ... $HOST" 59 | # Derive OS from cpu-vendor-[kernel-]os 60 | CPU=${HOST%%-*} 61 | REST=${HOST#*-} 62 | if [ "$CPU" != "$REST" ]; then 63 | VENDOR=${REST%%-*} 64 | REST=${REST#*-} 65 | if [ "$VENDOR" != "$REST" ]; then 66 | # Use kernel if given, otherwise os 67 | OS=${REST%%-*} 68 | else 69 | # 2 tupple 70 | OS=$VENDOR 71 | VENDOR= 72 | fi 73 | fi 74 | 75 | # Work with cpu-kernel-os, ie Debian 76 | case "$VENDOR" in 77 | linux*|kfreebsd*) OS=$VENDOR; VENDOR= ;; 78 | esac 79 | # Special case 80 | case "$OS" in 81 | gnu*) OS=hurd;; # No HURD support as yet 82 | esac 83 | fi 84 | 85 | echo "Configuring openresolv for ... $OS" 86 | rm -rf $CONFIG_MK 87 | echo "# $OS" >$CONFIG_MK 88 | 89 | case "$OS" in 90 | dragonfly*) 91 | # This means /usr HAS to be mounted not via dhcpcd 92 | : ${LIBEXECDIR:=${PREFIX:-/usr}/libexec/resolvconf} 93 | ;; 94 | linux*) 95 | # cksum doesn't support -a and netpgp is rare 96 | echo "CKSUM= sha256sum --tag" >>$CONFIG_MK 97 | echo "PGP= gpg2" >>$CONFIG_MK 98 | ;; 99 | esac 100 | 101 | case "$OS" in 102 | dragonfly*|freebsd*) 103 | # On FreeBSD, /etc/init.d/foo status returns 0 if foo is not enabled 104 | # regardless of if it's not running. 105 | # So we force onestatus to work around this silly bug. 106 | if [ -z "$STATUSARG" ]; then 107 | STATUSARG="onestatus" 108 | fi 109 | ;; 110 | esac 111 | 112 | 113 | if [ -z "$LIBEXECDIR" ]; then 114 | printf "Checking for directory /libexec ... " 115 | if [ -d /libexec ]; then 116 | echo "yes" 117 | LIBEXECDIR=$PREFIX/libexec/resolvconf 118 | else 119 | echo "no" 120 | LIBEXECDIR=$PREFIX/lib/resolvconf 121 | fi 122 | fi 123 | if [ -z "$RUNDIR" ]; then 124 | printf "Checking for directory /run ... " 125 | if [ -d /run ]; then 126 | echo "yes" 127 | RUNDIR=/run 128 | else 129 | echo "no" 130 | RUNDIR=/var/run 131 | fi 132 | fi 133 | 134 | : ${SYSCONFDIR:=$PREFIX/etc} 135 | : ${SBINDIR:=$PREFIX/sbin} 136 | : ${LIBEXECDIR:=$PREFIX/libexec/resolvconf} 137 | : ${STATEDIR:=/var} 138 | : ${RUNDIR:=$STATEDIR/run} 139 | : ${MANDIR:=${PREFIX:-/usr}/share/man} 140 | 141 | eval SYSCONFDIR="$SYSCONFDIR" 142 | eval SBINDIR="$SBINDIR" 143 | eval LIBEXECDIR="$LIBEXECDIR" 144 | eval VARDIR="$RUNDIR/resolvconf" 145 | eval MANDIR="$MANDIR" 146 | 147 | for x in SYSCONFDIR SBINDIR LIBEXECDIR VARDIR MANDIR RESTARTCMD RCDIR STATUSARG 148 | do 149 | eval v=\$$x 150 | # Make files look nice for import 151 | l=$((10 - ${#x})) 152 | unset t 153 | [ $l -gt 3 ] && t=" " 154 | echo "$x=$t $v" >>$CONFIG_MK 155 | done 156 | 157 | echo 158 | echo " SYSCONFDIR = $SYSCONFDIR" 159 | echo " SBINDIR = $SBINDIR" 160 | echo " LIBEXECDIR = $LIBEXECDIR" 161 | echo " VARDIR = $RUNDIR" 162 | echo " MANDIR = $MANDIR" 163 | echo 164 | echo " RESTARTCMD = $RESTARTCMD" 165 | echo " RCDIR = $RCDIR" 166 | echo " STATUSARG = $STATUSARG" 167 | echo 168 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | PKG= openresolv 2 | 3 | # Nasty hack so that make clean works without configure being run 4 | _CONFIG_MK!= test -e config.mk && echo config.mk || echo config-null.mk 5 | CONFIG_MK?= ${_CONFIG_MK} 6 | include ${CONFIG_MK} 7 | 8 | DIST!= if test -d .git; then echo "dist-git"; \ 9 | else echo "dist-inst"; fi 10 | 11 | SBINDIR?= /sbin 12 | SYSCONFDIR?= /etc 13 | LIBEXECDIR?= /libexec/resolvconf 14 | VARDIR?= /var/run/resolvconf 15 | 16 | ECHO?= echo 17 | INSTALL?= install 18 | SED?= sed 19 | 20 | VERSION!= ${SED} -n 's/OPENRESOLV_VERSION="\(.*\)".*/\1/p' resolvconf.in 21 | 22 | BINMODE?= 0755 23 | DOCMODE?= 0644 24 | MANMODE?= 0444 25 | 26 | RESOLVCONF= resolvconf resolvconf.8 resolvconf.conf.5 27 | SUBSCRIBERS= libc dnsmasq named pdnsd pdns_recursor unbound 28 | SUBSCRIBERS+= systemd-resolved resolvectl 29 | LIBC_SUBSCRIBERS= avahi-daemon mdnsd 30 | TARGET= ${RESOLVCONF} ${SUBSCRIBERS} ${LIBC_SUBSCRIBERS} 31 | SRCS= ${TARGET:C,$,.in,} # pmake 32 | SRCS:= ${TARGET:=.in} # gmake 33 | 34 | SED_SBINDIR= -e 's:@SBINDIR@:${SBINDIR}:g' 35 | SED_SYSCONFDIR= -e 's:@SYSCONFDIR@:${SYSCONFDIR}:g' 36 | SED_LIBEXECDIR= -e 's:@LIBEXECDIR@:${LIBEXECDIR}:g' 37 | SED_VARDIR= -e 's:@VARDIR@:${VARDIR}:g' 38 | SED_RCDIR= -e 's:@RCDIR@:${RCDIR}:g' 39 | SED_RESTARTCMD= -e 's:@RESTARTCMD@:${RESTARTCMD}:g' 40 | SED_RCDIR= -e 's:@RCDIR@:${RCDIR}:g' 41 | SED_STATUSARG= -e 's:@STATUSARG@:${STATUSARG}:g' 42 | 43 | DISTPREFIX?= ${PKG}-${VERSION} 44 | DISTFILE?= ${DISTPREFIX}.tar.xz 45 | DISTINFO= ${DISTFILE}.distinfo 46 | DISTINFOMD= ${DISTINFO}.md 47 | DISTSIGN= ${DISTFILE}.asc 48 | SHA256?= sha256 49 | PGP?= gpg2 50 | 51 | GITREF?= HEAD 52 | 53 | .SUFFIXES: .in 54 | 55 | all: ${TARGET} 56 | 57 | .in: Makefile ${CONFIG_MK} 58 | ${SED} ${SED_SBINDIR} ${SED_SYSCONFDIR} ${SED_LIBEXECDIR} \ 59 | ${SED_VARDIR} \ 60 | ${SED_RCDIR} ${SED_RESTARTCMD} ${SED_RCDIR} ${SED_STATUSARG} \ 61 | $< > $@ 62 | 63 | clean: 64 | rm -f ${TARGET} 65 | 66 | distclean: clean 67 | rm -f config.mk ${DISTFILE} ${DISTINFO} ${DISTINFOMD} ${DISTSIGN} 68 | 69 | installdirs: 70 | 71 | proginstall: ${TARGET} 72 | ${INSTALL} -d ${DESTDIR}${SBINDIR} 73 | ${INSTALL} -m ${BINMODE} resolvconf ${DESTDIR}${SBINDIR} 74 | ${INSTALL} -d ${DESTDIR}${SYSCONFDIR} 75 | test -e ${DESTDIR}${SYSCONFDIR}/resolvconf.conf || \ 76 | ${INSTALL} -m ${DOCMODE} resolvconf.conf ${DESTDIR}${SYSCONFDIR} 77 | ${INSTALL} -d ${DESTDIR}${LIBEXECDIR} 78 | ${INSTALL} -m ${DOCMODE} ${SUBSCRIBERS} ${DESTDIR}${LIBEXECDIR} 79 | ${INSTALL} -d ${DESTDIR}${LIBEXECDIR}/libc.d 80 | ${INSTALL} -m ${DOCMODE} ${LIBC_SUBSCRIBERS} \ 81 | ${DESTDIR}${LIBEXECDIR}/libc.d 82 | 83 | maninstall: 84 | ${INSTALL} -d ${DESTDIR}${MANDIR}/man8 85 | ${INSTALL} -m ${MANMODE} resolvconf.8 ${DESTDIR}${MANDIR}/man8 86 | ${INSTALL} -d ${DESTDIR}${MANDIR}/man5 87 | ${INSTALL} -m ${MANMODE} resolvconf.conf.5 ${DESTDIR}${MANDIR}/man5 88 | 89 | install: proginstall maninstall 90 | 91 | dist-git: 92 | git archive --prefix=${DISTPREFIX}/ ${GITREF} | xz >${DISTFILE} 93 | 94 | dist-inst: 95 | mkdir /tmp/${DISTPREFIX} 96 | cp -RPp * /tmp/${DISTPREFIX} 97 | (cd /tmp/${DISTPREFIX}; make clean) 98 | tar -cvJpf ${DISTFILE} -C /tmp ${DISTPREFIX} 99 | rm -rf /tmp/${DISTPREFIX} 100 | 101 | dist: ${DIST} 102 | 103 | distinfo: dist 104 | rm -f ${DISTINFO} ${DISTSIGN} 105 | ${SHA256} ${DISTFILE} >${DISTINFO} 106 | wc -c <${DISTFILE} \ 107 | | xargs printf 'Size (${DISTFILE}) = %s\n' >>${DISTINFO} 108 | ${PGP} --sign --armour --detach ${DISTFILE} 109 | chmod 644 ${DISTSIGN} 110 | ls -l ${DISTFILE} ${DISTINFO} ${DISTSIGN} 111 | 112 | ${DISTINFOMD}: ${DISTINFO} 113 | echo '```' >${DISTINFOMD} 114 | cat ${DISTINFO} >>${DISTINFOMD} 115 | echo '```' >>${DISTINFOMD} 116 | 117 | release: distinfo ${DISTINFOMD} 118 | gh release create v${VERSION} \ 119 | --title "openresolv ${VERSION}" --draft --generate-notes \ 120 | --notes-file ${DISTINFOMD} \ 121 | ${DISTFILE} ${DISTSIGN} 122 | 123 | import: dist 124 | rm -rf /tmp/${DISTPREFIX} 125 | ${INSTALL} -d /tmp/${DISTPREFIX} 126 | tar xvJpf ${DISTFILE} -C /tmp 127 | 128 | _import-src: 129 | rm -rf ${DESTDIR}/* 130 | ${INSTALL} -d ${DESTDIR} 131 | cp LICENSE README.md ${SRCS} resolvconf.conf ${DESTDIR}; 132 | cp resolvconf.8.in resolvconf.conf.5.in ${DESTDIR}; 133 | @${ECHO} 134 | @${ECHO} "=============================================================" 135 | @${ECHO} "openresolv-${VERSION} imported to ${DESTDIR}" 136 | 137 | import-src: 138 | ${MAKE} _import-src DESTDIR=`if [ -n "${DESTDIR}" ]; then echo "${DESTDIR}"; else echo /tmp/${DISTPREFIX}; fi` 139 | -------------------------------------------------------------------------------- /pdnsd.in: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Copyright (c) 2010-2023 Roy Marples 3 | # All rights reserved 4 | 5 | # pdnsd subscriber for resolvconf 6 | 7 | # Redistribution and use in source and binary forms, with or without 8 | # modification, are permitted provided that the following conditions 9 | # are met: 10 | # * Redistributions of source code must retain the above copyright 11 | # notice, this list of conditions and the following disclaimer. 12 | # * Redistributions in binary form must reproduce the above 13 | # copyright notice, this list of conditions and the following 14 | # disclaimer in the documentation and/or other materials provided 15 | # with the distribution. 16 | # 17 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | [ -f "@SYSCONFDIR@"/resolvconf.conf ] || exit 0 30 | . "@SYSCONFDIR@/resolvconf.conf" || exit 1 31 | [ -z "${pdnsd_conf}${pdnsd_resolv}" ] && exit 0 32 | [ -z "$RESOLVCONF" ] && eval "$(@SBINDIR@/resolvconf -v)" 33 | NL=" 34 | " 35 | 36 | : ${pdnsd_restart:=pdnsd-ctl config $pdnsd_conf} 37 | signature="# Generated by resolvconf" 38 | signature_end="# End of resolvconf" 39 | 40 | # We normally use sed to remove markers from a configuration file 41 | # but sed may not always be available at the time. 42 | remove_markers() 43 | { 44 | m1="$1" 45 | m2="$2" 46 | in_marker=0 47 | 48 | shift; shift 49 | if command -v sed >/dev/null 2>&1; then 50 | sed "/^$m1/,/^$m2/d" $@ 51 | else 52 | for x do 53 | while read line; do 54 | case "$line" in 55 | "$m1"*) in_marker=1;; 56 | "$m2"*) in_marker=0;; 57 | *) [ $in_marker = 0 ] && echo "$line";; 58 | esac 59 | done < "$x" 60 | done 61 | fi 62 | } 63 | 64 | # Compare two files 65 | # If different, replace first with second otherwise remove second 66 | change_file() 67 | { 68 | if [ -e "$1" ]; then 69 | if command -v cmp >/dev/null 2>&1; then 70 | cmp -s "$1" "$2" 71 | elif command -v diff >/dev/null 2>&1; then 72 | diff -q "$1" "$2" >/dev/null 73 | else 74 | # Hopefully we're only working on small text files ... 75 | [ "$(cat "$1")" = "$(cat "$2")" ] 76 | fi 77 | if [ $? -eq 0 ]; then 78 | rm -f "$2" 79 | return 1 80 | fi 81 | fi 82 | cat "$2" > "$1" 83 | rm -f "$2" 84 | return 0 85 | } 86 | 87 | newresolv="# Generated by resolvconf$NL" 88 | changed=false 89 | 90 | # Try to ensure that config dirs exist 91 | if command -v config_mkdirs >/dev/null 2>&1; then 92 | config_mkdirs "$pdnsd_resolv" "$pdnsd_conf" 93 | else 94 | @SBINDIR@/resolvconf -D "$pdnsd_resolv" "$pdnsd_conf" 95 | fi 96 | 97 | if [ -n "$pdnsd_resolv" ]; then 98 | for n in $NAMESERVERS; do 99 | newresolv="${newresolv}nameserver $n$NL" 100 | done 101 | fi 102 | 103 | # Only modify the configuration if it exists and we can write to it 104 | if [ -w "$pdnsd_conf" ]; then 105 | cf="$pdnsd_conf.new" 106 | newconf= 107 | 108 | if [ -z "$pdnsd_resolv" ]; then 109 | newconf="${newconf}server {$NL" 110 | newconf="${newconf} label=resolvconf;$NL" 111 | if [ -n "$NAMESERVERS" ]; then 112 | newconf="${newconf} ip=" 113 | first=true 114 | for n in $NAMESERVERS; do 115 | if $first; then 116 | first=false 117 | else 118 | newconf="${newconf}," 119 | fi 120 | newconf="$newconf$n" 121 | done 122 | newconf="${newconf};$NL" 123 | fi 124 | newconf="${newconf}}$NL" 125 | fi 126 | 127 | for d in $DOMAINS; do 128 | newconf="${newconf}server {$NL" 129 | newconf="${newconf} include=.${d%%:*}.;$NL" 130 | newconf="${newconf} policy=excluded;$NL" 131 | newconf="${newconf} ip=" 132 | ns="${d#*:}" 133 | while [ -n "$ns" ]; do 134 | newconf="${newconf}${ns%%,*}" 135 | [ "$ns" = "${ns#*,}" ] && break 136 | ns="${ns#*,}" 137 | newconf="${newconf}," 138 | done 139 | newconf="${newconf};$NL}$NL" 140 | done 141 | 142 | rm -f "$cf" 143 | remove_markers "$signature" "$signature_end" "$pdnsd_conf" > "$cf" 144 | if [ -n "$newconf" ]; then 145 | echo "$signature" >> "$cf" 146 | printf %s "$newconf" >> "$cf" 147 | echo "$signature_end" >> "$cf" 148 | fi 149 | if change_file "$pdnsd_conf" "$cf"; then 150 | changed=true 151 | fi 152 | fi 153 | 154 | if [ -n "$pdnsd_resolv" ]; then 155 | if [ ! -f "$pdnsd_resolv" ] || \ 156 | [ "$(cat "$pdnsd_resolv")" != "$(printf %s "$newresolv")" ] 157 | then 158 | changed=true 159 | printf %s "$newresolv" >"$pdnsd_resolv" 160 | fi 161 | fi 162 | 163 | if $changed; then 164 | eval $pdnsd_restart 165 | fi 166 | -------------------------------------------------------------------------------- /resolvectl.in: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Copyright (c) 2025 Roy Marples 3 | # All rights reserved 4 | 5 | # resolvectl subscriber for resolvconf 6 | 7 | # Redistribution and use in source and binary forms, with or without 8 | # modification, are permitted provided that the following conditions 9 | # are met: 10 | # * Redistributions of source code must retain the above copyright 11 | # notice, this list of conditions and the following disclaimer. 12 | # * Redistributions in binary form must reproduce the above 13 | # copyright notice, this list of conditions and the following 14 | # disclaimer in the documentation and/or other materials provided 15 | # with the distribution. 16 | # 17 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | [ -f "@SYSCONFDIR@"/resolvconf.conf ] || exit 0 30 | . "@SYSCONFDIR@/resolvconf.conf" || exit 1 31 | 32 | case "${resolvectl:-NO}" in 33 | [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1) ;; 34 | *) exit 0;; 35 | esac 36 | 37 | # If we don't have resolvectl or systemd-resolved isn't running then 38 | # we can't do much. 39 | # We can't persist our data in /run/systemd/resolve/netif/$ifindex 40 | # because systemd-resolved keeps it somehow, ie we can't change it 41 | # once we have inserted it 42 | if ! [ -d /sys/class/net ] || \ 43 | ! type resolvectl >/dev/null 2>&1 || \ 44 | ! pidof systemd-resolved >/dev/null 45 | then 46 | exit 1 47 | fi 48 | 49 | # resolvectl only accepts resolv.conf setup per physical interface 50 | # although resolvconf has always hinted that the named configuration 51 | # should be $interface.$protocol, this has never been a fixed requirement. 52 | # Because resolvectl only accepts one configuration per interface we need 53 | # to try and merge the resolv.conf's together. 54 | # Luckily resolvconf makes this easy for us. 55 | 56 | # Returns a list of resolvconf entries for a real interface 57 | get_resolvconf_interfaces() { 58 | IFACE="$1" 59 | [ -d /sys/class/net/"$IFACE" ] || return 1 60 | 61 | IFACES= 62 | for IFACE_PROTO in $(@SBINDIR@/resolvconf -Li "$IFACE" "$IFACE.*" 2>/dev/null); do 63 | # ens5 will work with ens5.dhcp and ens5.ra, 64 | # but not ens5.5 or ens5.5.dhcp 65 | if [ "$IFACE_PROTO" != "$IFACE" ]; then 66 | # Ensure that ens5.5.dhcp doesn't work for ens5 67 | if [ "${IFACE_PROTO%.*}" != "$IFACE" ]; then 68 | continue 69 | fi 70 | # Ensure that ens5.dhcp isn't a real interface 71 | # as ens5.5 likely is and the .5 matches the .dhcp 72 | if [ -d /sys/class/net/"$IFACE_PROTO" ]; then 73 | continue 74 | fi 75 | fi 76 | IFACES="$IFACES${IFACES:+ }$IFACE_PROTO" 77 | done 78 | echo "$IFACES" 79 | } 80 | 81 | # For the given interface, apply a list of resolvconf entries 82 | apply_resolvconf() { 83 | IFACE="$1" 84 | shift 85 | 86 | if [ -z "$1" ]; then 87 | resolvectl revert "$IFACE" 88 | return 89 | fi 90 | 91 | # Set the default-route property first to avoid leakage. 92 | # If any entry is private, the whole interface has to be private. 93 | # If a more granular approach is needed, consider using the 94 | # systemd-resolved subscriber instead which supports DNS delegates. 95 | if [ -n "$(@SBINDIR@/resolvconf -p $@)" ]; then 96 | resolvectl default-route "$IFACE" false 97 | else 98 | resolvectl default-route "$IFACE" true 99 | fi 100 | 101 | # Now set domain and dns 102 | DOMAIN=$(@SBINDIR@/resolvconf -L $@ 2>/dev/null | sed -n -e "s/domain //p" -e "s/search //p") 103 | NS=$(@SBINDIR@/resolvconf -L $@ 2>/dev/null | sed -n -e "s/nameserver //p") 104 | if [ -n "$DOMAIN" ]; then 105 | # If any entry is marked as not searchable, we mark all the 106 | # domains as non searchable. 107 | # If a more granular approach is needed, consider using the 108 | # systemd-resolved subscriber instead which supports DNS delegates. 109 | if [ -n "$(@SBINDIR@/resolvconf -pp $@)" ]; then 110 | ND= 111 | for d in $DOMAIN; do 112 | ND="$ND${ND:+ }~$d" 113 | done 114 | DOMAIN="$ND" 115 | fi 116 | resolvectl domain "$IFACE" $DOMAIN 117 | else 118 | resolvectl domain "$IFACE" "" 119 | fi 120 | if [ -n "$NS" ]; then 121 | resolvectl dns "$IFACE" $NS 122 | else 123 | resolvectl dns "$IFACE" "" 124 | fi 125 | } 126 | 127 | # To get the full features of resolvconf, we need to work out each interface 128 | # for every resolvconf addition and deletion 129 | # This is because resolvconf.conf might have changed OR an exclusive 130 | # interface deleted which makes other interfaces visible. 131 | cd /sys/class/net 132 | for IFACE in *; do 133 | if [ "$IFACE" = lo ]; then 134 | # systemd-resolved doesn't work with lo 135 | continue 136 | fi 137 | 138 | IFACES=$(get_resolvconf_interfaces "$IFACE") 139 | apply_resolvconf "$IFACE" $IFACES 140 | done 141 | 142 | # warn about resolv.conf with no matching interface 143 | FAILED= 144 | for IFACE_PROTO in $(@SBINDIR@/resolvconf -Li); do 145 | IFACE="${IFACE_PROTO%.*}" 146 | if [ "$IFACE" = lo ]; then 147 | # Don't warn about loopback interface as that is typically 148 | # used to configure libc for a nameserver on it and the libc 149 | # subscriber will process that just fine. 150 | continue 151 | fi 152 | 153 | if ! [ -d "/sys/class/net/$IFACE" ]; then 154 | FAILED="$FAILED${FAILED:+ }$IFACE_PROTO" 155 | fi 156 | done 157 | if [ -n "$FAILED" ]; then 158 | echo "Could not apply resolv.conf to resolvectl: $FAILED" >&2 159 | fi 160 | -------------------------------------------------------------------------------- /dnsmasq.in: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Copyright (c) 2007-2023 Roy Marples 3 | # All rights reserved 4 | 5 | # dnsmasq subscriber for resolvconf 6 | 7 | # Redistribution and use in source and binary forms, with or without 8 | # modification, are permitted provided that the following conditions 9 | # are met: 10 | # * Redistributions of source code must retain the above copyright 11 | # notice, this list of conditions and the following disclaimer. 12 | # * Redistributions in binary form must reproduce the above 13 | # copyright notice, this list of conditions and the following 14 | # disclaimer in the documentation and/or other materials provided 15 | # with the distribution. 16 | # 17 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | [ -f "@SYSCONFDIR@"/resolvconf.conf ] || exit 0 30 | . "@SYSCONFDIR@/resolvconf.conf" || exit 1 31 | [ -z "${dnsmasq_conf}${dnsmasq_resolv}" ] && exit 0 32 | [ -z "$RESOLVCONF" ] && eval "$(@SBINDIR@/resolvconf -v)" 33 | NL=" 34 | " 35 | 36 | : ${dnsmasq_pid:=/var/run/dnsmasq.pid} 37 | [ -s "$dnsmasq_pid" ] || dnsmasq_pid=/var/run/dnsmasq/dnsmasq.pid 38 | [ -s "$dnsmasq_pid" ] || unset dnsmasq_pid 39 | : ${dnsmasq_service:=dnsmasq} 40 | newconf="# Generated by resolvconf$NL" 41 | newresolv="$newconf" 42 | 43 | # Using dbus means that we never have to restart the daemon 44 | # This is important as it means we should not drop DNS queries 45 | # whilst changing DNS options around. However, dbus support is optional 46 | # so we need to validate a few things first. 47 | # Check for DBus support in the binary 48 | dbus=false 49 | dbus_ex=false 50 | dbus_introspect=$(dbus-send --print-reply --system \ 51 | --dest=uk.org.thekelleys.dnsmasq \ 52 | /uk/org/thekelleys/dnsmasq \ 53 | org.freedesktop.DBus.Introspectable.Introspect \ 54 | 2>/dev/null) 55 | if [ $? = 0 ]; then 56 | dbus=true 57 | if printf %s "$dbus_introspect" | \ 58 | grep -q '' 59 | then 60 | dbus_ex=true 61 | fi 62 | fi 63 | 64 | for n in $NAMESERVERS; do 65 | newresolv="${newresolv}nameserver $n$NL" 66 | done 67 | 68 | dbusdest= 69 | dbusdest_ex= 70 | conf= 71 | for d in $DOMAINS; do 72 | dn="${d%%:*}" 73 | ns="${d#*:}" 74 | while [ -n "$ns" ]; do 75 | n="${ns%%,*}" 76 | if $dbus && ! $dbus_ex; then 77 | case "$n" in 78 | *.*.*.*) 79 | SIFS=${IFS-y} OIFS=$IFS 80 | IFS=. 81 | set -- $n 82 | num="0x$(printf %02x $1 $2 $3 $4)" 83 | if [ "$SIFS" = y ]; then 84 | unset IFS 85 | else 86 | IFS=$OIFS 87 | fi 88 | dbusdest="$dbusdest uint32:$(printf %u $num)" 89 | dbusdest="$dbusdest string:$dn" 90 | ;; 91 | *:*%*) 92 | # This version of dnsmasq won't accept 93 | # scoped IPv6 addresses 94 | dbus=false 95 | ;; 96 | *:*) 97 | SIFS=${IFS-y} OIFS=$IFS bytes= front= back= 98 | empty=false i=0 99 | IFS=: 100 | set -- $n 101 | while [ -n "$1" ] || [ -n "$2" ]; do 102 | addr="$1" 103 | shift 104 | if [ -z "$addr" ]; then 105 | empty=true 106 | continue 107 | fi 108 | i=$((i + 1)) 109 | while [ ${#addr} -lt 4 ]; do 110 | addr="0${addr}" 111 | done 112 | byte1="$(printf %d 0x${addr%??})" 113 | byte2="$(printf %d 0x${addr#??})" 114 | if $empty; then 115 | back="$back byte:$byte1 byte:$byte2" 116 | else 117 | front="$front byte:$byte1 byte:$byte2" 118 | fi 119 | done 120 | while [ $i != 8 ]; do 121 | i=$((i + 1)) 122 | front="$front byte:0 byte:0" 123 | done 124 | front="${front}$back" 125 | if [ "$SIFS" = y ]; then 126 | unset IFS 127 | else 128 | IFS=$OIFS 129 | fi 130 | dbusdest="${dbusdest}$front string:$dn" 131 | ;; 132 | *) 133 | if ! $dbus_ex; then 134 | dbus=false 135 | fi 136 | ;; 137 | esac 138 | fi 139 | dbusdest_ex="$dbusdest_ex${dbusdest_ex:+,}/$dn/$n" 140 | conf="${conf}server=/$dn/$n$NL" 141 | [ "$ns" = "${ns#*,}" ] && break 142 | ns="${ns#*,}" 143 | done 144 | done 145 | 146 | if $dbus; then 147 | newconf="$newconf$NL# Domain specific servers will" 148 | newconf="$newconf be sent over dbus${NL}" 149 | else 150 | newconf="$newconf$conf" 151 | fi 152 | 153 | # Try to ensure that config dirs exist 154 | if command -v config_mkdirs >/dev/null 2>&1; then 155 | config_mkdirs "$dnsmasq_conf" "$dnsmasq_resolv" 156 | else 157 | @SBINDIR@/resolvconf -D "$dnsmasq_conf" "$dnsmasq_resolv" 158 | fi 159 | 160 | changed=false 161 | if [ -n "$dnsmasq_conf" ]; then 162 | if [ ! -f "$dnsmasq_conf" ] || \ 163 | [ "$(cat "$dnsmasq_conf")" != "$(printf %s "$newconf")" ] 164 | then 165 | changed=true 166 | printf %s "$newconf" >"$dnsmasq_conf" 167 | fi 168 | fi 169 | if [ -n "$dnsmasq_resolv" ]; then 170 | # dnsmasq polls this file so no need to set changed=true 171 | if [ -f "$dnsmasq_resolv" ]; then 172 | if [ "$(cat "$dnsmasq_resolv")" != "$(printf %s "$newresolv")" ] 173 | then 174 | printf %s "$newresolv" >"$dnsmasq_resolv" 175 | fi 176 | else 177 | printf %s "$newresolv" >"$dnsmasq_resolv" 178 | fi 179 | fi 180 | 181 | if $changed; then 182 | # dnsmasq does not re-read the configuration file on SIGHUP 183 | if [ -n "$dnsmasq_restart" ]; then 184 | eval $dnsmasq_restart 185 | elif [ -n "$RESTARTCMD" ]; then 186 | set -- ${dnsmasq_service} 187 | eval "$RESTARTCMD" 188 | else 189 | @SBINDIR@/resolvconf -r ${dnsmasq_service} 190 | fi 191 | fi 192 | if $dbus; then 193 | if [ -s "$dnsmasq_pid" ]; then 194 | $changed || kill -HUP $(cat "$dnsmasq_pid") 195 | fi 196 | # Send even if empty so old servers are cleared 197 | if $dbus_ex; then 198 | method=SetDomainServers 199 | if [ -n "$dbusdest_ex" ]; then 200 | dbusdest_ex="array:string:$dbusdest_ex" 201 | fi 202 | dbusdest="$dbusdest_ex" 203 | else 204 | method=SetServers 205 | fi 206 | dbus-send --system --dest=uk.org.thekelleys.dnsmasq \ 207 | /uk/org/thekelleys/dnsmasq uk.org.thekelleys.$method \ 208 | $dbusdest 209 | dbus-send --system --dest=uk.org.thekelleys.dnsmasq \ 210 | /uk/org/thekelleys/dnsmasq uk.org.thekelleys.ClearCache 211 | fi 212 | -------------------------------------------------------------------------------- /libc.in: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Copyright (c) 2007-2025 Roy Marples 3 | # All rights reserved 4 | 5 | # libc subscriber for resolvconf 6 | 7 | # Redistribution and use in source and binary forms, with or without 8 | # modification, are permitted provided that the following conditions 9 | # are met: 10 | # * Redistributions of source code must retain the above copyright 11 | # notice, this list of conditions and the following disclaimer. 12 | # * Redistributions in binary form must reproduce the above 13 | # copyright notice, this list of conditions and the following 14 | # disclaimer in the documentation and/or other materials provided 15 | # with the distribution. 16 | # 17 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | SYSCONFDIR=@SYSCONFDIR@ 30 | LIBEXECDIR=@LIBEXECDIR@ 31 | VARDIR=@VARDIR@ 32 | KEYDIR="$VARDIR/keys" 33 | # Compat 34 | if [ ! -d "$KEYDIR" ] && [ -d "$VARDIR/interfaces" ]; then 35 | KEYDIR="$VARDIR/interfaces" 36 | fi 37 | 38 | CMD="$1" 39 | KEY="$2" 40 | 41 | NL=" 42 | " 43 | 44 | warn() 45 | { 46 | echo "$(basename $0): $*" >&2 47 | } 48 | 49 | # sed may not be available, and this is faster on small files 50 | key_get_value() 51 | { 52 | key="$1" 53 | shift 54 | 55 | if [ $# -eq 0 ]; then 56 | while read -r line; do 57 | case "$line" in 58 | "$key"*) echo "${line##$key}";; 59 | esac 60 | done 61 | else 62 | for x do 63 | while read -r line; do 64 | case "$line" in 65 | "$key"*) echo "${line##$key}";; 66 | esac 67 | done < "$x" 68 | done 69 | fi 70 | } 71 | 72 | keys_remove() 73 | { 74 | while read -r line; do 75 | found=false 76 | for key do 77 | case "$line" in 78 | "$key"*|"#"*|" "*|" "*|"") found=true;; 79 | esac 80 | $found && break 81 | done 82 | $found || echo "$line" 83 | done 84 | } 85 | 86 | local_nameservers="127.* 0.0.0.0 255.255.255.255 ::1" 87 | 88 | # Support original resolvconf configuration layout 89 | # as well as the openresolv config file 90 | if [ -f "$SYSCONFDIR"/resolvconf.conf ]; then 91 | . "$SYSCONFDIR"/resolvconf.conf 92 | elif [ -d "$SYSCONFDIR"/resolvconf ]; then 93 | SYSCONFDIR="$SYSCONFDIR/resolvconf" 94 | base="$SYSCONFDIR/resolv.conf.d/base" 95 | if [ -f "$base" ]; then 96 | prepend_nameservers="$(key_get_value "nameserver " "$base")" 97 | domain="$(key_get_value "domain " "$base")" 98 | prepend_search="$(key_get_value "search " "$base")" 99 | resolv_conf_options="$(key_get_value "options " "$base")" 100 | resolv_conf_sortlist="$(key_get_value "sortlist " "$base")" 101 | fi 102 | if [ -f "$SYSCONFDIR"/resolv.conf.d/head ]; then 103 | resolv_conf_head="$(cat "${SYSCONFDIR}"/resolv.conf.d/head)" 104 | fi 105 | if [ -f "$SYSCONFDIR"/resolv.conf.d/tail ]; then 106 | resolv_conf_tail="$(cat "$SYSCONFDIR"/resolv.conf.d/tail)" 107 | fi 108 | fi 109 | : ${resolv_conf:=/etc/resolv.conf} 110 | if [ "$resolv_conf" = "/dev/null" ]; then 111 | exit 0 112 | fi 113 | : ${resolv_conf_tmp:="$resolv_conf.$$.openresolv"} 114 | : ${libc_service:=nscd} 115 | : ${list_resolv:=@SBINDIR@/resolvconf -L} 116 | if [ "${resolv_conf_head-x}" = x ] && [ -f "$SYSCONFDIR"/resolv.conf.head ] 117 | then 118 | resolv_conf_head="$(cat "${SYSCONFDIR}"/resolv.conf.head)" 119 | fi 120 | if [ "${resolv_conf_tail-x}" = x ] && [ -f "$SYSCONFDIR"/resolv.conf.tail ] 121 | then 122 | resolv_conf_tail="$(cat "$SYSCONFDIR"/resolv.conf.tail)" 123 | fi 124 | 125 | signature="# Generated by resolvconf" 126 | 127 | uniqify() 128 | { 129 | result= 130 | while [ -n "$1" ]; do 131 | case " $result " in 132 | *" $1 "*);; 133 | *) result="$result $1";; 134 | esac 135 | shift 136 | done 137 | echo "${result# *}" 138 | } 139 | 140 | case "${resolv_conf_passthrough:-NO}" in 141 | [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1) 142 | newest= 143 | for conf in "$KEYDIR"/*; do 144 | if [ -z "$newest" ] || [ "$conf" -nt "$newest" ]; then 145 | newest="$conf" 146 | fi 147 | done 148 | [ -z "$newest" ] && exit 0 149 | newconf="$signature$NL$(cat "$newest")$NL" 150 | ;; 151 | /dev/null|[Nn][Uu][Ll][Ll]) 152 | : ${resolv_conf_local_only:=NO} 153 | if [ "$local_nameservers" = "127.* 0.0.0.0 255.255.255.255 ::1" ]; then 154 | local_nameservers= 155 | fi 156 | # Need to overwrite our variables. 157 | eval "$(@SBINDIR@/resolvconf -V)" 158 | ;; 159 | 160 | *) 161 | [ -z "$RESOLVCONF" ] && eval "$(@SBINDIR@/resolvconf -v)" 162 | ;; 163 | esac 164 | case "${resolv_conf_passthrough:-NO}" in 165 | [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1) ;; 166 | *) 167 | : ${domain:=$DOMAIN} 168 | newsearch="$(uniqify $prepend_search $SEARCH $append_search)" 169 | NS="$LOCALNAMESERVERS $NAMESERVERS" 170 | newns= 171 | gotlocal=false 172 | for n in $(uniqify $prepend_nameservers $NS $append_nameservers); do 173 | add=true 174 | islocal=false 175 | for l in $local_nameservers; do 176 | case "$n" in 177 | $l) islocal=true; gotlocal=true; break;; 178 | esac 179 | done 180 | if ! $islocal; then 181 | case "${resolv_conf_local_only:-YES}" in 182 | [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1) 183 | $gotlocal && add=false;; 184 | esac 185 | fi 186 | $add && newns="$newns $n" 187 | done 188 | 189 | # Hold our new resolv.conf in a variable to save on temporary files 190 | newconf="$signature$NL" 191 | if [ -n "$resolv_conf_head" ]; then 192 | newconf="$newconf$resolv_conf_head$NL" 193 | fi 194 | 195 | [ -n "$domain" ] && newconf="${newconf}domain $domain$NL" 196 | if [ -n "$newsearch" ] && [ "$newsearch" != "$domain" ]; then 197 | newconf="${newconf}search $newsearch$NL" 198 | fi 199 | for n in $newns; do 200 | newconf="${newconf}nameserver $n$NL" 201 | done 202 | 203 | # Now add anything we don't care about such as sortlist and options 204 | stuff="$($list_resolv | keys_remove nameserver domain search)" 205 | if [ -n "$stuff" ]; then 206 | newconf="$newconf$stuff$NL" 207 | fi 208 | 209 | # Append any user defined ones 210 | if [ -n "$resolv_conf_options" ]; then 211 | newconf="${newconf}options $resolv_conf_options$NL" 212 | fi 213 | if [ -n "$resolv_conf_sortlist" ]; then 214 | newconf="${newconf}sortlist $resolv_conf_sortlist$NL" 215 | fi 216 | 217 | if [ -n "$resolv_conf_tail" ]; then 218 | newconf="$newconf$resolv_conf_tail$NL" 219 | fi 220 | ;; 221 | esac 222 | 223 | # Check if the file has actually changed or not 224 | if [ -e "$resolv_conf" ]; then 225 | if [ "$CMD" != u ] && \ 226 | [ "$(cat "$resolv_conf")" = "$(printf %s "$newconf")" ] 227 | then 228 | exit 0 229 | fi 230 | read line <"$resolv_conf" 231 | if [ "$line" != "$signature" ]; then 232 | if [ "$CMD" != u ]; then 233 | warn "signature mismatch: $resolv_conf" 234 | warn "run \`resolvconf -u\` to update" 235 | exit 1 236 | fi 237 | cp "$resolv_conf" "$resolv_conf.bak" 238 | fi 239 | fi 240 | 241 | # There are pros and cons for writing directly to resolv.conf 242 | # instead of a temporary file and then moving it over. 243 | # The default is to write to resolv.conf as it has the least 244 | # issues and has been the long standing default behaviour. 245 | # resolv.conf could also be bind mounted for network namespaces 246 | # so we cannot move in this instance. 247 | case "${resolv_conf_mv:-NO}" in 248 | [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1) 249 | # Protect against symlink attack, ensure new file does not exist 250 | rm -f "$resolv_conf_tmp" 251 | # Keep original file owner, group and mode 252 | [ -r "$resolv_conf" ] && cp -p "$resolv_conf" "$resolv_conf_tmp" 253 | # Create our resolv.conf now 254 | if (umask 022; printf %s "$newconf" >"$resolv_conf_tmp"); then 255 | mv "$resolv_conf_tmp" "$resolv_conf" 256 | fi 257 | ;; 258 | *) 259 | (umask 022; printf %s "$newconf" >"$resolv_conf") 260 | ;; 261 | esac 262 | 263 | if [ -n "$libc_restart" ]; then 264 | eval $libc_restart 265 | elif [ -n "$RESTARTCMD" ]; then 266 | set -- ${libc_service} 267 | eval "$RESTARTCMD" 268 | else 269 | @SBINDIR@/resolvconf -r ${libc_service} 270 | fi 271 | 272 | retval=0 273 | # Notify users of the resolver 274 | for script in "$LIBEXECDIR"/libc.d/*; do 275 | if [ -f "$script" ]; then 276 | if [ -x "$script" ]; then 277 | "$script" "$@" 278 | else 279 | (. "$script") 280 | fi 281 | retval=$(($retval + $?)) 282 | fi 283 | done 284 | exit $retval 285 | -------------------------------------------------------------------------------- /resolvconf.8.in: -------------------------------------------------------------------------------- 1 | .\" Copyright (c) 2007-2025 Roy Marples 2 | .\" All rights reserved 3 | .\" 4 | .\" Redistribution and use in source and binary forms, with or without 5 | .\" modification, are permitted provided that the following conditions 6 | .\" are met: 7 | .\" 1. Redistributions of source code must retain the above copyright 8 | .\" notice, this list of conditions and the following disclaimer. 9 | .\" 2. Redistributions in binary form must reproduce the above copyright 10 | .\" notice, this list of conditions and the following disclaimer in the 11 | .\" documentation and/or other materials provided with the distribution. 12 | .\" 13 | .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14 | .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | .\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 17 | .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 | .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 | .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 | .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 | .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 | .\" SUCH DAMAGE. 24 | .\" 25 | .Dd June 26, 2025 26 | .Dt RESOLVCONF 8 27 | .Os 28 | .Sh NAME 29 | .Nm resolvconf 30 | .Nd a framework for managing multiple DNS configurations 31 | .Sh SYNOPSIS 32 | .Nm 33 | .Fl I 34 | .Nm 35 | .Op Fl m Ar metric 36 | .Op Fl p 37 | .Op Fl x 38 | .Fl a Ar key 39 | .No < Ns Pa file 40 | .Nm 41 | .Fl C Ar pattern 42 | .Nm 43 | .Fl c Ar pattern 44 | .Nm 45 | .Op Fl f 46 | .Fl d Ar key 47 | .Nm 48 | .Op Fl x 49 | .Fl iLlp Ar pattern 50 | .Nm 51 | .Fl u 52 | .Nm 53 | .Fl Fl version 54 | .Sh DESCRIPTION 55 | .Nm 56 | manages 57 | .Xr resolv.conf 5 58 | files from multiple sources, such as DHCP and VPN clients. 59 | Traditionally, the host runs just one client and that updates 60 | .Pa /etc/resolv.conf . 61 | More modern systems frequently have wired and wireless interfaces and there is 62 | no guarantee both are on the same network. 63 | With the advent of VPN and other 64 | types of networking daemons, many things now contend for the contents of 65 | .Pa /etc/resolv.conf . 66 | .Pp 67 | .Nm 68 | solves this by letting the daemon send their 69 | .Xr resolv.conf 5 70 | file to 71 | .Nm 72 | via 73 | .Xr stdin 4 74 | with the argument 75 | .Fl a Ar key 76 | instead of the filesystem. 77 | .Nm 78 | then updates 79 | .Pa /etc/resolv.conf 80 | as it thinks best. 81 | If 82 | .Pa /etc/resolv.conf 83 | already exists and the top line does not match the expected signature, 84 | then 85 | .Nm 86 | will refuse to update it unless the 87 | .Fl u 88 | update command is given. 89 | When a local resolver other than libc is installed, such as 90 | .Xr dnsmasq 8 91 | or 92 | .Xr named 8 , 93 | then 94 | .Nm 95 | will supply files that the resolver should be configured to include. 96 | .Pp 97 | At it's heart, 98 | .Nm 99 | is a key/value store for 100 | .Pa resolv.conf 101 | files. 102 | Each entry must have a unique 103 | .Ar key 104 | and should be expressed as 105 | .Sy interface.protocol 106 | so that it's easy to tell from where the 107 | .Pa resolv.conf 108 | file came from. 109 | This also allows using pattern matching such as 110 | .Sy interface.* 111 | to match all protocols running on the interface. 112 | For example, a modern system will likely run DHCP, RA and DHCPv6 113 | which could be from separate programs or one program running 114 | many protocols. 115 | However, this is not a fixed requirement, 116 | .Nm 117 | will work with any key name and it should be treated as an opaque value 118 | outside of 119 | .Nm . 120 | .Pp 121 | .Nm 122 | can mark a 123 | .Pa resolv.conf 124 | as private and optionally non-searchable. 125 | This means that the name servers listed in that 126 | .Pa resolv.conf 127 | are only used for queries against the domain/search listed in the same file 128 | and if non-searchable then the domain/search listed are 129 | excluded from the global search list defined in 130 | .Pa /etc/resolv.conf . 131 | This only works when a local resolver other than libc is installed. 132 | See 133 | .Xr resolvconf.conf 5 134 | for how to configure 135 | .Nm 136 | to use a local name server and how to remove the private marking. 137 | .Pp 138 | .Nm 139 | can mark a 140 | .Pa resolv.conf 141 | as exclusive. 142 | Only the latest exclusive key is used for processing, otherwise all are. 143 | .Pp 144 | When a configuration source goes away, 145 | such as an interface going down or a VPN stopping, 146 | it should then call 147 | .Nm 148 | with 149 | .Fl d Ar key 150 | arguments to clean up the 151 | .Pa resolv.conf 152 | it added previously. 153 | For systems that support the concept of persisting configuration when 154 | the source is suspended, 155 | such as the carrier going down, 156 | then it should instead call 157 | .Nm 158 | with 159 | .Fl C Ar key 160 | arguments to deprecate the entry 161 | .Fl c Ar key 162 | to activate the entry when it comes back again. 163 | This only affects the order in which the 164 | .Pa resolv.conf 165 | entries are processed. 166 | .Pp 167 | Here are some options for the above commands: 168 | .Bl -tag -width pattern_opt 169 | .It Fl f 170 | Ignore non existent 171 | .Pa resolv.conf 172 | entries. 173 | Only really useful for deleting. 174 | .It Fl m Ar metric 175 | Set the metric of the 176 | .Pa resolv.conf 177 | entry when adding it, default of 0. 178 | Lower metrics take precedence. 179 | This affects the default order of entires when listed. 180 | .It Fl p Op Ar pattern 181 | Marks the 182 | .Pa resolv.conf 183 | as private if the 184 | .Fl a 185 | command is given, otherwise 186 | .Pa resolv.conf 187 | entries having their key matching 188 | .Ar pattern 189 | are listed. 190 | If an extra 191 | .Fl p 192 | is given then the 193 | .Pa resolv.conf 194 | is marked as non-searchable as well. 195 | .It Fl x 196 | Mark the 197 | .Pa resolv.conf 198 | as exclusive when adding, otherwise only use the latest exclusive key. 199 | .El 200 | .Pp 201 | .Nm 202 | has some more commands for general usage: 203 | .Bl -tag -width pattern_opt 204 | .It Fl i Op Ar pattern 205 | List the keys stored, optionally matching 206 | .Ar pattern , 207 | we have 208 | .Pa resolv.conf 209 | files for. 210 | If the 211 | .Fl L 212 | option is given first, then the keys will be list post-processed. 213 | .It Fl L Op Ar pattern 214 | List the 215 | .Pa resolv.conf 216 | files we have, 217 | post-processed by the 218 | .Xr resolvconf.conf 5 219 | configuration. 220 | If 221 | .Ar pattern 222 | is specified then we list the files for the keys which match it. 223 | .It Fl l Op Ar pattern 224 | List the 225 | .Pa resolv.conf 226 | files we have. 227 | If 228 | .Ar pattern 229 | is specified then we list the files for the keys which match it. 230 | that match it. 231 | .It Fl u 232 | Force 233 | .Nm 234 | to update all its subscribers. 235 | .Nm 236 | does not update the subscribers when adding a resolv.conf that matches 237 | what it already has for that key. 238 | .It Fl Fl version 239 | Echo the resolvconf version to 240 | .Em stdout . 241 | .El 242 | .Pp 243 | .Nm 244 | also has some commands designed to be used by its subscribers and 245 | system startup: 246 | .Bl -tag -width pattern_opt 247 | .It Fl I 248 | Initialise the state directory 249 | .Pa @VARDIR@ . 250 | This should be called after the base state directory has either been 251 | cleaned out or mounted as a memory backed filesystem during the 252 | initial boot sequence before any daemon has the chance to call 253 | .Nm . 254 | .It Fl R 255 | Echo the command used to restart a service. 256 | .It Fl r Ar service 257 | If the 258 | .Ar service 259 | is running then restart it. 260 | If the service does not exist or is not running then zero is returned, 261 | otherwise the result of restarting the service. 262 | .It Fl v 263 | Echo variables DOMAINS, SEARCH and NAMESERVERS so that the subscriber can 264 | configure the resolver easily. 265 | .It Fl V 266 | Same as 267 | .Fl v 268 | except that only the information configured in 269 | .Xr resolvconf.conf 5 270 | is set. 271 | .El 272 | .Sh RESOLV.CONF ORDERING 273 | For 274 | .Nm 275 | to work effectively, it has to process the 276 | .Pa resolv.conf 277 | entries in the correct order. 278 | .Nm 279 | first processes keys from the 280 | .Sy key_order 281 | list, then entries without a metric and that match the 282 | .Sy dynamic_order 283 | list, then entries with a metric in order and finally the rest in 284 | the operating systems lexical order. 285 | See 286 | .Xr resolvconf.conf 5 287 | for details on these lists. 288 | .Sh PROTOCOLS 289 | Here are some suggested protocol tags to use for each 290 | .Pa resolv.conf 291 | .Bl -tag -width pattern_opt 292 | .It dhcp 293 | Dynamic Host Configuration Protocol. 294 | .It ppp 295 | Point-to-Point Protocol. 296 | .It ra 297 | IPv6 Router Advertisement. 298 | .It dhcp6 299 | Dynamic Host Configuration Protocol, version 6. 300 | .El 301 | .Sh IMPLEMENTATION NOTES 302 | If a subscriber has the executable bit then it is executed otherwise it is 303 | assumed to be a shell script and sourced into the current environment in a 304 | subshell. 305 | This is done so that subscribers can remain fast, but are also not limited 306 | to the shell language. 307 | .Pp 308 | Portable subscribers should not use anything outside of 309 | .Pa /bin 310 | and 311 | .Pa /sbin 312 | because 313 | .Pa /usr 314 | and others may not be available when booting. 315 | Also, it would be unwise to assume any shell specific features. 316 | .Sh ENVIRONMENT 317 | .Bl -ohang 318 | .It Va IF_METRIC 319 | If the 320 | .Fl m 321 | option is not present then we use 322 | .Va IF_METRIC 323 | for the metric. 324 | .It Va IF_PRIVATE 325 | Marks the 326 | .Pa resolv.conf 327 | as private. 328 | .It Va IF_NOSEARCH 329 | Marks the 330 | .Pa resolv.conf 331 | as non-searchable. 332 | .It Va IF_EXCLUSIVE 333 | Marks the 334 | .Pa resolv.conf 335 | as exclusive. 336 | .El 337 | .Sh FILES 338 | .Bl -ohang 339 | .It Pa /etc/resolv.conf.bak 340 | Backup file of the original resolv.conf. 341 | .It Pa @SYSCONFDIR@/resolvconf.conf 342 | Configuration file for 343 | .Nm . 344 | .It Pa @LIBEXECDIR@ 345 | Directory of subscribers which are run every time 346 | .Nm 347 | adds, deletes or updates. 348 | .It Pa @LIBEXECDIR@/libc.d 349 | Directory of subscribers which are run after the libc subscriber is run. 350 | .It Pa @VARDIR@ 351 | State directory for 352 | .Nm . 353 | .El 354 | .Sh NOTES 355 | Domain labels are assumed to be in ASCII and are converted to lower case 356 | to avoid duplicate zones when given differing case from different sources. 357 | .Pp 358 | When running a local resolver other than libc, you will need to configure it 359 | to include files that 360 | .Nm 361 | will generate. 362 | You should consult 363 | .Xr resolvconf.conf 5 364 | for instructions on how to configure your resolver. 365 | .Sh SEE ALSO 366 | .Xr resolver 3 , 367 | .Xr stdin 4 , 368 | .Xr resolv.conf 5 , 369 | .Xr resolvconf.conf 5 370 | .Sh HISTORY 371 | This implementation of 372 | .Nm 373 | is called openresolv and is fully command line compatible with Debian's 374 | resolvconf, as written by Thomas Hood. 375 | .Sh AUTHORS 376 | .An Roy Marples Aq Mt roy@marples.name 377 | .Sh BUGS 378 | Please report them to 379 | .Lk http://roy.marples.name/projects/openresolv 380 | -------------------------------------------------------------------------------- /resolvconf.conf.5.in: -------------------------------------------------------------------------------- 1 | .\" Copyright (c) 2009-2025 Roy Marples 2 | .\" All rights reserved 3 | .\" 4 | .\" Redistribution and use in source and binary forms, with or without 5 | .\" modification, are permitted provided that the following conditions 6 | .\" are met: 7 | .\" 1. Redistributions of source code must retain the above copyright 8 | .\" notice, this list of conditions and the following disclaimer. 9 | .\" 2. Redistributions in binary form must reproduce the above copyright 10 | .\" notice, this list of conditions and the following disclaimer in the 11 | .\" documentation and/or other materials provided with the distribution. 12 | .\" 13 | .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14 | .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | .\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 17 | .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 | .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 | .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 | .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 | .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 | .\" SUCH DAMAGE. 24 | .\" 25 | .Dd May 15, 2025 26 | .Dt RESOLVCONF.CONF 5 27 | .Os 28 | .Sh NAME 29 | .Nm resolvconf.conf 30 | .Nd resolvconf configuration file 31 | .Sh DESCRIPTION 32 | .Nm 33 | is the configuration file for 34 | .Xr resolvconf 8 . 35 | The 36 | .Nm 37 | file is a shell script that is sourced by 38 | .Xr resolvconf 8 , 39 | meaning that 40 | .Nm 41 | must contain valid shell commands. 42 | Listed below are the standard 43 | .Nm 44 | variables that may be set. 45 | If the values contain whitespace, wildcards or other special shell characters, 46 | ensure they are quoted and escaped correctly. 47 | See the 48 | .Sy replace 49 | variable for an example on quoting. 50 | .Pp 51 | After updating this file, you may wish to run 52 | .Nm resolvconf -u 53 | to apply the new configuration. 54 | .Pp 55 | When a dynamically generated list is appended or prepended to, the whole 56 | is made unique where left-most wins. 57 | .Sh RESOLVCONF OPTIONS 58 | .Bl -tag -width indent 59 | .It Sy resolvconf 60 | Set to NO to disable 61 | .Nm resolvconf 62 | from running any subscribers. 63 | Defaults to YES. 64 | .It Sy allow_keys 65 | If set, only these keys will be processed. 66 | .It Sy deny_keys 67 | If set, these keys will not be processed. 68 | .It Sy exclude 69 | Is a space separated list of key/value pairs to match. 70 | If all key/value pairs in one element can be found in the file, 71 | then the whole file will be excluded from processing. 72 | The syntax is this: 73 | .Va $keyword Ns / Ns Va $match Ns Op / Ns Va $keyword Ns / Ns Va $match 74 | .Pp 75 | For example given this configuration: 76 | .Bd -literal -compact -offset indent 77 | exclude="search/foo*/nameserver/1.2.3.4 search/bar.org" 78 | .Ed 79 | .Pp 80 | Then any resolv.conf with both a search option starting with foo with a nameserver of 1.2.3.4 81 | OR a search option of bar.org would be excluded. 82 | .It Sy key_order 83 | These keys will always be processed first. 84 | If unset, defaults to the following: 85 | .Bd -literal -compact -offset indent 86 | lo lo[0-9]* 87 | .Ed 88 | .It Sy dynamic_order 89 | These keys will be processed next, unless they have a metric. 90 | If unset, defaults to the following: 91 | .Bd -literal -compact -offset indent 92 | tap[0-9]* tun[0-9]* vpn vpn[0-9]* wg[0-9]* ppp[0-9]* ippp[0-9]* 93 | .Ed 94 | .It Sy inclusive_keys 95 | Ignore any exclusive marking for these keys. 96 | This is handy when 3rd party integrations force the 97 | .Nm resolvconf -x 98 | option and you want to disable it easily. 99 | .It Sy local_nameservers 100 | If unset, defaults to the following: 101 | .Bd -literal -compact -offset indent 102 | 127.* 0.0.0.0 255.255.255.255 ::1 103 | .Ed 104 | .It Sy search_domains 105 | Prepend search domains to the dynamically generated list. 106 | .It Sy search_domains_append 107 | Append search domains to the dynamically generated list. 108 | .It Sy domain_blacklist 109 | A list of domains to be removed from consideration. 110 | To remove a domain, you can use foo.* 111 | To remove a sub domain, you can use *.bar 112 | .It Sy name_servers 113 | Prepend name servers to the dynamically generated list. 114 | You should set this to 127.0.0.1 if you use a local name server other than 115 | libc. 116 | .It Sy name_servers_append 117 | Append name servers to the dynamically generated list. 118 | .It Sy name_server_blacklist 119 | A list of name servers to be removed from consideration. 120 | The default is 0.0.0.0 as some faulty routers send it via DHCP. 121 | To remove a block, you can use 192.168.* 122 | .It Sy private_keys 123 | These keys name servers will only be queried for the domains listed 124 | in their resolv.conf. 125 | Useful for VPN domains. 126 | Setting 127 | .Sy private_keys Ns ="*" 128 | will stop the forwarding of the root zone and allows the local resolver to 129 | recursively query the root servers directly. 130 | Requires a local nameserver other than libc. 131 | This is equivalent to the 132 | .Nm resolvconf -p 133 | option. 134 | .It Sy nosearch_keys 135 | These keys domains/search won't be added to the global search list 136 | in 137 | .Pa /etc/resolv.conf . 138 | .It Sy public_keys 139 | Force these keys to be public, overriding the private and nosearch 140 | markings. 141 | This is handy when 3rd party integrations force the 142 | .Nm resolvconf -p 143 | option and you want to disable it easily. 144 | .It Sy replace 145 | Is a space separated list of replacement keywords. 146 | The syntax is this: 147 | .Va $keyword Ns / Ns Va $match Ns / Ns Va $replacement 148 | .Pp 149 | Example, given this resolv.conf: 150 | .Bd -literal -compact -offset indent 151 | domain foo.org 152 | search foo.org dead.beef 153 | nameserver 1.2.3.4 154 | nameserver 2.3.4.5 155 | .Ed 156 | .Pp 157 | and this configuration: 158 | .Bd -literal -compact -offset indent 159 | replace="search/foo*/bar.com" 160 | replace="$replace nameserver/1.2.3.4/5.6.7.8" 161 | replace="$replace nameserver/2.3.4.5/" 162 | .Ed 163 | .Pp 164 | you would get this resolv.conf instead: 165 | .Bd -literal -compact -offset indent 166 | domain foo.org 167 | search bar.com 168 | nameserver 5.6.7.8 169 | .Ed 170 | .Pp 171 | .It Sy replace_sub 172 | Works the same way as 173 | .Sy replace 174 | except it works on each space separated value rather than the whole line, 175 | so it's useful for the replacing a single domain within the search directive. 176 | Using the same example resolv.conf and changing 177 | .Sy replace 178 | to 179 | .Sy replace_sub , 180 | you would get this resolv.conf instead: 181 | .Bd -literal -compact -offset indent 182 | domain foo.org 183 | search bar.com dead.beef 184 | nameserver 5.6.7.8 185 | .Ed 186 | .Pp 187 | .It Sy state_dir 188 | Override the default state directory of 189 | .Pa @VARDIR@ . 190 | This should not be changed once 191 | .Nm resolvconf 192 | is in use unless the old directory is copied to the new one. 193 | .El 194 | .Sh LIBC OPTIONS 195 | The following variables affect 196 | .Xr resolv.conf 5 197 | directly: 198 | .Bl -tag -width indent 199 | .It Sy resolv_conf 200 | Defaults to 201 | .Pa /etc/resolv.conf 202 | if not set. 203 | Set to 204 | .Pa /dev/null 205 | to stop 206 | .Xr resolvconf 8 207 | from changing it. 208 | .It Sy resolv_conf_options 209 | A list of libc resolver options, as specified in 210 | .Xr resolv.conf 5 . 211 | .It Sy resolv_conf_passthrough 212 | When set to YES the latest resolv.conf is written to 213 | .Sy resolv_conf 214 | without any alteration. 215 | When set to /dev/null or NULL, 216 | .Sy resolv_conf_local_only 217 | is defaulted to NO, 218 | .Sy local_nameservers 219 | is unset unless overridden and only the information set in 220 | .Nm 221 | is written to 222 | .Sy resolv_conf . 223 | .It Sy resolv_conf_restore 224 | When set to YES and 225 | and an empty 226 | .Pa resolv.conf 227 | would be written, restore 228 | .Pa resolv.conf.bak 229 | instead if it exists. 230 | Defaults to YES if not set. 231 | .It Sy resolv_conf_sortlist 232 | A libc resolver sortlist, as specified in 233 | .Xr resolv.conf 5 . 234 | .It Sy resolv_conf_local_only 235 | If a local name server is configured then the default is just to specify that 236 | and ignore all other entries as they will be configured for the local 237 | name server. 238 | Set this to NO to also list non-local nameservers. 239 | This will give you working DNS even if the local nameserver stops functioning 240 | at the expense of duplicated server queries. 241 | .It Sy append_nameservers 242 | Append name servers to the dynamically generated list. 243 | .It Sy prepend_nameservers 244 | Prepend name servers to the dynamically generated list. 245 | .It Sy append_search 246 | Append search domains to the dynamically generated list. 247 | .It Sy prepend_search 248 | Prepend search domains to the dynamically generated list. 249 | .It Sy resolv_conf_mv 250 | Defaults to NO. 251 | Defines if 252 | .Pa /etc/resolv.conf 253 | is updated by writing to a temporary file and then moving it 254 | vs writing directly to it. 255 | .El 256 | .Sh SUBSCRIBER OPTIONS 257 | openresolv ships with subscribers for the name servers 258 | .Xr dnsmasq 8 , 259 | .Xr named 8 , 260 | .Xr pdnsd 8 , 261 | .Xr pdns_recursor 1 , 262 | and 263 | .Xr unbound 8 . 264 | Each subscriber can create configuration files which should be included in 265 | the subscribers main configuration file. 266 | .Pp 267 | To disable a subscriber, simply set its name to NO. 268 | If the subscriber name has a dash in it, then replace it with an underscore. 269 | For example, to disable the libc subscriber you would set: 270 | .Bd -literal -compact -offset indent 271 | libc=NO 272 | .Ed 273 | .Bl -tag -width indent 274 | .It Sy dnsmasq_conf 275 | This file tells dnsmasq which name servers to use for specific domains. 276 | .It Sy dnsmasq_resolv 277 | This file tells dnsmasq which name servers to use for global lookups. 278 | .Pp 279 | Example resolvconf.conf for dnsmasq: 280 | .Bd -literal -compact -offset indent 281 | name_servers=127.0.0.1 282 | dnsmasq_conf=/etc/dnsmasq-conf.conf 283 | dnsmasq_resolv=/etc/dnsmasq-resolv.conf 284 | .Ed 285 | .Pp 286 | Example dnsmasq.conf: 287 | .Bd -literal -compact -offset indent 288 | listen-address=127.0.0.1 289 | # If dnsmasq is compiled for DBus then we can take 290 | # advantage of not having to restart dnsmasq. 291 | enable-dbus 292 | conf-file=/etc/dnsmasq-conf.conf 293 | resolv-file=/etc/dnsmasq-resolv.conf 294 | .Ed 295 | .It Sy named_options 296 | Include this file in the named options block. 297 | This file tells named which name servers to use for global lookups. 298 | .It Sy named_zones 299 | Include this file in the named global scope, after the options block. 300 | This file tells named which name servers to use for specific domains. 301 | .Pp 302 | Example resolvconf.conf for named: 303 | .Bd -literal -compact -offset indent 304 | name_servers=127.0.0.1 305 | named_options=/etc/named-options.conf 306 | named_zones=/etc/named-zones.conf 307 | .Ed 308 | .Pp 309 | Example named.conf: 310 | .Bd -literal -compact -offset indent 311 | options { 312 | listen-on { 127.0.0.1; }; 313 | include "/etc/named-options.conf"; 314 | }; 315 | 316 | include "/etc/named-zones.conf"; 317 | .Ed 318 | .It Sy pdnsd_conf 319 | This is the main pdnsd configuration file which we modify to add our 320 | forward domains to. 321 | If this variable is not set then we rely on the pdnsd configuration file 322 | setup to read 323 | .Pa pdnsd_resolv 324 | as documented below. 325 | .It Sy pdnsd_resolv 326 | This file tells pdnsd about global name servers. 327 | If this variable is not set then it's written to 328 | .Pa pdnsd_conf . 329 | .Pp 330 | Example resolvconf.conf for pdnsd: 331 | .Bd -literal -compact -offset indent 332 | name_servers=127.0.0.1 333 | pdnsd_conf=/etc/pdnsd.conf 334 | # pdnsd_resolv=/etc/pdnsd-resolv.conf 335 | .Ed 336 | .Pp 337 | Example pdnsd.conf: 338 | .Bd -literal -compact -offset indent 339 | global { 340 | server_ip = 127.0.0.1; 341 | status_ctl = on; 342 | } 343 | server { 344 | # A server definition is required, even if empty. 345 | label="empty"; 346 | proxy_only=on; 347 | # file="/etc/pdnsd-resolv.conf"; 348 | } 349 | .Ed 350 | .It Sy pdns_zones 351 | This file tells pdns_recursor about specific and global name servers. 352 | .Pp 353 | Example resolvconf.conf for pdns_recursor: 354 | .Bd -literal -compact -offset indent 355 | name_servers=127.0.0.1 356 | pdns_zones=/etc/pdns/recursor-zones.conf 357 | .Ed 358 | .Pp 359 | Example recursor.conf: 360 | .Bd -literal -compact -offset indent 361 | allow-from=127.0.0.0/8, ::1/128 362 | forward-zones-file=/etc/pdns/recursor-zones.conf 363 | .Ed 364 | .It Sy resolvectl 365 | When set to YES, 366 | .Xr resolvectl 1 367 | will be used to write per interface entries from 368 | .Xr resolvconf 8 369 | to 370 | .Xr systemd-resolved 8 . 371 | A warning is emitted for any entry that cannot be matched to an 372 | interface. 373 | .Pp 374 | This subscriber should only be used if your systemd-resolved does 375 | not support DNS delegates and you need private or non searchable 376 | .Xr resolvconf 8 377 | entries, or you're really beholden to seeing DNS setup per interface via 378 | .Xr resolvectl 1 . 379 | The systemd-resolved subscriber documented below is the better option. 380 | .Pp 381 | Example resolvconf.conf for resolvectl: 382 | .Bd -literal -compact -offset indent 383 | # Keep /etc/resolv.conf as systemd-resolved wants it 384 | libc=NO 385 | resolvectl=YES 386 | .Ed 387 | .It Sy systemd_resolved 388 | When set to YES, global DNS will be written to the 389 | .Sy systemd_resolved_conf 390 | configuration file and DNS delegates will be written to the 391 | .Sy systemd_delegate_dir 392 | directory. 393 | .It Sy systemd_resolved_conf 394 | Defaults to 395 | .Pa /run/systemd/resolved.conf.d/60-resolvconf.conf . 396 | .It Sy systemd_delegate_dir 397 | Defaults to 398 | .Pa /run/systemd/dns-delegate.d . 399 | .Pp 400 | Example resolvconf.conf for systemd-resolved: 401 | .Bd -literal -compact -offset indent 402 | # Keep /etc/resolv.conf as systemd-resolved wants it 403 | libc=NO 404 | systemd_resolved=YES 405 | .Ed 406 | .It Sy unbound_conf 407 | This file tells unbound about specific and global name servers. 408 | .It Sy unbound_insecure 409 | When set to YES, unbound marks the domains as insecure, thus ignoring DNSSEC. 410 | .It Sy unbound_private 411 | When set to YES, unbound marks the domains as private, allowing it and its subdomains to contain private addresses. 412 | .It Sy unbound_forward_zone_options 413 | Options appended to each forward zone. 414 | Each option should be separated by an embedded new line. 415 | .Pp 416 | Example resolvconf.conf for unbound: 417 | .Bd -literal -compact -offset indent 418 | name_servers=127.0.0.1 419 | unbound_conf=/etc/unbound-resolvconf.conf 420 | .Ed 421 | .Pp 422 | Example unbound.conf: 423 | .Bd -literal -compact -offset indent 424 | include: /etc/unbound-resolvconf.conf 425 | .Ed 426 | .El 427 | .Sh SUBSCRIBER INTEGRATION 428 | Not all distributions store the files the subscribers need in the same 429 | locations. 430 | For example, named service scripts have been called named, bind and rc.bind 431 | and they could be located in a directory called /etc/rc.d, /etc/init.d or 432 | similar. 433 | Each subscriber attempts to automatically configure itself, but not every 434 | distribution has been catered for. 435 | Also, users could equally want to use a different version from the one 436 | installed by default, such as bind8 and bind9. 437 | To accommodate this, the subscribers have these files in configurable 438 | variables, documented below. 439 | .Bl -tag -width indent 440 | .It Sy dnsmasq_service 441 | Name of the dnsmasq service. 442 | .It Sy dnsmasq_restart 443 | Command to restart the dnsmasq service. 444 | .It Sy dnsmasq_pid 445 | Location of the dnsmasq pidfile. 446 | .It Sy libc_service 447 | Name of the libc service. 448 | .It Sy libc_restart 449 | Command to restart the libc service. 450 | .It Sy named_service 451 | Name of the named service. 452 | .It Sy named_restart 453 | Command to restart the named service. 454 | .It Sy pdnsd_restart 455 | Command to restart the pdnsd service. 456 | .It Sy pdns_service 457 | Command to restart the pdns_recursor service. 458 | .It Sy pdns_restart 459 | Command to restart the pdns_recursor service. 460 | .It Sy unbound_service 461 | Name of the unbound service. 462 | .It Sy unbound_restart 463 | Command to restart the unbound service. 464 | .It Sy unbound_pid 465 | Location of the unbound pidfile. 466 | .El 467 | .Sh SEE ALSO 468 | .Xr sh 1 , 469 | .Xr resolv.conf 5 , 470 | .Xr resolvconf 8 471 | .Sh AUTHORS 472 | .An Roy Marples Aq Mt roy@marples.name 473 | .Sh BUGS 474 | Each distribution is a special snowflake and likes to name the same thing 475 | differently, namely the named service script. 476 | .Pp 477 | Swapping between resolvectl and systemd-resolved subscribers at runtime 478 | is not supported. 479 | Files referenced by systemd_resolved_conf and systemd_delegate_dir 480 | need to be removed by hand. 481 | A reboot is recommended so that stale data is removed. 482 | While you could run them both at the same time, only using one is the 483 | recommended approach. 484 | .Pp 485 | Please report them to 486 | .Lk https://roy.marples.name/projects/openresolv 487 | -------------------------------------------------------------------------------- /resolvconf.in: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Copyright (c) 2007-2025 Roy Marples 3 | # All rights reserved 4 | 5 | # Redistribution and use in source and binary forms, with or without 6 | # modification, are permitted provided that the following conditions 7 | # are met: 8 | # * Redistributions of source code must retain the above copyright 9 | # notice, this list of conditions and the following disclaimer. 10 | # * Redistributions in binary form must reproduce the above 11 | # copyright notice, this list of conditions and the following 12 | # disclaimer in the documentation and/or other materials provided 13 | # with the distribution. 14 | # 15 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 16 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 17 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 18 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 19 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 21 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | 27 | RESOLVCONF="$0" 28 | OPENRESOLV_VERSION="3.17.3" 29 | SYSCONFDIR=@SYSCONFDIR@ 30 | LIBEXECDIR=@LIBEXECDIR@ 31 | VARDIR=@VARDIR@ 32 | RCDIR=@RCDIR@ 33 | RESTARTCMD=@RESTARTCMD@ 34 | 35 | if [ "$1" = "--version" ]; then 36 | echo "openresolv $OPENRESOLV_VERSION" 37 | echo "Copyright (c) 2007-2025 Roy Marples" 38 | exit 0 39 | fi 40 | 41 | # Disregard dhcpcd setting 42 | unset interface_order state_dir 43 | 44 | # If you change this, change the test in VFLAG and libc.in as well 45 | local_nameservers="127.* 0.0.0.0 255.255.255.255 ::1" 46 | 47 | dynamic_order="tap[0-9]* tun[0-9]* vpn vpn[0-9]* wg[0-9]* ppp[0-9]* ippp[0-9]*" 48 | interface_order="lo lo[0-9]*" 49 | name_server_blacklist="0.0.0.0" 50 | 51 | # Poor mans cat 52 | # /usr might not be available 53 | cat() 54 | { 55 | OIFS="$IFS" 56 | IFS='' 57 | if [ -n "$1" ]; then 58 | while read -r line; do 59 | printf "%s\n" "$line" 60 | done < "$1" 61 | else 62 | while read -r line; do 63 | printf "%s\n" "$line" 64 | done 65 | fi 66 | retval=$? 67 | IFS="$OIFS" 68 | return $retval 69 | } 70 | 71 | 72 | # Support original resolvconf configuration layout 73 | # as well as the openresolv config file 74 | if [ -f "$SYSCONFDIR"/resolvconf.conf ]; then 75 | . "$SYSCONFDIR"/resolvconf.conf 76 | [ -n "$state_dir" ] && VARDIR="$state_dir" 77 | elif [ -d "$SYSCONFDIR/resolvconf" ]; then 78 | SYSCONFDIR="$SYSCONFDIR/resolvconf" 79 | if [ -f "$SYSCONFDIR"/interface-order ]; then 80 | interface_order="$(cat "$SYSCONFDIR"/interface-order)" 81 | fi 82 | fi 83 | 84 | KEYDIR="$VARDIR/keys" 85 | METRICDIR="$VARDIR/metrics" 86 | PRIVATEDIR="$VARDIR/private" 87 | NOSEARCHDIR="$VARDIR/nosearch" 88 | EXCLUSIVEDIR="$VARDIR/exclusive" 89 | DEPRECATEDDIR="$VARDIR/deprecated" 90 | LOCKDIR="$VARDIR/lock" 91 | _PWD="$PWD" 92 | 93 | # Compat 94 | if [ ! -d "$KEYDIR" ] && [ -d "$VARDIR/interfaces" ]; then 95 | KEYDIR="$VARDIR/interfaces" 96 | fi 97 | : ${allow_keys:="$allow_interfaces"} 98 | : ${deny_keys:="$deny_interfaces"} 99 | : ${key_order:="$interface_order"} 100 | : ${inclusive_keys:="$inclusive_interfaces"} 101 | : ${exclusive_keys:="$exclusive_interfaces"} 102 | : ${private_keys:="$private_interfaces"} 103 | : ${public_keys:="$public_interfaces"} 104 | 105 | warn() 106 | { 107 | echo "$@" >&2 108 | } 109 | 110 | error_exit() 111 | { 112 | echo "$@" >&2 113 | exit 1 114 | } 115 | 116 | usage() 117 | { 118 | cat <<-EOF 119 | Usage: ${RESOLVCONF##*/} [options] command [argument] 120 | 121 | Inform the system about any DNS updates. 122 | 123 | Commands: 124 | -a \$KEY Add DNS information to the specified key 125 | (DNS supplied via stdin in resolv.conf format) 126 | -C \$PATTERN Deprecate DNS information for matched key 127 | -c \$PATTERN Configure DNS information for matched key 128 | -d \$PATTERN Delete DNS information from the matched key 129 | -h Show this help cruft 130 | -i [\$PATTERN] Show keys that have supplied DNS information 131 | optionally from keys that match the specified 132 | pattern 133 | -l [\$PATTERN] Show DNS information, optionally from keys 134 | that match the specified pattern 135 | -L [\$PATTERN] Same as -l, but adjusted by our config 136 | 137 | -u Run updates from our current DNS information 138 | --version Echo the ${RESOLVCONF##*/} version 139 | 140 | Options: 141 | -f Ignore non existent keys 142 | -m metric Give the added DNS information a metric 143 | -p Mark the resolv.conf as private 144 | -x Mark the resolv.conf as exclusive 145 | 146 | Subscriber and System Init Commands: 147 | -I Init the state dir 148 | -r \$SERVICE Restart the system service 149 | (restarting a non-existent or non-running service 150 | should have no output and return 0) 151 | -R Show the system service restart command 152 | -v [\$PATTERN] echo NEWDOMAIN, NEWSEARCH and NEWNS variables to 153 | the console 154 | -V [\$PATTERN] Same as -v, but only uses configuration in 155 | $SYSCONFDIR/resolvconf.conf 156 | EOF 157 | [ -z "$1" ] && exit 0 158 | echo 159 | error_exit "$@" 160 | } 161 | 162 | public_key() { 163 | key="$1" 164 | 165 | # Allow expansion 166 | cd "$KEYDIR" 167 | 168 | # Public keys override private ones. 169 | for p in $public_keys; do 170 | case "$key" in 171 | "$p"|"$p":*) return 0;; 172 | esac 173 | done 174 | 175 | return 1 176 | } 177 | 178 | private_key() 179 | { 180 | key="$1" 181 | 182 | if public_key "$key"; then 183 | return 1 184 | fi 185 | 186 | if [ -e "$PRIVATEDIR/$key" ]; then 187 | return 0 188 | fi 189 | 190 | for p in $private_keys; do 191 | case "$key" in 192 | "$p"|"$p":*) return 0;; 193 | esac 194 | done 195 | 196 | # Not a private key 197 | return 1 198 | } 199 | 200 | nosearch_key() 201 | { 202 | key="$1" 203 | 204 | if public_key "$key"; then 205 | return 1 206 | fi 207 | 208 | if [ -e "$NOSEARCHDIR/$key" ]; then 209 | return 0 210 | fi 211 | 212 | for p in $nosearch_keys; do 213 | case "$key" in 214 | "$p"|"$p":*) return 0;; 215 | esac 216 | done 217 | 218 | # Not a non searchable key 219 | return 1 220 | } 221 | 222 | exclusive_key() 223 | { 224 | key="$1" 225 | 226 | for x in "$EXCLUSIVEDIR/"*" $key"; do 227 | if [ -f "$x" ]; then 228 | return 0 229 | fi 230 | done 231 | 232 | # Not an exclusive key 233 | return 1 234 | } 235 | 236 | # Quote input so it can be safely used for variable assignment via eval 237 | quote() 238 | { 239 | if [ -z "$1" ]; then 240 | R="''" 241 | else 242 | R= 243 | for W; do 244 | while [ -n "$W" ]; do 245 | case "$W" in 246 | \'*) R="$R\\'"; W=${W#?};; 247 | ?\'*) R="$R\\${W%%\'*}"; W="${W#?}";; 248 | *\'*) R="$R'${W%%\'*}'"; W="'${W#*\'}";; 249 | ?) R="$R\\$W"; W=;; 250 | *) R="$R'$W'"; W=;; 251 | esac 252 | done 253 | done 254 | fi 255 | 256 | printf '%s\n' "$R" 257 | return 0 258 | } 259 | 260 | # Parse resolv.conf's and make variables 261 | # for domain name servers, search name servers and global nameservers 262 | # Important! Each printf here should use the above quote function 263 | # to ensure that user input is quoted for eval. 264 | parse_resolv() 265 | { 266 | domain= 267 | new=true 268 | newns= 269 | ns= 270 | private=false 271 | nosearch=false 272 | search= 273 | 274 | while read -r line; do 275 | value="${line#* }" 276 | case "$line" in 277 | "# resolv.conf from "*) 278 | if ${new}; then 279 | key="${line#\# resolv.conf from *}" 280 | new=false 281 | if nosearch_key "$key"; then 282 | private=true 283 | nosearch=true 284 | elif private_key "$key"; then 285 | private=true 286 | nosearch=false 287 | else 288 | private=false 289 | nosearch=false 290 | fi 291 | fi 292 | ;; 293 | "nameserver "*) 294 | islocal=false 295 | for l in $local_nameservers; do 296 | case "$value" in 297 | $l) 298 | islocal=true 299 | break 300 | ;; 301 | esac 302 | done 303 | if $islocal; then 304 | printf 'LOCALNAMESERVERS="$LOCALNAMESERVERS "%s\n' "$(quote "$value")" 305 | else 306 | ns="$ns${ns:+ }$value" 307 | fi 308 | ;; 309 | "domain "*) 310 | search="$value" 311 | if [ -z "$domain" ]; then 312 | domain="$search" 313 | if ! $nosearch; then 314 | printf 'DOMAIN=%s\n' "$(quote "$domain")" 315 | fi 316 | fi 317 | ;; 318 | "search "*) 319 | search="$value" 320 | ;; 321 | *) 322 | [ -n "$line" ] && continue 323 | if [ -n "$ns" ] && [ -n "$search" ]; then 324 | newns= 325 | for n in $ns; do 326 | newns="$newns${newns:+,}$n" 327 | done 328 | ds= 329 | for d in $search; do 330 | ds="$ds${ds:+ }$d:$newns" 331 | done 332 | printf 'DOMAINS="$DOMAINS "%s\n' "$(quote "$ds")" 333 | fi 334 | if ! $nosearch; then 335 | printf 'SEARCH="$SEARCH "%s\n' "$(quote "$search")" 336 | fi 337 | if ! $private; then 338 | printf 'NAMESERVERS="$NAMESERVERS "%s\n' "$(quote "$ns")" 339 | fi 340 | ns= 341 | search= 342 | new=true 343 | ;; 344 | esac 345 | done 346 | } 347 | 348 | uniqify() 349 | { 350 | result= 351 | while [ -n "$1" ]; do 352 | case " $result " in 353 | *" $1 "*);; 354 | *) result="$result $1";; 355 | esac 356 | shift 357 | done 358 | echo "${result# *}" 359 | } 360 | 361 | dirname() 362 | { 363 | OIFS="$IFS" 364 | IFS=/ 365 | set -- $@ 366 | IFS="$OIFS" 367 | if [ -n "$1" ]; then 368 | printf %s . 369 | else 370 | shift 371 | fi 372 | while [ -n "$2" ]; do 373 | printf "/%s" "$1" 374 | shift 375 | done 376 | printf "\n" 377 | } 378 | 379 | config_mkdirs() 380 | { 381 | for f; do 382 | [ -n "$f" ] || continue 383 | d="$(dirname "$f")" 384 | if [ ! -d "$d" ]; then 385 | mkdir -p "$d" || return $? 386 | fi 387 | done 388 | return 0 389 | } 390 | 391 | # With the advent of alternative init systems, it's possible to have 392 | # more than one installed. So we need to try and guess what one we're 393 | # using unless overridden by configure. 394 | # Note that restarting a service is a last resort - the subscribers 395 | # should make a reasonable attempt to reconfigure the service via some 396 | # method, normally SIGHUP. 397 | detect_init() 398 | { 399 | [ -n "$RESTARTCMD" ] && return 0 400 | 401 | # Detect the running init system. 402 | # As systemd and OpenRC can be installed on top of legacy init 403 | # systems we try to detect them first. 404 | status="@STATUSARG@" 405 | : ${status:=status} 406 | if [ -x /bin/systemctl ] && [ -S /run/systemd/private ]; then 407 | RESTARTCMD=' 408 | if /bin/systemctl --quiet is-active $1.service 409 | then 410 | /bin/systemctl restart $1.service 411 | fi' 412 | elif [ -x /usr/bin/systemctl ] && [ -S /run/systemd/private ]; then 413 | RESTARTCMD=' 414 | if /usr/bin/systemctl --quiet is-active $1.service 415 | then 416 | /usr/bin/systemctl restart $1.service 417 | fi' 418 | elif [ -x /sbin/rc-service ] && 419 | { [ -s /libexec/rc/init.d/softlevel ] || 420 | [ -s /run/openrc/softlevel ]; } 421 | then 422 | RESTARTCMD='/sbin/rc-service -i $1 -- -Ds restart' 423 | elif [ -x /usr/sbin/invoke-rc.d ]; then 424 | RCDIR=/etc/init.d 425 | RESTARTCMD=' 426 | if /usr/sbin/invoke-rc.d --quiet $1 status >/dev/null 2>&1 427 | then 428 | /usr/sbin/invoke-rc.d $1 restart 429 | fi' 430 | elif [ -x /usr/bin/s6-rc ] && [ -x /usr/bin/s6-svc ]; then 431 | RESTARTCMD=' 432 | if s6-rc -a list 2>/dev/null | grep -qFx $1-srv 433 | then 434 | s6-svc -r /run/service/$1-srv 435 | fi' 436 | elif [ -x /sbin/service ]; then 437 | # Old RedHat 438 | RCDIR=/etc/init.d 439 | RESTARTCMD=' 440 | if /sbin/service $1; then 441 | /sbin/service $1 restart 442 | fi' 443 | elif [ -x /usr/sbin/service ]; then 444 | # Could be FreeBSD 445 | RESTARTCMD=" 446 | if /usr/sbin/service \$1 $status >/dev/null 2>&1 447 | then 448 | /usr/sbin/service \$1 restart 449 | fi" 450 | elif [ -x /bin/sv ]; then 451 | RESTARTCMD='/bin/sv status $1 >/dev/null 2>&1 && 452 | /bin/sv try-restart $1' 453 | elif [ -x /usr/bin/sv ]; then 454 | RESTARTCMD='/usr/bin/sv status $1 >/dev/null 2>&1 && 455 | /usr/bin/sv try-restart $1' 456 | elif [ -e /etc/arch-release ] && [ -d /etc/rc.d ]; then 457 | RCDIR=/etc/rc.d 458 | RESTARTCMD=' 459 | if [ -e /var/run/daemons/$1 ] 460 | then 461 | /etc/rc.d/$1 restart 462 | fi' 463 | elif [ -e /etc/slackware-version ] && [ -d /etc/rc.d ]; then 464 | RESTARTCMD=' 465 | if /etc/rc.d/rc.$1 status >/dev/null 2>&1 466 | then 467 | /etc/rc.d/rc.$1 restart 468 | fi' 469 | elif [ -e /etc/rc.d/rc.subr ] && [ -d /etc/rc.d ]; then 470 | # OpenBSD 471 | RESTARTCMD=' 472 | if /etc/rc.d/$1 check >/dev/null 2>&1 473 | then 474 | /etc/rc.d/$1 restart 475 | fi' 476 | elif [ -d /etc/dinit.d ] && command -v dinitctl >/dev/null 2>&1; then 477 | RESTARTCMD='dinitctl --quiet restart --ignore-unstarted $1' 478 | else 479 | for x in /etc/init.d/rc.d /etc/rc.d /etc/init.d; do 480 | [ -d $x ] || continue 481 | RESTARTCMD=" 482 | if $x/\$1 $status >/dev/null 2>&1 483 | then 484 | $x/\$1 restart 485 | fi" 486 | break 487 | done 488 | fi 489 | 490 | if [ -z "$RESTARTCMD" ]; then 491 | if [ "$_NOINIT_WARNED" != true ]; then 492 | warn "could not detect a useable init system" 493 | _NOINIT_WARNED=true 494 | fi 495 | return 1 496 | fi 497 | _NOINIT_WARNED= 498 | return 0 499 | } 500 | 501 | echo_resolv() 502 | { 503 | OIFS="$IFS" 504 | 505 | [ -n "$1" ] && [ -f "$KEYDIR/$1" ] || return 1 506 | echo "# resolv.conf from $1" 507 | # Our variable maker works of the fact each resolv.conf per key 508 | # is separated by blank lines. 509 | # So we remove them when echoing them. 510 | while read -r line; do 511 | IFS="$OIFS" 512 | if [ -n "$line" ]; then 513 | # We need to set IFS here to preserve any whitespace 514 | IFS='' 515 | printf "%s\n" "$line" 516 | fi 517 | done < "$KEYDIR/$1" 518 | IFS="$OIFS" 519 | } 520 | 521 | deprecated_key() 522 | { 523 | [ -d "$DEPRECATEDDIR" ] || return 1 524 | 525 | cd "$DEPRECATEDDIR" 526 | for da; do 527 | for daf in *; do 528 | [ -f "$daf" ] || continue 529 | case "$da" in 530 | $daf) return 0;; 531 | esac 532 | done 533 | done 534 | return 1 535 | } 536 | 537 | match() 538 | { 539 | match="$1" 540 | file="$2" 541 | retval=1 542 | count=0 543 | 544 | while read -r keyword value; do 545 | new_match= 546 | for om in $match; do 547 | m="$om" 548 | keep= 549 | while [ -n "$m" ]; do 550 | k="${m%%/*}" 551 | r="${m#*/}" 552 | f="${r%%/*}" 553 | r="${r#*/}" 554 | # If the length of m is the same as k/f then 555 | # we know that we are done 556 | if [ ${#m} = $((${#k} + 1 + ${#f})) ]; then 557 | r= 558 | fi 559 | m="$r" 560 | matched=false 561 | case "$keyword" in 562 | $k) 563 | case "$value" in 564 | $f) 565 | matched=true 566 | ;; 567 | esac 568 | ;; 569 | esac 570 | if ! $matched; then 571 | keep="$keep${keep:+/}$k/$f" 572 | fi 573 | done 574 | if [ -n "$om" ] && [ -z "$keep" ]; then 575 | retval=0 576 | break 2 577 | fi 578 | new_match="${new_match}${new_match:+ }${keep}" 579 | done 580 | match="${new_match}" 581 | done < "$file" 582 | return $retval 583 | } 584 | 585 | list_keys() { 586 | list_cmd="$1" 587 | shift 588 | 589 | [ -d "$KEYDIR" ] || return 0 590 | cd "$KEYDIR" 591 | 592 | [ -n "$1" ] || set -- "*" 593 | list= 594 | retval=0 595 | if [ "$list_cmd" = -i ] || [ "$list_cmd" = -l ]; then 596 | for i in $@; do 597 | if [ ! -f "$i" ]; then 598 | if ! $force && [ "$i" != "*" ]; then 599 | echo "No resolv.conf for key $i" >&2 600 | fi 601 | retval=2 602 | continue 603 | fi 604 | list="$list $i" 605 | done 606 | [ -z "$list" ] || uniqify $list 607 | return $retval 608 | fi 609 | 610 | if [ "$list_cmd" != -I ] && [ "$list_cmd" != -L ]; then 611 | echo "list_keys: unknown command $list_cmd" >&2 612 | return 1 613 | fi 614 | 615 | if [ -d "$EXCLUSIVEDIR" ]; then 616 | cd "$EXCLUSIVEDIR" 617 | for i in $EXCLUSIVEDIR/*; do 618 | if [ -f "$i" ]; then 619 | cd "$KEYDIR" 620 | for ii in $inclusive_keys; do 621 | if [ -f "$ii" ] && [ "${i#* }" = "$ii" ]; then 622 | continue 2 623 | fi 624 | done 625 | list="${i#* }" 626 | break 627 | fi 628 | done 629 | cd "$KEYDIR" 630 | if [ -n "$list" ]; then 631 | for i in $@; do 632 | # list will be one item due to the above 633 | if [ -f "$i" ] && [ "$i" = "$list" ]; then 634 | echo "$i" 635 | return 0 636 | fi 637 | done 638 | return 0 639 | fi 640 | fi 641 | 642 | for i in $key_order; do 643 | for ii in "$i" "$i":* "$i".*; do 644 | [ -f "$ii" ] && list="$list $ii" 645 | done 646 | done 647 | 648 | for i in $dynamic_order; do 649 | for ii in "$i" "$i":* "$i".*; do 650 | if [ -f "$ii" ] && ! [ -e "$METRICDIR/"*" $ii" ] 651 | then 652 | list="$list $ii" 653 | fi 654 | done 655 | done 656 | 657 | # Interfaces have an implicit metric of 0 if not specified. 658 | for i in *; do 659 | if [ -f "$i" ] && ! [ -e "$METRICDIR/"*" $i" ]; then 660 | list="$list $i" 661 | fi 662 | done 663 | 664 | if [ -d "$METRICDIR" ]; then 665 | cd "$METRICDIR" 666 | for i in *; do 667 | [ -f "$i" ] && list="$list ${i#* }" 668 | done 669 | cd "$KEYDIR" 670 | fi 671 | 672 | # Move deprecated keys to the back 673 | active= 674 | deprecated= 675 | for i in $list; do 676 | if deprecated_key "$i"; then 677 | deprecated="$deprecated $i" 678 | else 679 | active="$active $i" 680 | fi 681 | done 682 | list="$active $deprecated" 683 | 684 | retval=0 685 | if [ "$1" != "*" ]; then 686 | cd "$KEYDIR" 687 | matched= 688 | for i in $@; do 689 | if ! [ -f "$i" ]; then 690 | if ! $force; then 691 | echo "No resolv.conf for key $i" >&2 692 | fi 693 | retval=2 694 | continue 695 | fi 696 | for ii in $list; do 697 | if [ "$i" = "$ii" ]; then 698 | matched="$matched${matched:+ }$i" 699 | break 700 | fi 701 | done 702 | done 703 | if [ -z "$matched" ]; then 704 | return $retval 705 | fi 706 | list="$matched" 707 | fi 708 | 709 | allowed= 710 | for i in $(uniqify $list); do 711 | if [ -n "$allow_keys" ]; then 712 | x=false 713 | for ii in $allow_keys; do 714 | if [ "$i" = "$ii" ]; then 715 | x=true 716 | break 717 | fi 718 | done 719 | $x || continue 720 | fi 721 | for ii in $deny_keys; do 722 | if [ "$i" = "$ii" ]; then 723 | continue 2 724 | fi 725 | done 726 | 727 | if [ -n "$exclude" ] && match "$exclude" "$i"; then 728 | continue 729 | fi 730 | allowed="$allowed${allowed:+ }$i" 731 | done 732 | 733 | cd "$KEYDIR" 734 | for i in $exclusive_keys; do 735 | for ii in $allowed; do 736 | if [ "$i" = "$ii" ]; then 737 | echo "$i" 738 | return 739 | fi 740 | done 741 | done 742 | [ -z "$allowed" ] || echo "$allowed" 743 | } 744 | 745 | list_resolv() 746 | { 747 | keys="$(list_keys "$@")" 748 | retval=$? 749 | if [ "$retval" != 0 ]; then 750 | return $retval 751 | fi 752 | for i in $keys; do 753 | echo_resolv "$i" && echo 754 | done 755 | } 756 | 757 | list_private() 758 | { 759 | KEYS= 760 | cd "$KEYDIR" 761 | if [ -z "$1" ]; then 762 | set -- "*" 763 | fi 764 | for i in $@; do 765 | if private_key "$i"; then 766 | KEYS="${KEYS}${KEYS:+ }$i" 767 | fi 768 | done 769 | if [ -n "$KEYS" ]; then 770 | echo "$KEYS" 771 | fi 772 | } 773 | 774 | list_nosearch() 775 | { 776 | 777 | KEYS= 778 | cd "$KEYDIR" 779 | if [ -z "$1" ]; then 780 | set -- "*" 781 | fi 782 | for i in $@; do 783 | if nosearch_key "$i"; then 784 | KEYS="${KEYS}${KEYS:+ }$i" 785 | fi 786 | done 787 | if [ -n "$KEYS" ]; then 788 | echo "$KEYS" 789 | fi 790 | } 791 | 792 | list_exclusive() 793 | { 794 | KEYS= 795 | cd "$KEYDIR" 796 | if [ -z "$1" ]; then 797 | set -- "*" 798 | fi 799 | for i in $@; do 800 | if exclusive_key "$i"; then 801 | KEYS="${KEYS}${KEYS:+ }$i" 802 | fi 803 | done 804 | if [ -n "$KEYS" ]; then 805 | echo "$KEYS" 806 | fi 807 | } 808 | 809 | list_remove() 810 | { 811 | [ -z "$2" ] && return 0 812 | eval list=\"\$$1\" 813 | shift 814 | result= 815 | retval=0 816 | 817 | set -f 818 | for e; do 819 | found=false 820 | for l in $list; do 821 | case "$e" in 822 | $l) found=true;; 823 | esac 824 | $found && break 825 | done 826 | if $found; then 827 | retval=$(($retval + 1)) 828 | else 829 | result="$result $e" 830 | fi 831 | done 832 | set +f 833 | echo "${result# *}" 834 | return $retval 835 | } 836 | 837 | echo_prepend() 838 | { 839 | echo "# Generated by resolvconf" 840 | if [ -n "$search_domains" ]; then 841 | echo "search $search_domains" 842 | fi 843 | for n in $name_servers; do 844 | echo "nameserver $n" 845 | done 846 | echo 847 | } 848 | 849 | echo_append() 850 | { 851 | echo "# Generated by resolvconf" 852 | if [ -n "$search_domains_append" ]; then 853 | echo "search $search_domains_append" 854 | fi 855 | for n in $name_servers_append; do 856 | echo "nameserver $n" 857 | done 858 | echo 859 | } 860 | 861 | tolower() { 862 | # There is no good way of doing this portably in shell :( 863 | # Luckily we are only doing this for domain names which we 864 | # know have to be ASCII. 865 | # Non ASCII domains *should* be translated to ASCII *before* 866 | # we get to this stage. 867 | # We could use echo "$@" | tr '[:upper:]' '[:lower:]' but 868 | # tr is in /usr/bin and may not be available when data is fed 869 | # to resolvconf. 870 | # So it's the cost of a pipe + fork vs this slow loop 871 | # 872 | for word; do 873 | # Check if we have any upper to avoid looping per char 874 | case "$word" in 875 | *[A-Z]*) ;; 876 | *) printf "%s " "$word"; continue;; 877 | esac 878 | 879 | while [ -n "$word" ]; do 880 | # Remove everything except the first character 881 | afterchar="${word#?}" 882 | # Remove the afterchar to get the first character 883 | char="${word%%$afterchar}" 884 | # Assign afterchar back to word for looping 885 | word="$afterchar" 886 | 887 | # Now enforce lowercase a-z 888 | case "$char" in 889 | A) char=a;; 890 | B) char=b;; 891 | C) char=c;; 892 | D) char=d;; 893 | E) char=e;; 894 | F) char=f;; 895 | G) char=g;; 896 | H) char=h;; 897 | I) char=i;; 898 | J) char=j;; 899 | K) char=k;; 900 | L) char=l;; 901 | M) char=m;; 902 | N) char=n;; 903 | O) char=o;; 904 | P) char=p;; 905 | Q) char=q;; 906 | R) char=r;; 907 | S) char=s;; 908 | T) char=t;; 909 | U) char=u;; 910 | V) char=v;; 911 | W) char=w;; 912 | X) char=x;; 913 | Y) char=y;; 914 | Z) char=z;; 915 | esac 916 | printf %s "$char" 917 | done 918 | printf " " 919 | done 920 | printf "\n" 921 | } 922 | 923 | # Strip any trailing dot from each name as a FQDN does not belong 924 | # in resolv.conf(5). 925 | # While DNS is not case sensitive, our labels for building the zones 926 | # are, so ensure it's lower case. 927 | process_domain() 928 | { 929 | for word in $(tolower "$@"); do 930 | printf "%s " "${word%.}" 931 | done 932 | printf "\n" 933 | } 934 | 935 | process_resolv() 936 | { 937 | while read -r keyword value; do 938 | for r in $replace; do 939 | k="${r%%/*}" 940 | r="${r#*/}" 941 | f="${r%%/*}" 942 | r="${r#*/}" 943 | v="${r%%/*}" 944 | case "$keyword" in 945 | $k) 946 | case "$value" in 947 | $f) value="$v";; 948 | esac 949 | ;; 950 | esac 951 | done 952 | val= 953 | for sub in $value; do 954 | for r in $replace_sub; do 955 | k="${r%%/*}" 956 | r="${r#*/}" 957 | f="${r%%/*}" 958 | r="${r#*/}" 959 | v="${r%%/*}" 960 | case "$keyword" in 961 | $k) 962 | case "$sub" in 963 | $f) sub="$v";; 964 | esac 965 | ;; 966 | esac 967 | done 968 | val="$val${val:+ }$sub" 969 | done 970 | case "$keyword" in 971 | \#) 972 | case "$val" in 973 | "resolv.conf from "*) ;; 974 | *) continue;; 975 | esac 976 | ;; 977 | \#*) continue;; 978 | esac 979 | case "$keyword" in 980 | domain|search) val="$(process_domain $val)";; 981 | esac 982 | printf "%s %s\n" "$keyword" "$val" 983 | done 984 | } 985 | 986 | make_vars() 987 | { 988 | # Clear variables 989 | DOMAIN= 990 | DOMAINS= 991 | SEARCH= 992 | NAMESERVERS= 993 | LOCALNAMESERVERS= 994 | 995 | if [ -n "${name_servers}${search_domains}" ]; then 996 | eval "$(echo_prepend | parse_resolv)" 997 | fi 998 | if [ -z "$VFLAG" ]; then 999 | eval "$(list_resolv -L "$@" | process_resolv | parse_resolv)" 1000 | fi 1001 | if [ -n "${name_servers_append}${search_domains_append}" ]; then 1002 | eval "$(echo_append | parse_resolv)" 1003 | fi 1004 | 1005 | # Ensure that we only list each domain once 1006 | newdomains= 1007 | for d in $DOMAINS; do 1008 | dn="${d%%:*}" 1009 | list_remove domain_blacklist "$dn" >/dev/null || continue 1010 | case " $newdomains" in 1011 | *" ${dn}:"*) continue;; 1012 | esac 1013 | newns= 1014 | for nd in $DOMAINS; do 1015 | if [ "$dn" = "${nd%%:*}" ]; then 1016 | ns="${nd#*:}" 1017 | while [ -n "$ns" ]; do 1018 | case ",$newns," in 1019 | *,${ns%%,*},*) ;; 1020 | *) list_remove name_server_blacklist \ 1021 | "${ns%%,*}" >/dev/null \ 1022 | && newns="$newns${newns:+,}${ns%%,*}";; 1023 | esac 1024 | [ "$ns" = "${ns#*,}" ] && break 1025 | ns="${ns#*,}" 1026 | done 1027 | fi 1028 | done 1029 | if [ -n "$newns" ]; then 1030 | newdomains="$newdomains${newdomains:+ }$dn:$newns" 1031 | fi 1032 | done 1033 | 1034 | DOMAIN="$(list_remove domain_blacklist $DOMAIN)" 1035 | SEARCH="$(uniqify $SEARCH)" 1036 | SEARCH="$(list_remove domain_blacklist $SEARCH)" 1037 | NAMESERVERS="$(uniqify $NAMESERVERS)" 1038 | NAMESERVERS="$(list_remove name_server_blacklist $NAMESERVERS)" 1039 | LOCALNAMESERVERS="$(uniqify $LOCALNAMESERVERS)" 1040 | LOCALNAMESERVERS="$(list_remove name_server_blacklist $LOCALNAMESERVERS)" 1041 | 1042 | # Ensure output is quoted for eval 1043 | printf 'DOMAIN=%s\n' "$(quote "$DOMAIN")" 1044 | printf 'SEARCH=%s\n' "$(quote "$SEARCH")" 1045 | printf 'NAMESERVERS=%s\n' "$(quote "$NAMESERVERS")" 1046 | printf 'LOCALNAMESERVERS=%s\n' "$(quote "$LOCALNAMESERVERS")" 1047 | printf 'DOMAINS=%s\n' "$(quote "$newdomains")" 1048 | } 1049 | 1050 | force=false 1051 | LFLAG= 1052 | VFLAG= 1053 | while getopts a:C:c:Dd:fhIiLlm:pRruvVx OPT; do 1054 | case "$OPT" in 1055 | f) force=true;; 1056 | h) usage;; 1057 | m) IF_METRIC="$OPTARG";; 1058 | p) 1059 | if [ "$IF_PRIVATE" = 1 ]; then 1060 | IF_NOSEARCH=1 1061 | else 1062 | IF_PRIVATE=1 1063 | fi 1064 | ;; 1065 | V) 1066 | VFLAG=1 1067 | if [ "$local_nameservers" = \ 1068 | "127.* 0.0.0.0 255.255.255.255 ::1" ] 1069 | then 1070 | local_nameservers= 1071 | fi 1072 | ;; 1073 | x) IF_EXCLUSIVE=1;; 1074 | '?') exit 1;; 1075 | *) 1076 | [ "$OPT" != L ] || LFLAG=1 1077 | cmd="$OPT"; key="$OPTARG";; 1078 | esac 1079 | done 1080 | shift $(($OPTIND - 1)) 1081 | if [ -n "$key" ]; then 1082 | set -- "$key" "$@" 1083 | fi 1084 | 1085 | if [ -z "$cmd" ]; then 1086 | if [ "$IF_PRIVATE" = 1 ]; then 1087 | cmd=p 1088 | elif [ "$IF_EXCLUSIVE" = 1 ]; then 1089 | cmd=x 1090 | fi 1091 | fi 1092 | 1093 | # -D ensures that the listed config file base dirs exist 1094 | if [ "$cmd" = D ]; then 1095 | config_mkdirs "$@" 1096 | exit $? 1097 | fi 1098 | 1099 | # -i lists which keys have a resolv file 1100 | if [ "$cmd" = i ]; then 1101 | # If the -L modifier is given, the list is post-processed 1102 | if [ "$LFLAG" = 1 ]; then 1103 | cmd="L" 1104 | fi 1105 | list_keys "-$cmd" "$@" 1106 | exit $? 1107 | fi 1108 | 1109 | # -l lists our resolv files, optionally for a specific key 1110 | if [ "$cmd" = l ]; then 1111 | list_resolv "-$cmd" "$@" 1112 | exit $? 1113 | fi 1114 | # -L is the same as -l, but post-processed from our config 1115 | if [ "$cmd" = L ]; then 1116 | list_resolv "-$cmd" "$@" | process_resolv 1117 | exit $? 1118 | fi 1119 | 1120 | if [ "$cmd" = p ]; then 1121 | if [ "$IF_NOSEARCH" = 1 ]; then 1122 | list_nosearch "$@" 1123 | else 1124 | list_private "$@" 1125 | fi 1126 | exit $? 1127 | fi 1128 | 1129 | if [ "$cmd" = x ]; then 1130 | list_exclusive "$@" 1131 | exit $? 1132 | fi 1133 | 1134 | # Restart a service or echo the command to restart a service 1135 | if [ "$cmd" = r ] || [ "$cmd" = R ]; then 1136 | detect_init || exit 1 1137 | if [ "$cmd" = r ]; then 1138 | eval "$RESTARTCMD" 1139 | else 1140 | echo "$RESTARTCMD" | 1141 | sed -e '/^$/d' -e 's/^ //g' 1142 | fi 1143 | exit $? 1144 | fi 1145 | 1146 | # Not normally needed, but subscribers should be able to run independently 1147 | if [ "$cmd" = v ] || [ -n "$VFLAG" ]; then 1148 | make_vars "$@" 1149 | exit $? 1150 | fi 1151 | 1152 | # Test that we have valid options 1153 | case "$cmd" in 1154 | a|d|C|c) 1155 | if [ -z "$key" ]; then 1156 | error_exit "Key not specified" 1157 | fi 1158 | ;; 1159 | I|u) ;; 1160 | *) 1161 | if [ -n "$cmd" ] && [ "$cmd" != h ]; then 1162 | error_exit "Unknown option $cmd" 1163 | fi 1164 | usage 1165 | ;; 1166 | esac 1167 | 1168 | if [ "$cmd" = a ]; then 1169 | for x in '/' \\ ' ' '*'; do 1170 | case "$iface" in 1171 | *[$x]*) error_exit "$x not allowed in key name";; 1172 | esac 1173 | done 1174 | for x in '.' '-' '~'; do 1175 | case "$iface" in 1176 | [$x]*) error_exit \ 1177 | "$x not allowed at start of key name";; 1178 | esac 1179 | done 1180 | [ "$cmd" = a ] && [ -t 0 ] && error_exit "No file given via stdin" 1181 | fi 1182 | 1183 | if [ ! -d "$VARDIR" ]; then 1184 | if [ -L "$VARDIR" ]; then 1185 | dir="$(readlink "$VARDIR")" 1186 | # link maybe relative 1187 | cd "${VARDIR%/*}" 1188 | if ! mkdir -m 0755 -p "$dir"; then 1189 | error_exit "Failed to create needed" \ 1190 | "directory $dir" 1191 | fi 1192 | else 1193 | if ! mkdir -m 0755 -p "$VARDIR"; then 1194 | error_exit "Failed to create needed" \ 1195 | "directory $VARDIR" 1196 | fi 1197 | fi 1198 | fi 1199 | 1200 | if [ ! -d "$KEYDIR" ]; then 1201 | mkdir -m 0755 -p "$KEYDIR" || \ 1202 | error_exit "Failed to create needed directory $KEYDIR" 1203 | if [ "$cmd" = d ]; then 1204 | # Provide the same error messages as below 1205 | if ! ${force}; then 1206 | cd "$KEYDIR" 1207 | for i in $@; do 1208 | warn "No resolv.conf for key $i" 1209 | done 1210 | fi 1211 | ${force} 1212 | exit $? 1213 | fi 1214 | fi 1215 | 1216 | # A key was added, changed, deleted or a general update was called. 1217 | # Due to exclusivity we need to ensure that this is an atomic operation. 1218 | # Our subscribers *may* need this as well if the init system is sub par. 1219 | # As such we spinlock at this point as best we can. 1220 | # We don't use flock(1) because it's not widely available and normally resides 1221 | # in /usr which we do our very best to operate without. 1222 | [ -w "$VARDIR" ] || error_exit "Cannot write to $LOCKDIR" 1223 | : ${lock_timeout:=10} 1224 | : ${clear_nopids:=5} 1225 | have_pid=false 1226 | had_pid=false 1227 | while true; do 1228 | if mkdir "$LOCKDIR" 2>/dev/null; then 1229 | trap 'rm -rf "$LOCKDIR";' EXIT 1230 | trap 'rm -rf "$LOCKDIR"; exit 1' INT QUIT ABRT SEGV ALRM TERM 1231 | echo $$ >"$LOCKDIR/pid" 1232 | break 1233 | fi 1234 | pid=$(cat "$LOCKDIR/pid" 2>/dev/null) 1235 | if [ "$pid" -gt 0 ] 2>/dev/null; then 1236 | have_pid=true 1237 | had_pid=true 1238 | else 1239 | have_pid=false 1240 | clear_nopids=$(($clear_nopids - 1)) 1241 | if [ "$clear_nopids" -le 0 ]; then 1242 | warn "not seen a pid, clearing lock directory" 1243 | rm -rf "$LOCKDIR" 1244 | else 1245 | lock_timeout=$(($lock_timeout - 1)) 1246 | sleep 1 1247 | fi 1248 | continue 1249 | fi 1250 | if $have_pid && ! kill -0 "$pid"; then 1251 | warn "clearing stale lock pid $pid" 1252 | rm -rf "$LOCKDIR" 1253 | continue 1254 | fi 1255 | lock_timeout=$(($lock_timeout - 1)) 1256 | if [ "$lock_timeout" -le 0 ]; then 1257 | if $have_pid; then 1258 | error_exit "timed out waiting for lock from pid $pid" 1259 | else 1260 | if $had_pid; then 1261 | error_exit "timed out waiting for lock" \ 1262 | "from some pids" 1263 | else 1264 | error_exit "timed out waiting for lock" 1265 | fi 1266 | fi 1267 | fi 1268 | sleep 1 1269 | done 1270 | unset have_pid had_pid clear_nopids 1271 | 1272 | case "$cmd" in 1273 | a) 1274 | # Read resolv.conf from stdin 1275 | resolv="$(cat)" 1276 | changed=false 1277 | changedfile=false 1278 | # If what we are given matches what we have, then do nothing 1279 | if [ -e "$KEYDIR/$key" ]; then 1280 | if [ "$(echo "$resolv")" != \ 1281 | "$(cat "$KEYDIR/$key")" ] 1282 | then 1283 | changed=true 1284 | changedfile=true 1285 | fi 1286 | else 1287 | changed=true 1288 | changedfile=true 1289 | fi 1290 | 1291 | # Set metric and private before creating the resolv.conf file 1292 | # to ensure that it will have the correct flags 1293 | [ ! -d "$METRICDIR" ] && mkdir "$METRICDIR" 1294 | oldmetric="$METRICDIR/"*" $key" 1295 | newmetric= 1296 | if [ -n "$IF_METRIC" ]; then 1297 | # Pad metric to 6 characters, so 5 is less than 10 1298 | while [ ${#IF_METRIC} -le 6 ]; do 1299 | IF_METRIC="0$IF_METRIC" 1300 | done 1301 | newmetric="$METRICDIR/$IF_METRIC $key" 1302 | fi 1303 | rm -f "$METRICDIR/"*" $key" 1304 | [ "$oldmetric" != "$newmetric" ] && 1305 | [ "$oldmetric" != "$METRICDIR/* $key" ] && 1306 | changed=true 1307 | [ -n "$newmetric" ] && echo " " >"$newmetric" 1308 | 1309 | case "$IF_PRIVATE" in 1310 | [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1) 1311 | if [ ! -d "$PRIVATEDIR" ]; then 1312 | [ -e "$PRIVATEDIR" ] && rm "$PRIVATEDIR" 1313 | mkdir "$PRIVATEDIR" 1314 | fi 1315 | [ -e "$PRIVATEDIR/$key" ] || changed=true 1316 | [ -d "$PRIVATEDIR" ] && echo " " >"$PRIVATEDIR/$key" 1317 | ;; 1318 | *) 1319 | if [ -e "$PRIVATEDIR/$key" ]; then 1320 | rm -f "$PRIVATEDIR/$key" 1321 | changed=true 1322 | fi 1323 | ;; 1324 | esac 1325 | 1326 | case "$IF_NOSEARCH" in 1327 | [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1) 1328 | if [ ! -d "$NOSEARCHDIR" ]; then 1329 | [ -e "$NOSEARCHDIR" ] && rm "$NOSEARCHDIR" 1330 | mkdir "$NOSEARCHDIR" 1331 | fi 1332 | [ -e "$NOSEARCHDIR/$key" ] || changed=true 1333 | [ -d "$NOSEARCHDIR" ] && echo " " >"$NOSEARCHDIR/$key" 1334 | ;; 1335 | *) 1336 | if [ -e "$NOSEARCHDIR/$key" ]; then 1337 | rm -f "$NOSEARCHDIR/$key" 1338 | changed=true 1339 | fi 1340 | ;; 1341 | esac 1342 | set +x 1343 | 1344 | oldexcl= 1345 | for x in "$EXCLUSIVEDIR/"*" $key"; do 1346 | if [ -f "$x" ]; then 1347 | oldexcl="$x" 1348 | break 1349 | fi 1350 | done 1351 | case "$IF_EXCLUSIVE" in 1352 | [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1) 1353 | if [ ! -d "$EXCLUSIVEDIR" ]; then 1354 | [ -e "$EXCLUSIVEDIR" ] && rm "$EXCLUSIVEDIR" 1355 | mkdir "$EXCLUSIVEDIR" 1356 | fi 1357 | cd "$EXCLUSIVEDIR" 1358 | for x in *; do 1359 | [ -f "$x" ] && break 1360 | done 1361 | if [ "${x#* }" != "$key" ]; then 1362 | if [ "$x" = "${x% *}" ]; then 1363 | x=10000000 1364 | else 1365 | x="${x% *}" 1366 | fi 1367 | if [ "$x" = "0000000" ]; then 1368 | warn "exclusive underflow" 1369 | else 1370 | x=$(($x - 1)) 1371 | fi 1372 | if [ -d "$EXCLUSIVEDIR" ]; then 1373 | echo " " >"$EXCLUSIVEDIR/$x $key" 1374 | fi 1375 | changed=true 1376 | fi 1377 | ;; 1378 | *) 1379 | if [ -f "$oldexcl" ]; then 1380 | rm -f "$oldexcl" 1381 | changed=true 1382 | fi 1383 | ;; 1384 | esac 1385 | 1386 | if $changedfile; then 1387 | printf "%s\n" "$resolv" >"$KEYDIR/$key" || exit $? 1388 | elif ! $changed && [ ! -e "$VARDIR"/error ]; then 1389 | exit 0 1390 | fi 1391 | unset changed changedfile oldmetric newmetric x oldexcl 1392 | ;; 1393 | 1394 | d) 1395 | # Delete any existing information about the key 1396 | cd "$KEYDIR" 1397 | changed=false 1398 | for i in $@; do 1399 | if [ -e "$i" ]; then 1400 | changed=true 1401 | elif ! ${force}; then 1402 | warn "No resolv.conf for key $i" 1403 | fi 1404 | rm -f "$i" "$METRICDIR/"*" $i" \ 1405 | "$PRIVATEDIR/$i" \ 1406 | "$EXCLUSIVEDIR/"*" $i" || exit $? 1407 | done 1408 | 1409 | if ! $changed && [ ! -e "$VARDIR"/error ]; then 1410 | # Set the return code based on the forced flag 1411 | $force 1412 | exit $? 1413 | fi 1414 | unset changed i 1415 | ;; 1416 | 1417 | C) 1418 | # Mark key as deprecated 1419 | [ ! -d "$DEPRECATEDDIR" ] && mkdir "$DEPRECATEDDIR" 1420 | cd "$DEPRECATEDDIR" 1421 | changed=false 1422 | for i in $@; do 1423 | if [ ! -e "$i" ]; then 1424 | changed=true 1425 | echo " " >"$i" || exit $? 1426 | fi 1427 | done 1428 | if ! $changed && [ ! -e "$VARDIR"/error ]; then 1429 | exit 0 1430 | fi 1431 | unset changed i 1432 | ;; 1433 | 1434 | c) 1435 | # Mark key as active 1436 | if [ -d "$DEPRECATEDDIR" ]; then 1437 | cd "$DEPRECATEDDIR" 1438 | changed=false 1439 | for i in $@; do 1440 | if [ -e "$i" ]; then 1441 | changed=true 1442 | rm "$i" || exit $? 1443 | fi 1444 | done 1445 | if ! $changed && [ ! -e "$VARDIR"/error ]; then 1446 | exit 0 1447 | fi 1448 | unset changed i 1449 | fi 1450 | ;; 1451 | I) 1452 | # Init the state dir, keeping our lock and key directories only 1453 | for i in "$VARDIR"/*; do 1454 | case "$i" in 1455 | "$LOCKDIR") ;; 1456 | "$KEYDIR") rm -rf "$KEYDIR"/*;; 1457 | *) rm -rf "$i";; 1458 | esac 1459 | done 1460 | ;; 1461 | esac 1462 | 1463 | case "${resolvconf:-YES}" in 1464 | [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1) ;; 1465 | *) exit 0;; 1466 | esac 1467 | 1468 | # Try and detect a suitable init system for our scripts 1469 | detect_init 1470 | export RESTARTCMD RCDIR _NOINIT_WARNED 1471 | 1472 | eval "$(make_vars)" 1473 | export RESOLVCONF DOMAINS SEARCH NAMESERVERS LOCALNAMESERVERS 1474 | : ${list_resolv:=list_resolv -L} 1475 | retval=0 1476 | 1477 | # Run scripts in the same directory resolvconf is run from 1478 | # in case any scripts accidentally dump files in the wrong place. 1479 | cd "$_PWD" 1480 | for script in "$LIBEXECDIR"/*; do 1481 | if [ -f "$script" ]; then 1482 | script_var="${script##*/}" 1483 | while [ "${script_var%%-*}" != "$script_var" ]; do 1484 | script_var="${script_var%%-*}_${script_var#*-}" 1485 | done 1486 | eval script_enabled="\$$script_var" 1487 | case "${script_enabled:-YES}" in 1488 | [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1) ;; 1489 | *) continue;; 1490 | esac 1491 | if [ -x "$script" ]; then 1492 | "$script" "$cmd" "$key" 1493 | else 1494 | (set -- "$cmd" "$key"; . "$script") 1495 | fi 1496 | retval=$(($retval + $?)) 1497 | fi 1498 | done 1499 | if [ "$retval" = 0 ]; then 1500 | rm -f "$VARDIR"/error 1501 | else 1502 | echo "$retval" >"$VARDIR"/error 1503 | fi 1504 | exit $retval 1505 | --------------------------------------------------------------------------------