Re-license as 3 clause BSD as I'm the author. The Gentoo Trustees have been notified...
[openresolv] / resolvconf
1 #!/bin/sh
2 # Copyright 2006 Gentoo Foundation
3 # Copyright 2007 Roy Marples
4 # All rights reserved.
5
6 # Redistribution and use in source and binary forms, with or without
7 # modification, are permitted provided that the following conditions are met:
8
9 #    * Redistributions of source code must retain the above copyright
10 #      notice, this list of conditions and the following disclaimer.
11 #    * Redistributions in binary form must reproduce the above copyright
12 #      notice, this list of conditions and the following disclaimer in the
13 #      documentation and/or other materials provided with the distribution.
14 #    * Neither the name of the above listed copyright holder nor the
15 #      names of its contributors may be used to endorse or promote products
16 #      derived from this software without specific prior written permission.
17 #
18 # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 # NOT 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 OF
27 # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
29 argv0="$0"
30
31 PREFIX=
32 RESOLVCONF="${PREFIX}"/etc/resolvconf
33 UPDATED="${RESOLVCONF}"/update.d
34 VARDIR="${RESOLVCONF}"/run
35 IFACEDIR="${VARDIR}/interfaces"
36
37 error_exit() {
38         echo "$*" >&2
39         exit 1
40 }
41
42 usage() {
43         cat <<-EOF
44         Usage: ${argv0##*/} [options]
45
46         Inform the system about any DNS updates.
47
48         Options:
49           -a \$INTERFACE    Add DNS information to the specified interface
50                            (DNS supplied via stdin in resolv.conf format)
51           -d \$INTERFACE    Delete DNS information from the specified interface
52           -u               Run updates from our current DNS information
53           -l [\$PATTERN]    Show DNS information, optionally from interfaces
54                            that match the specified pattern
55           -i [\$PATTERN]    Show interfaces that have supplied DNS information
56                    optionally from interfaces that match the specified
57                    pattern
58           -v [\$PATTERN]    echo NEWDOMAIN, NEWSEARCH and NEWNS variables to
59                            the console
60           -h               Show this help cruft
61         EOF
62         [ -z "$@" ] && exit 0
63         echo
64         error_exit "$*"
65 }
66
67 echo_resolv() {
68         [ -n "$1" -a -e "${IFACEDIR}/$1" ] || return 1
69         echo "# resolv.conf for interface $1"
70         grep "." "${IFACEDIR}/$1"
71         echo
72 }
73
74 uniqify() {
75     local result=
76     while [ -n "$1" ]; do
77                 case " ${result} " in
78                         *" $1 "*) ;;
79                         *) result="${result} $1" ;;
80                 esac
81                 shift
82         done
83     echo "${result# *}"
84 }
85
86 if [ -n "$1" ]; then
87         CMD="$1"
88         shift
89 fi
90 if [ -n "$1" ]; then
91         IFACE="$1"
92         shift
93 fi
94
95 # -l is a Gentoo option that lists our resolv files
96 # optionally for a specific interface
97 if [ "x${CMD}" = "x-l" -o "x${CMD}" = "x-i" ]; then
98         [ -d "${IFACEDIR}" ] || exit 0
99         
100         # If we have an interface ordering list, then use that.
101         # It works by just using pathname expansion in the interface directory.
102         if [ -n "${IFACE}" ]; then
103                 LIST="${IFACE} $@"
104         elif [ -r "${RESOLVCONF}"/interface-order ]; then
105                 LIST="$(cat "${RESOLVCONF}"/interface-order)"
106         fi
107
108         # If we don't have a list then prefer lo, tunnels, ppp
109         # and then anything else.
110         if [ -z "${LIST}" ]; then
111                 LIST="lo lo[0-9]* tap[0-9]* tun[0-9]* vpn vpn[0-9]* ppp[0-9]* ippp[0-9]* *"
112         fi
113
114         cd "${IFACEDIR}"
115         for IFACE in $(uniqify ${LIST}); do
116                 # Only list interfaces which we really have
117                 [ -e "${IFACE}" ] || continue
118                 
119                 if [ "x${CMD}" = "x-i" ]; then
120                         printf "${IFACE} "
121                 else
122                         echo_resolv "${IFACE}"
123                 fi
124         done
125         [ "x${CMD}" = "x-i" ] && echo
126         exit 0
127 fi
128
129 if [ "x${CMD}" = "x-v" ]; then
130         NS=
131         DOMAIN=
132         SEARCH=
133         NEWSEARCH=
134         NEWNS=
135         NEWDOMAIN=
136         LINES="$("${argv0}" -l "${IFACE}" | sed -e "s/'/'\\\\''/g" -e "s/^/'/g" -e "s/$/'/g")"
137         eval set -- ${LINES}
138         for LINE in "$@"; do
139                 case "${LINE}" in
140                         "nameserver "*)
141                                 case "${LINE#* }" in
142                                         127.*) continue ;;
143                                 esac
144                                 NS="${NS}${LINE#* } "
145                                 ;;
146                         "domain "*)
147                                 [ -z "${SEARCH}" ] && DOMAIN="${LINE#* }"
148                                 ;;
149                         "search "*)
150                                 SEARCH="${LINE#* }"
151                                 DOMAIN=
152                                 ;;
153                         *)
154                                 if [ -z "${LINE}" ]; then
155                                         for N in ${NS}; do
156                                                 if [ -n "${DOMAIN}" ]; then
157                                                         NEWDOMAIN="${NEWDOMAIN} ${DOMAIN},${N}"
158                                                 elif [ -n "${SEARCH}" ]; then
159                                                         for S in ${SEARCH}; do
160                                                                 NEWSEARCH="${NEWSEARCH} ${S},${N}"
161                                                         done
162                                                 else
163                                                         NEWNS="${NEWNS} ${N}"
164                                                 fi
165                                         done
166                                         NS=
167                                         DOMAIN=
168                                         SEARCH=
169                                 fi
170                                 ;;
171                 esac
172         done
173
174         # Prefer DOMAIN nameservers over SEARCH nameservers
175         # if we are supplied both.
176         NEWDOMAIN="$(uniqify ${NEWDOMAIN})"
177         NEWSEARCH="$(uniqify ${NEWSEARCH})"
178         NEWNS="$(uniqify ${NEWNS})"
179         for S in ${NEWSEARCH}; do
180                 for DN in ${NEWDOMAIN}; do
181                         if [ "${S%,*}" = "${DN%,*}" ]; then
182                                 NEWSEARCH="$(echo "${NEWSEARCH}" | sed -e "s/${S}/${DN}/g")"
183                                 NEWDOMAIN="$(echo "${NEWDOMAIN}" | sed -e "s/${DN}//g")"
184                                 break
185                         fi
186                 done
187         done
188
189         echo "NEWDOMAIN='${NEWDOMAIN}'"
190         echo "NEWSEARCH='${NEWSEARCH}'"
191         echo "NEWNS='${NEWNS}'"
192         exit 0
193 fi
194
195 # Test that we have valid options
196 if [ "x${CMD}" = "x-a" -o "x${CMD}" = "x-d" ]; then
197         if [ -z "${IFACE}" ]; then
198                 usage "Interface not specified"
199         fi
200 elif [ "x${CMD}" != "x-u" ]; then
201         [ -n "x${CMD}" -a "x${CMD}" != "x-h" ] && usage "Unknown option ${CMD}"
202         usage
203 fi
204 if [ "x${CMD}" = "x-a" ]; then
205         for x in '/' \\ ' ' '*'; do
206                 case "${IFACE}" in
207                         *[${x}]*) error_exit "${x} not allowed in interface name" ;;
208                 esac
209         done
210         for x in '.' '-' '~'; do
211                 case "${IFACE}" in
212                         [${x}]*) error_exit "${x} not allowed at start of interface name" ;;
213                 esac
214         done
215         [ "x${CMD}" = "x-a" -a -t 0 ] && error_exit "No file given via stdin"
216         IFACERESOLV="${IFACEDIR}/${IFACE}"
217 fi
218
219 # Ensure that libdir exists
220 if [ ! -d "${IFACEDIR}" ]; then
221         if [ ! -d "${VARDIR}" ]; then
222                 if [ -L "${VARDIR}" ]; then
223                         DIR="$(readlink "${VARDIR}")"
224                         # Change to /etc as link maybe relative
225                         cd "${VARDIR%/*}"
226                         if ! mkdir -m 0755 -p "${DIR}"; then
227                                 error_exit "Failed to create needed directory ${DIR}"
228                         fi
229                 else
230                         if ! mkdir -m 0755 -p "${VARDIR}"; then
231                                 error_exit "Failed to create needed directory ${VARDIR}"
232                         fi
233                 fi
234         fi
235         mkdir -m 0755 -p "${IFACEDIR}" || \
236                 error_exit "Failed to create needed directory ${IFACEDIR}"
237 else
238         # Delete any existing information about the interface
239         if [ "x${CMD}" = "x-a" -o "x${CMD}" = "x-d" ]; then
240                 cd "${IFACEDIR}"
241                 for iface in ${IFACE}; do
242                         rm -f "${iface}" || exit $?
243                 done
244         fi
245 fi
246
247 if [ "x${CMD}" = "x-a" ]; then
248         # Create our resolv.conf file
249         cat >"${IFACEDIR}"/"${IFACE}" || exit $?
250 fi
251
252 retval=0
253 for x in "${UPDATED}"/*; do
254         if [ -e "${x}" ]; then
255                 "${x}" "${CMD}" "${IFACE}"
256                 retval=$((${retval} + $?))
257         fi
258 done
259
260 exit ${retval}
261
262 # vim: set ts=4 :