3 # Copyright (c) 2013 Dag-Erling Smørgrav
6 # Redistribution and use in source and binary forms, with or without
7 # modification, are permitted provided that the following conditions
9 # 1. Redistributions of source code must retain the above copyright
10 # notice, this list of conditions and the following disclaimer.
11 # 2. 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.
15 # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 # Configuration variables
53 self=$(basename $(realpath "$0"))
54 bkext=$(date "+%Y%m%d.%H%M%S")
57 # Set default values for unset configuration variables.
61 : ${workdir:=/var/unbound}
62 : ${confdir:=${workdir}/conf.d}
63 : ${unbound_conf:=${workdir}/unbound.conf}
64 : ${forward_conf:=${workdir}/forward.conf}
65 : ${lanzones_conf:=${workdir}/lan-zones.conf}
66 : ${control_conf:=${workdir}/control.conf}
67 : ${control_socket:=/var/run/local_unbound.ctl}
68 : ${anchor:=${workdir}/root.key}
69 : ${pidfile:=/var/run/local_unbound.pid}
70 : ${resolv_conf:=/etc/resolv.conf}
71 : ${resolvconf_conf:=/etc/resolvconf.conf}
72 : ${service:=local_unbound}
73 : ${start_unbound:=yes}
77 # Verify that the configuration files are inside the working
78 # directory, and if so, set the chroot directory accordingly.
81 chrootdir="${workdir}"
82 for file in "${unbound_conf}" "${forward_conf}" \
83 "${lanzones_conf}" "${control_conf}" "${anchor}" ; do
84 if [ "${file#${workdir%/}/}" = "${file}" ] ; then
85 echo "warning: ${file} is outside ${workdir}" >&2
89 if [ -z "${chrootdir}" ] ; then
90 echo "warning: disabling chroot" >&2
95 # Scan through /etc/resolv.conf looking for uncommented nameserver
96 # lines that don't point to localhost and return their values.
100 local bareline=${line%%\#*}
101 local key=${bareline%% *}
102 local value=${bareline#* }
106 127.0.0.1|::1|localhost|localhost.*)
118 # Scan through /etc/resolv.conf looking for uncommented nameserver
119 # lines. Comment out any that don't point to localhost. Finally,
120 # append a nameserver line that points to localhost, if there wasn't
121 # one already, and enable the edns0 option.
127 local bareline=${line%%\#*}
128 local key=${bareline%% *}
129 local value=${bareline#* }
133 127.0.0.1|::1|localhost|localhost.*)
151 if [ "${localhost}" = "no" ] ; then
152 echo "nameserver 127.0.0.1"
154 if [ "${edns0}" = "no" ] ; then
163 echo "# This file was generated by $self."
164 echo "# Modifications will be overwritten."
168 # Generate resolvconf.conf so it updates forward.conf in addition to
169 # resolv.conf. Note "in addition to" rather than "instead of",
170 # because we still want it to update the domain name and search path
171 # if they change. Setting name_servers to "127.0.0.1" ensures that
172 # the libc resolver will try unbound first.
174 gen_resolvconf_conf() {
176 echo "resolv_conf=\"/dev/null\" # prevent updating ${resolv_conf}"
177 echo "unbound_conf=\"${forward_conf}\""
178 echo "unbound_pid=\"${pidfile}\""
179 echo "unbound_service=\"${service}\""
180 # resolvconf(8) likes to restart rather than reload
181 echo "unbound_restart=\"service ${service} reload\""
185 # Generate forward.conf
192 if expr "${forwarder}" : "^[0-9A-Fa-f:.]\{1,\}$" >/dev/null ; then
193 echo " forward-addr: ${forwarder}"
195 echo " forward-host: ${forwarder}"
201 # Generate lan-zones.conf
203 gen_lanzones_conf() {
206 echo " # Unblock reverse lookups for LAN addresses"
207 echo " unblock-lan-zones: yes"
208 echo " domain-insecure: 10.in-addr.arpa."
209 echo " domain-insecure: 127.in-addr.arpa."
210 echo " domain-insecure: 16.172.in-addr.arpa."
211 echo " domain-insecure: 17.172.in-addr.arpa."
212 echo " domain-insecure: 18.172.in-addr.arpa."
213 echo " domain-insecure: 19.172.in-addr.arpa."
214 echo " domain-insecure: 20.172.in-addr.arpa."
215 echo " domain-insecure: 21.172.in-addr.arpa."
216 echo " domain-insecure: 22.172.in-addr.arpa."
217 echo " domain-insecure: 23.172.in-addr.arpa."
218 echo " domain-insecure: 24.172.in-addr.arpa."
219 echo " domain-insecure: 25.172.in-addr.arpa."
220 echo " domain-insecure: 26.172.in-addr.arpa."
221 echo " domain-insecure: 27.172.in-addr.arpa."
222 echo " domain-insecure: 28.172.in-addr.arpa."
223 echo " domain-insecure: 29.172.in-addr.arpa."
224 echo " domain-insecure: 30.172.in-addr.arpa."
225 echo " domain-insecure: 31.172.in-addr.arpa."
226 echo " domain-insecure: 168.192.in-addr.arpa."
227 echo " domain-insecure: 254.169.in-addr.arpa."
228 echo " domain-insecure: d.f.ip6.arpa."
229 echo " domain-insecure: 8.e.ip6.arpa."
230 echo " domain-insecure: 9.e.ip6.arpa."
231 echo " domain-insecure: a.e.ip6.arpa."
232 echo " domain-insecure: b.e.ip6.arpa."
236 # Generate control.conf
240 echo "remote-control:"
241 echo " control-enable: yes"
242 echo " control-interface: ${control_socket}"
243 echo " control-use-cert: no"
247 # Generate unbound.conf
252 echo " username: ${user}"
253 echo " directory: ${workdir}"
254 echo " chroot: ${chrootdir}"
255 echo " pidfile: ${pidfile}"
256 echo " auto-trust-anchor-file: ${anchor}"
258 if [ -f "${forward_conf}" ] ; then
259 echo "include: ${forward_conf}"
261 if [ -f "${lanzones_conf}" ] ; then
262 echo "include: ${lanzones_conf}"
264 if [ -f "${control_conf}" ] ; then
265 echo "include: ${control_conf}"
267 if [ -d "${confdir}" ] ; then
268 echo "include: ${confdir}/*.conf"
273 # Replace one file with another, making a backup copy of the first,
274 # but only if the new file is different from the old.
279 if [ ! -f "${file}" ] ; then
280 echo "${file} created"
281 mv "${newfile}" "${file}"
282 elif ! cmp -s "${file}" "${newfile}" ; then
283 local oldfile="${file}.${bkext}"
284 echo "original ${file} saved as ${oldfile}"
285 mv "${file}" "${oldfile}"
286 mv "${newfile}" "${file}"
288 echo "${file} not modified"
294 # Print usage message and exit
298 echo "usage: $self [options] [forwarder ...]"
300 echo " -n do not start unbound"
301 echo " -a path full path to trust anchor file"
302 echo " -C path full path to additional configuration directory"
303 echo " -c path full path to unbound configuration file"
304 echo " -f path full path to forwarding configuration"
305 echo " -O path full path to remote control socket"
306 echo " -o path full path to remote control configuration"
307 echo " -p path full path to pid file"
308 echo " -R path full path to resolvconf.conf"
309 echo " -r path full path to resolv.conf"
310 echo " -s service name of unbound service"
311 echo " -u user user to run unbound as"
312 echo " -w path full path to working directory"
323 # Parse and validate command-line options
325 while getopts "a:C:c:f:no:p:R:r:s:u:w:" option ; do
334 unbound_conf="$OPTARG"
337 forward_conf="$OPTARG"
343 control_socket="$OPTARG"
346 control_conf="$OPTARG"
352 resolvconf_conf="$OPTARG"
355 resolv_conf="$OPTARG"
375 # Get the list of forwarders, either from the command line or
379 if [ -z "$forwarders" ] ; then
380 echo "Extracting forwarders from ${resolv_conf}."
381 forwarders=$(get_nameservers <"${resolv_conf}")
385 # Generate forward.conf.
387 if [ -z "${forwarders}" ] ; then
388 echo -n "No forwarders found in ${resolv_conf##*/}, "
389 if [ -f "${forward_conf}" ] ; then
390 echo "using existing ${forward_conf##*/}."
392 echo "unbound will recurse."
395 local tmp_forward_conf=$(mktemp -u "${forward_conf}.XXXXX")
396 gen_forward_conf ${forwarders} | unexpand >"${tmp_forward_conf}"
397 replace "${forward_conf}" "${tmp_forward_conf}"
401 # Generate lan-zones.conf.
403 local tmp_lanzones_conf=$(mktemp -u "${lanzones_conf}.XXXXX")
404 gen_lanzones_conf | unexpand >"${tmp_lanzones_conf}"
405 replace "${lanzones_conf}" "${tmp_lanzones_conf}"
408 # Generate control.conf.
410 local tmp_control_conf=$(mktemp -u "${control_conf}.XXXXX")
411 gen_control_conf | unexpand >"${tmp_control_conf}"
412 replace "${control_conf}" "${tmp_control_conf}"
415 # Generate unbound.conf.
417 local tmp_unbound_conf=$(mktemp -u "${unbound_conf}.XXXXX")
419 gen_unbound_conf | unexpand >"${tmp_unbound_conf}"
420 replace "${unbound_conf}" "${tmp_unbound_conf}"
423 # Start unbound, unless requested not to. Stop immediately if
424 # it is not enabled so we don't end up with a resolv.conf that
425 # points into nothingness. We could "onestart" it, but it
428 if [ "${start_unbound}" = "no" ] ; then
430 elif ! service "${service}" enabled ; then
431 echo "Please enable $service in rc.conf(5) and try again."
433 elif ! service "${service}" restart ; then
434 echo "Failed to start $service."
439 # Rewrite resolvconf.conf so resolvconf updates forward.conf
440 # instead of resolv.conf.
442 local tmp_resolvconf_conf=$(mktemp -u "${resolvconf_conf}.XXXXX")
443 gen_resolvconf_conf | unexpand >"${tmp_resolvconf_conf}"
444 replace "${resolvconf_conf}" "${tmp_resolvconf_conf}"
447 # Finally, rewrite resolv.conf.
449 local tmp_resolv_conf=$(mktemp -u "${resolv_conf}.XXXXX")
450 gen_resolv_conf <"${resolv_conf}" | unexpand >"${tmp_resolv_conf}"
451 replace "${resolv_conf}" "${tmp_resolv_conf}"