3 # SPDX-License-Identifier: BSD-2-Clause-FreeBSD
5 # Copyright (c) 2013 Dag-Erling Smørgrav
8 # Redistribution and use in source and binary forms, with or without
9 # modification, are permitted provided that the following conditions
11 # 1. Redistributions of source code must retain the above copyright
12 # notice, this list of conditions and the following disclaimer.
13 # 2. Redistributions in binary form must reproduce the above copyright
14 # notice, this list of conditions and the following disclaimer in the
15 # documentation and/or other materials provided with the distribution.
17 # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 echo "destination: ${D}"
36 # Configuration variables
59 self=$(basename $(realpath "$0"))
61 bkext=$(date "+%Y%m%d.%H%M%S")
66 RE_octet="([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])"
67 RE_ipv4="(${RE_octet}(\\.${RE_octet}){3})"
68 RE_word="([0-9A-Fa-f]{1,4})"
69 RE_ipv6="((${RE_word}:){1,}(:|(:${RE_word})*)|::1)"
70 RE_port="([1-9][0-9]{0,3}|[1-5][0-9]{4,4}|6([0-4][0-9]{3}|5([0-4][0-9]{2}|5([0-2][0-9]|3[0-5]))))"
71 RE_dnsname="([0-9A-Za-z-]{1,}(\\.[0-9A-Za-z-]{1,})*\\.?)"
72 RE_forward_addr="((${RE_ipv4}|${RE_ipv6})(@${RE_port})?)"
73 RE_forward_name="(${RE_dnsname}(@${RE_port})?)"
74 RE_forward_tls="(${RE_forward_addr}(#${RE_dnsname})?)"
77 # Set default values for unset configuration variables.
81 : ${workdir:=/var/unbound}
82 : ${confdir:=${workdir}/conf.d}
83 : ${unbound_conf:=${workdir}/unbound.conf}
84 : ${forward_conf:=${workdir}/forward.conf}
85 : ${lanzones_conf:=${workdir}/lan-zones.conf}
86 : ${control_conf:=${workdir}/control.conf}
87 : ${control_socket:=/var/run/local_unbound.ctl}
88 : ${anchor:=${workdir}/root.key}
89 : ${pidfile:=/var/run/local_unbound.pid}
90 : ${resolv_conf:=/etc/resolv.conf}
91 : ${resolvconf_conf:=/etc/resolvconf.conf}
92 : ${service:=local_unbound}
93 : ${start_unbound:=yes}
98 # Verify that the configuration files are inside the working
99 # directory, and if so, set the chroot directory accordingly.
102 chrootdir="${workdir}"
103 for file in "${unbound_conf}" "${forward_conf}" \
104 "${lanzones_conf}" "${control_conf}" "${anchor}" ; do
105 if [ "${file#${workdir%/}/}" = "${file}" ] ; then
106 echo "warning: ${file} is outside ${workdir}" >&2
110 if [ -z "${chrootdir}" ] ; then
111 echo "warning: disabling chroot" >&2
116 # Scan through /etc/resolv.conf looking for uncommented nameserver
117 # lines that don't point to localhost and return their values.
121 local bareline=${line%%\#*}
122 local key=${bareline%% *}
123 local value=${bareline#* }
127 127.0.0.1|::1|localhost|localhost.*)
139 # Scan through /etc/resolv.conf looking for uncommented nameserver
140 # lines. Comment out any that don't point to localhost. Finally,
141 # append a nameserver line that points to localhost, if there wasn't
142 # one already, and enable the edns0 option.
148 local bareline=${line%%\#*}
149 local key=${bareline%% *}
150 local value=${bareline#* }
154 127.0.0.1|::1|localhost|localhost.*)
172 if [ "${localhost}" = "no" ] ; then
173 echo "nameserver 127.0.0.1"
175 if [ "${edns0}" = "no" ] ; then
184 echo "# This file was generated by $self."
185 echo "# Modifications will be overwritten."
189 # Generate resolvconf.conf so it updates forward.conf in addition to
190 # resolv.conf. Note "in addition to" rather than "instead of",
191 # because we still want it to update the domain name and search path
192 # if they change. Setting name_servers to "127.0.0.1" ensures that
193 # the libc resolver will try unbound first.
195 gen_resolvconf_conf() {
198 echo "resolv_conf=\"/dev/null\" # prevent updating ${resolv_conf}"
199 if [ "${style}" = "dynamic" ] ; then
200 echo "unbound_conf=\"${forward_conf}\""
201 echo "unbound_pid=\"${pidfile}\""
202 echo "unbound_service=\"${service}\""
203 # resolvconf(8) likes to restart rather than reload
204 echo "unbound_restart=\"service ${service} reload\""
206 echo "# Static DNS configuration"
211 # Generate forward.conf
217 for forwarder ; do echo "${forwarder}" ; done |
218 if [ "${use_tls}" = "yes" ] ; then
219 echo " forward-tls-upstream: yes"
221 -e "s/^${RE_forward_tls}\$/ forward-addr: \\1/p"
224 -e "s/^${RE_forward_addr}\$/ forward-addr: \\1/p" \
225 -e "s/^${RE_forward_name}\$/ forward-host: \\1/p"
230 # Generate lan-zones.conf
232 gen_lanzones_conf() {
235 echo " # Unblock reverse lookups for LAN addresses"
236 echo " unblock-lan-zones: yes"
237 echo " insecure-lan-zones: yes"
241 # Generate control.conf
245 echo "remote-control:"
246 echo " control-enable: yes"
247 echo " control-interface: ${control_socket}"
248 echo " control-use-cert: no"
252 # Generate unbound.conf
257 echo " username: ${user}"
258 echo " directory: ${workdir}"
259 echo " chroot: ${chrootdir}"
260 echo " pidfile: ${pidfile}"
261 echo " auto-trust-anchor-file: ${anchor}"
262 if [ "${use_tls}" = "yes" ] ; then
263 echo " tls-cert-bundle: /etc/ssl/cert.pem"
266 if [ -f "${forward_conf}" ] ; then
267 echo "include: ${forward_conf}"
269 if [ -f "${lanzones_conf}" ] ; then
270 echo "include: ${lanzones_conf}"
272 if [ -f "${control_conf}" ] ; then
273 echo "include: ${control_conf}"
275 if [ -d "${confdir}" ] ; then
276 echo "include: ${confdir}/*.conf"
281 # Rename a file we are about to replace.
285 if [ -f "${D}${file}" ] ; then
286 local bkfile="${bkdir}/${file##*/}.${bkext}"
287 echo "Original ${file} saved as ${bkfile}"
288 mv "${D}${file}" "${D}${bkfile}"
293 # Wrapper for mktemp which respects DESTDIR
297 mktemp -u "${D}${file}.XXXXX"
301 # Replace one file with another, making a backup copy of the first,
302 # but only if the new file is different from the old.
307 if [ ! -f "${D}${file}" ] ; then
308 echo "${file} created"
309 mv "${newfile}" "${D}${file}"
310 elif ! cmp -s "${D}${file}" "${newfile}" ; then
312 mv "${newfile}" "${D}${file}"
314 echo "${file} not modified"
320 # Print usage message and exit
324 echo "usage: $self [options] [forwarder ...]"
326 echo " -n do not start unbound"
327 echo " -a path full path to trust anchor file"
328 echo " -C path full path to additional configuration directory"
329 echo " -c path full path to unbound configuration file"
330 echo " -f path full path to forwarding configuration"
331 echo " -O path full path to remote control socket"
332 echo " -o path full path to remote control configuration"
333 echo " -p path full path to pid file"
334 echo " -R path full path to resolvconf.conf"
335 echo " -r path full path to resolv.conf"
336 echo " -s service name of unbound service"
337 echo " -u user user to run unbound as"
338 echo " -w path full path to working directory"
349 # Parse and validate command-line options
351 while getopts "a:C:c:f:no:p:R:r:s:tu:w:" option ; do
360 unbound_conf="$OPTARG"
363 forward_conf="$OPTARG"
369 control_socket="$OPTARG"
372 control_conf="$OPTARG"
378 resolvconf_conf="$OPTARG"
381 resolv_conf="$OPTARG"
404 # Get the list of forwarders, either from the command line or
408 case "${forwarders}" in
414 if [ -f "${D}${resolv_conf}" ] ; then
415 echo "Extracting forwarders from ${resolv_conf}."
416 forwarders=$(get_nameservers <"${D}${resolv_conf}")
426 # Generate forward.conf.
428 if [ -z "${forwarders}" ] ; then
429 echo -n "No forwarders found in ${resolv_conf##*/}, "
430 if [ -f "${forward_conf}" ] ; then
431 echo "using existing ${forward_conf##*/}."
433 echo "unbound will recurse."
435 elif [ "${forwarders}" = "none" ] ; then
436 echo "Forwarding disabled, unbound will recurse."
437 backup "${forward_conf}"
439 local tmp_forward_conf=$(tmp "${forward_conf}")
440 gen_forward_conf ${forwarders} | unexpand >"${tmp_forward_conf}"
441 replace "${forward_conf}" "${tmp_forward_conf}"
445 # Generate lan-zones.conf.
447 local tmp_lanzones_conf=$(tmp "${lanzones_conf}")
448 gen_lanzones_conf | unexpand >"${tmp_lanzones_conf}"
449 replace "${lanzones_conf}" "${tmp_lanzones_conf}"
452 # Generate control.conf.
454 local tmp_control_conf=$(tmp "${control_conf}")
455 gen_control_conf | unexpand >"${tmp_control_conf}"
456 replace "${control_conf}" "${tmp_control_conf}"
459 # Generate unbound.conf.
461 local tmp_unbound_conf=$(tmp "${unbound_conf}")
463 gen_unbound_conf | unexpand >"${tmp_unbound_conf}"
464 replace "${unbound_conf}" "${tmp_unbound_conf}"
467 # Start unbound, unless requested not to. Stop immediately if
468 # it is not enabled so we don't end up with a resolv.conf that
469 # points into nothingness. We could "onestart" it, but it
472 if [ "${start_unbound}" = "no" ] ; then
474 elif ! service "${service}" enabled ; then
475 echo "Please enable $service in rc.conf(5) and try again."
477 elif ! service "${service}" restart ; then
478 echo "Failed to start $service."
483 # Rewrite resolvconf.conf so resolvconf updates forward.conf
484 # instead of resolv.conf.
486 local tmp_resolvconf_conf=$(tmp "${resolvconf_conf}")
487 gen_resolvconf_conf "${style}" | unexpand >"${tmp_resolvconf_conf}"
488 replace "${resolvconf_conf}" "${tmp_resolvconf_conf}"
491 # Finally, rewrite resolv.conf.
493 local tmp_resolv_conf=$(tmp "${resolv_conf}")
494 gen_resolv_conf <"${D}${resolv_conf}" | unexpand >"${tmp_resolv_conf}"
495 replace "${resolv_conf}" "${tmp_resolv_conf}"