From 31eb81961258aba2c557fb666ad2eb6d9d19446c Mon Sep 17 00:00:00 2001 From: Dirk Brenken Date: Thu, 9 Apr 2026 23:13:32 +0200 Subject: [PATCH] adblock: release 4.5.4-1 * 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 (cherry picked from commit 5116abcafa6da7853c2d5ff5798df402c442505e) --- net/adblock/Makefile | 4 +- net/adblock/files/README.md | 8 +- net/adblock/files/adblock.cgi | 112 ++++++---- net/adblock/files/adblock.init | 26 ++- net/adblock/files/adblock.mail | 46 ++-- net/adblock/files/adblock.sh | 377 +++++++++++++++++---------------- 6 files changed, 316 insertions(+), 257 deletions(-) diff --git a/net/adblock/Makefile b/net/adblock/Makefile index 50e345f051..bc6148db30 100644 --- a/net/adblock/Makefile +++ b/net/adblock/Makefile @@ -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 diff --git a/net/adblock/files/README.md b/net/adblock/files/README.md index 7393c9c5ff..832394d3d5 100644 --- a/net/adblock/files/README.md +++ b/net/adblock/files/README.md @@ -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 @@ -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://\cgi-bin/adblock (preferably transferred via QR code shown in LuCI) +* The client opens the URL, e.g. http(s)://\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 diff --git a/net/adblock/files/adblock.cgi b/net/adblock/files/adblock.cgi index f78858be97..6453025229 100644 --- a/net/adblock/files/adblock.cgi +++ b/net/adblock/files/adblock.cgi @@ -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" " +printf '%s\n\n' "Content-Type: text/html" +printf '%s\n' " @@ -104,7 +135,6 @@ printf "%s\n" " .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'; -} @@ -151,34 +172,44 @@ function showSpinner() {

Adblock Remote Allow

" -# 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" "
Remote allow is not enabled or no MAC addresses configured
" + printf '%s\n' " +
+ Remote allow is not enabled or no MAC addresses configured +
+ " exit 0 fi if [ -z "${query_mac}" ]; then - printf "%s\n" "
Could not determine MAC address
" + printf '%s\n' " +
+ Could not determine MAC address +
+ " 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" "
MAC ${query_mac} is not authorized to use remote allow
" + printf '%s\n' " +
+ This device (${query_mac}) is not registered for Adblock Remote Allow +
+ " 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" " + printf '%s\n' "
- MAC ${query_mac} is currently not in the remote allow Set

- Renew Set Entry + This device currently does not bypass ad blocking
+ Bypass
" 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' "
- Renewing remote allow for MAC ${query_mac}

+ Adding device...
" - nft add element inet adblock mac_remote "{ ${query_mac} }" >/dev/null 2>&1 - printf "%s\n" "" + nft add element inet adblock mac_remote "{ ${query_mac//[!0-9a-f:]} }" >/dev/null 2>&1 + printf '%s\n' "" + exit 0 fi -# success message +# show remaining time # -printf "%s\n" " +printf '%s\n' "
- MAC ${query_mac} is temporarily allowed
+ This device temporarily bypasses ad blocking
Remaining time: ${remaining:-${nft_remotetimeout}m}
diff --git a/net/adblock/files/adblock.init b/net/adblock/files/adblock.init index 95349da5df..47578ccf8f 100755 --- a/net/adblock/files/adblock.init +++ b/net/adblock/files/adblock.init @@ -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)" diff --git a/net/adblock/files/adblock.mail b/net/adblock/files/adblock.mail index 9d5475dfff..2c17404706 100755 --- a/net/adblock/files/adblock.mail +++ b/net/adblock/files/adblock.mail @@ -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="
"
-adb_mailtext="${adb_mailtext}\n++\n++ System Information ++\n++\n${sys_info}"
-adb_mailtext="${adb_mailtext}\n\n++\n++ Adblock Information ++\n++\n${adb_info}"
-if [ -n "${rep_info}" ]; then
-	adb_mailtext="${adb_mailtext}\n\n++\n++ Report Information ++\n++\n${rep_info}"
-fi
-adb_mailtext="${adb_mailtext}\n\n++\n++ Logfile Information ++\n++\n${log_info}"
-adb_mailtext="${adb_mailtext}
" +adb_mailtext="$( + printf '%s\n' "
"
+	printf '\n%s\n' "++
+++ System Information ++
+++"
+	printf '%s\n' "${sys_info:-"-"}"
+	printf '\n%s\n' "++
+++ Adblock Information ++
+++"
+	printf '%s\n' "${adb_info:-"-"}"
+	if [ -n "${rep_info}" ]; then
+		printf '\n%s\n' "++
+++ Report Information ++
+++"
+		printf '%s\n' "${rep_info}"
+	fi
+	printf '\n%s\n' "++
+++ Logfile Information ++
+++"
+	printf '%s\n' "${log_info:-"-"}"
+	printf '%s\n' "
" +)" # 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 diff --git a/net/adblock/files/adblock.sh b/net/adblock/files/adblock.sh index 46a39d92c6..c69de20a4c 100755 --- a/net/adblock/files/adblock.sh +++ b/net/adblock/files/adblock.sh @@ -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}" + ""|*[!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 - ;; - *[!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}" - 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 }