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