INSECURE_ZONES=
USE_FORWARDERS=1
+ENABLE_SAFE_SEARCH=off
+FORCE_TCP=off
# Cache any local zones for 60 seconds
LOCAL_TTL=60
# Load optional configuration
[ -e "/etc/sysconfig/unbound" ] && . /etc/sysconfig/unbound
-function cidr() {
- local cidr nbits IFS;
- IFS=. read -r i1 i2 i3 i4 <<< ${1}
- IFS=. read -r m1 m2 m3 m4 <<< ${2}
- cidr=$(printf "%d.%d.%d.%d\n" "$((i1 & m1))" "$((i2 & m2))" "$((i3 & m3))" "$((i4 & m4))")
- nbits=0
- IFS=.
- for dec in $2 ; do
- case $dec in
- 255) let nbits+=8;;
- 254) let nbits+=7;;
- 252) let nbits+=6;;
- 248) let nbits+=5;;
- 240) let nbits+=4;;
- 224) let nbits+=3;;
- 192) let nbits+=2;;
- 128) let nbits+=1;;
- 0);;
- *) echo "Error: $dec is not recognised"; exit 1
- esac
- done
- echo "${cidr}/${nbits}"
-}
+DIG_ARGS=()
+
+if [ "${FORCE_TCP}" = "on" ]; then
+ DIG_ARGS+=( "+tcp" )
+fi
ip_address_revptr() {
local addr=${1}
local i
for i in 1 2; do
echo "$(</var/ipfire/red/dns${i})"
- done | xargs echo
+ done 2>/dev/null | xargs echo
+}
+
+check_red_has_carrier_and_ip() {
+ # Interface configured ?
+ [ ! -e "/var/ipfire/red/iface" ] && return 0;
+
+ # Interface present ?
+ [ ! -e "/sys/class/net/$(</var/ipfire/red/iface)" ] && return 0;
+
+ # has carrier ?
+ [ ! "$(</sys/class/net/$(</var/ipfire/red/iface)/carrier)" = "1" ] && return 0;
+
+ # has ip ?
+ [ "$(ip address show dev $(</var/ipfire/red/iface) | grep "inet")" = "" ] && return 0;
+
+ return 1;
}
config_header() {
}
update_forwarders() {
- if [ "${USE_FORWARDERS}" = "1" -a -e "/var/ipfire/red/active" ]; then
+ check_red_has_carrier_and_ip
+ if [ "${USE_FORWARDERS}" = "1" -a "${?}" = "1" ]; then
local forwarders
local broken_forwarders
unbound-control -q forward off
}
+remove_forwarders() {
+ enable_dnssec
+ echo "local recursor" > /var/ipfire/red/dns
+ unbound-control -q forward off
+
+}
+
own_hostname() {
local hostname=$(hostname -f)
# 1.1.1.1 is reserved for unused green, skip this
}
update_hosts() {
- local enabled address hostname domainname
+ local enabled address hostname domainname generateptr
- while IFS="," read -r enabled address hostname domainname; do
+ while IFS="," read -r enabled address hostname domainname generateptr; do
[ "${enabled}" = "on" ] || continue
# Build FQDN
# Skip reverse resolution if the address equals the GREEN address
[ "${address}" = "${GREEN_ADDRESS}" ] && continue
+ # Skip reverse resolution if user requested not to do so
+ [ "${generateptr}" = "off" ] && continue
+
# Add RDNS
address=$(ip_address_revptr ${address})
unbound-control -q local_data "${address} ${LOCAL_TTL} IN PTR ${fqdn}"
(
config_header
+ # Force using TCP for upstream servers only
+ if [ "${FORCE_TCP}" = "on" ]; then
+ echo "# Force using TCP for upstream servers only"
+ echo "server:"
+ echo " tcp-upstream: yes"
+ echo
+ fi
+
local insecure_zones="${INSECURE_ZONES}"
- local enabled zone server remark
- while IFS="," read -r enabled zone server remark; do
+ local enabled zone server servers remark disable_dnssec rest
+ while IFS="," read -r enabled zone servers remark disable_dnssec rest; do
# Line must be enabled.
[ "${enabled}" = "on" ] || continue
*.local)
insecure_zones="${insecure_zones} ${zone}"
;;
+ *)
+ if [ "${disable_dnssec}" = "on" ]; then
+ insecure_zones="${insecure_zones} ${zone}"
+ fi
+ ;;
esac
- echo "forward-zone:"
- echo " name: ${zone}"
- echo " forward-addr: ${server}"
- echo
+ # Reverse-lookup zones must be stubs
+ case "${zone}" in
+ *.in-addr.arpa)
+ echo "stub-zone:"
+ echo " name: ${zone}"
+ for server in ${servers//|/ }; do
+ if [[ ${server} =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
+ echo " stub-addr: ${server}"
+ else
+ echo " stub-host: ${server}"
+ fi
+ done
+ echo
+ echo "server:"
+ echo " local-zone: \"${zone}\" transparent"
+ echo
+ ;;
+ *)
+ echo "forward-zone:"
+ echo " name: ${zone}"
+ for server in ${servers//|/ }; do
+ if [[ ${server} =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
+ echo " forward-addr: ${server}"
+ else
+ echo " forward-host: ${server}"
+ fi
+ done
+ echo
+ ;;
+ esac
done < /var/ipfire/dnsforward/config
if [ -n "${insecure_zones}" ]; then
# In the worst case scenario, unbound can use double the
# amount of memory allocated to a cache due to malloc overhead
+ # Even larger systems with more than 8GB of RAM
+ if [ ${mem} -ge 8192 ]; then
+ mem=1024
+
+ # Extra large systems with more than 4GB of RAM
+ elif [ ${mem} -ge 4096 ]; then
+ mem=512
+
# Large systems with more than 2GB of RAM
- if [ ${mem} -ge 2048 ]; then
+ elif [ ${mem} -ge 2048 ]; then
+ mem=256
+
+ # Medium systems with more than 1GB of RAM
+ elif [ ${mem} -ge 1024 ]; then
mem=128
# Small systems with less than 256MB of RAM
elif [ ${mem} -le 256 ]; then
- mem=8
+ mem=16
# Everything else
else
- mem=32
+ mem=64
fi
(
# Increase parallel queries
echo "outgoing-range: 8192"
echo "num-queries-per-thread: 4096"
+
+ # Use larger send/receive buffers
+ echo "so-sndbuf: 4m"
+ echo "so-rcvbuf: 4m"
) > /etc/unbound/tuning.conf
}
local ns=${1}
shift
- dig @${ns} +nodnssec A ${TEST_DOMAIN} $@ >/dev/null
+ dig "${DIG_ARGS[@]}" @${ns} +nodnssec A ${TEST_DOMAIN} $@ >/dev/null
}
# Resolving ${TEST_DOMAIN_FAIL} will fail if the nameserver is validating
local ns=${1}
shift
- dig @${ns} A ${TEST_DOMAIN_FAIL} $@ | grep -q SERVFAIL
+ if ! dig "${DIG_ARGS[@]}" @${ns} A ${TEST_DOMAIN_FAIL} $@ | grep -q SERVFAIL; then
+ return 1
+ else
+ # Determine if NS replies with "ad" data flag if DNSSEC enabled
+ dig "${DIG_ARGS[@]}" @${ns} +dnssec SOA ${TEST_DOMAIN} $@ | awk -F: '/\;\;\ flags\:/ { s=1; if (/\ ad/) s=0; exit s }'
+ fi
}
# Checks if we can retrieve the DNSKEY for this domain.
local ns=${1}
shift
- dig @${ns} DNSKEY ${TEST_DOMAIN} $@ | grep -qv SOA
+ dig "${DIG_ARGS[@]}" @${ns} DNSKEY ${TEST_DOMAIN} $@ | grep -qv SOA
}
ns_forwards_DS() {
local ns=${1}
shift
- dig @${ns} DS ${TEST_DOMAIN} $@ | grep -qv SOA
+ dig "${DIG_ARGS[@]}" @${ns} DS ${TEST_DOMAIN} $@ | grep -qv SOA
}
ns_forwards_RRSIG() {
local ns=${1}
shift
- dig @${ns} +dnssec A ${TEST_DOMAIN} $@ | grep -q RRSIG
+ dig "${DIG_ARGS[@]}" @${ns} +dnssec A ${TEST_DOMAIN} $@ | grep -q RRSIG
}
ns_supports_tcp() {
local ns=${1}
shift
- dig @${ns} +tcp A ${TEST_DOMAIN} $@ >/dev/null || return 1
+ # If TCP is forced we know by now if the server responds to it
+ if [ "${FORCE_TCP}" = "on" ]; then
+ return 0
+ fi
+
+ dig "${DIG_ARGS[@]}" @${ns} +tcp A ${TEST_DOMAIN} $@ >/dev/null || return 1
}
ns_determine_edns_buffer_size() {
local b
for b in 4096 2048 1500 1480 1464 1400 1280 512; do
- if dig @${ns} +dnssec +bufsize=${b} A ${TEST_DOMAIN} $@ >/dev/null; then
+ if dig "${DIG_ARGS[@]}" @${ns} +dnssec +bufsize=${b} A ${TEST_DOMAIN} $@ >/dev/null; then
echo "${b}"
return 0
fi
can_resolve_root() {
local ns
for ns in $(get_root_nameservers); do
- if dig @${ns} +dnssec SOA . $@ >/dev/null; then
+ if dig "${DIG_ARGS[@]}" @${ns} +dnssec SOA . $@ >/dev/null; then
return 0
fi
done
enable_dnssec() {
local status=$(unbound-control get_option val-permissive-mode)
+ # Log DNSSEC status
+ echo "on" > /var/ipfire/red/dnssec-status
+
# Don't do anything if DNSSEC is already activated
[ "${status}" = "no" ] && return 0
}
disable_dnssec() {
+ # Log DNSSEC status
+ echo "off" > /var/ipfire/red/dnssec-status
+
unbound-control -q set_option val-permissive-mode: yes
}
+fix_time_if_dns_fail() {
+ # If DNS still not work try to init ntp with
+ # hardcoded ntp.ipfire.org (81.3.27.46)
+ check_red_has_carrier_and_ip
+ if [ -e "/var/ipfire/red/iface" -a "${?}" = "1" ]; then
+ host 0.ipfire.pool.ntp.org > /dev/null 2>&1
+ if [ "${?}" != "0" ]; then
+ boot_mesg "DNS still not functioning... Trying to sync time with ntp.ipfire.org (81.3.27.46)..."
+ loadproc /usr/local/bin/settime 81.3.27.46
+ fi
+ fi
+}
+
+resolve() {
+ local hostname="${1}"
+
+ local found=0
+ local ns
+ for ns in $(read_name_servers); do
+ local answer
+ for answer in $(dig "${DIG_ARGS[@]}" +short "@${ns}" A "${hostname}"); do
+ found=1
+
+ # Filter out non-IP addresses
+ if [[ ! "${answer}" =~ \.$ ]]; then
+ echo "${answer}"
+ fi
+ done
+
+ # End loop when we have got something
+ [ ${found} -eq 1 ] && break
+ done
+}
+
+# Sets up Safe Search for various search engines
+write_safe_search_conf() {
+ local google_tlds=(
+ google.ad
+ google.ae
+ google.al
+ google.am
+ google.as
+ google.at
+ google.az
+ google.ba
+ google.be
+ google.bf
+ google.bg
+ google.bi
+ google.bj
+ google.bs
+ google.bt
+ google.by
+ google.ca
+ google.cat
+ google.cd
+ google.cf
+ google.cg
+ google.ch
+ google.ci
+ google.cl
+ google.cm
+ google.cn
+ google.co.ao
+ google.co.bw
+ google.co.ck
+ google.co.cr
+ google.co.id
+ google.co.il
+ google.co.in
+ google.co.jp
+ google.co.ke
+ google.co.kr
+ google.co.ls
+ google.com
+ google.co.ma
+ google.com.af
+ google.com.ag
+ google.com.ai
+ google.com.ar
+ google.com.au
+ google.com.bd
+ google.com.bh
+ google.com.bn
+ google.com.bo
+ google.com.br
+ google.com.bz
+ google.com.co
+ google.com.cu
+ google.com.cy
+ google.com.do
+ google.com.ec
+ google.com.eg
+ google.com.et
+ google.com.fj
+ google.com.gh
+ google.com.gi
+ google.com.gt
+ google.com.hk
+ google.com.jm
+ google.com.kh
+ google.com.kw
+ google.com.lb
+ google.com.ly
+ google.com.mm
+ google.com.mt
+ google.com.mx
+ google.com.my
+ google.com.na
+ google.com.nf
+ google.com.ng
+ google.com.ni
+ google.com.np
+ google.com.om
+ google.com.pa
+ google.com.pe
+ google.com.pg
+ google.com.ph
+ google.com.pk
+ google.com.pr
+ google.com.py
+ google.com.qa
+ google.com.sa
+ google.com.sb
+ google.com.sg
+ google.com.sl
+ google.com.sv
+ google.com.tj
+ google.com.tr
+ google.com.tw
+ google.com.ua
+ google.com.uy
+ google.com.vc
+ google.com.vn
+ google.co.mz
+ google.co.nz
+ google.co.th
+ google.co.tz
+ google.co.ug
+ google.co.uk
+ google.co.uz
+ google.co.ve
+ google.co.vi
+ google.co.za
+ google.co.zm
+ google.co.zw
+ google.cv
+ google.cz
+ google.de
+ google.dj
+ google.dk
+ google.dm
+ google.dz
+ google.ee
+ google.es
+ google.fi
+ google.fm
+ google.fr
+ google.ga
+ google.ge
+ google.gg
+ google.gl
+ google.gm
+ google.gp
+ google.gr
+ google.gy
+ google.hn
+ google.hr
+ google.ht
+ google.hu
+ google.ie
+ google.im
+ google.iq
+ google.is
+ google.it
+ google.je
+ google.jo
+ google.kg
+ google.ki
+ google.kz
+ google.la
+ google.li
+ google.lk
+ google.lt
+ google.lu
+ google.lv
+ google.md
+ google.me
+ google.mg
+ google.mk
+ google.ml
+ google.mn
+ google.ms
+ google.mu
+ google.mv
+ google.mw
+ google.ne
+ google.nl
+ google.no
+ google.nr
+ google.nu
+ google.pl
+ google.pn
+ google.ps
+ google.pt
+ google.ro
+ google.rs
+ google.ru
+ google.rw
+ google.sc
+ google.se
+ google.sh
+ google.si
+ google.sk
+ google.sm
+ google.sn
+ google.so
+ google.sr
+ google.st
+ google.td
+ google.tg
+ google.tk
+ google.tl
+ google.tm
+ google.tn
+ google.to
+ google.tt
+ google.vg
+ google.vu
+ google.ws
+ )
+
+ (
+ # Nothing to do if safe search is not enabled
+ if [ "${ENABLE_SAFE_SEARCH}" != "on" ]; then
+ exit 0
+ fi
+
+ # This all belongs into the server: section
+ echo "server:"
+
+ # Bing
+ echo " local-zone: bing.com transparent"
+ for address in $(resolve "strict.bing.com"); do
+ echo " local-data: \"www.bing.com ${LOCAL_TTL} IN A ${address}\""
+ done
+
+ # DuckDuckGo
+ echo " local-zone: duckduckgo.com typetransparent"
+ for address in $(resolve "safe.duckduckgo.com"); do
+ echo " local-data: \"duckduckgo.com ${LOCAL_TTL} IN A ${address}\""
+ done
+
+ # Google
+ addresses="$(resolve "forcesafesearch.google.com")"
+ local domain
+ for domain in ${google_tlds[@]}; do
+ echo " local-zone: ${domain} transparent"
+ for address in ${addresses}; do
+ echo " local-data: \"www.${domain} ${LOCAL_TTL} IN A ${address}\""
+ done
+ done
+
+ # Yandex
+ for domain in yandex.com yandex.ru; do
+ echo " local-zone: ${domain} typetransparent"
+ for address in $(resolve "familysearch.${domain}"); do
+ echo " local-data: \"${domain} ${LOCAL_TTL} IN A ${address}\""
+ done
+ done
+
+ # YouTube
+ echo " local-zone: youtube.com transparent"
+ for address in $(resolve "restrictmoderate.youtube.com"); do
+ echo " local-data: \"www.youtube.com ${LOCAL_TTL} IN A ${address}\""
+ done
+ ) > /etc/unbound/safe-search.conf
+}
+
case "$1" in
start)
# Print a nicer messagen when unbound is already running
eval $(/usr/local/bin/readhash /var/ipfire/ethernet/settings)
- # Create control keys at first run
- if [ ! -r "/etc/unbound/unbound_control.key" ]; then
- unbound-control-setup -d /etc/unbound &>/dev/null
- fi
-
# Update configuration files
write_tuning_conf
write_forward_conf
+ write_safe_search_conf
boot_mesg "Starting Unbound DNS Proxy..."
loadproc /usr/sbin/unbound || exit $?
# Update hosts
update_hosts
+
+ fix_time_if_dns_fail
;;
stop)
fi
update_forwarders
+
+ unbound-control flush_negative > /dev/null
+ unbound-control flush_bogus > /dev/null
+
+ fix_time_if_dns_fail
;;
+ remove-forwarders)
+ # Do not try updating forwarders when unbound is not running
+ if ! pgrep unbound &>/dev/null; then
+ exit 0
+ fi
+
+ remove_forwarders
+
+ unbound-control flush_negative > /dev/null
+ unbound-control flush_bogus > /dev/null
+ ;;
+
+
test-name-server)
ns=${2}
exit ${ret}
;;
+ resolve)
+ resolve "${2}"
+ ;;
+
*)
- echo "Usage: $0 {start|stop|restart|status|update-forwarders|test-name-server}"
+ echo "Usage: $0 {start|stop|restart|status|update-forwarders|remove-forwarders|test-name-server|resolve}"
exit 1
;;
esac