aboutsummaryrefslogtreecommitdiffstats
path: root/resolvconf.in
diff options
context:
space:
mode:
Diffstat (limited to 'resolvconf.in')
-rw-r--r--resolvconf.in421
1 files changed, 421 insertions, 0 deletions
diff --git a/resolvconf.in b/resolvconf.in
new file mode 100644
index 000000000000..c3a1aa331690
--- /dev/null
+++ b/resolvconf.in
@@ -0,0 +1,421 @@
+#!/bin/sh
+# Copyright (c) 2007-2009 Roy Marples
+# All rights reserved
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following
+# disclaimer in the documentation and/or other materials provided
+# with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+RESOLVCONF="$0"
+SYSCONFDIR=@SYSCONFDIR@
+LIBEXECDIR=@LIBEXECDIR@
+VARDIR=@VARDIR@
+# Support original resolvconf configuration layout
+# as well as the openresolv config file
+if [ -f "$SYSCONFDIR"/resolvconf.conf ]; then
+ . "$SYSCONFDIR"/resolvconf.conf
+ [ -n "$state_dir" ] && VARDIR="$state_dir"
+elif [ -d "$SYSCONFDIR/resolvconf" ]; then
+ SYSCONFDIR="$SYSCONFDIR/resolvconf"
+ if [ -f "$SYSCONFDIR"/interface-order ]; then
+ interface_order="$(cat "$SYSCONFDIR"/interface-order)"
+ fi
+fi
+IFACEDIR="$VARDIR/interfaces"
+METRICDIR="$VARDIR/metrics"
+PRIVATEDIR="$VARDIR/private"
+
+: ${dynamic_order:=tap[0-9]* tun[0-9]* vpn vpn[0-9]* ppp[0-9]* ippp[0-9]*}
+: ${interface_order:=lo lo[0-9]*}
+
+error_exit()
+{
+ echo "$*" >&2
+ exit 1
+}
+
+usage()
+{
+ cat <<-EOF
+ Usage: ${RESOLVCONF##*/} [options]
+
+ Inform the system about any DNS updates.
+
+ Options:
+ -a \$INTERFACE Add DNS information to the specified interface
+ (DNS supplied via stdin in resolv.conf format)
+ -m metric Give the added DNS information a metric
+ -p Mark the interface as private
+ -d \$INTERFACE Delete DNS information from the specified interface
+ -f Ignore non existant interfaces
+ -I Init the state dir
+ -u Run updates from our current DNS information
+ -l [\$PATTERN] Show DNS information, optionally from interfaces
+ that match the specified pattern
+ -i [\$PATTERN] Show interfaces that have supplied DNS information
+ optionally from interfaces that match the specified
+ pattern
+ -v [\$PATTERN] echo NEWDOMAIN, NEWSEARCH and NEWNS variables to
+ the console
+ -h Show this help cruft
+ EOF
+ [ -z "$1" ] && exit 0
+ echo
+ error_exit "$*"
+}
+
+echo_resolv()
+{
+ local line=
+ [ -n "$1" -a -e "$IFACEDIR/$1" ] || return 1
+ echo "# resolv.conf from $1"
+ # Our variable maker works of the fact each resolv.conf per interface
+ # is separated by blank lines.
+ # So we remove them when echoing them.
+ while read line; do
+ [ -n "$line" ] && echo "$line"
+ done < "$IFACEDIR/$1"
+ echo
+}
+
+# Parse resolv.conf's and make variables
+# for domain name servers, search name servers and global nameservers
+parse_resolv()
+{
+ local line= ns= ds= search= d= n= newns=
+ local new=true iface= private=false p=
+
+ echo "DOMAINS="
+ echo "SEARCH=\"$search_domains\""
+ # let our subscribers know about global nameservers
+ for n in $name_servers; do
+ case "$n" in
+ 127.*|0.0.0.0|255.255.255.255|::1) :;;
+ *) newns="$newns${newns:+ }$n";;
+ esac
+ done
+ echo "NAMESERVERS=\"$newns\""
+ echo "LOCALNAMESERVERS="
+ newns=
+
+ while read line; do
+ case "$line" in
+ "# resolv.conf from "*)
+ if ${new}; then
+ iface="${line#\# resolv.conf from *}"
+ new=false
+ if [ -e "$PRIVATEDIR/$iface" ]; then
+ private=true
+ else
+ # Allow expansion
+ cd "$IFACEDIR"
+ private=false
+ for p in $private_interfaces; do
+ if [ "$p" = "$iface" ]; then
+ private=true
+ break
+ fi
+ done
+ fi
+ fi
+ ;;
+ "nameserver "*)
+ case "${line#* }" in
+ 127.*|0.0.0.0|255.255.255.255|::1)
+ echo "LOCALNAMESERVERS=\"\$LOCALNAMESERVERS ${line#* }\""
+ continue
+ ;;
+ esac
+ ns="$ns${line#* } "
+ ;;
+ "domain "*|"search "*)
+ search="${line#* }"
+ ;;
+ *)
+ [ -n "$line" ] && continue
+ if [ -n "$ns" -a -n "$search" ]; then
+ newns=
+ for n in $ns; do
+ newns="$newns${newns:+,}$n"
+ done
+ ds=
+ for d in $search; do
+ ds="$ds${ds:+ }$d:$newns"
+ done
+ echo "DOMAINS=\"\$DOMAINS $ds\""
+ fi
+ echo "SEARCH=\"\$SEARCH $search\""
+ if ! $private; then
+ echo "NAMESERVERS=\"\$NAMESERVERS $ns\""
+ fi
+ ns=
+ search=
+ new=true
+ ;;
+ esac
+ done
+}
+
+uniqify()
+{
+ local result=
+ while [ -n "$1" ]; do
+ case " $result " in
+ *" $1 "*);;
+ *) result="$result $1";;
+ esac
+ shift
+ done
+ echo "${result# *}"
+}
+
+list_resolv()
+{
+ [ -d "$IFACEDIR" ] || return 0
+
+ local report=false list= retval=0 cmd="$1"
+ shift
+
+ # If we have an interface ordering list, then use that.
+ # It works by just using pathname expansion in the interface directory.
+ if [ -n "$1" ]; then
+ list="$@"
+ $force || report=true
+ else
+ cd "$IFACEDIR"
+ for i in $interface_order; do
+ [ -e "$i" ] && list="$list $i"
+ done
+ for i in $dynamic_order; do
+ if [ -e "$i" -a ! -e "$METRICDIR/"*" $i" ]; then
+ list="$list $i"
+ fi
+ done
+ if [ -d "$METRICDIR" ]; then
+ cd "$METRICDIR"
+ for i in *; do
+ list="$list ${i#* }"
+ done
+ fi
+ list="$list *"
+ fi
+
+ cd "$IFACEDIR"
+ for i in $(uniqify $list); do
+ # Only list interfaces which we really have
+ if ! [ -e "$i" ]; then
+ if $report; then
+ echo "No resolv.conf for interface $i" >&2
+ retval=$(($retval + 1))
+ fi
+ continue
+ fi
+
+ if [ "$cmd" = i -o "$cmd" = "-i" ]; then
+ printf "$i "
+ else
+ echo_resolv "$i"
+ fi
+ done
+ [ "$cmd" = i -o "$cmd" = "-i" ] && echo
+ return $retval
+}
+
+make_vars()
+{
+ eval "$(list_resolv -l "$@" | parse_resolv)"
+
+ # Ensure that we only list each domain once
+ newdomains=
+ for d in $DOMAINS; do
+ dn="${d%%:*}"
+ case " $newdomains" in
+ *" ${dn}:"*) continue;;
+ esac
+ newdomains="$newdomains${newdomains:+ }$dn:"
+ newns=
+ for nd in $DOMAINS; do
+ if [ "$dn" = "${nd%%:*}" ]; then
+ ns="${nd#*:}"
+ while [ -n "$ns" ]; do
+ case ",$newns," in
+ *,${ns%%,*},*) ;;
+ *) newns="$newns${newns:+,}${ns%%,*}";;
+ esac
+ [ "$ns" = "${ns#*,}" ] && break
+ ns="${ns#*,}"
+ done
+ fi
+ done
+ newdomains="$newdomains$newns"
+ done
+ echo "DOMAINS='$newdomains'"
+ echo "SEARCH='$(uniqify $SEARCH)'"
+ echo "NAMESERVERS='$(uniqify $NAMESERVERS)'"
+ echo "LOCALNAMESERVERS='$(uniqify $LOCALNAMESERVERS)'"
+}
+
+force=false
+while getopts a:d:fhIilm:puv OPT; do
+ case "$OPT" in
+ f) force=true;;
+ h) usage;;
+ m) IF_METRIC="$OPTARG";;
+ p) IF_PRIVATE=1;;
+ '?') ;;
+ *) cmd="$OPT"; iface="$OPTARG";;
+ esac
+done
+shift $(($OPTIND - 1))
+args="$iface${iface:+ }$@"
+
+# -I inits the state dir
+if [ "$cmd" = I ]; then
+ if [ -d "$VARDIR" ]; then
+ rm -rf "$VARDIR"/*
+ fi
+ exit $?
+fi
+
+# -l lists our resolv files, optionally for a specific interface
+if [ "$cmd" = l -o "$cmd" = i ]; then
+ list_resolv "$cmd" "$args"
+ exit $?
+fi
+
+# Not normally needed, but subscribers should be able to run independently
+if [ "$cmd" = v ]; then
+ make_vars "$iface"
+ exit $?
+fi
+
+# Test that we have valid options
+if [ "$cmd" = a -o "$cmd" = d ]; then
+ if [ -z "$iface" ]; then
+ usage "Interface not specified"
+ fi
+elif [ "$cmd" != u ]; then
+ [ -n "$cmd" -a "$cmd" != h ] && usage "Unknown option $cmd"
+ usage
+fi
+if [ "$cmd" = a ]; then
+ for x in '/' \\ ' ' '*'; do
+ case "$iface" in
+ *[$x]*) error_exit "$x not allowed in interface name";;
+ esac
+ done
+ for x in '.' '-' '~'; do
+ case "$iface" in
+ [$x]*) error_exit \
+ "$x not allowed at start of interface name";;
+ esac
+ done
+ [ "$cmd" = a -a -t 0 ] && error_exit "No file given via stdin"
+fi
+
+if [ ! -d "$IFACEDIR" ]; then
+ if [ ! -d "$VARDIR" ]; then
+ if [ -L "$VARDIR" ]; then
+ dir="$(readlink "$VARDIR")"
+ # link maybe relative
+ cd "${VARDIR%/*}"
+ if ! mkdir -m 0755 -p "$dir"; then
+ error_exit "Failed to create needed" \
+ "directory $dir"
+ fi
+ else
+ if ! mkdir -m 0755 -p "$VARDIR"; then
+ error_exit "Failed to create needed" \
+ "directory $VARDIR"
+ fi
+ fi
+ fi
+ mkdir -m 0755 -p "$IFACEDIR" || \
+ error_exit "Failed to create needed directory $IFACEDIR"
+else
+ # Delete any existing information about the interface
+ if [ "$cmd" = d ]; then
+ cd "$IFACEDIR"
+ for i in $args; do
+ if [ "$cmd" = d -a ! -e "$i" ]; then
+ $force && continue
+ error_exit "No resolv.conf for" \
+ "interface $i"
+ fi
+ rm -f "$i" "$METRICDIR/"*" $i" \
+ "$PRIVATEDIR/$i" || exit $?
+ done
+ fi
+fi
+
+if [ "$cmd" = a ]; then
+ # Read resolv.conf from stdin
+ resolv="$(cat)\n"
+ # If what we are given matches what we have, then do nothing
+ if [ -e "$IFACEDIR/$iface" ]; then
+ if [ "$(printf "$resolv")" = \
+ "$(cat "$IFACEDIR/$iface")" ]
+ then
+ exit 0
+ fi
+ rm "$IFACEDIR/$iface"
+ fi
+ printf "$resolv" >"$IFACEDIR/$iface" || exit $?
+ [ ! -d "$METRICDIR" ] && mkdir "$METRICDIR"
+ rm -f "$METRICDIR/"*" $iface"
+ if [ -n "$IF_METRIC" ]; then
+ # Pad metric to 6 characters, so 5 is less than 10
+ while [ ${#IF_METRIC} -le 6 ]; do
+ IF_METRIC="0$IF_METRIC"
+ done
+ echo " " >"$METRICDIR/$IF_METRIC $iface"
+ fi
+ case "$IF_PRIVATE" in
+ [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1)
+ if [ ! -d "$PRIVATEDIR" ]; then
+ [ -e "$PRIVATEDIR" ] && rm "$PRIVATEDIR"
+ mkdir "$PRIVATEDIR"
+ fi
+ [ -d "$PRIVATEDIR" ] && echo " " >"$PRIVATEDIR/$iface"
+ ;;
+ *)
+ if [ -e "$PRIVATEDIR/$iface" ]; then
+ rm -f "$PRIVATEDIR/$iface"
+ fi
+ ;;
+ esac
+fi
+
+eval "$(make_vars)"
+export RESOLVCONF DOMAINS SEARCH NAMESERVERS LOCALNAMESERVERS
+: ${list_resolv:=list_resolv -l}
+retval=0
+for script in "$LIBEXECDIR"/*; do
+ if [ -f "$script" ]; then
+ if [ -x "$script" ]; then
+ "$script" "$cmd" "$iface"
+ else
+ (. "$script" "$cmd" "$iface")
+ fi
+ retval=$(($retval + $?))
+ fi
+done
+exit $retval