adblock: update 4.5.3-4
authorDirk Brenken <redacted>
Fri, 27 Mar 2026 09:18:38 +0000 (10:18 +0100)
committerDirk Brenken <redacted>
Fri, 27 Mar 2026 09:20:30 +0000 (10:20 +0100)
* harden adblock backend: removed  all needless eval calls
* more fixes & optimizations
* LuCI: Allow dns instance selection for smartdns (not only for dnsmasq)
* LuCI: more fixes & optimizations
* readme update

Signed-off-by: Dirk Brenken <redacted>
net/adblock/Makefile
net/adblock/files/README.md
net/adblock/files/adblock.sh

index a21a05e5c7e5c2ea6681f84b1c8c48bbdc3512dd..50e345f05114979212be62f071fcd25385be3327 100644 (file)
@@ -7,7 +7,7 @@ include $(TOPDIR)/rules.mk
 
 PKG_NAME:=adblock
 PKG_VERSION:=4.5.3
-PKG_RELEASE:=3
+PKG_RELEASE:=4
 PKG_LICENSE:=GPL-3.0-or-later
 PKG_MAINTAINER:=Dirk Brenken <dev@brenken.org>
 
index f4000cd6955b46b870758aec498e5ee264f473cf..7393c9c5ffc076f025fadd7282f6c0ae300e4ba3 100644 (file)
@@ -202,11 +202,11 @@ Available commands:
 <a id="examples"></a>
 ## Examples
 
-**Change the DNS backend to 'unbound':**
+**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':**
+**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':
@@ -224,13 +224,13 @@ and at the end of the file add:
   };
 ```
 
-**Change the DNS backend to 'kresd':**
+**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':**
+**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:**
+**Service status output:**  
 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_:
 
@@ -253,21 +253,21 @@ root@blackhole:~# /etc/init.d/adblock status
 <a id="best-practice-and-tweaks"></a>
 ## Best practice and tweaks
 
-**Recommendation for low memory systems**
+**Recommendation for low memory systems**  
 adblock keeps all working data in RAM to avoid unnecessary flash wear. On devices with only 128–256 MB RAM, you can reduce memory pressure with the following optimizations:
 * Use external storage: Set adb_basedir, adb_backupdir and adb_reportdir to a USB drive or SSD to offload temporary and persistent data
 * Limit CPU parallelism: Set adb_cores=1 to reduce peak memory usage during feed processing
 * Enable blocklist shifting: Activate adb_dnsshift to store the generated blocklist on external storage and keep only a symlink in RAM
 * Use firewall‑based DNS redirection: Route DNS queries via nftables to external filtered DNS resolvers and keep only a minimal local blocklist active
 
-**Sensible choice of blocklists**
+**Sensible choice of blocklists**  
 The following feeds are just my personal recommendation as an initial setup:
 * '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.
 
-**DNS reporting, enable the GeoIP Map**
+**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.
 
@@ -277,12 +277,12 @@ To make this work, adblock uses the following external components:
 * [CARTO basemap styles](https://github.com/CartoDB/basemap-styles) based on [OpenMapTiles](https://openmaptiles.org/schema)
 * The free and quite fast [IP Geolocation API](https://ip-api.com/) to resolve the required IP/geolocation information (max. 45 blocked Domains per request)
 
-**External adblock test**
+**External adblock test**  
 In addition to the built‑in DNS reporting and GeoIP map, adblock users can verify the effectiveness of their configuration with an external test page. The [Adblock Test](https://adblock.turtlecute.org/) provides a simple way to check whether your current adblock setup is working as expected. It loads a series of test elements (ads, trackers, and other resources) and reports whether they are successfully blocked by your configuration.
 
 The test runs entirely in the browser and does not require additional configuration. For best results, open the page in the same environment where adblock is active and review the results displayed.
 
-**Firewall‑Based DNS Control**
+**Firewall‑Based DNS Control**  
 adblock provides several advanced firewall‑integrated features that allow you to enforce DNS policies directly at the network layer. These mechanisms operate independently of the local DNS resolver and ensure that DNS traffic follows your filtering rules, even when clients attempt to bypass them.
 * Unfiltered external DNS Routing: routes DNS queries from selected devices or interfaces to an external unfiltered DNS resolver
 * Filtered external DNS Routing: routes DNS queries from selected devices or interfaces to an external filtered DNS resolver
@@ -302,7 +302,7 @@ This mode guarantees that adblock’s filtering pipeline is always applied.
 
 adblock's firewall rules are based on nftables in a separate isolated nftables table (inet adblock) and chains (prerouting), with MAC addresses stored in a nftables set. The configuration is carried out centrally in LuCI on the ‘Firewall Settings’ tab in adblock.
 
-**Remote DNS Allow (Temporary MAC‑Based Bypass)**
+**Remote DNS Allow (Temporary MAC‑Based Bypass)**  
 This additional firewall feature lets selected client devices temporarily bypass local DNS blocking and use an external, unfiltered DNS resolver. It is designed for situations where a device needs short‑term access to content normally blocked by the adblock rules.
 
 A lightweight CGI endpoint handles the workflow:
@@ -315,14 +315,14 @@ A lightweight CGI endpoint handles the workflow:
 
 The CGI interface is mobile‑friendly and includes a LuCI‑style loading spinner during the renew process, giving immediate visual feedback while the nftables entry is created. All operations are atomic and safe even when multiple devices renew access in parallel.
 
-**Temporary DNS Bridging (Zero‑Downtime during DNS Restarts)**
+**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.
 
-**Jail mode (allowlist-only):**
+**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':**
+**Enable E-Mail notification via 'msmtp':**  
 To use the email notification you have to install & configure the package 'msmtp'.
 Modify the file '/etc/msmtprc':
 <pre><code>
@@ -343,7 +343,7 @@ password        xxx
 </code></pre>
 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**
+**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':
 
 Example 1
@@ -364,7 +364,7 @@ Example 3
 00 03 * * * /etc/init.d/adblock report mail
 ```
 
-**Change/add adblock feeds**
+**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.
 A valid JSON source object contains the following information, e.g.:
 
@@ -386,7 +386,7 @@ The rule consist of max. 4 individual, space separated parameters:
 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**
+**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.
 
 ## Support
index 430163c0f1944457734e15a96c1a035fd57a33d7..46a39d92c670959c1aacc278a391bc94164d4f7d 100755 (executable)
@@ -117,8 +117,8 @@ f_load() {
        adb_packages="$("${adb_ubuscmd}" -S call rpc-sys packagelist '{ "all": true }' 2>>"${adb_errorlog}")"
        adb_bver="$(printf "%s" "${adb_packages}" | "${adb_jsoncmd}" -ql1 -e '@.packages.adblock')"
        adb_fver="$(printf "%s" "${adb_packages}" | "${adb_jsoncmd}" -ql1 -e '@.packages["luci-app-adblock"]')"
-       adb_sysver="$("${adb_ubuscmd}" -S call system board 2>>"${adb_errorlog}" |
-               "${adb_jsoncmd}" -ql1 -e '@.model' -e '@.release.target' -e '@.release.distribution' -e '@.release.version' -e '@.release.revision' |
+       adb_sysver="$("${adb_ubuscmd}" -S call system board 2>>"${adb_errorlog}" | \
+               "${adb_jsoncmd}" -ql1 -e '@.model' -e '@.release.target' -e '@.release.distribution' -e '@.release.version' -e '@.release.revision' | \
                "${adb_awkcmd}" 'BEGIN{RS="";FS="\n"}{printf "%s, %s, %s %s (%s)",$1,$2,$3,$4,$5}')"
 
        # detect cpu cores for parallel processing
@@ -189,8 +189,9 @@ f_load() {
 # check & set environment
 #
 f_env() {
-       : > "${adb_errorlog}"
-       adb_starttime="$(date "+%s")"
+       : >"${adb_errorlog}"
+       read -r adb_starttime _ < "/proc/uptime"
+       adb_starttime="${adb_starttime%.*}"
        f_log "info" "adblock instance started ::: action: ${adb_action}, priority: ${adb_nicelimit:-"0"}, pid: ${$}"
        f_jsnup "processing"
        f_extconf
@@ -369,15 +370,10 @@ f_dns() {
 
        case "${adb_dns}" in
                "dnsmasq")
-                       adb_dnscachecmd="-"
+                       adb_dnscachecmd=""
                        adb_dnsinstance="${adb_dnsinstance:-"0"}"
-                       adb_dnsuser="${adb_dnsuser:-"dnsmasq"}"
+                       adb_dnsuser="dnsmasq"
                        adb_dnsdir="${adb_dnsdir:-""}"
-                       adb_dnsheader="${adb_dnsheader:-""}"
-                       adb_dnsdeny="${adb_dnsdeny:-"${adb_awkcmd} '{print \"local=/\"\$0\"/\"}'"}"
-                       adb_dnsallow="${adb_dnsallow:-"${adb_awkcmd} '{print \"local=/\"\$0\"/#\"}'"}"
-                       adb_dnssafesearch="${adb_dnssafesearch:-"${adb_awkcmd} -v item=\"\$item\" '{print \"address=/\"\$0\"/\"item\"\";print \"local=/\"\$0\"/\"}'"}"
-                       adb_dnsstop="${adb_dnsstop:-"address=/#/\nlocal=/#/"}"
                        if [ -z "${adb_dnsdir}" ]; then
                                dns_section="$("${adb_ubuscmd}" -S call uci get "{\"config\":\"dhcp\", \"section\":\"@dnsmasq[${adb_dnsinstance}]\", \"type\":\"dnsmasq\"}" 2>>"${adb_errorlog}")"
                                dns_info="$(printf "%s" "${dns_section}" | "${adb_jsoncmd}" -l1 -e '@.values["confdir"]')"
@@ -388,61 +384,127 @@ f_dns() {
                                        [ -n "${dns_info}" ] && adb_dnsdir="/tmp/dnsmasq.${dns_info}.d"
                                fi
                        fi
+                       adb_dnsheader=""
+                       adb_dnsdeny="1"
+                       adb_dnsallow="1"
+                       adb_dnssafesearch="1"
+                       adb_dnsstop="address=/#/\nlocal=/#/"
+                       f_dnsdeny() {
+                               "${adb_awkcmd}" '{print "local=/"$0"/"}' "${@}"
+                       }
+                       f_dnsallow() {
+                               "${adb_awkcmd}" '{print "local=/"$0"/#"}' "${@}"
+                       }
+                       f_dnssafesearch() {
+                               local item="${1}"
+
+                               shift
+                               "${adb_awkcmd}" -v item="${item}" '{print "address=/"$0"/"item"";print "local=/"$0"/"}' "${@}"
+                       }
                        ;;
                "unbound")
-                       adb_dnscachecmd="$(command -v unbound-control || printf "%s" "-")"
-                       adb_dnsinstance="${adb_dnsinstance:-"0"}"
-                       adb_dnsuser="${adb_dnsuser:-"unbound"}"
+                       adb_dnscachecmd="$(command -v unbound-control)"
+                       adb_dnsinstance=""
+                       adb_dnsuser="unbound"
                        adb_dnsdir="${adb_dnsdir:-"/var/lib/unbound"}"
-                       adb_dnsheader="${adb_dnsheader:-""}"
-                       adb_dnsdeny="${adb_dnsdeny:-"${adb_awkcmd} '{print \"local-zone: \\042\"\$0\"\\042 always_nxdomain\"}'"}"
-                       adb_dnsallow="${adb_dnsallow:-"${adb_awkcmd} '{print \"local-zone: \\042\"\$0\"\\042 always_transparent\"}'"}"
-                       adb_dnssafesearch="${adb_dnssafesearch:-"${adb_awkcmd} -v item=\"\$item\" '{type=\"AAAA\";if(match(item,/^([0-9]{1,3}\.){3}[0-9]{1,3}$/)){type=\"A\"}}{print \"local-data: \\042\"\$0\" \"type\" \"item\"\\042\"}'"}"
-                       adb_dnsstop="${adb_dnsstop:-"local-zone: \".\" always_nxdomain"}"
+                       adb_dnsheader=""
+                       adb_dnsdeny="1"
+                       adb_dnsallow="1"
+                       adb_dnssafesearch="1"
+                       adb_dnsstop="local-zone: \".\" always_nxdomain"
+                       f_dnsdeny() {
+                               "${adb_awkcmd}" '{print "local-zone: \042"$0"\042 always_nxdomain"}' "${@}"
+                       }
+                       f_dnsallow() {
+                               "${adb_awkcmd}" '{print "local-zone: \042"$0"\042 always_transparent"}' "${@}"
+                       }
+                       f_dnssafesearch() {
+                               local item="${1}"
+
+                               shift
+                               "${adb_awkcmd}" -v item="${item}" \
+                                       '{type="AAAA";if(match(item,/^([0-9]{1,3}\.){3}[0-9]{1,3}$/)){type="A"}}{print "local-data: \042"$0" "type" "item"\042"}' "${@}"
+                       }
                        ;;
                "named")
-                       adb_dnscachecmd="$(command -v rndc || printf "%s" "-")"
-                       adb_dnsinstance="${adb_dnsinstance:-"0"}"
-                       adb_dnsuser="${adb_dnsuser:-"bind"}"
+                       adb_dnscachecmd="$(command -v rndc)"
+                       adb_dnsinstance=""
+                       adb_dnsuser="bind"
                        adb_dnsdir="${adb_dnsdir:-"/var/lib/bind"}"
-                       adb_dnsheader="${adb_dnsheader:-"\$TTL 2h\n@ IN SOA localhost. root.localhost. (1 6h 1h 1w 2h)\n  IN NS  localhost.\n"}"
-                       adb_dnsdeny="${adb_dnsdeny:-"${adb_awkcmd} '{print \"\"\$0\" CNAME .\\n*.\"\$0\" CNAME .\"}'"}"
-                       adb_dnsallow="${adb_dnsallow:-"${adb_awkcmd} '{print \"\"\$0\" CNAME rpz-passthru.\\n*.\"\$0\" CNAME rpz-passthru.\"}'"}"
-                       adb_dnssafesearch="${adb_dnssafesearch:-"${adb_awkcmd} -v item=\"\$item\" '{print \"\"\$0\" CNAME \"item\".\\n*.\"\$0\" CNAME \"item\".\"}'"}"
-                       adb_dnsstop="${adb_dnsstop:-"* CNAME ."}"
+                       adb_dnsheader="\$TTL 2h\n@ IN SOA localhost. root.localhost. (1 6h 1h 1w 2h)\n  IN NS  localhost.\n"
+                       adb_dnsdeny="1"
+                       adb_dnsallow="1"
+                       adb_dnssafesearch="1"
+                       adb_dnsstop="* CNAME ."
+                       f_dnsdeny() {
+                               "${adb_awkcmd}" '{print ""$0" CNAME .\n*."$0" CNAME ."}' "${@}"
+                       }
+                       f_dnsallow() {
+                               "${adb_awkcmd}" '{print ""$0" CNAME rpz-passthru.\n*."$0" CNAME rpz-passthru."}' "${@}"
+                       }
+                       f_dnssafesearch() {
+                               local item="${1}"
+
+                               shift
+                               "${adb_awkcmd}" -v item="${item}" '{print ""$0" CNAME "item".\n*."$0" CNAME "item"."}' "${@}"
+                       }
                        ;;
                "kresd")
-                       adb_dnscachecmd="-"
-                       adb_dnsinstance="${adb_dnsinstance:-"0"}"
-                       adb_dnsuser="${adb_dnsuser:-"root"}"
+                       adb_dnscachecmd=""
+                       adb_dnsinstance=""
+                       adb_dnsuser="root"
                        adb_dnsdir="${adb_dnsdir:-"/tmp/kresd"}"
-                       adb_dnsheader="${adb_dnsheader:-"\$TTL 2h\n@ IN SOA localhost. root.localhost. (1 6h 1h 1w 2h)\n"}"
-                       adb_dnsdeny="${adb_dnsdeny:-"${adb_awkcmd} '{print \"\"\$0\" CNAME .\\n*.\"\$0\" CNAME .\"}'"}"
-                       adb_dnsallow="${adb_dnsallow:-"${adb_awkcmd} '{print \"\"\$0\" CNAME rpz-passthru.\\n*.\"\$0\" CNAME rpz-passthru.\"}'"}"
-                       adb_dnssafesearch="${adb_dnssafesearch:-"${adb_awkcmd} -v item=\"\$item\" '{print \"\"\$0\" CNAME \"item\".\\n*.\"\$0\" CNAME \"item\".\"}'"}"
-                       adb_dnsstop="${adb_dnsstop:-"* CNAME ."}"
+                       adb_dnsheader="\$TTL 2h\n@ IN SOA localhost. root.localhost. (1 6h 1h 1w 2h)\n"
+                       adb_dnsdeny="1"
+                       adb_dnsallow="1"
+                       adb_dnssafesearch="1"
+                       adb_dnsstop="* CNAME ."
+                       f_dnsdeny() {
+                               "${adb_awkcmd}" '{print ""$0" CNAME .\n*."$0" CNAME ."}' "${@}"
+                       }
+                       f_dnsallow() {
+                               "${adb_awkcmd}" '{print ""$0" CNAME rpz-passthru.\n*."$0" CNAME rpz-passthru."}' "${@}"
+                       }
+                       f_dnssafesearch() {
+                               local item="${1}"
+
+                               shift
+                               "${adb_awkcmd}" -v item="${item}" '{print ""$0" CNAME "item".\n*."$0" CNAME "item"."}' "${@}"
+                       }
                        ;;
                "smartdns")
-                       adb_dnscachecmd="-"
+                       adb_dnscachecmd=""
                        adb_dnsinstance="${adb_dnsinstance:-"0"}"
-                       adb_dnsuser="${adb_dnsuser:-"root"}"
+                       adb_dnsuser="root"
                        adb_dnsdir="${adb_dnsdir:-"/tmp/smartdns"}"
-                       adb_dnsheader="${adb_dnsheader:-""}"
-                       adb_dnsdeny="${adb_dnsdeny:-"${adb_awkcmd} '{print \"address /\"\$0\"/#\"}'"}"
-                       adb_dnsallow="${adb_dnsallow:-"${adb_awkcmd} '{print \"address /\"\$0\"/-\"}'"}"
-                       adb_dnssafesearch="${adb_dnssafesearch:-"${adb_awkcmd} -v item=\"\$item\" '{print \"cname /\"\$0\"/\"item\"\"}'"}"
-                       adb_dnsstop="${adb_dnsstop:-"address #"}"
+                       adb_dnsheader=""
+                       adb_dnsdeny="1"
+                       adb_dnsallow="1"
+                       adb_dnssafesearch="1"
+                       adb_dnsstop="address #"
+                       f_dnsdeny() {
+                               "${adb_awkcmd}" '{print "address /"$0"/#"}' "${@}"
+                       }
+                       f_dnsallow() {
+                               "${adb_awkcmd}" '{print "address /"$0"/-"}' "${@}"
+                       }
+                       f_dnssafesearch() {
+                               local item="${1}"
+
+                               shift
+                               "${adb_awkcmd}" -v item="${item}" '{print "cname /"$0"/"item""}' "${@}"
+                       }
                        ;;
                "raw")
-                       adb_dnscachecmd="-"
-                       adb_dnsinstance="${adb_dnsinstance:-"0"}"
-                       adb_dnsuser="${adb_dnsuser:-"root"}"
+                       adb_dnscachecmd=""
+                       adb_dnsinstance=""
+                       adb_dnsuser="root"
                        adb_dnsdir="${adb_dnsdir:-"/tmp"}"
-                       adb_dnsheader="${adb_dnsheader:-""}"
-                       adb_dnsdeny="${adb_dnsdeny:-"0"}"
-                       adb_dnsallow="${adb_dnsallow:-"0"}"
-                       adb_dnssafesearch="${adb_dnssafesearch:-"0"}"
-                       adb_dnsstop="${adb_dnsstop:-"0"}"
+                       adb_dnsheader=""
+                       adb_dnsdeny=""
+                       adb_dnsallow=""
+                       adb_dnssafesearch=""
+                       adb_dnsstop=""
                        ;;
        esac
 
@@ -475,7 +537,7 @@ f_dns() {
                exit 0
        fi
 
-       f_log "debug" "f_dns    ::: dns: ${adb_dns}, dns_instance: ${adb_dnsinstance}, dns_user: ${adb_dnsuser}, dns_dir: ${adb_dnsdir}, backup_dir: ${adb_backupdir}, final_dir: ${adb_finaldir}"
+       f_log "debug" "f_dns    ::: dns: ${adb_dns}, dns_instance: ${adb_dnsinstance:-"-"}, dns_user: ${adb_dnsuser}, dns_dir: ${adb_dnsdir}, backup_dir: ${adb_backupdir}, final_dir: ${adb_finaldir}"
 }
 
 # load fetch utility
@@ -652,7 +714,6 @@ f_extconf() {
 f_dnsup() {
        local restart_rc nft_rc cnt="0" out_rc="4"
 
-       adb_dnspid=""
        if [ "${adb_dns}" = "raw" ] || [ -z "${adb_dns}" ]; then
                out_rc="0"
        else
@@ -726,7 +787,6 @@ f_dnsup() {
                        fi
                fi
        fi
-       adb_dnspid="$("${adb_ubuscmd}" -S call service list 2>>"${adb_errorlog}" | "${adb_jsoncmd}" -l1 -e "@[\"${adb_dns}\"].instances.*.pid")"
 
        # remove external dns bridge
        #
@@ -740,7 +800,7 @@ f_dnsup() {
                fi
        fi
 
-       f_log "debug" "f_dnsup  ::: dns: ${adb_dns}, cache_cmd: ${adb_dnscachecmd:-"-"}, lookup_domain: ${adb_lookupdomain:-"-"}, restart_rc: ${restart_rc:-"-"}, dns_flush: ${adb_dnsflush}, dns_timeout: ${adb_dnstimeout}, dns_pid: ${adb_dnspid}, dns_cnt: ${cnt}, nft_rc: ${nft_rc:-"-"}, rc: ${out_rc}"
+       f_log "debug" "f_dnsup  ::: dns: ${adb_dns}, cache_cmd: ${adb_dnscachecmd:-"-"}, lookup_domain: ${adb_lookupdomain:-"-"}, restart_rc: ${restart_rc:-"-"}, dns_flush: ${adb_dnsflush}, dns_timeout: ${adb_dnstimeout}, dns_cnt: ${cnt}, nft_rc: ${nft_rc:-"-"}, rc: ${out_rc}"
        return "${out_rc}"
 }
 
@@ -973,14 +1033,14 @@ f_list() {
                                        fi
                                        ;;
                                "allowlist")
-                                       if [ -f "${adb_allowlist}" ] && [ "${adb_dnsallow}" != "0" ]; then
+                                       if [ -f "${adb_allowlist}" ] && [ "${adb_dnsallow}" = "1" ]; then
                                                file_name="${adb_tmpdir}/tmp.raw.${src_name}"
                                                [ "${adb_lookupdomain}" != "localhost" ] && { printf "%s\n" "${adb_lookupdomain}" | f_chkdom local 1; } >"${file_name}"
                                                f_chkdom local 1 < "${adb_allowlist}" >>"${file_name}"
                                                f_chkdom local 1 < "${file_name}" >"${adb_tmpdir}/tmp.rem.${src_name}"
-                                               eval "${adb_dnsallow}" "${file_name}" >"${adb_tmpdir}/tmp.add.${src_name}"
+                                               f_dnsallow "${file_name}" >"${adb_tmpdir}/tmp.add.${src_name}"
                                                out_rc="${?}"
-                                               if [ "${adb_jail}" = "1" ] && [ "${adb_dnsstop}" != "0" ]; then
+                                               if [ "${adb_jail}" = "1" ] && [ -n "${adb_dnsstop}" ]; then
                                                        printf "%b" "${adb_dnsheader}" >"${adb_tmpdir}/${adb_dnsfile}"
                                                        "${adb_catcmd}" "${adb_tmpdir}/tmp.add.${src_name}" >>"${adb_tmpdir}/${adb_dnsfile}"
                                                        printf "%b\n" "${adb_dnsstop}" >>"${adb_tmpdir}/${adb_dnsfile}"
@@ -1045,7 +1105,7 @@ f_list() {
                        if [ -s "${adb_tmpdir}/tmp.raw.safesearch.${src_name}" ]; then
                                : >"${file_name}"
                                for item in ${array}; do
-                                       if ! eval "${adb_dnssafesearch}" "${adb_tmpdir}/tmp.raw.safesearch.${src_name}" >>"${file_name}"; then
+                                       if ! f_dnssafesearch "${item}" "${adb_tmpdir}/tmp.raw.safesearch.${src_name}" >>"${file_name}"; then
                                                : >"${file_name}"
                                                break
                                        fi
@@ -1125,7 +1185,7 @@ f_list() {
                        for file in ${adb_feed}; do
                                files="${files} ! -name adb_list.${file}.gz"
                        done
-                       if [ "${adb_safesearch}" = "1" ] && [ "${adb_dnssafesearch}" != "0" ]; then
+                       if [ "${adb_safesearch}" = "1" ] && [ "${adb_dnssafesearch}" = "1" ]; then
                                files="${files} ! -name safesearch.google.gz"
                        fi
                        "${adb_findcmd}" "${adb_backupdir}" ${files} -print0 2>>"${adb_errorlog}" | xargs -0r "${adb_rmcmd}" -f
@@ -1149,8 +1209,8 @@ f_list() {
                        [ -n "${adb_dnsheader}" ] && printf "%b" "${adb_dnsheader}" >>"${file_name}"
                        [ -s "${adb_tmpdir}/tmp.add.allowlist" ] && "${adb_sortcmd}" ${adb_srtopts} -u "${adb_tmpdir}/tmp.add.allowlist" >>"${file_name}"
                        [ "${adb_safesearch}" = "1" ] && "${adb_catcmd}" "${adb_tmpdir}/tmp.safesearch."* 2>>"${adb_errorlog}" >>"${file_name}"
-                       if [ "${adb_dnsdeny}" != "0" ]; then
-                               eval "${adb_dnsdeny}" "${adb_tmpdir}/${adb_dnsfile}" >>"${file_name}"
+                       if [ "${adb_dnsdeny}" = "1" ]; then
+                               f_dnsdeny "${adb_tmpdir}/${adb_dnsfile}" >>"${file_name}"
                        else
                                "${adb_catcmd}" "${adb_tmpdir}/${adb_dnsfile}" >>"${file_name}"
                        fi
@@ -1365,7 +1425,8 @@ f_search() {
        # initialize tmp_result and start search
        #
        : >"${tmp_result}"
-       search_start="$(date "+%s")"
+       read -r search_start _ < "/proc/uptime"
+       search_start="${search_start%.*}"
        while :; do
                search="${domain//./\\.}"
                res="$("${adb_awkcmd}" -F '/|\"|\t| ' "/^(${prefix}${search}${suffix})$/{i++;if(i<=9){printf \"  + %s\n\",\$${field}}else if(i==10){printf \"  + %s\n\",\"[...]\";exit}}" "${adb_finaldir}/${adb_dnsfile}")"
@@ -1396,7 +1457,8 @@ f_search() {
                        fi
                        if [ "${rc}" = "0" ]; then
                                res="true"
-                               search_end="$(date "+%s")"
+                               read -r search_end _ < "/proc/uptime"
+                               search_end="${search_end%.*}"
                                if [ "$((search_end - search_start))" -gt "${search_timeout}" ]; then
                                        printf "%s\n\n" "  - [...]" >>"${tmp_result}"
                                        break
@@ -1415,7 +1477,10 @@ f_jsnup() {
        local pids object feeds end_time runtime dns dns_ver dns_mem free_mem custom_feed="0" status="${1:-"enabled"}"
        local duration jail="0" nft_unfiltered="0" nft_filtered="0" nft_remote="0" nft_bridge="0" nft_force="0"
 
-       if [ -n "${adb_dnspid}" ]; then
+       # get DNS memory usage and version
+       #
+       if adb_dnspid="$("${adb_ubuscmd}" -S call service list 2>>"${adb_errorlog}" | \
+               "${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
                        dns_mem="$((dns_mem + $("${adb_awkcmd}" '/^VmSize/{printf "%s", $2}' "/proc/${pid}/status" 2>>"${adb_errorlog}")))"
@@ -1439,6 +1504,9 @@ f_jsnup() {
        fi
        free_mem="$("${adb_awkcmd}" '/^MemAvailable/{printf "%.2f", $2/1024}' "/proc/meminfo" 2>>"${adb_errorlog}")"
        adb_cnt="$("${adb_awkcmd}" -v cnt="${adb_cnt}" 'BEGIN{res="";pos=0;for(i=length(cnt);i>0;i--){res=substr(cnt,i,1)res;pos++;if(pos==3&&i>1){res=" "res;pos=0;}}; printf"%s",res}')"
+
+       # check for custom feed and nft rules
+       #
        [ -s "${adb_customfeedfile}" ] && custom_feed="1"
        if [ "${adb_nftforce}" = "1" ] && [ -n "${adb_nftdevforce}" ] && [ -n "${adb_nftportforce}" ]; then
                nft_force="1"
@@ -1461,13 +1529,17 @@ f_jsnup() {
                && { [ -n "${adb_bridgednsv4}" ] || [ -n "${adb_bridgednsv6}" ]; }; then
                nft_bridge="1"
        fi
+
+       # update runtime information based on status
+       #
        case "${status}" in
                "enabled")
-                       if [ -n "${adb_starttime}" ] && [ "${adb_action}" != "boot" ]; then
-                               end_time="$(date "+%s")"
+                       if [ -n "${adb_starttime}" ]; then
+                               read -r end_time _ < "/proc/uptime"
+                               end_time="${end_time%.*}"
                                duration="$(((end_time - adb_starttime) / 60))m $(((end_time - adb_starttime) % 60))s"
                        fi
-                       runtime="mode: ${adb_action}, $(date -Iseconds), duration: ${duration:-"-"}, ${free_mem:-0} MB available"
+                       runtime="mode: ${adb_action}, $(date "+%d/%m/%Y %H:%M:%S"), duration: ${duration:-"-"}, ${free_mem:-0} MB available"
                        ;;
                "resume")
                        status="enabled"
@@ -1481,12 +1553,14 @@ f_jsnup() {
                        ;;
        esac
 
+       # update runtime file and send mail if enabled
+       #
        json_init
        if json_load_file "${adb_rtfile}" >/dev/null 2>&1; then
                [ -z "${adb_cnt}" ] && json_get_var adb_cnt "blocked_domains"
                [ -z "${runtime}" ] && json_get_var runtime "last_run"
                if [ "${status}" = "enabled" ]; then
-                       if [ "${adb_jail}" = "1" ] && [ "${adb_dnsstop}" != "0" ]; then
+                       if [ "${adb_jail}" = "1" ] && [ -n "${adb_dnsstop}" ]; then
                                jail="1"
                                adb_cnt="0"
                                feeds="restrictive jail (allowlist-only)"
@@ -1555,7 +1629,7 @@ f_main() {
 
        # jail mode preparation
        #
-       if [ "${adb_jail}" = "1" ] && [ "${adb_dnsstop}" != "0" ]; then
+       if [ "${adb_jail}" = "1" ] && [ -n "${adb_dnsstop}" ]; then
                "${adb_mvcmd}" -f "${adb_tmpdir}/${adb_dnsfile}" "${adb_finaldir}/${adb_dnsfile}"
                chown "${adb_dnsuser}" "${adb_finaldir}/${adb_dnsfile}" 2>>"${adb_errorlog}"
                if [ "${adb_dnsshift}" = "1" ] && [ ! -L "${adb_dnsdir}/${adb_dnsfile}" ]; then
@@ -1577,7 +1651,7 @@ f_main() {
 
        # safe search preparation
        #
-       if [ "${adb_safesearch}" = "1" ] && [ "${adb_dnssafesearch}" != "0" ]; then
+       if [ "${adb_safesearch}" = "1" ] && [ "${adb_dnssafesearch}" = "1" ]; then
                [ -z "${adb_safesearchlist}" ] && adb_safesearchlist="google bing brave duckduckgo pixabay yandex youtube"
                cnt="1"
                for entry in ${adb_safesearchlist}; do
@@ -1593,6 +1667,7 @@ f_main() {
        # main loop
        #
        cnt="1"
+       : >"${adb_tmpdir}/tmp.raw.allowlist"
        for src_name in ${adb_feed}; do
                if ! json_select "${src_name}" >/dev/null 2>&1; then
                        adb_feed="${adb_feed/${src_name}/}"
@@ -1622,9 +1697,9 @@ f_main() {
                #
                src_domain="${src_url#*://}"
                src_domain="${src_domain%%/*}"
-               if [ -n "${src_domain}" ] && [ "${adb_dnsallow}" != "0" ] && ! "${adb_grepcmd}" -qxF "${src_domain}" "${adb_tmpdir}/tmp.raw.allowlist"; then
+               if [ -n "${src_domain}" ] && [ "${adb_dnsallow}" = "1" ] && ! "${adb_grepcmd}" -qxF "${src_domain}" "${adb_tmpdir}/tmp.raw.allowlist"; then
                        printf "%s\n" "${src_domain}" >>"${adb_tmpdir}/tmp.raw.allowlist"
-                       eval "${adb_dnsallow}" "${adb_tmpdir}/tmp.raw.allowlist" >>"${adb_tmpdir}/tmp.add.allowlist"
+                       printf "%s\n" "${src_domain}" | f_dnsallow >>"${adb_tmpdir}/tmp.add.allowlist"
                fi
 
                # download queue processing
@@ -1644,7 +1719,7 @@ f_main() {
                                #
                                if [ "${adb_action}" = "boot" ] || [ "${adb_action}" = "start" ] || [ "${adb_action}" = "restart" ] || [ "${adb_action}" = "resume" ]; then
                                        if f_list restore && [ -s "${src_tmpfile}" ]; then
-                                               continue
+                                               exit 0
                                        fi
                                fi
 
@@ -1652,15 +1727,18 @@ f_main() {
                                #
                                if [ -n "${adb_etagparm}" ] && [ "${adb_action}" = "reload" ]; then
                                        etag_rc="0"
-                                       src_cnt="$(printf "%s" "${src_cat}" | "${adb_wccmd}" -w)"
+                                       src_cnt="0"
+                                       for _ in ${src_cat}; do
+                                               src_cnt="$((src_cnt + 1))"
+                                       done
                                        for suffix in ${src_cat}; do
                                                if ! f_etag "${src_name}" "${src_url}" "${suffix}" "${src_cnt}"; then
                                                        etag_rc="$((etag_rc + 1))"
                                                fi
                                        done
                                        if [ "${etag_rc}" = "0" ]; then
-                                               if f_list restore; then
-                                                       continue
+                                               if f_list restore && [ -s "${src_tmpfile}" ]; then
+                                                       exit 0
                                                fi
                                        fi
                                fi
@@ -1688,7 +1766,7 @@ f_main() {
                                #
                                if [ "${adb_action}" = "boot" ] || [ "${adb_action}" = "start" ] || [ "${adb_action}" = "restart" ] || [ "${adb_action}" = "resume" ]; then
                                        if f_list restore && [ -s "${src_tmpfile}" ]; then
-                                               continue
+                                               exit 0
                                        fi
                                fi
 
@@ -1697,7 +1775,7 @@ f_main() {
                                if [ -n "${adb_etagparm}" ] && [ "${adb_action}" = "reload" ]; then
                                        if f_etag "${src_name}" "${src_url}"; then
                                                if f_list restore && [ -s "${src_tmpfile}" ]; then
-                                                       continue
+                                                       exit 0
                                                fi
                                        fi
                                fi
@@ -1709,7 +1787,7 @@ f_main() {
                                                "${adb_fetchcmd}" ${adb_fetchparm} "${src_tmparchive}" "${src_url}" 2>>"${adb_errorlog}"
                                                src_rc="${?}"
                                                if [ "${src_rc}" = "0" ] && [ -s "${src_tmparchive}" ]; then
-                                                       src_suffix="$(eval printf "%s" \"\$\{adb_src_suffix_${src_name}:-\"domains\"\}\")"
+                                                       src_suffix="${adb_src_suffix_utcapitole:-"domains"}"
                                                        src_list="$(tar -tzf "${src_tmparchive}" 2>>"${adb_errorlog}")"
                                                        for src_item in ${src_cat}; do
                                                                src_entries="${src_entries} $(printf "%s" "${src_list}" | "${adb_grepcmd}" -E "${src_item}/${src_suffix}$")"
@@ -2058,7 +2136,13 @@ f_report() {
                json_get_keys key_list
                for key in ${key_list}; do
                        json_get_var value "${key}"
-                       eval "${key}=\"${value}\""
+                       case "${key}" in
+                               *[!a-z_]*)
+                               ;;
+                               *)
+                                       eval "${key}=\"${value}\""
+                               ;;
+                       esac
                done
                printf "  + %s\n  + %s\n" "Start    ::: ${start_date}, ${start_time}" "End      ::: ${end_date}, ${end_time}" >>"${report_txt}"
                printf "  + %s\n  + %s %s\n" "Total    ::: ${total}" "Blocked  ::: ${blocked}" "(${percent})" >>"${report_txt}"
@@ -2122,7 +2206,7 @@ f_report() {
                        "${adb_rmcmd}" -f "${report_txt}"
                        ;;
                "gen")
-                       printf "%s\n" "$(date "+%s")" >"/var/run/adblock.report"
+                       printf "%s\n" "1" >"/var/run/adblock.report"
                        ;;
        esac
 }
@@ -2167,9 +2251,9 @@ f_load
 case "${adb_action}" in
        "stop")
                f_temp
-               f_jsnup "stopped"
                f_nftremove
                f_rmdns
+               f_jsnup "stopped"
                ;;
        "suspend")
                [ "${adb_dns}" != "raw" ] && f_switch suspend
git clone https://git.99rst.org/PROJECT