diff --git a/net/adblock/Makefile b/net/adblock/Makefile index 8be459e42f..825bb0874e 100644 --- a/net/adblock/Makefile +++ b/net/adblock/Makefile @@ -7,7 +7,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=adblock PKG_VERSION:=4.5.5 -PKG_RELEASE:=1 +PKG_RELEASE:=2 PKG_LICENSE:=GPL-3.0-or-later PKG_MAINTAINER:=Dirk Brenken diff --git a/net/adblock/files/README.md b/net/adblock/files/README.md index d59b1294aa..f8cc467c50 100644 --- a/net/adblock/files/README.md +++ b/net/adblock/files/README.md @@ -6,7 +6,7 @@ ## Description A lot of people already use adblocker plugins within their desktop browsers, but what if you are using your (smart) phone, tablet, watch or any other (wlan) gadget!? Getting rid of annoying ads, trackers and other abuse sites (like facebook) is simple: block them with your router. -When the DNS server on your router receives DNS requests, you will sort out queries that ask for the resource records of ad servers and return a simple 'NXDOMAIN'. This is nothing but **N**on-e**X**istent Internet or Intranet domain name, if a domain name cannot be resolved using the DNS server, a condition called the 'NXDOMAIN' occurred. +When the DNS server on your router receives DNS requests, you will sort out queries that ask for the resource records of ad servers and return a simple `NXDOMAIN`. This is nothing but **N**on-e**X**istent Internet or Intranet domain name, if a domain name cannot be resolved using the DNS server, a condition called the `NXDOMAIN` occurred. ## Main Features @@ -63,18 +63,18 @@ When the DNS server on your router receives DNS requests, you will sort out quer * The download engine supports ETAG headers to download only updated feeds * Supports a wide range of router modes, even AP modes are supported * Full IPv4 and IPv6 support -* Provides top level domain compression ('tld compression'), this feature removes thousands of needless host entries from the blocklist and lowers the memory footprint for the DNS backend -* Provides a 'DNS Blocklist Shift', where the generated final DNS blocklist is moved to the backup directory and only a soft link to this file is set in memory. As long as your backup directory is located on an external drive, you should activate this option to save valuable RAM. -* Feed parsing by a very fast & secure domain validator, all domain rules and feed information are placed in an external JSON file ('/etc/adblock/adblock.feeds') -* Overall duplicate removal in generated blocklist file 'adb_list.overall' -* Additional local allowlist for manual overrides, located in '/etc/adblock/adblock.allowlist' -* Additional local blocklist for manual overrides, located in '/etc/adblock/adblock.blocklist' +* Provides top level domain compression (`tld compression`), this feature removes thousands of needless host entries from the blocklist and lowers the memory footprint for the DNS backend +* Provides a `DNS Blocklist Shift`, where the generated final DNS blocklist is moved to the backup directory and only a soft link to this file is set in memory. As long as your backup directory is located on an external drive, you should activate this option to save valuable RAM. +* Feed parsing by a very fast & secure domain validator, all domain rules and feed information are placed in an external JSON file (`/etc/adblock/adblock.feeds`) +* Overall duplicate removal in generated blocklist file `adb_list.overall` +* Additional local allowlist for manual overrides, located in `/etc/adblock/adblock.allowlist` +* Additional local blocklist for manual overrides, located in `/etc/adblock/adblock.blocklist` * Implements firewall‑based DNS Control to force DNS interfaces/ports and to redirect to external unfiltered/filtered DNS server * Includes firewall‑based Remote DNS Allow, a CGI-Interface to allow certain MACs temporary bypass the local adblock DNS * Supports firewall‑based temporary DNS Bridging, to ensure a Zero‑Downtime during adblock-related DNS Restarts * Connection checks during blocklist update to ensure a reliable DNS backend service * Minimal status & error logging to syslog, enable debug logging to receive more output -* Procd based init system support ('start', 'stop', 'restart', 'reload', 'enable', 'disable', 'running', 'status', 'suspend', 'resume', 'search', 'report') +* Procd based init system support (`start`, `stop`, `restart`, `reload`, `enable`, `disable`, `running`, `status`, `suspend`, `resume`, `search`, `report`) * Auto-Startup via procd network interface trigger or via classic time based startup * Suspend & Resume adblock temporarily without blocklist re-processing * Provides comprehensive runtime information @@ -83,31 +83,31 @@ When the DNS server on your router receives DNS requests, you will sort out quer * Implements a jail mode - only domains on the allowlist are permitted, all other DNS requests are rejected * Automatic blocklist backup & restore, these backups will be used in case of download errors and during startup * Send notification E-Mails, see example configuration below -* Add new adblock feeds on your own with the 'Custom Feed Editor' in LuCI or via CLI, see example below +* Add new adblock feeds on your own with the `Custom Feed Editor` in LuCI or via CLI, see example below * Strong LuCI support, all relevant options are exposed to the web frontend ## Prerequisites * **[OpenWrt](https://openwrt.org)**, latest stable release or a development snapshot * A usual setup with a working DNS backend -* A download utility with SSL support: 'wget', 'uclient-fetch' with one of the 'libustream-*' ssl libraries or 'curl' is required -* A certificate store such as 'ca-bundle' or 'ca-certificates', as adblock checks the validity of the SSL certificates of all download sites by default -* For E-Mail notifications you need to install and setup the additional 'msmtp' package -* For DNS reporting you need to install the additional package 'tcpdump-mini' or 'tcpdump' +* A download utility with SSL support: `wget`, `uclient-fetch` with one of the `libustream-*` ssl libraries or `curl` is required +* A certificate store such as `ca-bundle` or `ca-certificates`, as adblock checks the validity of the SSL certificates of all download sites by default +* For E-Mail notifications you need to install and setup the additional `msmtp` package +* For DNS reporting you need to install the additional package `tcpdump-mini` or `tcpdump` **Please note:** * Devices with less than 128MB of RAM are **_not_** supported * For performance reasons, adblock depends on gnu sort and gawk -* Before update from former adblock releases please make a backup of your local allow- and blocklists. In the latest adblock these lists have been renamed to '/etc/adblock/adblock.allowlist' and '/etc/adblock/adblock.blocklist'. There is no automatic content transition to the new files. +* Before update from former adblock releases please make a backup of your local allow- and blocklists. In the latest adblock these lists have been renamed to `/etc/adblock/adblock.allowlist` and `/etc/adblock/adblock.blocklist`. There is no automatic content transition to the new files. * The uci configuration of adblock is automatically migrated during package installation via the uci-defaults mechanism using a housekeeping script ## Installation & Usage * Make a backup and update your local opkg/apk repository -* Install the LuCI companion package 'luci-app-adblock' which also installs the main 'adblock' package as a dependency +* Install the LuCI companion package `luci-app-adblock` which also installs the main `adblock` package as a dependency * Enable the adblock system service (System -> Startup) and enable adblock itself (adblock -> General Settings) -* It's strongly recommended to use the LuCI frontend to easily configure all aspects of adblock, the application is located in LuCI under the 'Services' menu -* It's also strongly recommended to configure a ‘Startup Trigger Interface’ to ensure automatic adblock startup on WAN-ifup events during boot or reboot of your router +* It's strongly recommended to use the LuCI frontend to easily configure all aspects of adblock, the application is located in LuCI under the `Services` menu +* It's also recommended to configure a `Startup Trigger Interface` to depend on your WAN ifup events during boot or restart of your router. Avoid IPv6 (wan6) interfaces here, as IPv6/netifd is chatty and would trigger frequent unnecessary adblock restarts ## Adblock CLI interface @@ -143,17 +143,18 @@ Available commands: | :------------------- | :--------------------------------- | :------------------------------------------------------------------------------------------------- | | adb_enabled | 1, enabled | set to 0 to disable the adblock service | | adb_feedfile | /etc/adblock/adblock.feeds | full path to the used adblock feed file | -| adb_dns | -, auto-detected | 'dnsmasq', 'unbound', 'named', 'kresd', 'smartdns' or 'raw' | +| adb_dns | -, auto-detected | `dnsmasq`, `unbound`, `named`, `kresd`, `smartdns` or `raw` | | adb_cores | -, auto-detected | limit the cpu cores used by adblock to save RAM | -| adb_fetchcmd | -, auto-detected | 'uclient-fetch', 'wget' or 'curl' | +| adb_fetchcmd | -, auto-detected | `uclient-fetch`, `wget` or `curl` | | adb_fetchparm | -, auto-detected | manually override the config options for the selected download utility | +| adb_fetchretry | 5 | number of download attempts in case of an error (not supported by uclient-fetch) | | adb_fetchinsecure | 0, disabled | don't check SSL server certificates during download | -| adb_trigger | -, not set | trigger network interface or 'not set' to use a time-based startup | +| adb_trigger | -, not set | logical reload trigger interface(s), e.g. `wan` (avoid IPv6 interfaces) | | adb_triggerdelay | 5 | additional trigger delay in seconds before adblock processing begins | | adb_debug | 0, disabled | set to 1 to enable the debug output | | adb_nicelimit | 0, standard prio. | valid nice level range 0-19 of the adblock processes | | adb_dnsshift | 0, disabled | shift the blocklist to the backup directory and only set a soft link to this file in memory | -| adb_dnsdir | -, auto-detected | path for the generated blocklist file 'adb_list.overall' | +| adb_dnsdir | -, auto-detected | path for the generated blocklist file `adb_list.overall` | | adb_dnstimeout | 20 | timeout in seconds to wait for a successful DNS backend restart | | adb_dnsinstance | 0, first instance | set the relevant dnsmasq backend instance used by adblock | | adb_dnsflush | 0, disabled | set to 1 to flush the DNS Cache before & after adblock processing | @@ -161,7 +162,7 @@ Available commands: | adb_report | 0, disabled | set to 1 to enable the background tcpdump gathering process for reporting | | adb_map | 0, disabled | enable a GeoIP Map with blocked domains | | adb_reportdir | /tmp/adblock-report | path for DNS related report files | -| adb_repiface | -, auto-detected | name of the reporting interface or 'any' used by tcpdump | +| adb_repiface | -, auto-detected | name of the reporting interface or `any` used by tcpdump | | adb_repport | 53 | list of reporting port(s) used by tcpdump | | adb_repchunkcnt | 5 | report chunk count used by tcpdump | | adb_repchunksize | 1 | report chunk size used by tcpdump in MB | @@ -175,7 +176,7 @@ Available commands: | adb_mailreceiver | -, not set | receiver address for adblock notification E-Mails | | adb_mailsender | no-reply@adblock | sender address for adblock notification E-Mails | | adb_mailtopic | adblock notification | topic for adblock notification E-Mails | -| adb_mailprofile | adb_notify | mail profile used in 'msmtp' for adblock notification E-Mails | +| adb_mailprofile | adb_notify | mail profile used in `msmtp` for adblock notification E-Mails | | adb_jail | 0 | jail mode - only domains on the allowlist are permitted, all other DNS requests are rejected | | adb_nftforce | 0, disabled | redirect all local DNS queries from specified LAN zones to the local DNS resolver | | adb_nftdevforce | -, not set | firewall LAN Devices/VLANs that should be forced locally | @@ -202,17 +203,17 @@ Available commands: ## Examples -**Change the DNS backend to 'unbound':** -No further configuration is needed, adblock deposits the final blocklist 'adb_list.overall' in '/var/lib/unbound' by default. -To preserve the DNS cache after adblock processing please install the additional package 'unbound-control'. +**Change the DNS backend to `unbound`:** +No further configuration is needed, adblock deposits the final blocklist `adb_list.overall` in `/var/lib/unbound` by default. +To preserve the DNS cache after adblock processing please install the additional package `unbound-control`. -**Change the DNS backend to 'bind':** -Adblock deposits the final blocklist 'adb_list.overall' in '/var/lib/bind' by default. -To preserve the DNS cache after adblock processing please install the additional package 'bind-rndc'. -To use the blocklist please modify '/etc/bind/named.conf': +**Change the DNS backend to `bind`:** +Adblock deposits the final blocklist `adb_list.overall` in `/var/lib/bind` by default. +To preserve the DNS cache after adblock processing please install the additional package `bind-rndc`. +To use the blocklist please modify `/etc/bind/named.conf`: ``` -in the 'options' namespace add: +in the `options` namespace add: response-policy { zone "rpz"; }; and at the end of the file add: @@ -224,14 +225,14 @@ and at the end of the file add: }; ``` -**Change the DNS backend to 'kresd':** -Adblock deposits the final blocklist 'adb_list.overall' in '/tmp/kresd', no further configuration needed. +**Change the DNS backend to `kresd`:** +Adblock deposits the final blocklist `adb_list.overall` in `/tmp/kresd`, no further configuration needed. -**Change the DNS backend to 'smartdns':** -No further configuration is needed, adblock deposits the final blocklist 'adb_list.overall' in '/tmp/smartdns' by default. +**Change the DNS backend to `smartdns`:** +No further configuration is needed, adblock deposits the final blocklist `adb_list.overall` in `/tmp/smartdns` by default. **Service status output:** -In LuCI you'll see the realtime status in the 'Runtime' section on the overview page. +In LuCI you'll see the realtime status in the `Runtime` section on the overview page. To get the status in the CLI, just call _/etc/init.d/adblock status_ or _/etc/init.d/adblock status\_service_: ```sh @@ -262,14 +263,14 @@ adblock keeps all working data in RAM to avoid unnecessary flash wear. On device **Sensible choice of blocklists** The following feeds are just my personal recommendation as an initial setup: -* 'adguard', 'adguard_tracking' and 'certpl' +* `adguard`, `adguard_tracking` and `certpl` In total, this feed selection blocks about 280K domains. It may also be useful to include compilations like hagezi, stevenblack or oisd. -Please note: don't just blindly activate too many feeds at once, sooner or later this will lead to OOM conditions. +Please note: don`t just blindly activate too many feeds at once, sooner or later this will lead to OOM conditions. **DNS reporting, enable the GeoIP Map** adblock includes a powerful reporting tool on the DNS Report tab which shows the latest DNS statistics generated by tcpdump. To get the latest statistics always press the "Refresh" button. -In addition to a tabular overview adblock reporting includes a GeoIP map in a modal popup window/iframe that shows the geolocation of your own uplink addresses (in green) and the locations of blocked domains in red. To enable the GeoIP Map set the following option in "Advanced Report Settings" config tab: set 'adb_map' to '1' to include the external components listed below and activate the GeoIP map. +In addition to a tabular overview adblock reporting includes a GeoIP map in a modal popup window/iframe that shows the geolocation of your own uplink addresses (in green) and the locations of blocked domains in red. To enable the GeoIP Map set the following option in "Advanced Report Settings" config tab: set `adb_map` to `1` to include the external components listed below and activate the GeoIP map. To make this work, adblock uses the following external components: * [Leaflet](https://leafletjs.com/) is a lightweight open-source JavaScript library for interactive maps @@ -317,14 +318,25 @@ The CGI interface is mobile‑friendly and includes a LuCI‑style loading spinn **Temporary DNS Bridging (Zero‑Downtime during DNS Restarts)** Adblock can optionally enable a temporary DNS bridging mode to avoid DNS downtime during DNS backend restarts. -When this feature is enabled, all DNS queries from LAN clients are briefly redirected to an external fallback resolver until the local DNS backend becomes available again. This ensures that DNS resolution continues to work seamlessly for all clients, even while adblock reloads blocklists or restarts the DNS service. Just set the options 'adb_nftbridge', 'adb_bridgednsv4' and 'adb_bridgednsv6' accordingly. +When this feature is enabled, all DNS queries from LAN clients are briefly redirected to an external fallback resolver until the local DNS backend becomes available again. This ensures that DNS resolution continues to work seamlessly for all clients, even while adblock reloads blocklists or restarts the DNS service. Just set the options `adb_nftbridge`, `adb_bridgednsv4` and `adb_bridgednsv6` accordingly. **Jail mode (allowlist-only):** Enforces a strict allowlist‑only DNS policy in which only domains listed in the allowlist file are resolved, while every other query is rejected. This mode is intended for highly restrictive environments and depends on a carefully maintained allowlist, typically managed manually. -**Enable E-Mail notification via 'msmtp':** -To use the email notification you have to install & configure the package 'msmtp'. -Modify the file '/etc/msmtprc': +**Download options** +By default adblock uses the following pre-configured download options: + +``` + * curl: --connect-timeout 20 --retry-delay 10 --retry 4 --retry-all-errors --fail --silent --show-error --location -o + * wget: --no-cache --no-cookies --timeout=20 --waitretry=10 --tries=5 --retry-connrefused --max-redirect=0 -O + * uclient-fetch: --timeout=20 -O +``` + +To override the default set `adb_fetchretry`, `adb_fetchinsecure` or globally `adb_fetchparm` to your needs. + +**Enable E-Mail notification via `msmtp`:** +To use the email notification you have to install & configure the package `msmtp`. +Modify the file `/etc/msmtprc`:

 [...]
 defaults
@@ -344,7 +356,7 @@ password        xxx
 Finally enable E-Mail support, add a valid E-Mail receiver address in LuCI and setup an appropriate cron job.
 
 **Automatic adblock feed updates and E-Mail reports**  
-For a regular, automatic update of the used feeds or other regular adblock tasks set up a cron job. In LuCI you find the cron settings under 'System' => 'Scheduled Tasks'. On the command line the cron file is located at '/etc/crontabs/root':
+For a regular, automatic update of the used feeds or other regular adblock tasks set up a cron job. In LuCI you find the cron settings under `System` => `Scheduled Tasks`. On the command line the cron file is located at `/etc/crontabs/root`:
 
 Example 1
 ```sh
@@ -365,7 +377,7 @@ Example 3
 ```
 
 **Change/add adblock feeds**  
-The adblock blocklist feeds are stored in an external JSON file '/etc/adblock/adblock.feeds'. All custom changes should be stored in an external JSON file '/etc/adblock/adblock.custom.feeds' (empty by default). It's recommended to use the LuCI based Custom Feed Editor to make changes to this file.
+The adblock blocklist feeds are stored in an external JSON file `/etc/adblock/adblock.feeds`. All custom changes should be stored in an external JSON file `/etc/adblock/adblock.custom.feeds` (empty by default). It's recommended to use the LuCI based Custom Feed Editor to make changes to this file.
 A valid JSON source object contains the following information, e.g.:
 
 ```json
@@ -381,13 +393,13 @@ A valid JSON source object contains the following information, e.g.:
 
 Add a unique feed name (no spaces, no special chars) and make the required changes: adapt at least the URL, check/change the rule, the size and the description for a new feed.
 The rule consist of max. 4 individual, space separated parameters:
-1. type: always 'feed' (required)
-2. prefix: an optional search term (a string literal, no regex) to identify valid domain list entries, e.g. '0.0.0.0'
-3. column: the domain column within the feed file, e.g. '2' (required)
-4. separator: an optional field separator, default is the character class '[[:space:]]'
+1. type: always `feed` (required)
+2. prefix: an optional search term (a string literal, no regex) to identify valid domain list entries, e.g. `0.0.0.0`
+3. column: the domain column within the feed file, e.g. `2` (required)
+4. separator: an optional field separator, default is the character class `[[:space:]]`
 
 **Enable debug mode**  
-Adblock provides an optional debug mode that writes diagnostic information to the system log and captures internal error output in a dedicated error logfile - by default located in the adblock base directory as '/tmp/adb_error.log'. The log file is automatically cleared at the beginning of each run. Under normal conditions, all error messages are discarded to keep regular runs clean and silent. To enable debug mode, set the option 'adb_debug' to '1'. When enabled, the script produces significantly more log output to assist with troubleshooting.
+Adblock provides an optional debug mode that writes diagnostic information to the system log and captures internal error output in a dedicated error logfile - by default located in the adblock base directory as `/tmp/adb_error.log`. The log file is automatically cleared at the beginning of each run. Under normal conditions, all error messages are discarded to keep regular runs clean and silent. To enable debug mode, set the option `adb_debug` to `1`. When enabled, the script produces significantly more log output to assist with troubleshooting.
 
 ## Support
 Please join the adblock discussion in this [forum thread](https://forum.openwrt.org/t/adblock-support-thread/507) or contact me by mail 
diff --git a/net/adblock/files/adblock.init b/net/adblock/files/adblock.init
index 0b70f2e390..72dd1a6c51 100755
--- a/net/adblock/files/adblock.init
+++ b/net/adblock/files/adblock.init
@@ -19,23 +19,37 @@ adb_rundir="/var/run/adblock"
 adb_pidfile="/var/run/adblock/adblock.pid"
 
 if [ -z "${IPKG_INSTROOT}" ]; then
+
+	# ensure runtime directory exists
+	#
 	[ ! -d "${adb_rundir}" ] && mkdir -p "${adb_rundir}"
+
+	# check for running instance and handle boot trigger
+	#
 	case "${action}" in
 	"boot")
 		"${adb_init}" running && exit 0
 		;;
 	esac
+
+	# reset pidfile if no/stale process is found,
+	# otherwise exit with error to prevent multiple instances
+	#
 	if [ -s "${adb_pidfile}" ]; then
-		case "${action}" in
-		"start" | "stop" | "restart" | "reload" | "report" | "suspend" | "resume" | "search")
-			exit 1
-			;;
-		esac
+		pid="$(cat "${adb_pidfile}" 2>/dev/null)"
+		if [ -n "${pid}" ] && kill -0 "${pid}" 2>/dev/null; then
+			case "${action}" in
+			"start" | "stop" | "restart" | "reload" | "report" | "suspend" | "resume" | "search")
+				exit 1
+				;;
+			esac
+		else
+			: >"${adb_pidfile}"
+		fi
 	fi
 fi
 
 boot() {
-	: >"${adb_pidfile}"
 	rc_procd start_service boot
 }
 
@@ -88,7 +102,7 @@ status() {
 }
 
 status_service() {
-	local key keylist value values
+	local key keylist type value values
 
 	json_init
 	json_load_file "/var/run/adblock/adblock.runtime.json" >/dev/null 2>&1
@@ -96,10 +110,12 @@ status_service() {
 	if [ -n "${keylist}" ]; then
 		printf '%s\n' "::: adblock runtime information"
 		for key in ${keylist}; do
-			json_get_var value "${key}" >/dev/null 2>&1
-			if [ "${key}" = "active_feeds" ]; then
+			json_get_type type "${key}" >/dev/null 2>&1
+			if [ "${type}" = "array" ]; then
 				json_get_values values "${key}" >/dev/null 2>&1
 				value="${values}"
+			else
+				json_get_var value "${key}" >/dev/null 2>&1
 			fi
 			printf '  + %-15s : %s\n' "${key}" "${value:-"-"}"
 		done
diff --git a/net/adblock/files/adblock.sh b/net/adblock/files/adblock.sh
index e4621cecf2..5cf95bfd77 100755
--- a/net/adblock/files/adblock.sh
+++ b/net/adblock/files/adblock.sh
@@ -36,7 +36,7 @@ adb_bridgednsv4=""
 adb_bridgednsv6=""
 adb_dnsshift="0"
 adb_dnsflush="0"
-adb_dnstimeout="20"
+adb_dnstimeout="30"
 adb_safesearch="0"
 adb_report="0"
 adb_trigger=""
@@ -65,6 +65,7 @@ adb_customfeedfile="/etc/adblock/adblock.custom.feeds"
 adb_errorlog="/dev/null"
 adb_fetchcmd=""
 adb_fetchinsecure=""
+adb_fetchretry="5"
 adb_fetchparm=""
 adb_etagparm=""
 adb_geoparm=""
@@ -104,7 +105,7 @@ f_cmd() {
 # load adblock environment
 #
 f_load() {
-	local bg_pid port filter tcpdump_filter cpu
+	local cnt bg_pid port filter tcpdump_filter cpu
 
 	# load adblock config and set debug log file
 	#
@@ -495,11 +496,17 @@ f_dns() {
 		;;
 	esac
 
+	# determine final dns file directory based on dns shifting
+	#
 	if [ "${adb_dnsshift}" = "0" ]; then
 		adb_finaldir="${adb_dnsdir}"
+		[ -L "${adb_dnsdir}/${adb_dnsfile}" ] && "${adb_rmcmd}" -f "${adb_dnsdir}/${adb_dnsfile}"
 	else
 		adb_finaldir="${adb_backupdir}"
 	fi
+
+	# create dns file with header if it doesn't exist or dns flushing is enabled, also create backup and final directories if they don't exist
+	#
 	if [ "${adb_action}" != "stop" ]; then
 		for dir in "${adb_dnsdir:-"/tmp"}" "${adb_backupdir:-"/tmp"}"; do
 			[ ! -d "${dir}" ] && mkdir -p "${dir}"
@@ -562,13 +569,13 @@ f_fetch() {
 	case "${adb_fetchcmd##*/}" in
 	"curl")
 		[ "${adb_fetchinsecure}" = "1" ] && insecure="--insecure"
-		adb_fetchparm="${adb_fetchparm:-"${insecure} --connect-timeout 20 --fail --silent --show-error --location -o"}"
+		adb_fetchparm="${adb_fetchparm:-"${insecure} --connect-timeout 20 --retry-delay 10 --retry $((adb_fetchretry - 1)) --retry-max-time $(((adb_fetchretry - 1) * 20)) --retry-all-errors --fail --silent --show-error --location -o"}"
 		adb_etagparm="--connect-timeout 5 --silent --location --head"
 		adb_geoparm="--connect-timeout 5 --silent --location"
 		;;
 	"wget")
 		[ "${adb_fetchinsecure}" = "1" ] && insecure="--no-check-certificate"
-		adb_fetchparm="${adb_fetchparm:-"${insecure} --no-cache --no-cookies --max-redirect=0 --timeout=20 -O"}"
+		adb_fetchparm="${adb_fetchparm:-"${insecure} --no-cache --no-cookies --timeout=20 --waitretry=10 --tries=${adb_fetchretry} --retry-connrefused -O"}"
 		adb_etagparm="--timeout=5 --spider --server-response"
 		adb_geoparm="--timeout=5 --quiet -O-"
 		;;
@@ -579,7 +586,7 @@ f_fetch() {
 		;;
 	esac
 
-	f_log "debug" "f_fetch  ::: update: ${update}, cmd: ${adb_fetchcmd:-"-"}"
+	f_log "debug" "f_fetch  ::: update: ${update}, cmd: ${adb_fetchcmd:-"-"}, parm: ${adb_fetchparm:-"-"}, etag_parm: ${adb_etagparm:-"-"}, geo_parm: ${adb_geoparm:-"-"}"
 }
 
 # create temporary files, directories and set dependent options
@@ -607,8 +614,10 @@ f_rmtemp() {
 # remove dns related files
 #
 f_rmdns() {
-	printf '%b' "${adb_dnsheader}" >"${adb_finaldir}/${adb_dnsfile}"
-	f_dnsup
+	if [ -n "${adb_finaldir}" ]; then
+		printf '%b' "${adb_dnsheader}" >"${adb_finaldir}/${adb_dnsfile}"
+		f_dnsup
+	fi
 	f_rmtemp
 	if [ -d "${adb_backupdir}" ] && { [ "${adb_action}" = "stop" ] || [ "${adb_enabled}" = "0" ]; }; then
 		"${adb_findcmd}" "${adb_backupdir}" -maxdepth 1 -type f -name '*.gz' -exec "${adb_rmcmd}" -f {} +
@@ -735,7 +744,7 @@ f_dnsup() {
 					nft_rc="$((nft_rc + $?))"
 				fi
 				if [ "${nft_rc}" = "0" ]; then
-					f_log "info" "external DNS bridge loaded: ${adb_bridgednsv4:-"-"} / ${adb_bridgednsv6:-"-"}"
+					f_log "debug" "external DNS bridge loaded: ${adb_bridgednsv4:-"-"} / ${adb_bridgednsv6:-"-"}"
 				else
 					f_log "err" "failed to load external DNS bridge: ${adb_bridgednsv4:-"-"} / ${adb_bridgednsv6:-"-"}"
 				fi
@@ -784,7 +793,7 @@ f_dnsup() {
 				break
 			fi
 			cnt="$((cnt + 1))"
-			sleep 2
+			sleep 1
 		done
 		if [ "${out_rc}" = "0" ] && [ "${adb_dns}" = "unbound" ]; then
 			if [ -x "${adb_dnscachecmd}" ] && [ -d "${adb_tmpdir}" ] && [ -s "${adb_tmpdir}/adb_cache.dump" ]; then
@@ -800,7 +809,7 @@ f_dnsup() {
 		"${adb_nftcmd}" flush chain inet adblock dns-bridge 2>>"${adb_errorlog}"
 		nft_rc="${?}"
 		if [ "${nft_rc}" = "0" ]; then
-			f_log "info" "external DNS bridge removed"
+			f_log "debug" "external DNS bridge removed"
 		else
 			f_log "err" "failed to remove external DNS bridge"
 		fi
@@ -824,7 +833,7 @@ f_etag() {
 		# fetch http headers and extract http code and etag/last-modified header
 		#
 		http_head="$("${adb_fetchcmd}" ${adb_etagparm} "${feed_url}${feed_suffix}" 2>&1)"
-		http_code="$(printf '%s' "${http_head}" | "${adb_awkcmd}" 'tolower($0)~/^http\/[0123\.]+ /{printf "%s",$2}')"
+		http_code="$(printf '%s' "${http_head}" | "${adb_awkcmd}" 'tolower($0)~/^[[:space:]]*http\/[0-9.]+ /{printf "%s",$2}')"
 		etag_id="$(printf '%s' "${http_head}" | "${adb_awkcmd}" 'tolower($0)~/^[[:space:]]*etag: /{gsub("\"","");printf "%s",$2}')"
 
 		# if etag header is not present, try to use last-modified header as fallback for change detection
@@ -944,7 +953,7 @@ f_nftadd() {
 				[ -n "${adb_allowdnsv4}" ] && printf '%s\n' "add rule inet adblock pre-routing iifname \"${device}\" meta nfproto ipv4 meta l4proto { udp, tcp } th dport 53 counter dnat to ${adb_allowdnsv4}:53"
 				[ -n "${adb_allowdnsv6}" ] && printf '%s\n' "add rule inet adblock pre-routing iifname \"${device}\" meta nfproto ipv6 meta l4proto { udp, tcp } th dport 53 counter dnat to [${adb_allowdnsv6}]:53"
 			done
-			f_log "info" "adblock-related nft allow rules prepared for external DNS ${adb_allowdnsv4:-"-"} / ${adb_allowdnsv6:-"-"}"
+			f_log "debug" "adblock-related nft allow rules prepared for external DNS ${adb_allowdnsv4:-"-"} / ${adb_allowdnsv6:-"-"}"
 		fi
 
 		# external remote allow rules
@@ -952,7 +961,7 @@ f_nftadd() {
 		if [ "${adb_nftremote}" = "1" ] && [ -n "${adb_nftmacremote}" ]; then
 			[ -n "${adb_remotednsv4}" ] && printf '%s\n' "add rule inet adblock pre-routing meta nfproto ipv4 ether saddr @mac_remote meta l4proto { udp, tcp } th dport 53 counter dnat to ${adb_remotednsv4}:53"
 			[ -n "${adb_remotednsv6}" ] && printf '%s\n' "add rule inet adblock pre-routing meta nfproto ipv6 ether saddr @mac_remote meta l4proto { udp, tcp } th dport 53 counter dnat to [${adb_remotednsv6}]:53"
-			f_log "info" "adblock-related nft remote allow rules prepared for external DNS ${adb_remotednsv4:-"-"} / ${adb_remotednsv6:-"-"} with timeout of ${adb_nftremotetimeout} minutes"
+			f_log "debug" "adblock-related nft remote allow rules prepared for external DNS ${adb_remotednsv4:-"-"} / ${adb_remotednsv6:-"-"} with timeout of ${adb_nftremotetimeout} minutes"
 		fi
 
 		# external block rules
@@ -966,7 +975,7 @@ f_nftadd() {
 				[ -n "${adb_blockdnsv4}" ] && printf '%s\n' "add rule inet adblock pre-routing iifname \"${device}\" meta nfproto ipv4 meta l4proto { udp, tcp } th dport 53 counter dnat to ${adb_blockdnsv4}:53"
 				[ -n "${adb_blockdnsv6}" ] && printf '%s\n' "add rule inet adblock pre-routing iifname \"${device}\" meta nfproto ipv6 meta l4proto { udp, tcp } th dport 53 counter dnat to [${adb_blockdnsv6}]:53"
 			done
-			f_log "info" "adblock-related nft block rules prepared for external DNS ${adb_blockdnsv4:-"-"} / ${adb_blockdnsv6:-"-"}"
+			f_log "debug" "adblock-related nft block rules prepared for external DNS ${adb_blockdnsv4:-"-"} / ${adb_blockdnsv6:-"-"}"
 		fi
 
 		# local dns enforcement
@@ -1006,7 +1015,7 @@ f_nftadd() {
 					fi
 				done
 			done
-			f_log "info" "adblock-related nft local DNS enforcement rules prepared for devices: ${adb_nftdevforce// /, } and ports: ${adb_nftportforce// /, }"
+			f_log "debug" "adblock-related nft local DNS enforcement rules prepared for devices: ${adb_nftdevforce// /, } and ports: ${adb_nftportforce// /, }"
 		fi
 	} >"${file}"
 	if "${adb_nftcmd}" -f "${file}" 2>>"${adb_errorlog}"; then
@@ -1050,7 +1059,7 @@ f_list() {
 				file_name="${adb_tmpfile}.${src_name}"
 				f_chkdom local 1 <"${adb_blocklist}" >"${adb_tmpdir}/tmp.raw.${src_name}"
 				if [ "${adb_tld}" = "1" ]; then
-					if [ -s "${adb_allowlist}" ]; then
+					if [ -s "${adb_tmpdir}/tmp.rem.allowlist" ]; then
 						"${adb_awkcmd}" '
 									NR==FNR { member[$1]; next }
 									!($1 in member) {
@@ -1058,14 +1067,14 @@ f_list() {
 										for (f = n; f > 1; f--) printf "%s.", seg[f]
 										print seg[1]
 									}
-								' "${adb_allowlist}" "${adb_tmpdir}/tmp.raw.${src_name}"
+								' "${adb_tmpdir}/tmp.rem.allowlist" "${adb_tmpdir}/tmp.raw.${src_name}"
 					else
 						"${adb_awkcmd}" 'BEGIN{FS="."}{for(f=NF;f>1;f--)printf "%s.",$f;print $1}' "${adb_tmpdir}/tmp.raw.${src_name}"
 					fi | "${adb_sortcmd}" ${adb_srtopts} -u >"${file_name}"
 					out_rc="${?}"
 				else
-					if [ -s "${adb_allowlist}" ]; then
-						"${adb_awkcmd}" 'NR==FNR{member[$1];next}!($1 in member)' "${adb_allowlist}" "${adb_tmpdir}/tmp.raw.${src_name}" |
+					if [ -s "${adb_tmpdir}/tmp.rem.allowlist" ]; then
+						"${adb_awkcmd}" 'NR==FNR{member[$1];next}!($1 in member)' "${adb_tmpdir}/tmp.rem.allowlist" "${adb_tmpdir}/tmp.raw.${src_name}" |
 							"${adb_sortcmd}" ${adb_srtopts} -u >"${file_name}" 2>>"${adb_errorlog}"
 					else
 						"${adb_sortcmd}" ${adb_srtopts} -u "${adb_tmpdir}/tmp.raw.${src_name}" 2>>"${adb_errorlog}" >"${file_name}"
@@ -1233,14 +1242,14 @@ f_list() {
 
 		# remove stale backup files
 		#
-		files="-maxdepth 1 -type f -name *.gz"
+		files=""
 		for file in ${adb_feed}; do
 			files="${files} ! -name adb_list.${file}.gz"
 		done
 		if [ "${adb_safesearch}" = "1" ] && [ "${adb_dnssafesearch}" = "1" ]; then
 			files="${files} ! -name safesearch.google.gz"
 		fi
-		"${adb_findcmd}" "${adb_backupdir}" ${files} -print0 2>>"${adb_errorlog}" | "${adb_xargscmd}" -0r "${adb_rmcmd}" -f
+		"${adb_findcmd}" "${adb_backupdir}" -maxdepth 1 -type f -name '*.gz' ${files} -print0 2>>"${adb_errorlog}" | "${adb_xargscmd}" -0r "${adb_rmcmd}" -f
 
 		# merge files
 		#
@@ -1397,7 +1406,7 @@ f_switch() {
 				"${adb_mvcmd}" -f "${adb_backupdir}/${adb_dnsfile}" "${adb_finaldir}/${adb_dnsfile}"
 				f_count "final" "${adb_finaldir}/${adb_dnsfile}"
 				switch="dns"
-			elif [ "${adb_dnsshift}" = "1" ] && [ ! -L "${adb_finaldir}/${adb_dnsfile}" ]; then
+			elif [ "${adb_dnsshift}" = "1" ] && [ ! -L "${adb_dnsdir}/${adb_dnsfile}" ]; then
 				"${adb_lncmd}" -fs "${adb_finaldir}/${adb_dnsfile}" "${adb_dnsdir}/${adb_dnsfile}"
 				f_count "final" "${adb_finaldir}/${adb_dnsfile}"
 				switch="dns"
@@ -1547,7 +1556,7 @@ f_search() {
 #
 f_jsnup() {
 	local s_shift s_custom s_unfiltered s_filtered s_remote s_bridge s_force s_flush s_tld s_search s_report s_mail
-	local s_jail s_debug pids object feeds end_time runtime dns dns_ver free_mem custom_feed="0" status="${1:-"enabled"}"
+	local s_jail s_debug pid pids object feeds end_time runtime dns dns_ver free_mem custom_feed="0" status="${1:-"enabled"}"
 	local vm_mem dns_mem="0" duration jail="0" nft_unfiltered="0" nft_filtered="0" nft_remote="0" nft_bridge="0" nft_force="0"
 
 	# get DNS memory usage and version
@@ -1556,7 +1565,7 @@ f_jsnup() {
 		"${adb_jsoncmd}" -l1 -e "@[\"${adb_dns}\"].instances.*.pid")" && [ -n "${adb_dnspid}" ]; then
 		pids="$("${adb_pgrepcmd}" -P "${adb_dnspid}" 2>>"${adb_errorlog}")"
 		for pid in ${adb_dnspid} ${pids}; do
-			vm_mem="$("${adb_awkcmd}" '/^VmSize/{printf "%d", $2}' "/proc/${pid}/status" 2>>"${adb_errorlog}")"
+			vm_mem="$("${adb_awkcmd}" '/^VmRSS/{printf "%d", $2}' "/proc/${pid}/status" 2>>"${adb_errorlog}")"
 			dns_mem="$((dns_mem + ${vm_mem:-0}))"
 		done
 		case "${adb_dns}" in
@@ -1682,7 +1691,7 @@ f_jsnup() {
 	json_add_string "system_info" "cores: ${adb_cores}, fetch: ${adb_fetchcmd##*/}, ${adb_sysver}"
 	json_dump >"${adb_rtfile}"
 
-	if [ "${adb_mail}" = "1" ] && [ -x "${adb_mailservice}" ] && [ "${status}" = "enabled" ]; then
+	if [ "${adb_mail}" = "1" ] && [ -x "${adb_mailservice}" ] && [ "${status}" = "enabled" ] && [ "${adb_action}" != "resume" ]; then
 		"${adb_mailservice}" >/dev/null 2>&1
 	fi
 }
@@ -1693,8 +1702,11 @@ f_log() {
 	local class="${1}" log_msg="${2}"
 
 	if [ -n "${log_msg}" ] && { [ "${class}" != "debug" ] || [ "${adb_debug}" = "1" ]; }; then
-		[ -x "${adb_loggercmd}" ] && "${adb_loggercmd}" -p "${class}" -t "adblock-${adb_bver}[${$}]" "${log_msg::256}" ||
-			printf '%s %s %s\n' "${class}" "adblock-${adb_bver}[${$}]" "${log_msg::256}"
+		if [ -x "${adb_loggercmd}" ]; then
+			"${adb_loggercmd}" -p "${class}" -t "adblock-${adb_bver}[${$}]" "${log_msg::512}"
+		else
+			printf '%s %s %s\n' "${class}" "adblock-${adb_bver}[${$}]" "${log_msg::512}"
+		fi
 		if [ "${class}" = "err" ] || [ "${class}" = "emerg" ]; then
 			[ "${adb_action}" != "mail" ] && f_rmdns
 			f_jsnup "error"
@@ -1707,24 +1719,26 @@ f_log() {
 #
 f_main() {
 	local src_name src_domain src_rset src_url src_cat src_item src_list src_entries src_suffix src_rc entry cnt
-	local src_tmpcat src_tmparchive src_tmpload src_tmpfile seen_domains feed_restore
+	local src_tmpcat src_tmparchive src_tmpload src_tmpfile seen_domains feed_restore map_domain
 
 	# allow- and blocklist preparation
 	#
-	cnt="1"
 	for entry in ${adb_locallist}; do
-		(
-			f_list "${entry}" "${entry}"
-		) &
-		[ "${cnt}" -gt "${adb_cores}" ] && wait -n
-		cnt="$((cnt + 1))"
+		f_list "${entry}" "${entry}"
 	done
-	wait
 
 	# jail mode preparation
 	#
 	if [ "${adb_jail}" = "1" ] && [ -n "${adb_dnsstop}" ]; then
-		"${adb_mvcmd}" -f "${adb_tmpdir}/${adb_dnsfile}" "${adb_finaldir}/${adb_dnsfile}"
+		if [ -s "${adb_tmpdir}/${adb_dnsfile}" ]; then
+			"${adb_mvcmd}" -f "${adb_tmpdir}/${adb_dnsfile}" "${adb_finaldir}/${adb_dnsfile}"
+		else
+			f_log "info" "jail mode active without allowlist, blocking all queries"
+			{
+				printf '%b' "${adb_dnsheader}"
+				printf '%b\n' "${adb_dnsstop}"
+			} >"${adb_finaldir}/${adb_dnsfile}"
+		fi
 		chown "${adb_dnsuser}" "${adb_finaldir}/${adb_dnsfile}" 2>>"${adb_errorlog}"
 		if [ "${adb_dnsshift}" = "1" ] && [ ! -L "${adb_dnsdir}/${adb_dnsfile}" ]; then
 			"${adb_lncmd}" -fs "${adb_finaldir}/${adb_dnsfile}" "${adb_dnsdir}/${adb_dnsfile}"
@@ -1758,10 +1772,21 @@ f_main() {
 		wait
 	fi
 
+	# add map service domain to allowlist if map is enabled
+	#
+	if [ "${adb_map}" = "1" ] && [ "${adb_dnsallow}" = "1" ] && [ -n "${adb_geourl}" ]; then
+		map_domain="${adb_geourl#*://}"
+		map_domain="${map_domain%%/*}"
+		if [ -n "${map_domain}" ]; then
+			printf '%s\n' "${map_domain}" | f_dnsallow >>"${adb_tmpdir}/tmp.add.allowlist"
+			printf '%s\n' "${map_domain}" >>"${adb_tmpdir}/tmp.rem.allowlist"
+		fi
+	fi
+
 	# main loop
 	#
 	cnt="1"
-	seen_domains=""
+	seen_domains="${map_domain}"
 
 	# determine if feed restore should be attempted based on action
 	#
@@ -1815,6 +1840,7 @@ f_main() {
 			*)
 				seen_domains="${seen_domains} ${src_domain}"
 				printf '%s\n' "${src_domain}" | f_dnsallow >>"${adb_tmpdir}/tmp.add.allowlist"
+				printf '%s\n' "${src_domain}" >>"${adb_tmpdir}/tmp.rem.allowlist"
 				;;
 			esac
 		fi
@@ -1823,18 +1849,37 @@ f_main() {
 		#
 		src_cat=""
 		src_entries=""
+
+		# category handling for feeds with fixed categories
+		#
 		case "${src_name}" in
 		"1hosts")
 			src_cat="${adb_hst_feed}"
+			if [ -z "${src_cat}" ]; then
+				f_log "info" "feed '${src_name}' requires category configuration, skipping"
+				continue
+			fi
 			;;
 		"hagezi")
 			src_cat="${adb_hag_feed}"
+			if [ -z "${src_cat}" ]; then
+				f_log "info" "feed '${src_name}' requires category configuration, skipping"
+				continue
+			fi
 			;;
 		"ipfire_dbl")
 			src_cat="${adb_ipf_feed}"
+			if [ -z "${src_cat}" ]; then
+				f_log "info" "feed '${src_name}' requires category configuration, skipping"
+				continue
+			fi
 			;;
 		"stevenblack")
 			src_cat="${adb_stb_feed}"
+			if [ -z "${src_cat}" ]; then
+				f_log "info" "feed '${src_name}' requires category configuration, skipping"
+				continue
+			fi
 			;;
 		esac
 
@@ -1961,8 +2006,8 @@ f_main() {
 #
 f_report() {
 	local report_raw report_txt content status total start end start_date start_time end_date end_time blocked percent top_list top array item index value key key_list
-	local ip request requests iface_v4 iface_v6 ip_v4 ip_v6 map_jsn cnt report_srt report_jsn top_tmpclients top_tmpdomains top_tmpblocked
-	local resolve="-nn" action="${1}" top_count="${2:-"10"}" res_count="${3:-"50"}" search="${4:-"+"}"
+	local domain rc map_seen ip request requests iface_v4 iface_v6 ip_v4 ip_v6 map_jsn cnt report_srt report_jsn top_tmpclients top_tmpdomains top_tmpblocked
+	local file jsn resolve="-nn" action="${1}" top_count="${2:-"10"}" res_count="${3:-"50"}" search="${4:-"+"}"
 
 	report_raw="${adb_reportdir}/adb_report.raw"
 	report_srt="${adb_reportdir}/adb_report.srt"