adblock: release 4.5.4-1
authorDirk Brenken <redacted>
Thu, 9 Apr 2026 21:13:32 +0000 (23:13 +0200)
committerDirk Brenken <redacted>
Thu, 9 Apr 2026 21:13:53 +0000 (23:13 +0200)
* fixed a parsing issue in the DNS reporting,
  see https://github.com/openwrt/packages/pull/29063 for details
* optimized the CGI/Adblock Remote Allow
* optimized the TLD function
* optimized the mail include
* removed needless forks
* various code-cleanups & small fixes
* updated the readme
* LuCI: small fixes & optimizations

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

index 50e345f05114979212be62f071fcd25385be3327..bc6148db30633d36759f186c3ca3c7df7fca84cd 100644 (file)
@@ -6,8 +6,8 @@
 include $(TOPDIR)/rules.mk
 
 PKG_NAME:=adblock
-PKG_VERSION:=4.5.3
-PKG_RELEASE:=4
+PKG_VERSION:=4.5.4
+PKG_RELEASE:=1
 PKG_LICENSE:=GPL-3.0-or-later
 PKG_MAINTAINER:=Dirk Brenken <dev@brenken.org>
 
index 7393c9c5ffc076f025fadd7282f6c0ae300e4ba3..832394d3d52c543bc9b7126d19a07f44ff9741be 100644 (file)
@@ -83,7 +83,7 @@ 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
 
 <a id="prerequisites"></a>
@@ -95,7 +95,7 @@ When the DNS server on your router receives DNS requests, you will sort out quer
 * 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:**
+**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.
@@ -306,10 +306,10 @@ adblock's firewall rules are based on nftables in a separate isolated nftables t
 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:
-* The client opens the URL, e.g. https://\<ROUTER-IP\>cgi-bin/adblock (preferably transferred via QR code shown in LuCI)
+* The client opens the URL, e.g. http(s)://\<ROUTER-IP\>cgi-bin/adblock (preferably transferred via QR code shown in LuCI)
 * The script automatically detects the device’s MAC address
 * If the MAC is authorized, the script displays the current status:
-  * Not in the nftables set → option to request a temporary allow (“Renew”)
+  * Not in the nftables set → option to request a temporary allow (“Bypass”)
   * Already active → shows remaining timeout
 * When renewing, the CGI adds the MAC to an nftables Set with a per‑entry timeout
 
index f78858be97a4b8cf1a3bfbe72efd87a94adb8ea5..64530252297bc63f9d41dd4391b7edf72926891e 100644 (file)
@@ -16,21 +16,52 @@ nft_authorized="0"
 # parse query
 #
 query_str="${QUERY_STRING}"
-query_mac="$(printf "%s" "${query_str}" | sed -n 's/.*mac=\([^&]*\).*/\1/p' 2>/dev/null)"
-query_mode="$(printf "%s" "${query_str}" | sed -n 's/.*mode=\([^&]*\).*/\1/p' 2>/dev/null)"
+query_mac="${query_str#*mac=}"
+query_mac="${query_mac%%&*}"
+query_mode="${query_str#*mode=}"
+query_mode="${query_mode%%&*}"
+[ "${query_mac}" = "${query_str}" ] && query_mac=""
+[ "${query_mode}" = "${query_str}" ] && query_mode=""
+
+# URL decode helper
+#
+urldecode() {
+       printf '%b' "${1//%/\\x}"
+}
+
+# lowercase helper
+#
+tolower() {
+       local low="${1}"
+
+       low="${low//A/a}"
+       low="${low//B/b}"
+       low="${low//C/c}"
+       low="${low//D/d}"
+       low="${low//E/e}"
+       low="${low//F/f}"
+       printf '%s' "${low}"
+}
 
 # determine MAC if not provided
 #
 if [ -z "${query_mac}" ]; then
        query_ip="${REMOTE_ADDR}"
        query_mac="$(ip neigh show 2>/dev/null | awk -v ip="${query_ip}" '$1==ip {print $5; exit}' 2>/dev/null)"
+else
+       query_mac="$(urldecode "${query_mac}")"
 fi
 
 # validate MAC address
 #
-printf '%s\n' "${query_mac}" | grep -Eq '^([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}$' 2>/dev/null \
-       && query_mac="$(printf '%s\n' "${query_mac}" | awk '{ print tolower($0) }' 2>/dev/null)" \
-       || query_mac=""
+case "${query_mac}" in
+       [0-9A-Fa-f][0-9A-Fa-f]:[0-9A-Fa-f][0-9A-Fa-f]:[0-9A-Fa-f][0-9A-Fa-f]:[0-9A-Fa-f][0-9A-Fa-f]:[0-9A-Fa-f][0-9A-Fa-f]:[0-9A-Fa-f][0-9A-Fa-f])
+               query_mac="$(tolower "${query_mac}")"
+               ;;
+       *)
+               query_mac=""
+               ;;
+esac
 
 # validate mode
 #
@@ -38,8 +69,8 @@ printf '%s\n' "${query_mac}" | grep -Eq '^([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}$' 2
 
 # output header and start html
 #
-printf "%s\n\n" "Content-Type: text/html"
-printf "%s\n" "<!DOCTYPE html>
+printf '%s\n\n' "Content-Type: text/html"
+printf '%s\n' "<!DOCTYPE html>
 <html>
 <head>
 <meta charset=\"utf-8\">
@@ -104,7 +135,6 @@ printf "%s\n" "<!DOCTYPE html>
        .btn:hover {
                background: #6bb6ff;
        }
-
        .spinner {
                margin: 1.5rem auto;
                width: 40px;
@@ -134,15 +164,6 @@ function stopRefresh() {
                refreshTimer = null;
        }
 }
-
-function setStatus(mac) {
-       window.location.href = '?mac=' + mac;
-}
-
-function showSpinner() {
-       var s = document.getElementById('spinner');
-       if (s) s.style.display = 'block';
-}
 </script>
 
 </head>
@@ -151,34 +172,44 @@ function showSpinner() {
 <h1>Adblock Remote Allow</h1>
 "
 
-# check if remote allow is enabled
+# check if remote allow is enabled and MAC addresses are configured
 #
 if [ "${nft_remote}" != "1" ] || [ -z "${nft_macremote}" ]; then
-       printf "%s\n" "<div class=\"msg err\">Remote allow is not enabled or no MAC addresses configured</div></div></body></html>"
+       printf '%s\n' "
+               <div class=\"msg err\">
+                       Remote allow is not enabled or no MAC addresses configured
+               </div>
+       </div></body></html>"
        exit 0
 fi
 if [ -z "${query_mac}" ]; then
-       printf "%s\n" "<div class=\"msg err\">Could not determine MAC address</div></div></body></html>"
+       printf '%s\n' "
+               <div class=\"msg err\">
+                       Could not determine MAC address
+               </div>
+       </div></body></html>"
        exit 0
 fi
 
 # check MAC authorization
 #
-for mac in ${nft_macremote}; do
-       mac="$(printf '%s' "${mac}" | awk '{ print tolower($0) }')"
-       if [ "${mac}" = "${query_mac}" ]; then
+nft_macremote="$(tolower "${nft_macremote}")"
+case " ${nft_macremote} " in
+       *" ${query_mac} "*)
                nft_authorized="1"
-               break
-       fi
-done
+               ;;
+esac
 if [ "${nft_authorized}" = "0" ]; then
-       printf "%s\n" "<div class=\"msg err\">MAC ${query_mac} is not authorized to use remote allow</div></div></body></html>"
+       printf '%s\n' "
+               <div class=\"msg err\">
+                       This device (${query_mac}) is not registered for Adblock Remote Allow
+               </div>
+       </div></body></html>"
        exit 0
 fi
 
 # extract remaining timeout
 #
-# extract remaining timeout (strip ms part)
 remaining="$(nft list set inet adblock mac_remote 2>/dev/null | \
        awk -v mac="${query_mac}" '
                $0 ~ mac {
@@ -197,33 +228,34 @@ remaining="$(nft list set inet adblock mac_remote 2>/dev/null | \
 # show renew option
 #
 if [ -z "${query_mode}" ] && [ -z "${remaining}" ]; then
-       printf "%s\n" "<script>stopRefresh();</script>
+       printf '%s\n' "<script>stopRefresh();</script>
                <div class=\"msg ok\">
-                       MAC ${query_mac} is currently not in the remote allow Set<br><br>
-                       <a class=\"btn\" href=\"?mac=${query_mac}&mode=renew\">Renew Set Entry</a>
+                       This device currently does not bypass ad blocking<br>
+                       <a class=\"btn\" href=\"?mac=${query_mac}&mode=renew\">Bypass</a>
                </div>
        </div></body></html>"
        exit 0
 fi
 
-# add MAC
+# add MAC and redirect to main page to show remaining time
 #
-if [ -z "${remaining}" ]; then
-       printf "%s\n" "
+if [ -z "${remaining}" ] && [ "${query_mode}" = "renew" ]; then
+       printf '%s\n' "
                <div class=\"msg ok\">
-                       Renewing remote allow for MAC ${query_mac}<br><br>
+                       Adding device...<br>
                        <div class=\"spinner\"></div>
                </div>
        </div></body></html>"
-       nft add element inet adblock mac_remote "{ ${query_mac} }" >/dev/null 2>&1
-       printf "%s\n" "<script>setStatus('${query_mac}');</script>"
+       nft add element inet adblock mac_remote "{ ${query_mac//[!0-9a-f:]} }" >/dev/null 2>&1
+       printf '%s\n' "<script>window.location.href='?mac=${query_mac}';</script>"
+       exit 0
 fi
 
-# success message
+# show remaining time
 #
-printf "%s\n" "
+printf '%s\n' "
        <div class=\"msg ok\">
-               MAC ${query_mac} is temporarily allowed<br>
+               This device temporarily bypasses ad blocking<br>
                Remaining time: ${remaining:-${nft_remotetimeout}m}
        </div>
        <script>startRefresh();</script>
index 95349da5dfda561bd49bd8e3bf75a134c4ff9beb..47578ccf8f000e031b2c66e3501e71b64d282ecc 100755 (executable)
@@ -18,13 +18,17 @@ adb_script="/usr/bin/adblock.sh"
 adb_pidfile="/var/run/adblock.pid"
 
 if [ -z "${IPKG_INSTROOT}" ]; then
-       if [ "${action}" = "boot" ] && "${adb_init}" running; then
-               exit 0
-       elif [ -s "${adb_pidfile}" ] &&
-               { [ "${action}" = "start" ] || [ "${action}" = "stop" ] || [ "${action}" = "restart" ] ||
-               [ "${action}" = "reload" ] || [ "${action}" = "report" ] || [ "${action}" = "suspend" ] ||
-               [ "${action}" = "resume" ] || [ "${action}" = "search" ]; }; then
-               exit 1
+       case "${action}" in
+               "boot")
+                       "${adb_init}" running && exit 0
+                       ;;
+       esac
+       if [ -s "${adb_pidfile}" ]; then
+               case "${action}" in
+                       "start"|"stop"|"restart"|"reload"|"report"|"suspend"|"resume"|"search")
+                               exit 1
+                               ;;
+               esac
        fi
 fi
 
@@ -88,22 +92,22 @@ status_service() {
        json_load_file "/var/run/adb_runtime.json" >/dev/null 2>&1
        json_get_keys keylist
        if [ -n "${keylist}" ]; then
-               printf "%s\n" "::: adblock runtime information"
+               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_values values "${key}" >/dev/null 2>&1
                                value="${values}"
                        fi
-                       printf "  + %-15s : %s\n" "${key}" "${value:-"-"}"
+                       printf '  + %-15s : %s\n' "${key}" "${value:-"-"}"
                done
        else
-               printf "%s\n" "::: no adblock runtime information available"
+               printf '%s\n' "::: no adblock runtime information available"
        fi
 }
 
 service_triggers() {
-       local iface delay
+       local iface delay trigger
 
        delay="$(uci_get adblock global adb_triggerdelay "5")"
        trigger="$(uci_get adblock global adb_trigger)"
index 9d5475dfff994477b3ee167771d665b32e38799e..2c1740470649c24e28187a13e9f92ef95b5a50ff 100755 (executable)
@@ -19,38 +19,52 @@ adb_mailprofile="$(uci_get adblock global adb_mailprofile "adb_notify")"
 [ -z "${adb_mailreceiver}" ] && f_log "info" "please set the mail receiver with the 'adb_mailreceiver' option"
 [ "${adb_debug}" = "1" ] && debug="--debug"
 
-adb_mailhead="From: ${adb_mailsender}\nTo: ${adb_mailreceiver}\nSubject: ${adb_mailtopic}\nReply-to: ${adb_mailsender}\nMime-Version: 1.0\nContent-Type: text/html;charset=utf-8\nContent-Disposition: inline\n\n"
-
 # info preparation
 #
-sys_info="$("${adb_catcmd}" /etc/banner 2>/dev/null; "${adb_ubuscmd}" call system board | "${adb_awkcmd}" 'BEGIN{FS="[{}\"]"}{if($2=="kernel"||$2=="hostname"||$2=="system"||$2=="model"||$2=="description")printf "  + %-12s: %s\n",$2,$4}' 2>/dev/null)"
+sys_info="$(strings /etc/banner 2>/dev/null; "${adb_ubuscmd}" call system board | "${adb_awkcmd}" 'BEGIN{FS="[{}\"]"}{if($2=="kernel"||$2=="hostname"||$2=="system"||$2=="model"||$2=="description")printf "  + %-12s: %s\n",$2,$4}' 2>/dev/null)"
 adb_info="$(/etc/init.d/adblock status 2>/dev/null)"
 rep_info="${1}"
 if [ -x "${adb_logreadcmd}" ]; then
-       log_info="$("${adb_logreadcmd}" -l 100 -e "adblock-" | "${adb_awkcmd}" '{NR=1;max=120;if(length($0)>max+1)while($0){if(NR==1){print substr($0,1,max)}else{print substr($0,1,max)}{$0=substr($0,max+1);NR=NR+1}}else print}')"
+       log_info="$("${adb_logreadcmd}" -l 100 -e "adblock-" 2>/dev/null)"
 fi
 
 # mail body
 #
-adb_mailtext="<html><body><pre style='display:block;font-family:monospace;font-size:1rem;padding:20;background-color:#f3eee5;white-space:pre'>"
-adb_mailtext="${adb_mailtext}\n<strong>++\n++ System Information ++\n++</strong>\n${sys_info}"
-adb_mailtext="${adb_mailtext}\n\n<strong>++\n++ Adblock Information ++\n++</strong>\n${adb_info}"
-if [ -n "${rep_info}" ]; then
-       adb_mailtext="${adb_mailtext}\n\n<strong>++\n++ Report Information ++\n++</strong>\n${rep_info}"
-fi
-adb_mailtext="${adb_mailtext}\n\n<strong>++\n++ Logfile Information ++\n++</strong>\n${log_info}"
-adb_mailtext="${adb_mailtext}</pre></body></html>"
+adb_mailtext="$(
+       printf '%s\n' "<html><body><pre style='display:block;font-family:monospace;font-size:1rem;padding:20;background-color:#f3eee5;white-space:pre'>"
+       printf '\n%s\n' "<strong>++
+++ System Information ++
+++</strong>"
+       printf '%s\n' "${sys_info:-"-"}"
+       printf '\n%s\n' "<strong>++
+++ Adblock Information ++
+++</strong>"
+       printf '%s\n' "${adb_info:-"-"}"
+       if [ -n "${rep_info}" ]; then
+               printf '\n%s\n' "<strong>++
+++ Report Information ++
+++</strong>"
+               printf '%s\n' "${rep_info}"
+       fi
+       printf '\n%s\n' "<strong>++
+++ Logfile Information ++
+++</strong>"
+       printf '%s\n' "${log_info:-"-"}"
+       printf '%s\n' "</pre></body></html>"
+)"
 
 # send mail
 #
 if [ -x "${adb_mailcmd}" ]; then
-       printf "%b" "${adb_mailhead}${adb_mailtext}" 2>/dev/null | "${adb_mailcmd}" ${debug} -a "${adb_mailprofile}" "${adb_mailreceiver}" 2>>"${adb_errorlog}"
-       if [ "${?}" = "0" ]; then
+       adb_mailhead="From: ${adb_mailsender}\nTo: ${adb_mailreceiver}\nSubject: ${adb_mailtopic}\nReply-to: ${adb_mailsender}\nMime-Version: 1.0\nContent-Type: text/html;charset=utf-8\nContent-Disposition: inline\n\n"
+       printf '%b' "${adb_mailhead}${adb_mailtext}" 2>/dev/null | "${adb_mailcmd}" ${debug} -a "${adb_mailprofile}" "${adb_mailreceiver}" 2>>"${adb_errorlog}"
+       mail_rc="${?}"
+       if [ "${mail_rc}" = "0" ]; then
                f_log "info" "mail successfully sent to '${adb_mailreceiver}'"
        else
-               f_log "info" "failed to send mail to '${adb_mailreceiver}' with rc '${?}'"
+               f_log "info" "failed to send mail to '${adb_mailreceiver}' with rc '${mail_rc}'"
        fi
 else
        f_log "info" "msmtp mail daemon not found"
 fi
-exit 0
\ No newline at end of file
+exit 0
index 46a39d92c670959c1aacc278a391bc94164d4f7d..c69de20a4cd79ce752307177c7ff88994dd48f62 100755 (executable)
@@ -89,12 +89,12 @@ f_cmd() {
                        cmd="$(command -v "${sec_cmd}" 2>/dev/null)"
                fi
                if [ -x "${cmd}" ]; then
-                       printf "%s" "${cmd}"
+                       printf '%s' "${cmd}"
                else
                        f_log "emerg" "command '${pri_cmd:-"-"}'/'${sec_cmd:-"-"}' not found"
                fi
        else
-               printf "%s" "${cmd}"
+               printf '%s' "${cmd}"
        fi
 }
 
@@ -115,8 +115,8 @@ f_load() {
        # fetch installed packages amd system information
        #
        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_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_awkcmd}" 'BEGIN{RS="";FS="\n"}{printf "%s, %s, %s %s (%s)",$1,$2,$3,$4,$5}')"
@@ -236,9 +236,9 @@ f_conf() {
                                *)
                                        eval "append=\"\${${option}}\""
                                        if [ -n "${append}" ]; then
-                                               eval "${option}=\"${append} ${value}\""
+                                               eval "${option}=\"\${append} \${value}\""
                                        else
-                                               eval "${option}=\"${value}\""
+                                               eval "${option}=\"\${value}\""
                                        fi
                                        ;;
                        esac
@@ -322,7 +322,7 @@ f_char() {
        else
                result="✘"
        fi
-       printf "%s" "${result}"
+       printf '%s' "${result}"
 }
 
 # load dns backend config
@@ -338,7 +338,7 @@ f_dns() {
        if [ -z "${adb_dns}" ]; then
                dns_list="knot-resolver bind-server unbound-daemon smartdns dnsmasq-full dnsmasq-dhcpv6 dnsmasq"
                for dns in ${dns_list}; do
-                       if printf "%s" "${adb_packages}" | "${adb_jsoncmd}" -ql1 -e "@.packages[\"${dns}\"]" >/dev/null 2>&1; then
+                       if printf '%s' "${adb_packages}" | "${adb_jsoncmd}" -ql1 -e "@.packages[\"${dns}\"]" >/dev/null 2>&1; then
                                case "${dns}" in
                                        "knot-resolver")
                                                dns="kresd"
@@ -376,11 +376,11 @@ f_dns() {
                        adb_dnsdir="${adb_dnsdir:-""}"
                        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"]')"
+                               dns_info="$(printf '%s' "${dns_section}" | "${adb_jsoncmd}" -l1 -e '@.values["confdir"]')"
                                if [ -n "${dns_info}" ]; then
                                        adb_dnsdir="${dns_info}"
                                else
-                                       dns_info="$(printf "%s" "${dns_section}" | "${adb_jsoncmd}" -l1 -e '@.values[".name"]')"
+                                       dns_info="$(printf '%s' "${dns_section}" | "${adb_jsoncmd}" -l1 -e '@.values[".name"]')"
                                        [ -n "${dns_info}" ] && adb_dnsdir="/tmp/dnsmasq.${dns_info}.d"
                                fi
                        fi
@@ -518,10 +518,10 @@ f_dns() {
                        [ ! -d "${dir}" ] && mkdir -p "${dir}"
                done
                if [ "${adb_dnsflush}" = "1" ] || [ "${free_mem}" -lt "64" ]; then
-                       printf "%b" "${adb_dnsheader}" >"${adb_finaldir}/${adb_dnsfile}"
+                       printf '%b' "${adb_dnsheader}" >"${adb_finaldir}/${adb_dnsfile}"
                        f_dnsup
                elif [ ! -f "${adb_finaldir}/${adb_dnsfile}" ]; then
-                       printf "%b" "${adb_dnsheader}" >"${adb_finaldir}/${adb_dnsfile}"
+                       printf '%b' "${adb_dnsheader}" >"${adb_finaldir}/${adb_dnsfile}"
                fi
        fi
 
@@ -549,7 +549,7 @@ f_fetch() {
        if [ ! -x "${adb_fetchcmd}" ]; then
                fetch_list="curl wget-ssl libustream-openssl libustream-wolfssl libustream-mbedtls"
                for fetch in ${fetch_list}; do
-                       if printf "%s" "${adb_packages}" | "${adb_grepcmd}" -q "\"${fetch}"; then
+                       case "${adb_packages}" in *"\"${fetch}"*)
                                case "${fetch}" in
                                        "wget-ssl")
                                                fetch="wget"
@@ -565,7 +565,8 @@ f_fetch() {
                                        f_uci "adblock"
                                        break
                                fi
-                       fi
+                               ;;
+                       esac
                done
        fi
 
@@ -605,7 +606,7 @@ f_temp() {
        else
                f_log "err" "the base directory '${adb_basedir}' does not exist/is not mounted yet, please create the directory or raise the 'adb_triggerdelay' to defer the adblock start"
        fi
-       [ ! -s "${adb_pidfile}" ] && printf "%s" "${$}" >"${adb_pidfile}"
+       [ ! -s "${adb_pidfile}" ] && printf '%s' "${$}" >"${adb_pidfile}"
 }
 
 # remove temporary files and directories
@@ -619,7 +620,7 @@ f_rmtemp() {
 # remove dns related files
 #
 f_rmdns() {
-       printf "%b" "${adb_dnsheader}" >"${adb_finaldir}/${adb_dnsfile}"
+       printf '%b' "${adb_dnsheader}" >"${adb_finaldir}/${adb_dnsfile}"
        f_dnsup
        f_rmtemp
        if [ -d "${adb_backupdir}" ] && { [ "${adb_action}" = "stop" ] || [ "${adb_enabled}" = "0" ]; }; then
@@ -635,7 +636,7 @@ f_uci() {
        if [ -n "$(uci -q changes "${config}")" ]; then
                uci_commit "${config}"
                if [ "${config}" = "resolver" ]; then
-                       printf "%b" "${adb_dnsheader}" >"${adb_finaldir}/${adb_dnsfile}"
+                       printf '%b' "${adb_dnsheader}" >"${adb_finaldir}/${adb_dnsfile}"
                        adb_cnt="0"
                        f_jsnup "processing"
                        "/etc/init.d/${adb_dns}" reload >/dev/null 2>&1
@@ -652,7 +653,7 @@ f_count() {
        if [ -s "${file}" ]; then
                adb_cnt="$("${adb_wccmd}" -l 2>>"${adb_errorlog}" <"${file}")"
                if [ -n "${var}" ]; then
-                       printf "%s" "${adb_cnt}"
+                       printf '%s' "${adb_cnt}"
                else
                        if [ "${mode}" = "final" ]; then
                                if [ -s "${adb_tmpdir}/tmp.add.allowlist" ]; then
@@ -663,7 +664,7 @@ f_count() {
                                                adb_cnt="$((adb_cnt - $("${adb_wccmd}" -l 2>>"${adb_errorlog}" <"${file}")))"
                                        fi
                                done
-                               [ -n "${adb_dnsheader}" ] && adb_cnt="$(((adb_cnt - $(printf "%b" "${adb_dnsheader}" | "${adb_grepcmd}" -c "^")) / 2))"
+                               [ -n "${adb_dnsheader}" ] && adb_cnt="$(((adb_cnt - $(printf '%b' "${adb_dnsheader}" | "${adb_grepcmd}" -c "^")) / 2))"
                        fi
                fi
        fi
@@ -811,10 +812,10 @@ f_etag() {
 
        [ ! -f "${adb_backupdir}/adblock.etag" ] && : >"${adb_backupdir}/adblock.etag"
        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}')"
-       etag_id="$(printf "%s" "${http_head}" | "${adb_awkcmd}" 'tolower($0)~/^[[:space:]]*etag: /{gsub("\"","");printf "%s",$2}')"
+       http_code="$(printf '%s' "${http_head}" | "${adb_awkcmd}" 'tolower($0)~/^http\/[0123\.]+ /{printf "%s",$2}')"
+       etag_id="$(printf '%s' "${http_head}" | "${adb_awkcmd}" 'tolower($0)~/^[[:space:]]*etag: /{gsub("\"","");printf "%s",$2}')"
        if [ -z "${etag_id}" ]; then
-               etag_id="$(printf "%s" "${http_head}" | "${adb_awkcmd}" 'tolower($0)~/^[[:space:]]*last-modified: /{gsub(/[Ll]ast-[Mm]odified:|[[:space:]]|,|:/,"");printf "%s\n",$1}')"
+               etag_id="$(printf '%s' "${http_head}" | "${adb_awkcmd}" 'tolower($0)~/^[[:space:]]*last-modified: /{gsub(/[Ll]ast-[Mm]odified:|[[:space:]]|,|:/,"");printf "%s\n",$1}')"
        fi
        etag_cnt="$("${adb_grepcmd}" -c "^${feed} " "${adb_backupdir}/adblock.etag")"
        if [ "${http_code}" = "200" ] && [ "${etag_cnt}" = "${feed_cnt}" ] && [ -n "${etag_id}" ] &&
@@ -826,7 +827,7 @@ f_etag() {
                else
                        "${adb_sedcmd}" -i "/^${feed} ${feed_suffix//\//\\/}/d" "${adb_backupdir}/adblock.etag"
                fi
-               printf "%-80s%s\n" "${feed} ${feed_suffix}" "${etag_id}" >>"${adb_backupdir}/adblock.etag"
+               printf '%-80s%s\n' "${feed} ${feed_suffix}" "${etag_id}" >>"${adb_backupdir}/adblock.etag"
                out_rc="2"
        fi
 
@@ -868,51 +869,48 @@ f_nftadd() {
        {
                # nft header (tables, sets, base and regular chains)
                #
-               printf "%s\n\n" "#!${adb_nftcmd} -f"
-               if "${adb_nftcmd}" -t list table inet adblock >/dev/null 2>&1; then
-                       printf "%s\n" "delete table inet adblock"
-               fi
-               printf "%s\n" "add table inet adblock"
+               printf '%s\n\n' "#!${adb_nftcmd} -f"
+               printf '%s\n' "add table inet adblock"
 
                # allow Set
                #
                if [ "${adb_nftallow}" = "1" ] && [ -n "${adb_nftmacallow}" ]; then
-                       printf "%s\n" "add set inet adblock mac_allow { type ether_addr; flags interval; auto-merge; elements = { ${adb_nftmacallow// /, } }; }"
+                       printf '%s\n' "add set inet adblock mac_allow { type ether_addr; flags interval; auto-merge; elements = { ${adb_nftmacallow// /, } }; }"
                fi
 
                # remote allow Set with timeout, for MACs that should be temporary allowed to bypass dns blocking
                #
                if [ "${adb_nftremote}" = "1" ] && [ -n "${adb_nftmacremote}" ]; then
-                       printf "%s\n" "add set inet adblock mac_remote { type ether_addr; flags timeout; timeout ${adb_nftremotetimeout}m; }"
+                       printf '%s\n' "add set inet adblock mac_remote { type ether_addr; flags timeout; timeout ${adb_nftremotetimeout}m; }"
                fi
 
                # adblock pre-routing chain for allow/block rules
                #
                if [ "${adb_nftblock}" = "1" ] && [ -n "${adb_nftmacblock}" ]; then
-                       printf "%s\n" "add set inet adblock mac_block { type ether_addr; flags interval; auto-merge; elements = { ${adb_nftmacblock// /, } }; }"
+                       printf '%s\n' "add set inet adblock mac_block { type ether_addr; flags interval; auto-merge; elements = { ${adb_nftmacblock// /, } }; }"
                fi
-               printf "%s\n" "add chain inet adblock pre-routing { type nat hook prerouting priority -150; policy accept; }"
-               printf "%s\n" "add chain inet adblock _reject"
+               printf '%s\n' "add chain inet adblock pre-routing { type nat hook prerouting priority -150; policy accept; }"
+               printf '%s\n' "add chain inet adblock _reject"
 
                # dns-bridge base chain
                #
-               printf "%s\n" "add chain inet adblock dns-bridge { type nat hook prerouting priority -160; policy accept; }"
+               printf '%s\n' "add chain inet adblock dns-bridge { type nat hook prerouting priority -160; policy accept; }"
 
                # reject chain rules
                #
-               printf "%s\n" "add rule inet adblock _reject meta l4proto tcp counter reject with tcp reset"
-               printf "%s\n" "add rule inet adblock _reject counter reject with icmpx host-unreachable"
+               printf '%s\n' "add rule inet adblock _reject meta l4proto tcp counter reject with tcp reset"
+               printf '%s\n' "add rule inet adblock _reject counter reject with icmpx host-unreachable"
 
                # external allow rules
                #
                if [ "${adb_nftallow}" = "1" ]; then
                        if [ -n "${adb_nftmacallow}" ]; then
-                               [ -n "${adb_allowdnsv4}" ] && printf "%s\n" "add rule inet adblock pre-routing meta nfproto ipv4 ether saddr @mac_allow 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 meta nfproto ipv6 ether saddr @mac_allow meta l4proto { udp, tcp } th dport 53 counter dnat to [${adb_allowdnsv6}]:53"
+                               [ -n "${adb_allowdnsv4}" ] && printf '%s\n' "add rule inet adblock pre-routing meta nfproto ipv4 ether saddr @mac_allow 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 meta nfproto ipv6 ether saddr @mac_allow meta l4proto { udp, tcp } th dport 53 counter dnat to [${adb_allowdnsv6}]:53"
                        fi
                        for device in ${adb_nftdevallow}; do
-                               [ -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"
+                               [ -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:-"-"}"
                fi
@@ -920,8 +918,8 @@ f_nftadd() {
                # external remote allow rules
                #
                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"
+                       [ -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"
                fi
 
@@ -929,12 +927,12 @@ f_nftadd() {
                #
                if [ "${adb_nftblock}" = "1" ]; then
                        if [ -n "${adb_nftmacblock}" ]; then
-                               [ -n "${adb_blockdnsv4}" ] && printf "%s\n" "add rule inet adblock pre-routing meta nfproto ipv4 ether saddr @mac_block 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 meta nfproto ipv6 ether saddr @mac_block meta l4proto { udp, tcp } th dport 53 counter dnat to [${adb_blockdnsv6}]:53"
+                               [ -n "${adb_blockdnsv4}" ] && printf '%s\n' "add rule inet adblock pre-routing meta nfproto ipv4 ether saddr @mac_block 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 meta nfproto ipv6 ether saddr @mac_block meta l4proto { udp, tcp } th dport 53 counter dnat to [${adb_blockdnsv6}]:53"
                        fi
                        for device in ${adb_nftdevblock}; do
-                               [ -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"
+                               [ -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:-"-"}"
                fi
@@ -950,7 +948,7 @@ f_nftadd() {
                                        *" ${device} "*)
                                                ;;
                                        *)      [ -n "${devices}" ] && devices="${devices} ${device}" || devices="${device}"
-                                               printf "%s\n" "add rule inet adblock pre-routing iifname \"${device}\" return"
+                                               printf '%s\n' "add rule inet adblock pre-routing iifname \"${device}\" return"
                                                ;;
                                esac
                        done
@@ -959,19 +957,19 @@ f_nftadd() {
                        #
                        for device in ${adb_nftdevforce}; do
                                if [ "${adb_nftallow}" = "1" ] && [ -n "${adb_nftmacallow}" ]; then
-                                       printf "%s\n" "add rule inet adblock pre-routing iifname \"${device}\" ether saddr @mac_allow return"
+                                       printf '%s\n' "add rule inet adblock pre-routing iifname \"${device}\" ether saddr @mac_allow return"
                                fi
                                if [ "${adb_nftblock}" = "1" ] && [ -n "${adb_nftmacblock}" ]; then
-                                       printf "%s\n" "add rule inet adblock pre-routing iifname \"${device}\" ether saddr @mac_block return"
+                                       printf '%s\n' "add rule inet adblock pre-routing iifname \"${device}\" ether saddr @mac_block return"
                                fi
 
                                # dns enforce rules
                                #
                                for port in ${adb_nftportforce}; do
                                        if [ "${port}" = "53" ]; then
-                                               printf "%s\n" "add rule inet adblock pre-routing iifname \"${device}\" meta nfproto { ipv4, ipv6 } meta l4proto { udp, tcp } th dport ${port} counter redirect to :${port}"
+                                               printf '%s\n' "add rule inet adblock pre-routing iifname \"${device}\" meta nfproto { ipv4, ipv6 } meta l4proto { udp, tcp } th dport ${port} counter redirect to :${port}"
                                        else
-                                               printf "%s\n" "add rule inet adblock pre-routing iifname \"${device}\" meta nfproto { ipv4, ipv6 } meta l4proto { udp, tcp } th dport ${port} counter goto _reject"
+                                               printf '%s\n' "add rule inet adblock pre-routing iifname \"${device}\" meta nfproto { ipv4, ipv6 } meta l4proto { udp, tcp } th dport ${port} counter goto _reject"
                                        fi
                                done
                        done
@@ -992,8 +990,8 @@ f_nftremove() {
 
        if "${adb_nftcmd}" -t list table inet adblock >/dev/null 2>&1; then
                {
-                       printf "%s\n" "#!${adb_nftcmd} -f"
-                       printf "%s\n" "delete table inet adblock"
+                       printf '%s\n' "#!${adb_nftcmd} -f"
+                       printf '%s\n' "delete table inet adblock"
                } >"${file}"
 
                if "${adb_nftcmd}" -f "${file}" 2>>"${adb_errorlog}"; then
@@ -1035,15 +1033,15 @@ f_list() {
                                "allowlist")
                                        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}"
+                                               [ "${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}"
                                                f_dnsallow "${file_name}" >"${adb_tmpdir}/tmp.add.${src_name}"
                                                out_rc="${?}"
                                                if [ "${adb_jail}" = "1" ] && [ -n "${adb_dnsstop}" ]; then
-                                                       printf "%b" "${adb_dnsheader}" >"${adb_tmpdir}/${adb_dnsfile}"
+                                                       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}"
+                                                       printf '%b\n' "${adb_dnsstop}" >>"${adb_tmpdir}/${adb_dnsfile}"
                                                fi
                                        fi
                                        ;;
@@ -1098,7 +1096,7 @@ f_list() {
                                        safe_ips="$("${adb_lookupcmd}" "${safe_cname}" 2>>"${adb_errorlog}" | "${adb_awkcmd}" '/^Address[ 0-9]*: /{ORS=" ";print $NF}')"
                                fi
                                if [ -n "${safe_ips}" ] || [ "${use_cname}" = "1" ]; then
-                                       printf "%s\n" ${safe_domains} >"${adb_tmpdir}/tmp.raw.safesearch.${src_name}"
+                                       printf '%s\n' ${safe_domains} >"${adb_tmpdir}/tmp.raw.safesearch.${src_name}"
                                        [ "${use_cname}" = "1" ] && array="${safe_cname}" || array="${safe_ips}"
                                fi
                        fi
@@ -1192,21 +1190,20 @@ f_list() {
 
                        # merge files
                        #
-                       files="$(printf "%s\n" "${adb_tmpfile}".*)"
-                       if [ "${files}" = "${adb_tmpfile}.*" ]; then
-                               : > "${file_name}"
-                               out_rc="4"
-                       else
-                               "${adb_sortcmd}" ${adb_srtopts} -mu ${files} 2>>"${adb_errorlog}" > "${file_name}"
+                       for file in "${adb_tmpfile}".*; do
+                               [ -e "${file}" ] || continue
+                               "${adb_sortcmd}" ${adb_srtopts} -mu "${adb_tmpfile}".* 2>>"${adb_errorlog}" > "${file_name}"
                                out_rc="${?}"
-                       fi
+                               break
+                       done
+                       [ -z "${out_rc}" ] && { : > "${file_name}"; out_rc="4"; }
                        "${adb_rmcmd}" -f "${adb_tmpfile}".*
                        ;;
                "final")
                        src_name=""
                        file_name="${adb_finaldir}/${adb_dnsfile}"
                        "${adb_rmcmd}" -f "${file_name}"
-                       [ -n "${adb_dnsheader}" ] && printf "%b" "${adb_dnsheader}" >>"${file_name}"
+                       [ -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}" = "1" ]; then
@@ -1234,11 +1231,28 @@ f_list() {
 f_tld() {
        local cnt_tld cnt_rem source="${1}" temp_tld="${1}.tld"
 
-       if "${adb_awkcmd}" '{if(NR==1){tld=$NF};while(getline){if(index($NF,tld".")==0){print tld;tld=$NF}}print tld}' "${source}" |
-               "${adb_awkcmd}" 'BEGIN{FS="."}{out=$NF;for(i=NF-1;i>=1;i--)out=out"."$i;print out}' >"${temp_tld}"; then
+       # reverse domain, get unique tlds and unreverse them back to original form
+       #
+       if "${adb_awkcmd}" '
+               function unreverse(dom, n, seg, out, i) {
+                       n = split(dom, seg, ".")
+                       out = seg[n]
+                       for (i = n-1; i >= 1; i--) out = out "." seg[i]
+                       return out
+               }
+               {
+                       if (NR == 1) { tld = $NF }
+                       else if (index($NF, tld ".") == 0) { print unreverse(tld); tld = $NF }
+               }
+               END { if (tld) print unreverse(tld) }
+       ' "${source}" >"${temp_tld}"; then
                [ "${adb_debug}" = "1" ] && cnt_tld="$(f_count tld "${temp_tld}" "var")"
+
+               # remove allowlisted domains from tld list if allowlist is enabled and not empty
+               #
                if [ -s "${adb_tmpdir}/tmp.rem.allowlist" ]; then
                        "${adb_awkcmd}" 'NR==FNR{del[$0];next}!($0 in del)' "${adb_tmpdir}/tmp.rem.allowlist" "${temp_tld}" > "${source}"
+                       "${adb_rmcmd}" -f "${temp_tld}"
                        [ "${adb_debug}" = "1" ] && cnt_rem="$(f_count tld "${source}" "var")"
                else
                        "${adb_mvcmd}" -f "${temp_tld}" "${source}"
@@ -1283,11 +1297,11 @@ f_switch() {
                else
                        if [ "${adb_dnsshift}" = "0" ] && [ -f "${adb_finaldir}/${adb_dnsfile}" ]; then
                                "${adb_mvcmd}" -f "${adb_finaldir}/${adb_dnsfile}" "${adb_backupdir}/${adb_dnsfile}"
-                               printf "%b" "${adb_dnsheader}" >"${adb_finaldir}/${adb_dnsfile}"
+                               printf '%b' "${adb_dnsheader}" >"${adb_finaldir}/${adb_dnsfile}"
                                done="dns"
                        elif [ "${adb_dnsshift}" = "1" ] && [ -L "${adb_dnsdir}/${adb_dnsfile}" ]; then
                                "${adb_rmcmd}" -f "${adb_dnsdir}/${adb_dnsfile}"
-                               printf "%b" "${adb_dnsheader}" >"${adb_dnsdir}/${adb_dnsfile}"
+                               printf '%b' "${adb_dnsheader}" >"${adb_dnsdir}/${adb_dnsfile}"
                                done="dns"
                        fi
                fi
@@ -1348,31 +1362,11 @@ f_search() {
        # input validation
        #
        case "${domain}" in
-               "")
-                       printf "%s\n" "::: invalid input, please submit a single (sub-)domain :::"
-                       printf "%s\n" "::: invalid input, please submit a single (sub-)domain :::" >"${result}"
-                       return
-               ;;
-               *[!a-zA-Z0-9.-]*)
-                       printf "%s\n" "::: invalid input, please submit a single (sub-)domain :::"
-                       printf "%s\n" "::: invalid input, please submit a single (sub-)domain :::" >"${result}"
-                       return
-               ;;
-               -*)
-                       printf "%s\n" "::: invalid input, please submit a single (sub-)domain :::"
-                       printf "%s\n" "::: invalid input, please submit a single (sub-)domain :::" >"${result}"
-                       return
-               ;;
-               *-)
-                       printf "%s\n" "::: invalid input, please submit a single (sub-)domain :::"
-                       printf "%s\n" "::: invalid input, please submit a single (sub-)domain :::" >"${result}"
-                       return
-               ;;
-               *..*|*.)
-                       printf "%s\n" "::: invalid input, please submit a single (sub-)domain :::"
-                       printf "%s\n" "::: invalid input, please submit a single (sub-)domain :::" >"${result}"
+               ""|*[!a-zA-Z0-9.-]*|-*|*-|*..*|*.)
+                       printf '%s\n' "::: invalid input, please submit a single (sub-)domain :::"
+                       printf '%s\n' "::: invalid input, please submit a single (sub-)domain :::" >"${result}"
                        return
-               ;;
+                       ;;
        esac
 
        # length validation for domain part, max. 253 characters according to RFC 1035
@@ -1381,8 +1375,8 @@ f_search() {
                [0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-3])
                ;;
                *)
-                       printf "%s\n" "::: invalid input, domain exceeds 253 characters :::"
-                       printf "%s\n" "::: invalid input, domain exceeds 253 characters :::" >"${result}"
+                       printf '%s\n' "::: invalid input, domain exceeds 253 characters :::"
+                       printf '%s\n' "::: invalid input, domain exceeds 253 characters :::" >"${result}"
                        return
                ;;
        esac
@@ -1430,15 +1424,15 @@ f_search() {
        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}")"
-               printf "%s\n%s\n%s\n" ":::" "::: domain '${domain}' in active blocklist" ":::" >>"${tmp_result}"
-               printf "%s\n\n" "${res:-"  - no match"}" >>"${tmp_result}"
+               printf '%s\n%s\n%s\n' ":::" "::: domain '${domain}' in active blocklist" ":::" >>"${tmp_result}"
+               printf '%s\n\n' "${res:-"  - no match"}" >>"${tmp_result}"
                [ "${domain}" = "${tld}" ] && break
                domain="${tld}"
                tld="${domain#*.}"
        done
        if [ -d "${adb_backupdir}" ]; then
                search="${1//./\\.}"
-               printf "%s\n%s\n%s\n" ":::" "::: domain '${1}' in backups and in local block-/allowlist" ":::" >>"${tmp_result}"
+               printf '%s\n%s\n%s\n' ":::" "::: domain '${1}' in backups and in local block-/allowlist" ":::" >>"${tmp_result}"
                for file in "${adb_backupdir}/adb_list".*.gz "${adb_blocklist}" "${adb_allowlist}"; do
                        suffix="${file##*.}"
                        if [ "${suffix}" = "gz" ]; then
@@ -1460,15 +1454,15 @@ f_search() {
                                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}"
+                                       printf '%s\n\n' "  - [...]" >>"${tmp_result}"
                                        break
                                fi
                        fi
                done
-               [ "${res}" != "true" ] && printf "%s\n\n" "  - no match" >>"${tmp_result}"
+               [ "${res}" != "true" ] && printf '%s\n\n' "  - no match" >>"${tmp_result}"
        fi
        "${adb_mvcmd}" -f "${tmp_result}" "${result}"
-       "${adb_catcmd}" "${result}"
+       printf '%s\n' "$(< "${result}")"
 }
 
 # update runtime information
@@ -1499,7 +1493,7 @@ f_jsnup() {
                                dns='dnsmasq", "dnsmasq-full", "dnsmasq-dhcpv6'
                                ;;
                esac
-               dns_ver="$(printf "%s" "${adb_packages}" | "${adb_jsoncmd}" -ql1 -e "@.packages[\"${dns:-"${adb_dns}"}\"]")"
+               dns_ver="$(printf '%s' "${adb_packages}" | "${adb_jsoncmd}" -ql1 -e "@.packages[\"${dns:-"${adb_dns}"}\"]")"
                dns_mem="$("${adb_awkcmd}" -v mem="${dns_mem}" 'BEGIN{printf "%.2f", mem/1024}' 2>>"${adb_errorlog}")"
        fi
        free_mem="$("${adb_awkcmd}" '/^MemAvailable/{printf "%.2f", $2/1024}' "/proc/meminfo" 2>>"${adb_errorlog}")"
@@ -1539,7 +1533,7 @@ f_jsnup() {
                                end_time="${end_time%.*}"
                                duration="$(((end_time - adb_starttime) / 60))m $(((end_time - adb_starttime) % 60))s"
                        fi
-                       runtime="mode: ${adb_action}, $(date "+%d/%m/%Y %H:%M:%S"), duration: ${duration:-"-"}, ${free_mem:-0} MB available"
+                       runtime="mode: ${adb_action}, date / time: $(date "+%d/%m/%Y %H:%M:%S"), duration: ${duration:-"-"}, memory: ${free_mem:-0} MB available"
                        ;;
                "resume")
                        status="enabled"
@@ -1565,11 +1559,11 @@ f_jsnup() {
                                adb_cnt="0"
                                feeds="restrictive jail (allowlist-only)"
                        else
-                               feeds="$(printf "%s\n" ${adb_feed// /, } | ${adb_sortcmd} | xargs)"
+                               feeds="$(printf '%s\n' ${adb_feed// /, } | ${adb_sortcmd} | xargs)"
                        fi
                fi
        fi
-       printf "%s\n" "{}" >"${adb_rtfile}"
+       printf '%s\n' "{}" >"${adb_rtfile}"
        json_init
        json_load_file "${adb_rtfile}" >/dev/null 2>&1
        json_add_string "adblock_status" "${status}"
@@ -1601,7 +1595,7 @@ f_log() {
 
        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}"
+                       printf '%s %s %s\n' "${class}" "adblock-${adb_bver}[${$}]" "${log_msg::256}"
                if [ "${class}" = "err" ] || [ "${class}" = "emerg" ]; then
                        [ "${adb_action}" != "mail" ] && f_rmdns
                        f_jsnup "error"
@@ -1698,8 +1692,8 @@ f_main() {
                src_domain="${src_url#*://}"
                src_domain="${src_domain%%/*}"
                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"
-                       printf "%s\n" "${src_domain}" | f_dnsallow >>"${adb_tmpdir}/tmp.add.allowlist"
+                       printf '%s\n' "${src_domain}" >>"${adb_tmpdir}/tmp.raw.allowlist"
+                       printf '%s\n' "${src_domain}" | f_dnsallow >>"${adb_tmpdir}/tmp.add.allowlist"
                fi
 
                # download queue processing
@@ -1790,7 +1784,7 @@ f_main() {
                                                        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}$")"
+                                                               src_entries="${src_entries} $(printf '%s' "${src_list}" | "${adb_grepcmd}" -E "${src_item}/${src_suffix}$")"
                                                        done
                                                        if [ -n "${src_entries}" ]; then
                                                                tar -xOzf "${src_tmparchive}" ${src_entries} 2>>"${adb_errorlog}" >"${src_tmpload}"
@@ -1817,7 +1811,7 @@ f_main() {
                [ "${adb_tld}" = "1" ] && f_tld "${adb_tmpdir}/${adb_dnsfile}"
                f_list final
        else
-               printf "%b" "${adb_dnsheader}" >"${adb_finaldir}/${adb_dnsfile}"
+               printf '%b' "${adb_dnsheader}" >"${adb_finaldir}/${adb_dnsfile}"
                f_log "info" "no merge input, only header written to ${adb_finaldir}/${adb_dnsfile}"
        fi
        chown "${adb_dnsuser}" "${adb_finaldir}/${adb_dnsfile}" 2>>"${adb_errorlog}"
@@ -1834,7 +1828,7 @@ 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="0" resolve="-nn" action="${1}" top_count="${2:-"10"}" res_count="${3:-"50"}" search="${4:-"+"}"
+       local ip request requests iface_v4 iface_v6 ip_v4 ip_v6 map_jsn cnt 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"
@@ -1848,20 +1842,22 @@ f_report() {
        # build report
        #
        if [ "${action}" != "json" ]; then
-               : >"${report_raw}" >"${report_srt}" >"${report_txt}" >"${report_jsn}" >"${map_jsn}"
+               : >"${report_srt}" >"${report_txt}" >"${report_jsn}" >"${map_jsn}"
                : >"${top_tmpclients}" >"${top_tmpdomains}" >"${top_tmpblocked}"
                [ "${adb_represolve}" = "1" ] && resolve=""
+               cnt="1"
                for file in "${adb_reportdir}/adb_report.pcap"*; do
+                       [ -s "${file}" ] || continue
                        (
-                               "${adb_dumpcmd}" ${resolve} --immediate-mode -tttt -T domain -r "${file}" 2>>"${adb_errorlog}" |
+                               "${adb_dumpcmd}" ${resolve} --immediate-mode -tttt -T domain -r "${file}" 2>/dev/null |
                                "${adb_awkcmd}" -v repiface="${adb_repiface}" '
                                        BEGIN {
                                                pending = 0
                                        }
                                        # ignore Reverse DNS
                                        /\.in-addr\.arpa/ || /\.ip6\.arpa/ { next }
-                                       # domain request parser
-                                       /\+[[:space:]]+(A\?|AAAA\?)/ {
+                                       # domain request parser (with optional EDNS marker support)
+                                       /\+[[:space:]]+(\[.*\][[:space:]]+)?(A\?|AAAA\?)/ {
                                                # drop unresolved previous query
                                                if (pending)
                                                        pending = 0
@@ -1891,7 +1887,7 @@ f_report() {
                                                next
                                        }
                                        # ok answer
-                                               / (A|AAAA|CNAME) / && !/NXDomain/ && !/ServFail/ {
+                                       / (A|AAAA|CNAME) / && !/NXDomain/ && !/ServFail/ {
                                                if (pending) {
                                                        printf "%s\t%s\t%s\t%s\t%s\t%s\tOK\n",
                                                        last_date, last_time, last_client, last_interface, last_qtype, last_domain
@@ -1920,16 +1916,18 @@ f_report() {
                                        END {
                                                # no fallback
                                        }
-                               ' >> "${report_raw}"
+                               ' > "${report_raw}.${cnt}"
                        ) &
                        [ "${cnt}" -gt "${adb_cores}" ] && wait -n
                        cnt="$((cnt + 1))"
                done
                wait
-               if [ -s "${report_raw}" ]; then
-                       "${adb_sortcmd}" ${adb_srtopts} -ru "${report_raw}" > "${report_srt}"
-                       "${adb_rmcmd}" -f "${report_raw}"
-               fi
+               for file in "${report_raw}".*; do
+                       [ -s "${file}" ] || continue
+                       "${adb_sortcmd}" ${adb_srtopts} -ru "${report_raw}".* > "${report_srt}"
+                       "${adb_rmcmd}" -f "${report_raw}".*
+                       break
+               done
 
                # build json
                #
@@ -1940,14 +1938,14 @@ f_report() {
                        blocked="$("${adb_awkcmd}" '{if($7=="NX")cnt++}END{printf "%s",cnt}' "${report_srt}")"
                        percent="$("${adb_awkcmd}" -v t="${total}" -v b="${blocked}" 'BEGIN{ if(t>0) printf "%.2f%s",b/t*100,"%"; else printf "0.00%%"}')"
                        {
-                               printf "%s\n" "{ "
-                               printf "\t%s\n" "\"start_date\": \"${start%_*}\", "
-                               printf "\t%s\n" "\"start_time\": \"${start#*_}\", "
-                               printf "\t%s\n" "\"end_date\": \"${end%_*}\", "
-                               printf "\t%s\n" "\"end_time\": \"${end#*_}\", "
-                               printf "\t%s\n" "\"total\": \"${total}\", "
-                               printf "\t%s\n" "\"blocked\": \"${blocked}\", "
-                               printf "\t%s\n" "\"percent\": \"${percent}\", "
+                               printf '%s\n' "{ "
+                               printf '\t%s\n' "\"start_date\": \"${start%_*}\", "
+                               printf '\t%s\n' "\"start_time\": \"${start#*_}\", "
+                               printf '\t%s\n' "\"end_date\": \"${end%_*}\", "
+                               printf '\t%s\n' "\"end_time\": \"${end#*_}\", "
+                               printf '\t%s\n' "\"total\": \"${total}\", "
+                               printf '\t%s\n' "\"blocked\": \"${blocked}\", "
+                               printf '\t%s\n' "\"percent\": \"${percent}\", "
                        } >"${report_jsn}"
 
                        # build top list counters
@@ -1996,7 +1994,7 @@ f_report() {
                        #
                        top_list="top_clients top_domains top_blocked"
                        for top in ${top_list}; do
-                               printf "\t\"%s\": [ " "${top}" >>"${report_jsn}"
+                               printf '\t"%s": [ ' "${top}" >>"${report_jsn}"
                                case "${top}" in
                                        top_clients)
                                                "${adb_sortcmd}" ${adb_srtopts} -nr "${top_tmpclients}" |
@@ -2035,50 +2033,47 @@ f_report() {
                                                ' >>"${report_jsn}"
                                        ;;
                                esac
-                               printf "\n\t],\n" >>"${report_jsn}"
+                               printf '\n\t],\n' >>"${report_jsn}"
                        done
                        "${adb_rmcmd}" -f "${top_tmpclients}" "${top_tmpdomains}" "${top_tmpblocked}"
 
                        # build json request list
                        #
-                       search="${search//./\\.}"
                        search="${search//[+*~%\$&\"\' ]/}"
-                       "${adb_awkcmd}" "
+                       case "${res_count}" in
+                               ''|*[!0-9]* )
+                                       res_count="50"
+                               ;;
+                       esac
+                       "${adb_awkcmd}" -v search="${search}" -v res_count="${res_count}" '
                                BEGIN {
                                        i = 0
-                                       printf \"\t\\\"requests\\\": [\n\"
+                                       printf "\t\"requests\": [\n"
                                }
-                               (/(${search})/ && NF == 7) {
+
+                               # only match if search is empty or non-empty and NF == 7
+                               ((search == "" || index($0, search)) && NF == 7) {
                                        i++
-                                       if (i == 1) {
-                                               printf \"\n\t\t{\
-                                               \n\t\t\t\\\"date\\\": \\\"%s\\\",\
-                                               \n\t\t\t\\\"time\\\": \\\"%s\\\",\
-                                               \n\t\t\t\\\"client\\\": \\\"%s\\\",\
-                                               \n\t\t\t\\\"iface\\\": \\\"%s\\\",\
-                                               \n\t\t\t\\\"type\\\": \\\"%s\\\",\
-                                               \n\t\t\t\\\"domain\\\": \\\"%s\\\",\
-                                               \n\t\t\t\\\"rc\\\": \\\"%s\\\"\
-                                               \n\t\t}\",
-                                               \$1, \$2, \$3, \$4, \$5, \$6, \$7
+                                       if (res_count > 0 && i > res_count) {
+                                               next
                                        }
-                                       else if (i <= ${res_count}) {
-                                               printf \",\n\t\t{\
-                                               \n\t\t\t\\\"date\\\": \\\"%s\\\",\
-                                               \n\t\t\t\\\"time\\\": \\\"%s\\\",\
-                                               \n\t\t\t\\\"client\\\": \\\"%s\\\",\
-                                               \n\t\t\t\\\"iface\\\": \\\"%s\\\",\
-                                               \n\t\t\t\\\"type\\\": \\\"%s\\\",\
-                                               \n\t\t\t\\\"domain\\\": \\\"%s\\\",\
-                                               \n\t\t\t\\\"rc\\\": \\\"%s\\\"\
-                                               \n\t\t}\",
-                                               \$1, \$2, \$3, \$4, \$5, \$6, \$7
+                                       if (i > 1) {
+                                               printf ",\n"
                                        }
+
+                                       printf "\n\t\t{\n"
+                                       printf "\t\t\t\"date\": \"%s\",\n", $1
+                                       printf "\t\t\t\"time\": \"%s\",\n", $2
+                                       printf "\t\t\t\"client\": \"%s\",\n", $3
+                                       printf "\t\t\t\"iface\": \"%s\",\n", $4
+                                       printf "\t\t\t\"type\": \"%s\",\n", $5
+                                       printf "\t\t\t\"domain\": \"%s\",\n", $6
+                                       printf "\t\t\t\"rc\": \"%s\"\n", $7
+                                       printf "\t\t}"
                                }
                                END {
-                                       printf \"\n\t]\n}\n\"
-                               }
-                       " "${adb_reportdir}/adb_report.srt" >> "${report_jsn}"
+                                       printf "\n\t]\n}\n"
+                       }' "${report_srt}" >> "${report_jsn}"
                        "${adb_rmcmd}" -f "${report_srt}"
                fi
 
@@ -2090,18 +2085,19 @@ f_report() {
                        network_find_wan6 iface_v6 && network_get_ipaddr6 ip_v6 "${iface_v6}"
                        if [ -n "${ip_v4}" ] || [ -n "${ip_v6}" ]; then
                                f_fetch
-                               printf "%s" ",[{}" >"${map_jsn}"
+                               printf '%s' ",[{}" >"${map_jsn}"
                        fi
                        for ip in ${ip_v4} ${ip_v6}; do
                                (
                                        "${adb_fetchcmd}" ${adb_geoparm} "${adb_geourl}/${ip}" 2>>"${adb_errorlog}" |
-                                               "${adb_awkcmd}" -v feed="homeIP" '{printf ",{\"%s\": %s}\n",feed,$0}' >>"${map_jsn}"
+                                               "${adb_awkcmd}" -v feed="homeIP" '{printf ",{\"%s\": %s}\n",feed,$0}' > "${map_jsn}.${cnt}"
                                ) &
                                [ "${cnt}" -gt "${adb_cores}" ] && wait -n
                                cnt="$((cnt + 1))"
                        done
                        wait
                        if [ -s "${map_jsn}" ] && [ "${cnt}" -lt "45" ]; then
+                               map_seen=""
                                json_init
                                if json_load_file "${report_jsn}" >/dev/null 2>&1; then
                                        json_select "requests" >/dev/null 2>&1
@@ -2110,27 +2106,40 @@ f_report() {
                                                json_select "${request}" >/dev/null 2>&1
                                                json_get_var rc "rc" >/dev/null 2>&1
                                                json_get_var domain "domain" >/dev/null 2>&1
-                                               if [ "${rc}" = "NX" ] && ! "${adb_grepcmd}" -q "\"${domain}\":" "${map_jsn}"; then
-                                                       (
-                                                               "${adb_fetchcmd}" ${adb_geoparm} "${adb_geourl}/${domain}" 2>>"${adb_errorlog}" |
-                                                                       "${adb_awkcmd}" -v feed="${domain}" '{printf ",{\"%s\": %s}\n",feed,$0}' >>"${map_jsn}"
-                                                       ) &
-                                                       [ "${cnt}" -gt "${adb_cores}" ] && wait -n
-                                                       cnt="$((cnt + 1))"
-                                                       [ "${cnt}" -ge "45" ] && break
+                                               if [ "${rc}" = "NX" ]; then
+                                                       case " ${map_seen} " in
+                                                               *" ${domain} "*)
+                                                                       ;;
+                                                               *)
+                                                                       map_seen="${map_seen} ${domain} "
+                                                                       (
+                                                                               "${adb_fetchcmd}" ${adb_geoparm} "${adb_geourl}/${domain}" 2>>"${adb_errorlog}" |
+                                                                                       "${adb_awkcmd}" -v feed="${domain}" '{printf ",{\"%s\": %s}\n",feed,$0}' > "${map_jsn}.${cnt}"
+                                                                       ) &
+                                                                       [ "${cnt}" -gt "${adb_cores}" ] && wait -n
+                                                                       cnt="$((cnt + 1))"
+                                                                       [ "${cnt}" -ge "45" ] && break
+                                                                       ;;
+                                                       esac
                                                fi
                                                json_select ".."
                                        done
                                        wait
                                fi
                        fi
+                       for file in "${map_jsn}".*; do
+                               [ -s "${file}" ] || continue
+                               "${adb_catcmd}" "${map_jsn}".* >> "${map_jsn}" 2>/dev/null
+                               "${adb_rmcmd}" -f "${map_jsn}".*
+                               break
+                       done
                fi
        fi
 
        # output preparation
        #
        if [ -s "${report_jsn}" ] && { [ "${action}" = "cli" ] || [ "${action}" = "mail" ]; }; then
-               printf "%s\n%s\n%s\n" ":::" "::: Adblock DNS Report" ":::" >>"${report_txt}"
+               printf '%s\n%s\n%s\n' ":::" "::: Adblock DNS Report" ":::" >>"${report_txt}"
                json_init
                json_load_file "${report_jsn}"
                json_get_keys key_list
@@ -2140,12 +2149,12 @@ f_report() {
                                *[!a-z_]*)
                                ;;
                                *)
-                                       eval "${key}=\"${value}\""
+                                       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}"
+               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}"
                top_list="top_clients top_domains top_blocked requests"
                for top in ${top_list}; do
                        case "${top}" in
@@ -2160,23 +2169,23 @@ f_report() {
                                        ;;
                        esac
                        if json_get_type status "${top}" && [ "${top}" != "requests" ] && [ "${status}" = "array" ]; then
-                               printf "%s\n%s\n%s\n" ":::" "${item}" ":::" >>"${report_txt}"
+                               printf '%s\n%s\n%s\n' ":::" "${item}" ":::" >>"${report_txt}"
                                json_select "${top}"
                                index="1"
                                item=""
                                while json_get_type status "${index}" && [ "${status}" = "object" ]; do
                                        json_get_values item "${index}"
-                                       printf "  + %-9s::: %s\n" ${item} >>"${report_txt}"
+                                       printf '  + %-9s::: %s\n' ${item} >>"${report_txt}"
                                        index="$((index + 1))"
                                done
                        elif json_get_type status "${top}" && [ "${top}" = "requests" ] && [ "${status}" = "array" ]; then
-                               printf "%s\n%s\n%s\n" ":::" "::: Latest DNS Queries" ":::" >>"${report_txt}"
-                               printf "%-11s%-9s%-40s%-15s%-5s%-70s%s\n" "Date" "Time" "Client" "Interface" "Type" "Domain" "Answer" >>"${report_txt}"
+                               printf '%s\n%s\n%s\n' ":::" "::: Latest DNS Queries" ":::" >>"${report_txt}"
+                               printf '%-11s%-9s%-40s%-15s%-5s%-70s%s\n' "Date" "Time" "Client" "Interface" "Type" "Domain" "Answer" >>"${report_txt}"
                                json_select "${top}"
                                index="1"
                                while json_get_type status "${index}" && [ "${status}" = "object" ]; do
                                        json_get_values item "${index}"
-                                       printf "%-11s%-9s%-40s%-15s%-5s%-70s%s\n" ${item} >>"${report_txt}"
+                                       printf '%-11s%-9s%-40s%-15s%-5s%-70s%s\n' ${item} >>"${report_txt}"
                                        index="$((index + 1))"
                                done
                        fi
@@ -2190,15 +2199,15 @@ f_report() {
        #
        case "${action}" in
                "cli")
-                       printf "%s\n" "${content}"
+                       printf '%s\n' "${content}"
                        ;;
                "json")
                        if [ "${adb_map}" = "1" ] && [ -s "${map_jsn}" ]; then
                                jsn="$("${adb_catcmd}" ${report_jsn} ${map_jsn} 2>>"${adb_errorlog}")"
-                               [ -n "${jsn}" ] && printf "[%s]]\n" "${jsn}"
+                               [ -n "${jsn}" ] && printf '[%s]]\n' "${jsn}"
                        else
-                               jsn="$("${adb_catcmd}" ${report_jsn} 2>>"${adb_errorlog}")"
-                               [ -n "${jsn}" ] && printf "[%s]\n" "${jsn}"
+                               jsn="$(< "${report_jsn}")" 2>>"${adb_errorlog}"
+                               [ -n "${jsn}" ] && printf '[%s]\n' "${jsn}"
                        fi
                        ;;
                "mail")
@@ -2206,7 +2215,7 @@ f_report() {
                        "${adb_rmcmd}" -f "${report_txt}"
                        ;;
                "gen")
-                       printf "%s\n" "1" >"/var/run/adblock.report"
+                       printf '%s\n' "1" >"/var/run/adblock.report"
                        ;;
        esac
 }
git clone https://git.99rst.org/PROJECT