#!/bin/bash # dnsmasq config, drop-in, refresh timer and watchdog units. # # Sourced by setup_dns_blocker.sh; split out to keep it under the 250-line cap. # Sourced rather than run, so it inherits the caller's strict mode. # Upstream resolvers for every name not in the blocklist. readonly UPSTREAMS=(1.1.1.1 9.9.9.9) # Hostnames browsers use to reach DoH providers; answered 0.0.0.0 so a browser # set to "secure DNS" falls back to this resolver instead of bypassing it. readonly DOH_HOSTNAMES=( dns.google cloudflare-dns.com one.one.one.one dns.quad9.net dns.nextdns.io doh.opendns.com dns.adguard-dns.com doh.cleanbrowsing.org dns.mullvad.net dns.controld.com freedns.controld.com doh.libredns.gr ) write_dnsmasq_conf() { ensure_dir "$(dirname "$DNSMASQ_CONF")" # /etc/dnsmasq.d may not exist yet local server_lines doh_lines server_lines="$(printf 'server=%s\n' "${UPSTREAMS[@]}")" doh_lines="$(printf 'address=/%s/0.0.0.0\n' "${DOH_HOSTNAMES[@]}")" log_info "Writing ${DNSMASQ_CONF} (listen on ${LAN_IFACE}/${LAN_IP}, upstream ${UPSTREAMS[*]})." cat >"$DNSMASQ_CONF" <"$DNSMASQ_DROPIN" <<'EOF' # Managed by setup_dns_blocker.sh -- keep the resolver up. # No network-online ordering: the stock unit is Before=network-online.target, # so After= here was an ordering cycle systemd broke by deleting a job at every # boot. bind-dynamic (dnsmasq.conf) makes waiting for the NIC unnecessary. [Service] Restart=always RestartSec=5 EOF } install_refresh_timer() { cat >"$REFRESH_SERVICE" <"$REFRESH_TIMER" <<'EOF' [Unit] Description=Daily rebuild of the LAN DNS blocklist [Timer] OnCalendar=daily Persistent=true RandomizedDelaySec=15m [Install] WantedBy=timers.target EOF } # The whole LAN's DHCP/DNS depends on this one process. Restart=always in the # drop-in only catches an actual crash; an incident on 2026-07-23 found # dnsmasq dead with ZERO error logged (no crash, no "Stopping"/"Stopped" line # either -- consistent with something issuing a plain stop, not a failure) and # it stayed down for hours, silently, until noticed by a phone unable to join # WiFi. Mirrors the existing shutdown-timer-monitor-watchdog pattern used # elsewhere in this repo: same 5-minute cadence, same is-active-or-start shape, # plus a logger call so a future incident leaves a trace instead of none. install_watchdog() { cat >"$WATCHDOG_SERVICE" <<'EOF' [Unit] Description=Watchdog for dnsmasq (LAN DHCP/DNS) After=multi-user.target [Service] Type=oneshot ExecStart=/bin/bash -c 'systemctl is-active --quiet dnsmasq || { logger -t dnsmasq-watchdog "dnsmasq was down -- restarting"; systemctl restart dnsmasq; }' EOF cat >"$WATCHDOG_TIMER" <<'EOF' [Unit] Description=Watchdog Timer for dnsmasq [Timer] OnBootSec=60 OnUnitActiveSec=300 Persistent=true [Install] WantedBy=timers.target EOF }