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