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