Home | History | Annotate | Line # | Download | only in dist
      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