From 309372ed41b92a13c3313674e6456799f0694c4a Mon Sep 17 00:00:00 2001
From: Aleksey Vasilenko <aleksey.vasilenko@gmail.com>
Date: Mon, 23 Sep 2024 23:13:51 +0300
Subject: [PATCH 01/11] iperf3: explicitly disable SCTP

Since https://github.com/openwrt/openwrt/commit/3fa5ee0b28b736c5d06af34ed5c3e80f78235fe8
OpenWrt no longer disables SCTP support by default.
It caused the leak of libsctp dependency to iperf3.
Here we disable it explicitly to fix the build.

Signed-off-by: Aleksey Vasilenko <aleksey.vasilenko@gmail.com>
(cherry picked from commit 6c1ce8ccc78f1b34ade513aa134962eb143826f9)
---
 net/iperf3/Makefile | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/net/iperf3/Makefile b/net/iperf3/Makefile
index 44065e6e1a..41ae2d6f13 100644
--- a/net/iperf3/Makefile
+++ b/net/iperf3/Makefile
@@ -9,7 +9,7 @@ include $(TOPDIR)/rules.mk
 
 PKG_NAME:=iperf
 PKG_VERSION:=3.17.1
-PKG_RELEASE:=2
+PKG_RELEASE:=3
 
 PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
 PKG_SOURCE_URL:=https://downloads.es.net/pub/iperf
@@ -66,6 +66,8 @@ else
 	CONFIGURE_ARGS += --without-openssl
 endif
 
+CONFIGURE_ARGS += --without-sctp
+
 MAKE_FLAGS += noinst_PROGRAMS=
 
 define Package/iperf3/description

From baf67aa3e01ef03a845f1e5cbb5830829667aaa7 Mon Sep 17 00:00:00 2001
From: Rosen Penev <rosenp@gmail.com>
Date: Wed, 20 Nov 2024 15:21:43 -0800
Subject: [PATCH 02/11] iperf3: fix crashing with musl

Upstream backport.

Signed-off-by: Rosen Penev <rosenp@gmail.com>
(cherry picked from commit 9c9f4dd08875c77e145e834b939d0c40c043db60)
---
 net/iperf3/Makefile                     |  2 +-
 net/iperf3/patches/030-musl-crash.patch | 19 +++++++++++++++++++
 2 files changed, 20 insertions(+), 1 deletion(-)
 create mode 100644 net/iperf3/patches/030-musl-crash.patch

diff --git a/net/iperf3/Makefile b/net/iperf3/Makefile
index 41ae2d6f13..45d4721bf2 100644
--- a/net/iperf3/Makefile
+++ b/net/iperf3/Makefile
@@ -9,7 +9,7 @@ include $(TOPDIR)/rules.mk
 
 PKG_NAME:=iperf
 PKG_VERSION:=3.17.1
-PKG_RELEASE:=3
+PKG_RELEASE:=4
 
 PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
 PKG_SOURCE_URL:=https://downloads.es.net/pub/iperf
diff --git a/net/iperf3/patches/030-musl-crash.patch b/net/iperf3/patches/030-musl-crash.patch
new file mode 100644
index 0000000000..bc1df68c8e
--- /dev/null
+++ b/net/iperf3/patches/030-musl-crash.patch
@@ -0,0 +1,19 @@
+From 3da07ae96f5b40f76b75e1ccd4b20267f6a5988e Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Jakub=20Kul=C3=ADk?= <kulikjak@gmail.com>
+Date: Wed, 28 Aug 2024 09:43:04 +0200
+Subject: [PATCH] remove incorrect freeaddrinfo call
+
+---
+ src/net.c | 1 -
+ 1 file changed, 1 deletion(-)
+
+--- a/src/net.c
++++ b/src/net.c
+@@ -145,7 +145,6 @@ create_socket(int domain, int proto, con
+     if ((gerror = getaddrinfo(server, portstr, &hints, &server_res)) != 0) {
+ 	if (local)
+ 	    freeaddrinfo(local_res);
+-        freeaddrinfo(server_res);
+         return -1;
+     }
+ 

From 1850b3aac3e57ab30b1c9d5b01259d9c4f570f39 Mon Sep 17 00:00:00 2001
From: Stan Grishin <stangri@melmac.ca>
Date: Fri, 6 Dec 2024 03:10:08 +0000
Subject: [PATCH 03/11] adblock-fast: bugfix: dnsmasq instances confdir hack

* temporary hack until https://github.com/openwrt/openwrt/pull/16806 is merged

Signed-off-by: Stan Grishin <stangri@melmac.ca>
---
 net/adblock-fast/Makefile                     |   4 +-
 .../files/etc/init.d/adblock-fast             | 619 +++++++++++-------
 .../files/etc/uci-defaults/90-adblock-fast    |  51 +-
 3 files changed, 423 insertions(+), 251 deletions(-)

diff --git a/net/adblock-fast/Makefile b/net/adblock-fast/Makefile
index 0b56133243..a70f211aba 100644
--- a/net/adblock-fast/Makefile
+++ b/net/adblock-fast/Makefile
@@ -6,7 +6,7 @@ include $(TOPDIR)/rules.mk
 
 PKG_NAME:=adblock-fast
 PKG_VERSION:=1.1.2
-PKG_RELEASE:=5
+PKG_RELEASE:=18
 PKG_MAINTAINER:=Stan Grishin <stangri@melmac.ca>
 PKG_LICENSE:=AGPL-3.0-or-later
 
@@ -28,7 +28,7 @@ define Package/adblock-fast
 endef
 
 define Package/adblock-fast/description
-Fast AdBlocking script to block ad or abuse/malware domains with DNSMASQ or Unbound.
+Fast AdBlocking script to block ad or abuse/malware domains with Dnsmasq, SmartDNS or Unbound.
 Script supports local/remote list of domains and hosts-files for both block-listing and allow-listing.
 Please see https://docs.openwrt.melmac.net/adblock-fast/ for more information.
 endef
diff --git a/net/adblock-fast/files/etc/init.d/adblock-fast b/net/adblock-fast/files/etc/init.d/adblock-fast
index 54d0ec59c8..81bca4e0b0 100755
--- a/net/adblock-fast/files/etc/init.d/adblock-fast
+++ b/net/adblock-fast/files/etc/init.d/adblock-fast
@@ -20,23 +20,11 @@ if type extra_command 1>/dev/null 2>&1; then
 	extra_command 'show_blocklist' 'List currently blocked domains'
 	extra_command 'sizes' 'Displays the file-sizes of enabled block-lists'
 	extra_command 'version' 'Show version information'
-else
-# shellcheck disable=SC2034
-	EXTRA_COMMANDS='allow check dl killcache pause sizes status_service version'
-# shellcheck disable=SC2034
-	EXTRA_HELP='	allow	Allows domain(s) in current block-list and config
-	check	Checks if specified domain is found in current block-list
-	check_lists	Checks if specified domain is found in enabled block-lists
-	dl	Force-downloads all enabled block-list
-	pause	Pauses AdBlocking for specified number of seconds (default: 60)
-	show_blocklist	List currently blocked domains
-	sizes	Displays the file-sizes of enabled block-lists
-	version	Show version information'
 fi
 
 readonly packageName='adblock-fast'
 readonly PKG_VERSION='dev-test'
-readonly packageCompat='2'
+readonly packageCompat='4'
 readonly serviceName="$packageName $PKG_VERSION"
 readonly packageConfigFile="/etc/config/${packageName}"
 readonly dnsmasqAddnhostsFile="/var/run/${packageName}/dnsmasq.addnhosts"
@@ -45,17 +33,17 @@ readonly dnsmasqAddnhostsGzip="${packageName}.dnsmasq.addnhosts.gz"
 readonly dnsmasqAddnhostsFilter='s|^|127.0.0.1 |;s|$||'
 readonly dnsmasqAddnhostsFilterIPv6='s|^|:: |;s|$||'
 readonly dnsmasqAddnhostsOutputFilter='s|^127.0.0.1 ||;s|^:: ||;'
-readonly dnsmasqConfFile="/tmp/dnsmasq.d/${packageName}"
+readonly dnsmasqConfFile="${packageName}"
 readonly dnsmasqConfCache="/var/run/${packageName}/dnsmasq.conf.cache"
 readonly dnsmasqConfGzip="${packageName}.dnsmasq.conf.gz"
 readonly dnsmasqConfFilter='s|^|local=/|;s|$|/|'
 readonly dnsmasqConfOutputFilter='s|local=/||;s|/$||;'
-readonly dnsmasqIpsetFile="/tmp/dnsmasq.d/${packageName}.ipset"
+readonly dnsmasqIpsetFile="${packageName}.ipset"
 readonly dnsmasqIpsetCache="/var/run/${packageName}/dnsmasq.ipset.cache"
 readonly dnsmasqIpsetGzip="${packageName}.dnsmasq.ipset.gz"
 readonly dnsmasqIpsetFilter='s|^|ipset=/|;s|$|/adb|'
 readonly dnsmasqIpsetOutputFilter='s|ipset=/||;s|/adb$||;'
-readonly dnsmasqNftsetFile="/tmp/dnsmasq.d/${packageName}.nftset"
+readonly dnsmasqNftsetFile="${packageName}.nftset"
 readonly dnsmasqNftsetCache="/var/run/${packageName}/dnsmasq.nftset.cache"
 readonly dnsmasqNftsetGzip="${packageName}.dnsmasq.nftset.gz"
 readonly dnsmasqNftsetFilter='s|^|nftset=/|;s|$|/4#inet#fw4#adb4|'
@@ -65,6 +53,8 @@ readonly dnsmasqServersFile="/var/run/${packageName}/dnsmasq.servers"
 readonly dnsmasqServersCache="/var/run/${packageName}/dnsmasq.servers.cache"
 readonly dnsmasqServersGzip="${packageName}.dnsmasq.servers.gz"
 readonly dnsmasqServersFilter='s|^|server=/|;s|$|/|'
+readonly dnsmasqServersAllowFilter='s|(.*)|server=/\1/#|'
+readonly dnsmasqServersBlockedCountFilter='\|/#|d'
 readonly dnsmasqServersOutputFilter='s|server=/||;s|/$||;'
 readonly smartdnsDomainSetFile="/var/run/${packageName}/smartdns.domainset"
 readonly smartdnsDomainSetCache="/var/run/${packageName}/smartdns.domainset.cache"
@@ -120,9 +110,12 @@ readonly triggersRestart='compressed_cache compressed_cache_dir force_dns led fo
 dl_command=
 dl_flag=
 isSSLSupported=
+outputAllowFilter=
+outputBlockedCountFilter=
 outputFilter=
 outputFilterIPv6=
 outputFile=
+outputDnsmasqFileList=
 outputGzip=
 outputCache=
 outputOutputFilter=
@@ -239,6 +232,13 @@ config_cache() {
 	;;
 	esac
 }
+count_blocked_domains() {
+	if [ -n "$outputBlockedCountFilter" ]; then
+		sed "$outputBlockedCountFilter" "$outputFile" | wc -l
+	else
+		wc -l < "$outputFile"
+	fi
+}
 debug() { local __i __j; for __i in "$@"; do eval "__j=\$$__i"; echo "${__i}: ${__j} "; done; }
 dns_set_output_values() {
 	case "$1" in
@@ -283,6 +283,8 @@ dns_set_output_values() {
 			outputCache="$dnsmasqServersCache"
 			outputGzip="${compressed_cache_dir}/${dnsmasqServersGzip}"
 			outputOutputFilter="$dnsmasqServersOutputFilter"
+			outputAllowFilter="$dnsmasqServersAllowFilter"
+			outputBlockedCountFilter="$dnsmasqServersBlockedCountFilter"
 		;;
 		smartdns.domainset)
 			outputFilter="$smartdnsDomainSetFilter"
@@ -316,6 +318,7 @@ dns_set_output_values() {
 			outputOutputFilter="$unboundOutputFilter"
 		;;
 	esac
+	resolver 'on_load'
 }
 dnsmasq_hup() { killall -q -s HUP dnsmasq; }
 dnsmasq_kill() { killall -q -s KILL dnsmasq; }
@@ -340,13 +343,7 @@ is_fw4_restart_needed() {
 		return 1
 	fi
 }
-is_integer() {
-	case "$1" in
-		(*[!0123456789]*) return 1;;
-		('')              return 1;;
-		(*)               return 0;;
-	esac
-}
+is_integer() { case "$1" in ''|*[!0-9]*) return 1;; esac; }
 is_greater() { test "$(printf '%s\n' "$@" | sort -V | head -n 1)" != "$1"; }
 is_greater_or_equal() { test "$(printf '%s\n' "$@" | sort -V | head -n 1)" = "$2"; }
 # shellcheck disable=SC3057
@@ -371,10 +368,10 @@ led_on(){ if [ -n "${1}" ] && [ -e "${1}/trigger" ]; then echo 'default-on' > "$
 led_off(){ if [ -n "${1}" ] &&  [ -e "${1}/trigger" ]; then echo 'none' > "${1}/trigger" 2>&1; fi; }
 logger() { /usr/bin/logger -t "$packageName" "$@"; }
 nft() { "$nft" "$@" >/dev/null 2>&1; }
-output_ok() { output 1 "$_OK_"; output 2 "$__OK__\\n"; }
-output_okn() { output 1 "$_OK_\\n"; output 2 "$__OK__\\n"; }
-output_fail() { output 1 "$_FAIL_"; output 2 "$__FAIL__\\n"; }
-output_failn() { output 1 "$_FAIL_\\n"; output 2 "$__FAIL__\\n"; }
+output_ok() { output 1 "$_OK_"; output 2 "$__OK__\n"; }
+output_okn() { output 1 "$_OK_\n"; output 2 "$__OK__\n"; }
+output_fail() { output 1 "$_FAIL_"; output 2 "$__FAIL__\n"; }
+output_failn() { output 1 "$_FAIL_\n"; output 2 "$__FAIL__\n"; }
 print_json_bool() { json_init; json_add_boolean "$1" "$2"; json_dump; json_cleanup; }
 print_json_int() { json_init; json_add_int "$1" "$2"; json_dump; json_cleanup; }
 print_json_string() { json_init; json_add_string "$1" "$2"; json_dump; json_cleanup; }
@@ -382,11 +379,13 @@ sanitize_dir() { [ -d "$(readlink -fn "$1")" ] && readlink -fn "$1"; }
 smartdns_restart() { /etc/init.d/smartdns restart >/dev/null 2>&1; }
 str_contains() { test "$1" != "$(str_replace "$1" "$2" '')"; }
 str_contains_word() { echo "$1" | grep -q -w "$2"; }
+str_first_word() { echo "${1%% *}"; }
 # shellcheck disable=SC2018,SC2019
 str_to_lower() { echo "$1" | tr 'A-Z' 'a-z'; }
 # shellcheck disable=SC2018,SC2019
 str_to_upper() { echo "$1" | tr 'a-z' 'A-Z'; }
-str_replace() { printf "%b" "$1" | sed -e "s/$(printf "%b" "$2")/$(printf "%b" "$3")/g"; }
+# shellcheck disable=SC3060
+str_replace() { echo "${1//$2/$3}"; }
 ubus_get_data() { ubus call service list "{ 'name': '$packageName' }" | jsonfilter -e "@['${packageName}'].instances.main.data.${1}"; }
 ubus_get_ports() { ubus call service list "{ 'name': '$packageName' }" | jsonfilter -e "@['${packageName}'].instances.main.data.firewall.*.dest_port"; }
 uci_get_protocol() { uci_get 'network' "$1" 'proto'; }
@@ -464,35 +463,18 @@ get_url_filesize() {
 	echo -en "$size"
 }
 
-output() {
-# Target verbosity level with the first parameter being an integer
-	is_integer() {
-		case "$1" in
-			(*[!0123456789]*) return 1;;
-			('')              return 1;;
-			(*)               return 0;;
-		esac
-	}
-	local msg memmsg logmsg text
-	local sharedMemoryOutput="/dev/shm/$packageName-output"
-	if [ -z "$verbosity" ] && [ -n "$packageName" ]; then
-		verbosity="$(uci_get "$packageName" 'config' 'verbosity' '2')"
-	fi
-	if [ $# -ne 1 ] && is_integer "$1"; then
-		if [ $((verbosity & $1)) -gt 0 ] || [ "$verbosity" = "$1" ]; then shift; text="$*"; else return 0; fi
-	fi
-	text="${text:-$*}";
-	[ -t 1 ] && printf "%b" "$text"
 # shellcheck disable=SC3060
-	msg="${text//$serviceName /service }";
-	if [ "$(printf "%b" "$msg" | wc -l)" -gt 0 ]; then
-		[ -s "$sharedMemoryOutput" ] && memmsg="$(cat "$sharedMemoryOutput")"
-		logmsg="$(printf "%b" "${memmsg}${msg}" | sed 's/\x1b\[[0-9;]*m//g')"
-		logger -t "${packageName:-service} [$$]" "$(printf "%b" "$logmsg")"
-		rm -f "$sharedMemoryOutput"
-	else
-		printf "%b" "$msg" >> "$sharedMemoryOutput"
-	fi
+output() {
+	[ -z "$verbosity" ] && verbosity="$(uci_get "$packageName" 'config' 'verbosity' '1')"
+	[ "$#" -ne '1' ] && {
+		case "$1" in [0-9]) [ $((verbosity & $1)) -gt 0 ] && shift || return 0;; esac }
+	local msg="$*" queue="/dev/shm/$packageName-output"
+	[ -t 1 ] && printf "%b" "$msg"
+	[ "$msg" != "${msg//\\n}" ] && {
+		[ -s "$queue" ] && msg="$(cat "$queue")${msg}" && rm -f "$queue"
+		msg="$(printf "%b" "$msg" | sed 's/\x1b\[[0-9;]*m//g')"
+		logger -t "$packageName [$$]" "$(printf "%b" "$msg")"
+	} || printf "%b" "$msg" >> "$queue"
 }
 
 uci_add_list_if_new() {
@@ -531,14 +513,15 @@ get_text() {
 		errorNoNft) r="dnsmasq nft sets support is enabled in $packageName, but nft is not installed";;
 		errorNoWanGateway) r="The ${serviceName} failed to discover WAN gateway";;
 		errorOutputDirCreate) r="failed to create directory for %s file";;
-		errorOutputFileCreate) r="failed to create $outputFile file";;
+		errorOutputFileCreate) r="failed to create %s file";;
 		errorFailDNSReload) r="failed to restart/reload DNS resolver";;
 		errorSharedMemory) r="failed to access shared memory";;
 		errorSorting) r="failed to sort data file";;
 		errorOptimization) r="failed to optimize data file";;
 		errorAllowListProcessing) r="failed to process allow-list";;
 		errorDataFileFormatting) r="failed to format data file";;
-		errorMovingDataFile) r="failed to move data file '${A_TMP}' to '${outputFile}'";;
+		errorCopyingDataFile) r="failed to copy data file to '%s'";;
+		errorMovingDataFile) r="failed to move data file to '%s'";;
 		errorCreatingCompressedCache) r="failed to create compressed cache";;
 		errorRemovingTempFiles) r="failed to remove temporary files";;
 		errorRestoreCompressedCache) r="failed to unpack compressed cache";;
@@ -555,6 +538,10 @@ get_text() {
 		errorDetectingFileType) r="failed to detect format";;
 		errorNothingToDo) r="no blocked list URLs nor blocked-domains enabled";;
 		errorTooLittleRam) r="free ram (%s) is not enough to process all enabled block-lists";;
+		errorCreatingBackupFile) r="failed to create backup file %s";;
+		errorDeletingDataFile) r="failed to delete data file %s";;
+		errorRestoringBackupFile) r="failed to restore backup file %s";;
+		errorNoOutputFile) r="failed to create final block-list %s";;
 
 		statusNoInstall) r="$serviceName is not installed or not found";;
 		statusStopped) r="Stopped";;
@@ -571,6 +558,7 @@ get_text() {
 		warningMissingRecommendedPackages) r="some recommended packages are missing";;
 		warningInvalidCompressedCacheDir) r="invalid compressed cache directory '%s'";;
 		warningFreeRamCheckFail) r="can't detect free RAM";;
+		*) r="Unknown text '$1'";;
 	esac
 	shift
 # shellcheck disable=SC2059
@@ -586,16 +574,16 @@ load_network() {
 		network_flush_cache
 		network_find_wan wan_if
 		if [ -n "$wan_if" ]; then
-			output "WAN interface found: '${wan_if}'.\\n"
+			output "WAN interface found: '${wan_if}'.\n"
 			break
 		fi
 		if [ "$counter" -gt "$wan_if_timeout" ]; then
-			output "WAN interface timeout, assuming 'wan'.\\n"
+			output "WAN interface timeout, assuming 'wan'.\n"
 			wan_if='wan'
 			break
 		fi
 		counter=$((counter+1))
-		output "Waiting to discover WAN Interface...\\n"
+		output "Waiting to discover WAN Interface...\n"
 		sleep 1
 	done
 
@@ -607,15 +595,15 @@ load_network() {
 		network_flush_cache
 		network_get_gateway wan_gw "$wan_if"
 		if [ -n "$wan_gw" ]; then
-			output "WAN gateway found: '${wan_gw}.'\\n"
+			output "WAN gateway found: '${wan_gw}.'\n"
 			return 0
 		fi
 		counter=$((counter+1))
-		output "Waiting to discover $wan_if gateway...\\n"
+		output "Waiting to discover $wan_if gateway...\n"
 		sleep 1
 	done
 	json add error 'errorNoWanGateway'
-	output "${_ERROR_}: $(get_text 'errorNoWanGateway')!\\n"; return 1;
+	output "${_ERROR_}: $(get_text 'errorNoWanGateway')!\n"; return 1;
 }
 
 append_url() {
@@ -664,16 +652,16 @@ load_environment() {
 
 	if [ "$validation_result" != '0' ]; then
 		json add error 'errorConfigValidationFail'
-		output "${_ERROR_}: $(get_text 'errorConfigValidationFail')!\\n"
-		output "Please check if the '$packageConfigFile' contains correct values for config options.\\n"
+		output "${_ERROR_}: $(get_text 'errorConfigValidationFail')!\n"
+		output "Please check if the '$packageConfigFile' contains correct values for config options.\n"
 		return 1
 	fi
 
 	if [ "$enabled" -eq 0 ]; then
 		json add error 'errorServiceDisabled'
-		output "${_ERROR_}: $(get_text 'errorServiceDisabled')!\\n"
-		output "Run the following commands before starting service again:\\n"
-		output "uci set ${packageName}.config.enabled='1'; uci commit $packageName;\\n"
+		output "${_ERROR_}: $(get_text 'errorServiceDisabled')!\n"
+		output "Run the following commands before starting service again:\n"
+		output "uci set ${packageName}.config.enabled='1'; uci commit $packageName;\n"
 		return 1
 	fi
 
@@ -691,7 +679,7 @@ load_environment() {
 			*)
 				if [ "$param" != 'quiet' ]; then
 					json add warning 'warningExternalDnsmasqConfig'
-					output "${_WARNING_}: $(get_text 'warningExternalDnsmasqConfig')!\\n"
+					output "${_WARNING_}: $(get_text 'warningExternalDnsmasqConfig')!\n"
 				fi
 			;;
 		esac
@@ -716,14 +704,14 @@ load_environment() {
 			if dnsmasq -v 2>/dev/null | grep -q 'no-ipset' || ! dnsmasq -v 2>/dev/null | grep -q -w 'ipset'; then
 				if [ "$param" != 'quiet' ]; then
 					json add error 'errorNoDnsmasqIpset'
-					output "${_ERROR_}: $(get_text 'errorNoDnsmasqIpset')!\\n"
+					output "${_ERROR_}: $(get_text 'errorNoDnsmasqIpset')!\n"
 				fi
 				dns='dnsmasq.servers'
 			fi
 			if ! ipset help hash:net; then
 				if [ "$param" != 'quiet' ]; then
 					json add error 'errorNoIpset'
-					output "${_ERROR_}: $(get_text 'errorNoIpset')!\\n"
+					output "${_ERROR_}: $(get_text 'errorNoIpset')!\n"
 				fi
 				dns='dnsmasq.servers'
 			fi
@@ -732,14 +720,14 @@ load_environment() {
 			if dnsmasq -v 2>/dev/null | grep -q 'no-nftset' || ! dnsmasq -v 2>/dev/null | grep -q -w 'nftset'; then
 				if [ "$param" != 'quiet' ]; then
 					json add error 'errorNoDnsmasqNftset'
-					output "${_ERROR_}: $(get_text 'errorNoDnsmasqNftset')!\\n"
+					output "${_ERROR_}: $(get_text 'errorNoDnsmasqNftset')!\n"
 				fi
 				dns='dnsmasq.servers'
 			fi
 			if [ -z "$nft" ]; then
 				if [ "$param" != 'quiet' ]; then
 					json add error 'errorNoNft'
-					output "${_ERROR_}: $(get_text 'errorNoNft')!\\n"
+					output "${_ERROR_}: $(get_text 'errorNoNft')!\n"
 				fi
 				dns='dnsmasq.servers'
 			fi
@@ -748,7 +736,7 @@ load_environment() {
 			if ! ipset help hash:net; then
 				if [ "$param" != 'quiet' ]; then
 					json add error 'errorNoIpset'
-					output "${_ERROR_}: $(get_text 'errorNoIpset')!\\n"
+					output "${_ERROR_}: $(get_text 'errorNoIpset')!\n"
 				fi
 				dns='smartdns.domainset'
 			fi
@@ -757,7 +745,7 @@ load_environment() {
 			if [ -z "$nft" ]; then
 				if [ "$param" != 'quiet' ]; then
 					json add error 'errorNoNft'
-					output "${_ERROR_}: $(get_text 'errorNoNft')!\\n"
+					output "${_ERROR_}: $(get_text 'errorNoNft')!\n"
 				fi
 				dns='smartdns.domainset'
 			fi
@@ -770,16 +758,16 @@ load_environment() {
 		compressed_cache_dir="$(sanitize_dir "$compressed_cache_dir")"
 	else
 		json add warning 'warningInvalidCompressedCacheDir' "$compressed_cache_dir"
-		output "${_WARNING_}: $(get_text 'warningInvalidCompressedCacheDir' "$compressed_cache_dir")!\\n"
+		output "${_WARNING_}: $(get_text 'warningInvalidCompressedCacheDir' "$compressed_cache_dir")!\n"
 		compressed_cache_dir="/etc"
 	fi
 
 	dns_set_output_values "$dns"
 
 	[ "$dns" = 'dnsmasq.addnhosts' ]  || rm -f "$dnsmasqAddnhostsFile" "$dnsmasqAddnhostsCache" "${compressed_cache_dir}/${dnsmasqAddnhostsGzip}"
-	[ "$dns" = 'dnsmasq.conf' ]       || rm -f "$dnsmasqConfFile" "$dnsmasqConfCache" "${compressed_cache_dir}/${dnsmasqConfGzip}"
-	[ "$dns" = 'dnsmasq.ipset' ]      || rm -f "$dnsmasqIpsetFile" "$dnsmasqIpsetCache" "${compressed_cache_dir}/${dnsmasqIpsetGzip}"
-	[ "$dns" = 'dnsmasq.nftset' ]     || rm -f "$dnsmasqNftsetFile" "$dnsmasqNftsetCache" "${compressed_cache_dir}/${dnsmasqNftsetGzip}"
+	[ "$dns" = 'dnsmasq.conf' ]       || rm -f "$dnsmasqConfCache" "${compressed_cache_dir}/${dnsmasqConfGzip}"
+	[ "$dns" = 'dnsmasq.ipset' ]      || rm -f "$dnsmasqIpsetCache" "${compressed_cache_dir}/${dnsmasqIpsetGzip}"
+	[ "$dns" = 'dnsmasq.nftset' ]     || rm -f "$dnsmasqNftsetCache" "${compressed_cache_dir}/${dnsmasqNftsetGzip}"
 	[ "$dns" = 'dnsmasq.servers' ]    || rm -f "$dnsmasqServersFile" "$dnsmasqServersCache" "${compressed_cache_dir}/${dnsmasqServersGzip}"
 	[ "$dns" = 'smartdns.domainset' ] || rm -f "$smartdnsDomainSetFile" "$smartdnsDomainSetCache" "${compressed_cache_dir}/${smartdnsDomainSetGzip}" "$smartdnsDomainSetConfig"
 	[ "$dns" = 'smartdns.ipset' ]     || rm -f "$smartdnsIpsetFile" "$smartdnsIpsetCache" "${compressed_cache_dir}/${smartdnsIpsetGzip}" "$smartdnsIpsetConfig"
@@ -791,7 +779,7 @@ load_environment() {
 		if ! mkdir -p "${i%/*}"; then
 			if [ "$param" != 'quiet' ]; then
 				json add error 'errorOutputDirCreate' "$i"
-				output "${_ERROR_}: $(get_text 'errorOutputDirCreate' "$i")!\\n"
+				output "${_ERROR_}: $(get_text 'errorOutputDirCreate' "$i")!\n"
 			fi
 		fi
 	done
@@ -800,14 +788,13 @@ load_environment() {
 	if ! is_present '/usr/libexec/grep-gnu' || ! is_present '/usr/libexec/sed-gnu' || \
 		! is_present '/usr/libexec/sort-coreutils' || ! is_present 'gawk'; then
 			local s
-			is_present 'gawk' || s="${s:+$s }gawk"
-			is_present '/usr/libexec/grep-gnu' || s="${s:+$s }grep"
-			is_present '/usr/libexec/sed-gnu' || s="${s:+$s }sed"
-			is_present '/usr/libexec/sort-coreutils' || s="${s:+$s }coreutils-sort"
+			is_present 'gawk' || { json add warning 'warningMissingRecommendedPackages' 'gawk'; s="${s:+$s }gawk"; }
+			is_present '/usr/libexec/grep-gnu' || { json add warning 'warningMissingRecommendedPackages' 'grep'; s="${s:+$s }grep"; }
+			is_present '/usr/libexec/sed-gnu' || { json add warning 'warningMissingRecommendedPackages' 'sed'; s="${s:+$s }sed"; }
+			is_present '/usr/libexec/sort-coreutils' || { json add warning 'warningMissingRecommendedPackages' 'coreutils-sort'; s="${s:+$s }coreutils-sort"; }
 			if [ "$param" != 'quiet' ]; then
-				json add warning 'warningMissingRecommendedPackages' "$s"
-				output "${_WARNING_}: $(get_text 'warningMissingRecommendedPackages'), install them by running:\\n"
-				output "opkg update; opkg --force-overwrite install $s;\\n"
+				output "${_WARNING_}: $(get_text 'warningMissingRecommendedPackages'), install them by running:\n"
+				output "opkg update; opkg --force-overwrite install $s;\n"
 			fi
 	fi
 	# Prefer curl because it supports the file:// scheme.
@@ -845,7 +832,7 @@ load_environment() {
 	config_load "$packageName"
 	config_foreach append_url 'file_url'
 	load_environment_flag=1
-	cache 'test' && return 0 
+	cache 'test' && return 0
 	cache 'test_gzip' && return 0
 	if [ "$param" = 'on_boot' ]; then
 		load_network "$param"
@@ -857,7 +844,7 @@ load_environment() {
 
 resolver() {
 	_dnsmasq_instance_config() {
-		local cfg="$1" param="$2"
+		local cfg="$1" param="$2" confdir confdirFile
 		[ -s "/etc/config/dhcp" ] || return 0
 		case "$param" in
 			dnsmasq.addnhosts)
@@ -866,7 +853,20 @@ resolver() {
 				fi
 				uci_add_list_if_new 'dhcp' "$cfg" 'addnhosts' "$dnsmasqAddnhostsFile"
 			;;
-			cleanup|dnsmasq.conf|dnsmasq.ipset|dnsmasq.nftset|unbound.adb_list)
+			cleanup|unbound.adb_list)
+# shellcheck disable=SC2016
+				if grep -q 'config_get dnsmasqconfdir "$cfg" confdir "/tmp/dnsmasq${cfg:+.$cfg}.d"' '/etc/init.d/dnsmasq'; then
+					config_get confdir "$cfg" 'confdir' "/tmp/dnsmasq${cfg:+.$cfg}.d"
+				else
+					config_get confdir "$cfg" 'confdir' '/tmp/dnsmasq.d'
+				fi
+				rm -f "${confdir}/${dnsmasqConfFile}" "${confdir}/${dnsmasqIpsetFile}" "${confdir}/${dnsmasqNftsetFile}"
+				uci_remove_list 'dhcp' "$cfg" 'addnhosts' "$dnsmasqAddnhostsFile"
+				if [ "$(uci_get 'dhcp' "$cfg" 'serversfile')" = "$dnsmasqServersFile" ]; then
+					uci_remove 'dhcp' "$cfg" 'serversfile'
+				fi
+			;;
+			dnsmasq.conf|dnsmasq.ipset|dnsmasq.nftset)
 				uci_remove_list 'dhcp' "$cfg" 'addnhosts' "$dnsmasqAddnhostsFile"
 				if [ "$(uci_get 'dhcp' "$cfg" 'serversfile')" = "$dnsmasqServersFile" ]; then
 					uci_remove 'dhcp' "$cfg" 'serversfile'
@@ -880,6 +880,24 @@ resolver() {
 			;;
 		esac
 	}
+# shellcheck disable=SC2016,SC2317
+	_dnsmasq_instance_init() {
+		local cfg="$1" param="$2" confdir confdirFile
+		[ -s "/etc/config/dhcp" ] || return 0
+		case "$param" in
+			dnsmasq.conf|dnsmasq.ipset|dnsmasq.nftset)
+				if grep -q 'config_get dnsmasqconfdir "$cfg" confdir "/tmp/dnsmasq${cfg:+.$cfg}.d"' '/etc/init.d/dnsmasq'; then
+					config_get confdir "$cfg" 'confdir' "/tmp/dnsmasq${cfg:+.$cfg}.d"
+				else
+					config_get confdir "$cfg" 'confdir' '/tmp/dnsmasq.d'
+				fi
+				confdirFile="${confdir}/${outputFile}"
+				if ! str_contains "$outputDnsmasqFileList" "$confdirFile"; then
+					outputDnsmasqFileList="${outputDnsmasqFileList:+$outputDnsmasqFileList }${confdirFile}"
+				fi
+			;;
+		esac
+	}
 	_smartdns_instance_config() {
 		[ -s "/etc/config/smartdns" ] || return 0
 		local cfg="$1" param="$2"
@@ -912,9 +930,9 @@ resolver() {
 	case $1 in
 		cleanup)
 			rm -f "$dnsmasqAddnhostsFile" "$dnsmasqAddnhostsCache" "${compressed_cache_dir}/${dnsmasqAddnhostsGzip}"
-			rm -f "$dnsmasqConfFile" "$dnsmasqConfCache" "${compressed_cache_dir}/${dnsmasqConfGzip}"
-			rm -f "$dnsmasqIpsetFile" "$dnsmasqIpsetCache" "${compressed_cache_dir}/${dnsmasqIpsetGzip}"
-			rm -f "$dnsmasqNftsetFile" "$dnsmasqNftsetCache" "${compressed_cache_dir}/${dnsmasqNftsetGzip}"
+			rm -f "$dnsmasqConfCache" "${compressed_cache_dir}/${dnsmasqConfGzip}"
+			rm -f "$dnsmasqIpsetCache" "${compressed_cache_dir}/${dnsmasqIpsetGzip}"
+			rm -f "$dnsmasqNftsetCache" "${compressed_cache_dir}/${dnsmasqNftsetGzip}"
 			rm -f "$dnsmasqServersFile" "$dnsmasqServersCache" "${compressed_cache_dir}/${dnsmasqServersGzip}"
 			rm -f "$smartdnsDomainSetFile" "$smartdnsDomainSetCache" "${compressed_cache_dir}/${smartdnsDomainSetGzip}" "$smartdnsDomainSetConfig"
 			rm -f "$smartdnsIpsetFile" "$smartdnsIpsetCache" "${compressed_cache_dir}/${smartdnsIpsetGzip}" "$smartdnsIpsetConfig"
@@ -931,14 +949,29 @@ resolver() {
 				[ -n "$(uci_changes 'smartdns')" ] && uci_commit 'smartdns'
 			fi
 		;;
+		on_load)
+			case "$dns" in
+				dnsmasq.conf|dnsmasq.ipset|dnsmasq.nftset)
+					[ -z "$outputDnsmasqFileList" ] || return 0
+					config_load 'dhcp'
+					if [ "$dnsmasq_instance" = "*" ]; then
+						config_foreach _dnsmasq_instance_init 'dnsmasq' "$dns"
+					elif [ -n "$dnsmasq_instance" ]; then
+						for i in $dnsmasq_instance; do
+							_dnsmasq_instance_init "@dnsmasq[$i]" "$dns" || _dnsmasq_instance_init "$i" "$dns"
+						done
+					fi
+					outputFile="$(str_first_word "$outputDnsmasqFileList")"
+				;;
+			esac
+		;;
 		on_start)
 			if [ ! -s "$outputFile" ]; then
 				json set status 'statusFail'
-				json add error 'errorOutputFileCreate'
-				output "${_ERROR_}: $(get_text 'errorOutputFileCreate')!\\n"
+				json add error 'errorOutputFileCreate' "$outputFile"
+				output "${_ERROR_}: $(get_text 'errorOutputFileCreate' "$outputFile")!\n"
 				return 1
 			fi
-
 			config_load 'dhcp'
 			if [ "$dnsmasq_instance" = "*" ]; then
 				config_foreach _dnsmasq_instance_config 'dnsmasq' "$dns"
@@ -958,8 +991,21 @@ resolver() {
 
 			case "$dns" in
 				dnsmasq.*)
-					chmod 660 "$outputFile"
-					chown root:dnsmasq "$outputFile" >/dev/null 2>/dev/null
+					if [ -n "$outputDnsmasqFileList" ]; then
+						local i
+						for i in $outputDnsmasqFileList; do
+							chmod 660 "$i"
+							chown root:dnsmasq "$i" >/dev/null 2>/dev/null
+						done
+					elif [ -s "$outputFile" ]; then
+						chmod 660 "$outputFile"
+						chown root:dnsmasq "$outputFile" >/dev/null 2>/dev/null
+					else
+						json set status 'statusFail'
+						json add error 'errorNoOutputFile' "$outputFile"
+						output "${_ERROR_}: $(get_text 'errorNoOutputFile' "$outputFile")!\n"
+						return 1
+					fi
 					param='dnsmasq_restart'
 					output_text='Restarting dnsmasq'
 				;;
@@ -1002,7 +1048,7 @@ resolver() {
 				output_fail
 				json set status 'statusFail'
 				json add error 'errorDNSReload'
-				output "${_ERROR_}: $(get_text 'errorDNSReload')!\\n"
+				output "${_ERROR_}: $(get_text 'errorDNSReload')!\n"
 				return 1
 			fi
 		;;
@@ -1051,12 +1097,44 @@ cache() {
 	local R_TMP
 	case "$1" in
 		create|backup)
-			[ -s "$outputFile" ] && { mv -f "$outputFile" "$outputCache"; } >/dev/null 2>/dev/null
-			return $?
+			if [ -n "$outputDnsmasqFileList" ]; then
+				local i __firstFile
+				for i in $outputDnsmasqFileList; do
+				if [ -z "$__firstFile" ]; then
+					__firstFile="$i"
+					if ! mv "$i" "$outputCache"; then
+						json add error 'errorCreatingBackupFile' "$outputCache"
+					fi
+				else
+					if ! rm -f "$i"; then
+						json add error 'errorDeletingDataFile' "$i"
+					fi
+				fi
+				done
+			else
+				[ -s "$outputFile" ] && { mv -f "$outputFile" "$outputCache"; } >/dev/null 2>/dev/null
+				return $?
+			fi
 		;;
 		restore|use)
-			[ -s "$outputCache" ] && mv "$outputCache" "$outputFile" >/dev/null 2>/dev/null
-			return $?
+			if [ -n "$outputDnsmasqFileList" ]; then
+				local i __firstFile
+				for i in $outputDnsmasqFileList; do
+				if [ -z "$__firstFile" ]; then
+					__firstFile="$i"
+					if ! mv "$outputCache" "$i"; then
+						json add error 'errorRestoringBackupFile' "$i"
+					fi
+				else
+					if ! cp "$__firstFile" "$i"; then
+						json add error 'errorRestoringBackupFile' "$i"
+					fi
+				fi
+				done
+			else
+				[ -s "$outputCache" ] && mv "$outputCache" "$outputFile" >/dev/null 2>/dev/null
+				return $?
+			fi
 		;;
 		test)
 			[ -s "$outputCache" ]
@@ -1091,8 +1169,8 @@ cache() {
 process_file_url_wrapper() {
 	if [ "$2" != '0' ]; then
 		json add error 'errorConfigValidationFail'
-		output "${_ERROR_}: $(get_text 'errorConfigValidationFail')!\\n"
-		output "Please check if the '$packageConfigFile' contains correct values for config options.\\n"
+		output "${_ERROR_}: $(get_text 'errorConfigValidationFail')!\n"
+		output "Please check if the '$packageConfigFile' contains correct values for config options.\n"
 	fi
 	if [ "$parallel_downloads" -gt 0 ]; then
 		process_file_url "$1" &
@@ -1102,6 +1180,16 @@ process_file_url_wrapper() {
 }
 
 process_file_url() {
+	_sanitize_source() {
+		local type="$1" file="$2"
+		case "$type" in
+			hosts)
+				sed -i '/# Title: StevenBlack/,/# Start StevenBlack/d' "$file"
+#				sed -i -E '/^(.*)[\t ](local|localhost|localhost.localdomain)$/d;/^255.255.255.255[\t ]broadcasthost$/d;/^0.0.0.0[\t ]0.0.0.0$/d' "$file"
+#				sed -i -E '/^(.*)[\t ](ip6-localhost|ip6-loopback|ip6-localnet|ip6-mcastprefix|ip6-allnodes|ip6-allrouters|ip6-allhosts)/d' "$file"
+			;;
+		esac
+	}
 	local cfg="$1" new_size
 	local label type D_TMP R_TMP filter
 	if [ -z "$cfg" ] || [ -n "${2}${3}" ]; then
@@ -1126,7 +1214,7 @@ process_file_url() {
 	esac
 	if is_https_url "$url" && [ -z "$isSSLSupported" ]; then
 		output 1 "$_FAIL_"
-		output 2 "[DL] $type $label $__FAIL__\\n"
+		output 2 "[DL] $type $label $__FAIL__\n"
 		echo "errorNoSSLSupport|${1}" >> "$runningErrorFile"
 		return 0
 	fi
@@ -1136,7 +1224,7 @@ process_file_url() {
 	if [ -z "$url" ] || ! $dl_command "$url" "$dl_flag" "$R_TMP" 2>/dev/null || \
 		[ ! -s "$R_TMP" ]; then
 		output 1 "$_FAIL_"
-		output 2 "[DL] $type $label $__FAIL__\\n"
+		output 2 "[DL] $type $label $__FAIL__\n"
 		echo "errorDownloadingList|${url}" >> "$runningErrorFile"
 	else
 		append_newline "$R_TMP"
@@ -1151,10 +1239,13 @@ process_file_url() {
 			dnsmasq2) filter="$dnsmasq2FileFilter";;
 			dnsmasq3) filter="$dnsmasq3FileFilter";;
 			domains) filter="$domainsFilter";;
-			hosts) filter="$hostsFilter";;
+			hosts)
+				filter="$hostsFilter"
+				_sanitize_source 'hosts' "$R_TMP"
+			;;
 			*)
 				output 1 "$_FAIL_"
-				output 2 "[DL] $type $label $__FAIL__\\n"
+				output 2 "[DL] $type $label $__FAIL__\n"
 				echo "errorDetectingFileType|${url}" >> "$runningErrorFile"
 				rm -f "$R_TMP"
 				return 0
@@ -1165,13 +1256,13 @@ process_file_url() {
 		fi
 		if [ ! -s "$R_TMP" ]; then
 			output 1 "$_FAIL_"
-			output 2 "[DL] $type $label ($format) $__FAIL__\\n"
+			output 2 "[DL] $type $label ($format) $__FAIL__\n"
 			echo "errorParsingList|${url}" >> "$runningErrorFile"
 		else
 			append_newline "$R_TMP"
 			cat "${R_TMP}" >> "$D_TMP"
 			output 1 "$_OK_"
-			output 2 "[DL] $type $label ($format) $__OK__\\n"
+			output 2 "[DL] $type $label ($format) $__OK__\n"
 		fi
 	fi
 	rm -f "$R_TMP"
@@ -1202,12 +1293,25 @@ download_dnsmasq_file() {
 		rm -f "$runningErrorFile"
 	fi
 	output 2 'Moving dnsmasq file '
-	if mv "$B_TMP" "$outputFile"; then
-		output 2 "$__OK__\\n"
+	local i __firstFile
+	for i in $outputDnsmasqFileList; do
+	if [ -z "$__firstFile" ]; then
+		__firstFile="$i"
+		if mv "$B_TMP" "$i"; then
+			output 2 "$__OK__\n"
+		else
+			output 2 "$__FAIL__\n"
+			json add error 'errorMovingDataFile' "$i"
+		fi
 	else
-		output 2 "$__FAIL__\\n"
-		json add error 'errorMovingDataFile'
+		if cp "$__firstFile" "$i"; then
+			output 2 "$__OK__\n"
+		else
+			output 2 "$__FAIL__\n"
+			json add error 'errorCopyingDataFile' "$i"
+		fi
 	fi
+	done
 	output 1 '\n'
 }
 
@@ -1228,14 +1332,14 @@ download_lists() {
 		free_mem="$(get_ram_available)"
 		if [ -z "$free_mem" ]; then
 			json add warnning 'warningFreeRamCheckFail'
-			output "${_WARNING_}: $(get_text 'warningFreeRamCheckFail')!\\n"
+			output "${_WARNING_}: $(get_text 'warningFreeRamCheckFail')!\n"
 			return 0
 		fi
 		config_load "$packageName"
 		config_foreach _config_calculate_sizes 'file_url'
 		if [ $((free_mem)) -lt $((total_sizes * 2)) ]; then
 			json add error 'errorTooLittleRam' "$free_mem"
-			output "${_ERROR_}: $(get_text 'errorTooLittleRam' "$free_mem")!\\n"
+			output "${_ERROR_}: $(get_text 'errorTooLittleRam' "$free_mem")!\n"
 			return 1
 		else
 			return 0
@@ -1265,7 +1369,7 @@ download_lists() {
 	wait
 	if [ -n "$(uci_changes "$packageName")" ]; then 
 		output 2 "Saving updated file size(s) "
-		if uci_commit "$packageName"; then output_okn; else output_failn; fi
+		if uci_commit "$packageName"; then output_ok; else output_fail; fi
 	fi
 	output 1 '\n'
 
@@ -1287,16 +1391,12 @@ download_lists() {
 	for hf in $blocked_domain $canaryDomains; do
 		printf "%s\n" "$(echo "$hf" | sed "$domainsFilter")" >> "$B_TMP"
 	done
-	allowed_domain="${allowed_domain}
-$(sed '/^[[:space:]]*$/d' "$A_TMP")"
-	for hf in ${allowed_domain}; do
-		hf="$(echo "$hf" | sed 's/\./\\./g')"
-		echo "/(^|\.)${hf}$/d;" >> "$SED_TMP"
-	done
-
 	sed -i '/^[[:space:]]*$/d' "$B_TMP"
 	[ ! -s "$B_TMP" ] && return 1
 
+	allowed_domain="${allowed_domain}
+$(sed '/^[[:space:]]*$/d' "$A_TMP")"
+
 	output 1 'Processing downloads '
 	output 2 'Sorting combined list '
 	json set status 'statusProcessing'
@@ -1305,76 +1405,84 @@ $(sed '/^[[:space:]]*$/d' "$A_TMP")"
 		if sort -u "$B_TMP" > "$A_TMP"; then
 			output_ok
 		else
-			output_failn
+			output_fail
 			json add error 'errorSorting'
 		fi
 	else
 		if sort -u "$B_TMP" | grep -E -v '[^a-zA-Z0-9=/.-]' > "$A_TMP"; then
 			output_ok
 		else
-			output_failn
+			output_fail
 			json add error 'errorSorting'
 		fi
 	fi
 
-	if [ "$dns" = 'dnsmasq.conf' ] || \
-		 [ "$dns" = 'dnsmasq.ipset' ] || \
-		 [ "$dns" = 'dnsmasq.nftset' ] || \
-		 [ "$dns" = 'dnsmasq.servers' ] || \
-		 [ "$dns" = 'smartdns.domainset' ] || \
-		 [ "$dns" = 'smartdns.ipset' ] || \
-		 [ "$dns" = 'smartdns.nftset' ] || \
-		 [ "$dns" = 'unbound.adb_list' ]; then
-		# TLD optimization written by Dirk Brenken (dev@brenken.org)
-		output 2 'Optimizing combined list '
-		json set message "$(get_text 'statusProcessing'): optimizing combined list"
-# 	sed -E 'G;:t;s/(.*)(\.)(.*)(\n)(.*)/\1\4\5\2\3/;tt;s/(.*)\n(\.)(.*)/\3\2\1/' is actually slower than command below
-# shellcheck disable=SC2016
-		if $awk -F "." '{for(f=NF;f>1;f--)printf "%s.",$f;print $1}' "$A_TMP" > "$B_TMP"; then
-			if sort "$B_TMP" > "$A_TMP"; then
-				if $awk '{if(NR=1){tld=$NF};while(getline){if($NF!~tld"\\."){print tld;tld=$NF}}print tld}' "$A_TMP" > "$B_TMP"; then
-					if $awk -F "." '{for(f=NF;f>1;f--)printf "%s.",$f;print $1}' "$B_TMP" > "$A_TMP"; then
-						if sort -u "$A_TMP" > "$B_TMP"; then
-							output_ok
+	case "$dns" in
+		'dnsmasq.conf' | 'dnsmasq.ipset' | 'dnsmasq.nftset' | 'dnsmasq.servers' | \
+		'smartdns.domainset' | 'smartdns.ipset' | 'smartdns.nftset' | \
+		'unbound.adb_list' )
+			# TLD optimization written by Dirk Brenken (dev@brenken.org)
+			output 2 'Optimizing combined list '
+			json set message "$(get_text 'statusProcessing'): optimizing combined list"
+	# 	sed -E 'G;:t;s/(.*)(\.)(.*)(\n)(.*)/\1\4\5\2\3/;tt;s/(.*)\n(\.)(.*)/\3\2\1/' is actually slower than command below
+	# shellcheck disable=SC2016
+			if $awk -F "." '{for(f=NF;f>1;f--)printf "%s.",$f;print $1}' "$A_TMP" > "$B_TMP"; then
+				if sort "$B_TMP" > "$A_TMP"; then
+					if $awk '{if(NR=1){tld=$NF};while(getline){if($NF!~tld"\\."){print tld;tld=$NF}}print tld}' "$A_TMP" > "$B_TMP"; then
+						if $awk -F "." '{for(f=NF;f>1;f--)printf "%s.",$f;print $1}' "$B_TMP" > "$A_TMP"; then
+							if sort -u "$A_TMP" > "$B_TMP"; then
+								output_ok
+							else
+								output_failn
+								json add error 'errorOptimization'
+								mv "$A_TMP" "$B_TMP"
+							fi
 						else
 							output_failn
 							json add error 'errorOptimization'
-							mv "$A_TMP" "$B_TMP"
 						fi
 					else
 						output_failn
 						json add error 'errorOptimization'
+						mv "$A_TMP" "$B_TMP"
 					fi
 				else
 					output_failn
 					json add error 'errorOptimization'
-					mv "$A_TMP" "$B_TMP"
 				fi
 			else
 				output_failn
 				json add error 'errorOptimization'
+				mv "$A_TMP" "$B_TMP"
 			fi
-		else
-			output_failn
-			json add error 'errorOptimization'
+		;;
+		*)
 			mv "$A_TMP" "$B_TMP"
-		fi
-	else
-		mv "$A_TMP" "$B_TMP"
-	fi
+		;;
+	esac
 
-	if [ -s "$SED_TMP" ]; then
-		output 2 'Allowing domains '
+	if [ -n "$allowed_domain" ]; then
+		output 2 'Removing allowed domains from combined list'
 		json set message "$(get_text 'statusProcessing'): allowing domains"
-		if sed -i -E -f "$SED_TMP" "$B_TMP"; then
-			output_ok
+		for hf in ${allowed_domain}; do
+			hf="$(echo "$hf" | sed 's/\./\\./g')"
+			echo "/(^|\.)${hf}$/d;" >> "$SED_TMP"
+		done
+		if [ -s "$SED_TMP" ]; then
+			if sed -i -E -f "$SED_TMP" "$B_TMP"; then
+				output_ok
+			else
+				output_failn
+				json add error 'errorAllowListProcessing'
+			fi
 		else
 			output_failn
 			json add error 'errorAllowListProcessing'
 		fi
 	fi
-	output 2 'Formatting merged file '
-	json set message "$(get_text 'statusProcessing'): formatting merged file"
+
+	output 2 'Formatting combined list file '
+	json set message "$(get_text 'statusProcessing'): formatting combined list file"
 	if [ -z "$outputFilterIPv6" ]; then
 		if sed "$outputFilter" "$B_TMP" > "$A_TMP"; then
 			output_ok
@@ -1396,21 +1504,43 @@ $(sed '/^[[:space:]]*$/d' "$A_TMP")"
 		esac
 	fi
 
+	if [ -n "$outputAllowFilter" ] && [ -n "$allowed_domain" ]; then
+		rm -f "$SED_TMP"; touch "$SED_TMP";
+		output 2 'Allowing domains '
+		json set message "$(get_text 'statusProcessing'): allowing domains"
+		for hf in ${allowed_domain}; do
+			echo "$hf" | sed -E "$outputAllowFilter" >> "$SED_TMP"
+		done
+		if [ -s "$SED_TMP" ]; then
+			if cat "$SED_TMP" "$A_TMP" > "$B_TMP"; then
+				output_ok
+			else
+				output_failn
+				json add error 'errorAllowListProcessing'
+			fi
+		else
+			output_failn
+			json add error 'errorAllowListProcessing'
+		fi
+	else
+		mv "$A_TMP" "$B_TMP"
+	fi
+
 	case "$dns" in
 		dnsmasq.addnhosts)
 			output 2 'Creating dnsmasq addnhosts file '
 			json set message "$(get_text 'statusProcessing'): creating dnsmasq addnhosts file"
 		;;
 		dnsmasq.conf)
-			output 2 'Creating dnsmasq config file '
+			output 2 'Creating dnsmasq config file(s) '
 			json set message "$(get_text 'statusProcessing'): creating dnsmasq config file"
 		;;
 		dnsmasq.ipset)
-			output 2 'Creating dnsmasq ipset file '
+			output 2 'Creating dnsmasq ipset file(s) '
 			json set message "$(get_text 'statusProcessing'): creating dnsmasq ipset file"
 		;;
 		dnsmasq.nftset)
-			output 2 'Creating dnsmasq nft set file '
+			output 2 'Creating dnsmasq nft set file(s) '
 			json set message "$(get_text 'statusProcessing'): creating dnsmasq nft set file"
 		;;
 		dnsmasq.servers)
@@ -1435,16 +1565,45 @@ $(sed '/^[[:space:]]*$/d' "$A_TMP")"
 		;;
 	esac
 
-	if mv "$A_TMP" "$outputFile"; then
-		output_ok
-	else
-		output_failn
-		json add error 'errorMovingDataFile'
-	fi
 	case "$dns" in
+		dnsmasq.conf|dnsmasq.ipset|dnsmasq.nftset)
+			local i __firstFile
+			for i in $outputDnsmasqFileList; do
+			if [ -z "$__firstFile" ]; then
+				__firstFile="$i"
+				if mv "$B_TMP" "$i"; then
+					output 2 "$__OK__\n"
+				else
+					output 2 "$__FAIL__\n"
+					json add error 'errorMovingDataFile' "$i"
+				fi
+			else
+				if cp "$__firstFile" "$i"; then
+					output 2 "$__OK__\n"
+				else
+					output 2 "$__FAIL__\n"
+					json add error 'errorCopyingDataFile' "$i"
+				fi
+			fi
+			done
+		;;
 		unbound.adb_list)
+			if mv "$B_TMP" "$outputFile"; then
+				output_ok
+			else
+				output_failn
+				json add error 'errorMovingDataFile' "$outputFile"
+			fi
 			sed -i '1 i\server:' "$outputFile"
 		;;
+		*)
+			if mv "$B_TMP" "$outputFile"; then
+				output_ok
+			else
+				output_failn
+				json add error 'errorMovingDataFile' "$outputFile"
+			fi
+		;;
 	esac
 	if [ "$compressed_cache" -gt 0 ]; then
 		output 2 'Creating compressed cache '
@@ -1475,24 +1634,38 @@ adb_allow() {
 	local validation_result="$3"
 	load_environment "$validation_result" 'quiet' || return 1
 	if [ ! -s "$outputFile" ]; then
-		output "No block-list ('$outputFile') found.\\n"
+		output "No block-list ('$outputFile') found.\n"
 		return 0
 	elif [ -z "$string" ]; then
-		output "Usage: /etc/init.d/${packageName} allow 'domain' ...\\n"
+		output "Usage: /etc/init.d/${packageName} allow 'domain' ...\n"
 		return 0
 	elif [ -n "$dnsmasq_config_file_url" ]; then
-		output "Allowing individual domains is not possible when using external dnsmasq config file.\\n"
+		output "Allowing individual domains is not possible when using external dnsmasq config file.\n"
 		return 0
 	fi
 	case "$dns" in
 		dnsmasq.*)
 			output 1 "Allowing domain(s) and restarting dnsmasq "
-			output 2 "Allowing domain(s) \\n"
+			output 2 "Allowing domain(s) \n"
 			for c in $string; do
 				output 2 "  $c "
 				hf="$(echo "$c" | sed 's/\./\\./g')"
-				if sed -i "\:\(/\|\.\)${hf}/:d" "$outputFile" && \
-					uci_add_list_if_new "${packageName}" 'config' 'allowed_domain' "$c"; then
+				local f
+				for f in ${outputDnsmasqFileList:-$outputFile}; do
+					if sed -i "\:\(/\|\.\)${hf}/:d" "$f"; then
+							output_ok
+					else
+						output_fail
+					fi
+				done
+				if [ -n "$outputAllowFilter" ]; then
+					if echo "$c" | sed -E "$outputAllowFilter" >> "$outputFile"; then
+							output_ok
+					else
+						output_fail
+					fi
+				fi
+				if uci_add_list_if_new "${packageName}" 'config' 'allowed_domain' "$c"; then
 						output_ok
 				else
 					output_fail
@@ -1503,15 +1676,15 @@ adb_allow() {
 				if cache 'create_gzip'; then
 					output_ok
 				else
-					output_failn
+					output_fail
 				fi
 			fi
 			output 2 "Committing changes to config "
 			if uci_commit "$packageName"; then
 				allowed_domain="$(uci_get "$packageName" 'config' 'allowed_domain')"
 				config_cache 'create'
-				json set stats "$serviceName is blocking $(wc -l < "$outputFile") domains (with ${dns})"
-				output_ok; 
+				json set stats "$serviceName is blocking $(count_blocked_domains) domains (with ${dns})"
+				output_ok
 				if [ "$dns" = 'dnsmasq.ipset' ]; then
 					output 2 "Flushing adb ipset "
 					if ipset -q -! flush adb; then output_ok; else output_fail; fi
@@ -1524,12 +1697,12 @@ adb_allow() {
 				output 2 "Restarting dnsmasq "
 				if dnsmasq_restart; then output_okn; else output_failn; fi
 			else 
-				output_fail; 
+				output_failn
 			fi
 		;;
 		smartdns.*)
 			output 1 "Allowing domain(s) and restarting smartdns "
-			output 2 "Allowing domain(s) \\n"
+			output 2 "Allowing domain(s) \n"
 			for c in $string; do 
 				output 2 "  $c "
 				hf="$(echo "$c" | sed 's/\./\\./g')"
@@ -1545,24 +1718,24 @@ adb_allow() {
 				if cache 'create_gzip'; then
 					output_ok
 				else
-					output_failn
+					output_fail
 				fi
 			fi
 			output 2 "Committing changes to config "
 			if uci_commit "$packageName"; then
 				allowed_domain="$(uci_get "$packageName" 'config' 'allowed_domain')"
 				config_cache 'create'
-				json set stats "$serviceName is blocking $(wc -l < "$outputFile") domains (with ${dns})"
+				json set stats "$serviceName is blocking $(count_blocked_domains) domains (with ${dns})"
 				output_ok; 
 				output 2 "Restarting Unbound "
 				if unbound_restart; then output_okn; else output_failn; fi
 			else 
-				output_fail; 
+				output_failn
 			fi
 		;;
 		unbound.*)
 			output 1 "Allowing domain(s) and restarting Unbound "
-			output 2 "Allowing domain(s) \\n"
+			output 2 "Allowing domain(s) \n"
 			for c in $string; do 
 				output 2 "  $c "
 				hf="$(echo "$c" | sed 's/\./\\./g')"
@@ -1585,12 +1758,12 @@ adb_allow() {
 			if uci_commit "$packageName"; then
 				allowed_domain="$(uci_get "$packageName" 'config' 'allowed_domain')"
 				config_cache 'create'
-				json set stats "$serviceName is blocking $(wc -l < "$outputFile") domains (with ${dns})"
+				json set stats "$serviceName is blocking $(count_blocked_domains) domains (with ${dns})"
 				output_ok; 
 				output 2 "Restarting Unbound "
 				if unbound_restart; then output_okn; else output_failn; fi
 			else 
-				output_fail; 
+				output_failn
 			fi
 		;;
 	esac
@@ -1601,25 +1774,25 @@ adb_check() {
 	local validation_result="$3"
 	load_environment "$validation_result" 'quiet' || return 1
 	if [ ! -s "$outputFile" ]; then
-		output "No block-list ('$outputFile') found.\\n"
+		output "No block-list ('$outputFile') found.\n"
 		return 0
 	elif [ -z "$param" ]; then
-		output "Usage: /etc/init.d/${packageName} check 'domain' ...\\n"
+		output "Usage: /etc/init.d/${packageName} check 'domain' ...\n"
 		return 0
 	fi
 	for string in ${param}; do
 		c="$(grep -c "$string" "$outputFile")"
 		if [ "$c" -gt 0 ]; then
 			if [ "$c" -eq 1 ]; then
-				output "Found 1 match for '$string' in '$outputFile'.\\n"
+				output "Found 1 match for '$string' in '$outputFile'.\n"
 			else
-				output "Found $c matches for '$string' in '$outputFile'.\\n"
+				output "Found $c matches for '$string' in '$outputFile'.\n"
 			fi
 			if [ "$c" -le 20 ]; then
 				grep "$string" "$outputFile" | sed "$outputOutputFilter"
 			fi
 		else
-			output "The '$string' is not found in current block-list ('$outputFile').\\n"
+			output "The '$string' is not found in current block-list ('$outputFile').\n"
 		fi
 	done
 }
@@ -1635,27 +1808,27 @@ adb_check_lists() {
 		[ "$en" = '0' ] && return 0
 		[ "$action" != 'block' ] && return 0
 		if is_https_url "$url" && [ -z "$isSSLSupported" ]; then
-			output "[DL] $url $__FAIL__\\n"
+			output "[DL] $url $__FAIL__\n"
 		fi
 		while [ -z "$R_TMP" ] || [ -e "$R_TMP" ]; do
 			R_TMP="$(mktemp -u -q -t "${packageName}_tmp.XXXXXXXX")"
 		done
 		if [ -z "$url" ] || ! $dl_command "$url" "$dl_flag" "$R_TMP" 2>/dev/null || \
 			[ ! -s "$R_TMP" ]; then
-			output "[DL] $url $__FAIL__\\n"
+			output "[DL] $url $__FAIL__\n"
 		else
 			append_newline "$R_TMP"
 			for string in ${param}; do
 				c="$(grep -c "$string" "$R_TMP")"
 				if [ "$c" -gt 0 ]; then
 					if [ "$c" -eq 1 ]; then
-						output "Found 1 match for '$string' in '$url'.\\n"
+						output "Found 1 match for '$string' in '$url'.\n"
 					else
-						output "Found $c matches for '$string' in '$url'.\\n"
+						output "Found $c matches for '$string' in '$url'.\n"
 					fi
 					grep "$string" "$R_TMP"
 				else
-					output "The '$string' is not found in '$url'.\\n"
+					output "The '$string' is not found in '$url'.\n"
 				fi
 			done
 		rm -f "$R_TMP"
@@ -1665,7 +1838,7 @@ adb_check_lists() {
 	local validation_result="$3"
 	load_environment "$validation_result" 'quiet' || return 1
 	if [ -z "$param" ]; then
-		output "Usage: /etc/init.d/${packageName} check_lists 'domain' ...\\n"
+		output "Usage: /etc/init.d/${packageName} check_lists 'domain' ...\n"
 		return 0
 	fi
 	config_load "$packageName"
@@ -1703,16 +1876,16 @@ adb_config_update() {
 	done
 	if ! $dl_command "$config_update_url" "$dl_flag" "$R_TMP" 2>/dev/null || [ ! -s "$R_TMP" ]; then
 		append_newline "$R_TMP"
-		output 1 "$_FAIL_\\n"
-		output 2 "[DL] Config  Update:  $label $__FAIL__\\n"
+		output 1 "$_FAIL_\n"
+		output 2 "[DL] Config  Update:  $label $__FAIL__\n"
 		json add error 'errorDownloadingConfigUpdate'
 	else
 		if [ -s "$R_TMP" ] && sed -f "$R_TMP" -i "$packageConfigFile" 2>/dev/null; then
-			output 1 "$_OK_\\n"
-			output 2 "[DL] Config  Update:  $label $__OK__\\n"
+			output 1 "$_OK_\n"
+			output 2 "[DL] Config  Update:  $label $__OK__\n"
 		else
-			output 1 "$_FAIL_\\n"
-			output 2 "[DL] Config  Update:  $label $__FAIL__\\n"
+			output 1 "$_FAIL_\n"
+			output 2 "[DL] Config  Update:  $label $__FAIL__\n"
 			json add error 'errorParsingConfigUpdate'
 		fi
 	fi
@@ -1794,7 +1967,7 @@ adb_start() {
 
 	if [ "$action" = 'restore' ]; then
 		output 0 "Starting $serviceName... "
-		output 3 "Starting $serviceName...\\n"
+		output 3 "Starting $serviceName...\n"
 		json set status 'statusStarting'
 		if cache 'test_gzip' && ! cache 'test' && [ ! -s "$outputFile" ]; then
 			output 3 'Found compressed cache file, unpacking it '
@@ -1804,7 +1977,7 @@ adb_start() {
 			else
 				output_failn
 				json add error 'errorRestoreCompressedCache'
-				output "${_ERROR_}: $(get_text 'errorRestoreCompressedCache')!\\n"
+				output "${_ERROR_}: $(get_text 'errorRestoreCompressedCache')!\n"
 				action='download'
 			fi
 		fi
@@ -1817,7 +1990,7 @@ adb_start() {
 			else
 				output_failn
 				json add error 'errorRestoreCache'
-				output "${_ERROR_}: $(get_text 'errorRestoreCache')!\\n"
+				output "${_ERROR_}: $(get_text 'errorRestoreCache')!\n"
 				action='download'
 			fi
 		fi
@@ -1826,15 +1999,15 @@ adb_start() {
 		if [ -z "$blocked_url" ] && [ -z "$blocked_domain" ]; then
 			json set status 'statusFail'
 			json add error 'errorNothingToDo'
-			output "${_ERROR_}: $(get_text 'errorNothingToDo')!\\n"
+			output "${_ERROR_}: $(get_text 'errorNothingToDo')!\n"
 		else
 			if [ -s "$outputFile" ] || cache 'test' || cache 'test_gzip'; then
 				output 0 "Force-reloading $serviceName... "
-				output 3 "Force-reloading $serviceName...\\n"
+				output 3 "Force-reloading $serviceName...\n"
 				json set status 'statusForceReloading'
 			else
 				output 0 "Starting $serviceName... "
-				output 3 "Starting $serviceName...\\n"
+				output 3 "Starting $serviceName...\n"
 				json set status 'statusStarting'
 			fi
 			resolver 'cleanup'
@@ -1848,25 +2021,25 @@ adb_start() {
 	fi
 	if [ "$action" = 'restart' ]; then
 		output 0 "Restarting $serviceName... "
-		output 3 "Restarting $serviceName...\\n"
+		output 3 "Restarting $serviceName...\n"
 		json set status 'statusRestarting'
 		resolver 'on_start'
 	fi
 	if [ "$action" = 'start' ]; then
 		output 0 "Starting $serviceName... "
-		output 3 "Starting $serviceName...\\n"
+		output 3 "Starting $serviceName...\n"
 		json set status 'statusStarting'
 		resolver 'on_start'
 	fi
 	if [ -s "$outputFile" ] && [ "$(json get status)" != "statusFail" ]; then
-		output 0 "$__OK__\\n";
+		output 0 "$__OK__\n";
 		json del message
 		json set status 'statusSuccess'
-		json set stats "$serviceName is blocking $(wc -l < "$outputFile") domains (with ${dns})"
+		json set stats "$serviceName is blocking $(count_blocked_domains) domains (with ${dns})"
 		status_service 'quiet'
 
 	else
-		output 0 "$__FAIL__\\n";
+		output 0 "$__FAIL__\n";
 		json set status 'statusFail'
 		json add error 'errorOhSnap'
 		status_service 'quiet'
@@ -1881,7 +2054,7 @@ adb_start() {
 	json_add_string 'errors' "$(json get error)"
 	json_add_string 'warnings' "$(json get warning)"
 	if [ -s "$outputFile" ]; then
-		json_add_int 'entries' "$(wc -l < "$outputFile")"
+		json_add_int 'entries' "$(count_blocked_domains)"
 	else
 		json_add_int 'entries' '0'
 	fi
@@ -1993,20 +2166,20 @@ adb_status() {
 		if [ -n "$status" ] && [ -n "$message" ]; then 
 			status="${status}: $message"
 		fi
-		[ -n "$status" ] && output "$serviceName $status!\\n"
+		[ -n "$status" ] && output "$serviceName $status!\n"
 	fi
 	if [ "$param" != 'quiet' ] && [ -n "$error" ]; then
 		for c in $error; do
 			local error_param="${c##*|}"
 			local error_code="${c%|*}"
-			output "${_ERROR_}: $(get_text "$error_code" "$error_param")!\\n"
+			output "${_ERROR_}: $(get_text "$error_code" "$error_param")!\n"
 		done
 	fi
 	if [ "$param" != 'quiet' ] && [ -n "$warning" ]; then
 		for c in $warning; do
 			local warning_param="${c##*|}"
 			local warning_code="${c%|*}"
-			output "${_WARNING_}: $(get_text "$warning_code" "$warning_param").\\n"
+			output "${_WARNING_}: $(get_text "$warning_code" "$warning_param").\n"
 		done
 	fi
 	return 0
@@ -2025,14 +2198,14 @@ adb_stop() {
 			nft delete set inet fw4 adb4
 			nft delete set inet fw4 adb6
 			led_off "$led"
-			output 0 "$__OK__\\n"; output_okn;
+			output 0 "$__OK__\n"; output_okn;
 			json set status 'statusStopped'
 			json del message
 		else 
-			output 0 "$__FAIL__\\n"; output_fail;
+			output 0 "$__FAIL__\n"; output_fail;
 			json set status 'statusFail'
 			json add error 'errorStopping'
-			output "${_ERROR_}: $(get_text 'errorStopping')!\\n"
+			output "${_ERROR_}: $(get_text 'errorStopping')!\n"
 		fi
 	fi
 	return 0
diff --git a/net/adblock-fast/files/etc/uci-defaults/90-adblock-fast b/net/adblock-fast/files/etc/uci-defaults/90-adblock-fast
index e4f8683dd9..3cf0db1984 100644
--- a/net/adblock-fast/files/etc/uci-defaults/90-adblock-fast
+++ b/net/adblock-fast/files/etc/uci-defaults/90-adblock-fast
@@ -17,7 +17,7 @@ _enable_url() {
 	config_get u "$cfg" 'url'
 	config_get a "$cfg" 'action' 'block'
 	if [ "$u" = "$url" ] && [ "$a" = "$action" ]; then
-		uci del "${packageName}.${cfg}.enabled" && _found=1
+		uci_remove "$packageName" "$cfg" 'enabled' && _found=1
 	fi
 }
 
@@ -26,32 +26,32 @@ enable_add_url() {
 	config_load "$packageName"
 	config_foreach _enable_url 'file_url' "$url" "$action"
 	if [ -z "$_found" ]; then
-		uci add "${packageName}" 'file_url' >/dev/null 2>&1
-		uci set "${packageName}.@file_url[-1].url=$url"
-		uci set "${packageName}.@file_url[-1].size=$(get_url_filesize "$url")"
-		uci set "${packageName}.@file_url[-1].action=$action"
+		uci_add "$packageName" 'file_url'
+		uci_set "$packageName" '@file_url[-1]' 'url' "$url"
+		uci_set "$packageName" '@file_url[-1]' 'size' "$(get_url_filesize "$url")"
+		uci_set "$packageName" '@file_url[-1]' 'action' "$action"
 	fi
 }
 
 if [ -s '/etc/config/simple-adblock' ] \
 	&& [ ! -s '/etc/config/adblock-fast-opkg' ] \
-	&& [ "$(uci get adblock-fast.config.enabled)" = '0' ]; then
+	&& [ "$(uci_get adblock-fast config enabled)" = '0' ]; then
 	cp -f '/etc/config/adblock-fast' '/etc/config/adblock-fast-opkg'
-	enabled="$(uci get simple-adblock.config.enabled)"
+	enabled="$(uci_get simple-adblock config enabled)"
 	if [ -x '/etc/init.d/simple-adblock' ]; then
 		output "Stopping and disabling simple-adblock "
 		if /etc/init.d/simple-adblock stop  >/dev/null 2>&1 \
 			&& /etc/init.d/simple-adblock disable \
-			&& uci set simple-adblock.config.enabled=0 \
-			&& uci commit simple-adblock; then
+			&& uci_set simple-adblock config enabled 0 \
+			&& uci_commit simple-adblock; then
 			output_okn
 		else
 			output_failn
 		fi
 	else
 		output "Disabling simple-adblock."
-		if uci set simple-adblock.config.enabled=0 \
-			&& uci commit simple-adblock; then
+		if uci_set simple-adblock config enabled 0 \
+			&& uci_commit simple-adblock; then
 			output_okn
 		else
 			output_failn
@@ -63,31 +63,30 @@ if [ -s '/etc/config/simple-adblock' ] \
 		curl_additional_param curl_max_file_size curl_retry download_timeout \
 		debug dns dns_instance dnsmasq_config_file_url force_dns led \
 		parallel_downloads procd_trigger_wan6 procd_boot_wan_timeout verbosity; do
-		j="$(uci -q get simple-adblock.config.${i})"
-		[ -n "$j" ] && uci set "${packageName}.config.${i}=${j}"
+		j="$(uci_get simple-adblock.config.${i})"
+		[ -n "$j" ] && uci_set "$packageName" config "$i" "$j"
 	done
-	[ -n "$enabled" ] && uci set "${packageName}.config.enabled=${enabled}"
-	j="$(uci -q get simple-adblock.config.config_update_url)"
+	[ -n "$enabled" ] && uci_set "$packageName" config enabled "$enabled"
+	j="$(uci_get simple-adblock config config_update_url)"
 	if [ "${j//simple-adblock/}" = "$j" ]; then
-		uci set "${packageName}.config.config_update_url=$j"
+		uci_set "$packageName" config config_update_url "$j"
 	fi
-	ccd="$(uci get simple-adblock.config.compressed_cache_dir)"
-	ccd="${ccd:-/etc}"
-	for j in $(uci -q get simple-adblock.config.allowed_domain); do
-		[ -n "$j" ] && uci add_list "${packageName}.config.allowed_domain=${j}"
+	ccd="$(uci_get simple-adblock config compressed_cache_dir '/etc')"
+	for j in $(uci_get simple-adblock config allowed_domain); do
+		[ -n "$j" ] && uci_add_list "$packageName" config allowed_domain "$j"
 	done
-	for j in $(uci -q get simple-adblock.config.blocked_domain); do
-		[ -n "$j" ] && uci add_list "${packageName}.config.blocked_domain=${j}"
+	for j in $(uci_get simple-adblock config blocked_domain); do
+		[ -n "$j" ] && uci_add_list "$packageName" config blocked_domain "$j"
 	done
-	for j in $(uci -q get simple-adblock.config.force_dns_port); do
-		[ -n "$j" ] && uci add_list "${packageName}.config.force_dns_port=${j}"
+	for j in $(uci_get simple-adblock config force_dns_port); do
+		[ -n "$j" ] && uci_add_list "$packageName" config force_dns_port "$j"
 	done
 	output_okn
 
 	for i in allowed_domains_url blocked_adblockplus_url blocked_domains_url \
 		blocked_hosts_url; do
 		output "Migrating simple-adblock ${i} "
-		for j in $(uci -q get simple-adblock.config.${i}); do
+		for j in $(uci_get simple-adblock config "$i"); do
 			if [ "$i" = 'allowed_domains_url' ]; then
 				enable_add_url "$j" 'allow'
 			else
@@ -96,7 +95,7 @@ if [ -s '/etc/config/simple-adblock' ] \
 		done
 		output_okn
 	done
-	uci commit "$packageName"
+	uci_commit "$packageName"
 	output "Migrating simple-adblock cache file(s) "
 	for i in '/var/run/simple-adblock/dnsmasq.addnhosts.cache' \
 		'/var/run/simple-adblock/dnsmasq.conf.cache' \

From 1c0336d4ccd6de22e94462c6ba499c6aef995f65 Mon Sep 17 00:00:00 2001
From: Moritz Warning <moritzwarning@web.de>
Date: Mon, 6 May 2024 22:50:36 +0200
Subject: [PATCH 04/11] zerotier: update to 1.14.0

Includes refreshed patches.

Signed-off-by: Moritz Warning <moritzwarning@web.de>
(cherry picked from commit cf6fef36b44de2c9d1b7910f6afd43e0c6ee946c)
---
 net/zerotier/Makefile                                |  6 +++---
 .../0001-fix-miniupnpc-natpmp-include-paths.patch    |  8 ++++----
 net/zerotier/patches/0002-remove-PIE-options.patch   | 12 ++++++------
 ...0003-fix-compilation-for-arm_cortex-a7-neon.patch |  2 +-
 .../patches/0004-add-missing-libatomic.patch         |  8 ++++----
 net/zerotier/patches/0005-remove-noexecstack.patch   |  4 ++--
 6 files changed, 20 insertions(+), 20 deletions(-)

diff --git a/net/zerotier/Makefile b/net/zerotier/Makefile
index e9c1426a14..6d38423f91 100644
--- a/net/zerotier/Makefile
+++ b/net/zerotier/Makefile
@@ -6,12 +6,12 @@
 include $(TOPDIR)/rules.mk
 
 PKG_NAME:=zerotier
-PKG_VERSION:=1.12.2
-PKG_RELEASE:=2
+PKG_VERSION:=1.14.0
+PKG_RELEASE:=1
 
 PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
 PKG_SOURCE_URL:=https://codeload.github.com/zerotier/ZeroTierOne/tar.gz/$(PKG_VERSION)?
-PKG_HASH:=7c6512cfc208374ea9dc9931110e35f71800c34890e0f35991ea485aae66e31c
+PKG_HASH:=7191623a81b0d1b552b9431e8864dd3420783ee518394ac1376cee6aaf033291
 PKG_BUILD_DIR:=$(BUILD_DIR)/ZeroTierOne-$(PKG_VERSION)
 
 PKG_MAINTAINER:=Moritz Warning <moritzwarning@web.de>
diff --git a/net/zerotier/patches/0001-fix-miniupnpc-natpmp-include-paths.patch b/net/zerotier/patches/0001-fix-miniupnpc-natpmp-include-paths.patch
index 8779829f44..a4a129ae3c 100644
--- a/net/zerotier/patches/0001-fix-miniupnpc-natpmp-include-paths.patch
+++ b/net/zerotier/patches/0001-fix-miniupnpc-natpmp-include-paths.patch
@@ -1,6 +1,6 @@
-From 8b30e8617720c2a540c84c5c00508e8081e9f06e Mon Sep 17 00:00:00 2001
+From f53004bd22365900a1dbb120dae62ce8b614d31d Mon Sep 17 00:00:00 2001
 From: Moritz Warning <moritzwarning@web.de>
-Date: Sun, 27 Aug 2023 23:26:33 +0200
+Date: Mon, 6 May 2024 22:31:57 +0200
 Subject: [PATCH 1/5] fix miniupnpc/natpmp include paths
 
 Signed-off-by: Moritz Warning <moritzwarning@web.de>
@@ -10,7 +10,7 @@ Signed-off-by: Moritz Warning <moritzwarning@web.de>
 
 --- a/make-linux.mk
 +++ b/make-linux.mk
-@@ -25,8 +25,8 @@ TIMESTAMP=$(shell date +"%Y%m%d%H%M")
+@@ -26,8 +26,8 @@ TIMESTAMP=$(shell date +"%Y%m%d%H%M")
  # otherwise build into binary as done on Mac and Windows.
  ONE_OBJS+=osdep/PortMapper.o
  override DEFS+=-DZT_USE_MINIUPNPC
@@ -21,7 +21,7 @@ Signed-off-by: Moritz Warning <moritzwarning@web.de>
  ifeq ($(MINIUPNPC_IS_NEW_ENOUGH),1)
  	override DEFS+=-DZT_USE_SYSTEM_MINIUPNPC
  	LDLIBS+=-lminiupnpc
-@@ -34,7 +34,7 @@ else
+@@ -35,7 +35,7 @@ else
  	override DEFS+=-DMINIUPNP_STATICLIB -DMINIUPNPC_SET_SOCKET_TIMEOUT -DMINIUPNPC_GET_SRC_ADDR -D_BSD_SOURCE -D_DEFAULT_SOURCE -D_XOPEN_SOURCE=600 -DOS_STRING="\"Linux\"" -DMINIUPNPC_VERSION_STRING="\"2.0\"" -DUPNP_VERSION_STRING="\"UPnP/1.1\"" -DENABLE_STRNATPMPERR
  	ONE_OBJS+=ext/miniupnpc/connecthostport.o ext/miniupnpc/igd_desc_parse.o ext/miniupnpc/minisoap.o ext/miniupnpc/minissdpc.o ext/miniupnpc/miniupnpc.o ext/miniupnpc/miniwget.o ext/miniupnpc/minixml.o ext/miniupnpc/portlistingparse.o ext/miniupnpc/receivedata.o ext/miniupnpc/upnpcommands.o ext/miniupnpc/upnpdev.o ext/miniupnpc/upnperrors.o ext/miniupnpc/upnpreplyparse.o
  endif
diff --git a/net/zerotier/patches/0002-remove-PIE-options.patch b/net/zerotier/patches/0002-remove-PIE-options.patch
index 0b21991eb9..4e44f2ef45 100644
--- a/net/zerotier/patches/0002-remove-PIE-options.patch
+++ b/net/zerotier/patches/0002-remove-PIE-options.patch
@@ -1,6 +1,6 @@
-From 9b97099560a79529f2b5a748e73164ae038d2b76 Mon Sep 17 00:00:00 2001
+From c10b5ed4c6c44e36178b0a5a82da9e8eaa957008 Mon Sep 17 00:00:00 2001
 From: Moritz Warning <moritzwarning@web.de>
-Date: Sun, 27 Aug 2023 23:27:33 +0200
+Date: Mon, 6 May 2024 22:34:15 +0200
 Subject: [PATCH 2/5] remove PIE options
 
 Signed-off-by: Moritz Warning <moritzwarning@web.de>
@@ -10,16 +10,16 @@ Signed-off-by: Moritz Warning <moritzwarning@web.de>
 
 --- a/make-linux.mk
 +++ b/make-linux.mk
-@@ -70,7 +70,7 @@ else
+@@ -71,7 +71,7 @@ else
  	override CFLAGS+=-Wall -Wno-deprecated -pthread $(INCLUDES) -DNDEBUG $(DEFS)
  	CXXFLAGS?=-O3 -fstack-protector
  	override CXXFLAGS+=-Wall -Wno-deprecated -std=c++17 -pthread $(INCLUDES) -DNDEBUG $(DEFS)
 -	LDFLAGS=-pie -Wl,-z,relro,-z,now
 +	LDFLAGS=-Wl,-z,relro,-z,now
- 	RUSTFLAGS=--release
+ 	ZT_CARGO_FLAGS=--release
  endif
  
-@@ -327,7 +327,7 @@ ifeq ($(ZT_CONTROLLER),1)
+@@ -333,7 +333,7 @@ ifeq ($(ZT_CONTROLLER),1)
  endif
  
  # ARM32 hell -- use conservative CFLAGS
@@ -28,7 +28,7 @@ Signed-off-by: Moritz Warning <moritzwarning@web.de>
  	ifeq ($(shell if [ -e /usr/bin/dpkg ]; then dpkg --print-architecture; fi),armel)
  		override CFLAGS+=-march=armv5t -mfloat-abi=soft -msoft-float -mno-unaligned-access -marm
  		override CXXFLAGS+=-march=armv5t -mfloat-abi=soft -msoft-float -mno-unaligned-access -marm
-@@ -354,8 +354,8 @@ ifeq ($(ZT_USE_ARM32_NEON_ASM_CRYPTO),1)
+@@ -360,8 +360,8 @@ ifeq ($(ZT_USE_ARM32_NEON_ASM_CRYPTO),1)
  endif
  
  # Position Independence
diff --git a/net/zerotier/patches/0003-fix-compilation-for-arm_cortex-a7-neon.patch b/net/zerotier/patches/0003-fix-compilation-for-arm_cortex-a7-neon.patch
index 8831edef7b..34b336196b 100644
--- a/net/zerotier/patches/0003-fix-compilation-for-arm_cortex-a7-neon.patch
+++ b/net/zerotier/patches/0003-fix-compilation-for-arm_cortex-a7-neon.patch
@@ -1,4 +1,4 @@
-From 1689085363cf87deb3877139931a8a88d8c7bcd3 Mon Sep 17 00:00:00 2001
+From fee674d5a5c7cc847d7e1925ddf41eea89d915c4 Mon Sep 17 00:00:00 2001
 From: Moritz Warning <moritzwarning@web.de>
 Date: Mon, 4 Jul 2022 00:10:52 +0200
 Subject: [PATCH 3/5] fix compilation for arm_cortex-a7+neon
diff --git a/net/zerotier/patches/0004-add-missing-libatomic.patch b/net/zerotier/patches/0004-add-missing-libatomic.patch
index d8d9c61732..5b7cb80c28 100644
--- a/net/zerotier/patches/0004-add-missing-libatomic.patch
+++ b/net/zerotier/patches/0004-add-missing-libatomic.patch
@@ -1,6 +1,6 @@
-From b3879d721734862aa64433f7faf124a0862da029 Mon Sep 17 00:00:00 2001
+From f8b4c4a045a9711c316a5c48b238c24cc0948da1 Mon Sep 17 00:00:00 2001
 From: Moritz Warning <moritzwarning@web.de>
-Date: Sun, 27 Aug 2023 22:27:12 +0200
+Date: Mon, 6 May 2024 22:35:41 +0200
 Subject: [PATCH 4/5] add missing libatomic
 
 Signed-off-by: Moritz Warning <moritzwarning@web.de>
@@ -12,10 +12,10 @@ Signed-off-by: Moritz Warning <moritzwarning@web.de>
 +++ b/make-linux.mk
 @@ -11,7 +11,7 @@ endif
  
- INCLUDES?=-Izeroidc/target -isystem ext -Iext/prometheus-cpp-lite-1.0/core/include -Iext-prometheus-cpp-lite-1.0/3rdparty/http-client-lite/include -Iext/prometheus-cpp-lite-1.0/simpleapi/include
+ INCLUDES?=-Irustybits/target -isystem ext -Iext/prometheus-cpp-lite-1.0/core/include -Iext-prometheus-cpp-lite-1.0/3rdparty/http-client-lite/include -Iext/prometheus-cpp-lite-1.0/simpleapi/include
  DEFS?=
 -LDLIBS?=
 +LDLIBS?=-latomic
  DESTDIR?=
+ EXTRA_DEPS?=
  
- include objects.mk
diff --git a/net/zerotier/patches/0005-remove-noexecstack.patch b/net/zerotier/patches/0005-remove-noexecstack.patch
index 07b1ac3b4c..8569fa8e8b 100644
--- a/net/zerotier/patches/0005-remove-noexecstack.patch
+++ b/net/zerotier/patches/0005-remove-noexecstack.patch
@@ -1,4 +1,4 @@
-From adcc68c6c3de8460bd6263d3478873953a4cf894 Mon Sep 17 00:00:00 2001
+From 2a5a279ac0192bc444cd1c3059169f576817d8b9 Mon Sep 17 00:00:00 2001
 From: Moritz Warning <moritzwarning@web.de>
 Date: Mon, 28 Aug 2023 09:48:28 +0200
 Subject: [PATCH 5/5] remove noexecstack
@@ -10,7 +10,7 @@ The compilers for arm_cortex-a9 do not recognize this argument.
 
 --- a/make-linux.mk
 +++ b/make-linux.mk
-@@ -358,7 +358,7 @@ endif
+@@ -364,7 +364,7 @@ endif
  #override CXXFLAGS+=-fPIC -fPIE
  
  # Non-executable stack

From 3ab18e4225c64ad518fc832fa0064ab54ece25e1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=93scar=20Garc=C3=ADa=20Amor?= <contact@ogarcia.me>
Date: Mon, 12 Aug 2024 21:20:56 +0200
Subject: [PATCH 05/11] zerotier: split configuration
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Split configuration in global and per-network sections.
This change breaks existing configurations.

The following per-network settings are available:

* allow_managed
* allow_global
* allow_default
* allow_dns

See  https://docs.zerotier.com/config/#network-specific-configuration

Signed-off-by: Óscar García Amor <contact@ogarcia.me>
Reviewed-by: Moritz Warning <moritzwarning@web.de>
(cherry picked from commit 5af81638787a1dd1f057bc238bc243225110607f)
---
 net/zerotier/Makefile                         |   3 +-
 net/zerotier/files/etc/config/zerotier        |  45 ++++--
 net/zerotier/files/etc/init.d/zerotier        | 129 ++++++++----------
 .../files/etc/uci-defaults/80-zt-migration    |  19 +++
 4 files changed, 108 insertions(+), 88 deletions(-)
 create mode 100644 net/zerotier/files/etc/uci-defaults/80-zt-migration

diff --git a/net/zerotier/Makefile b/net/zerotier/Makefile
index 6d38423f91..5ecb76f217 100644
--- a/net/zerotier/Makefile
+++ b/net/zerotier/Makefile
@@ -7,7 +7,7 @@ include $(TOPDIR)/rules.mk
 
 PKG_NAME:=zerotier
 PKG_VERSION:=1.14.0
-PKG_RELEASE:=1
+PKG_RELEASE:=2
 
 PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
 PKG_SOURCE_URL:=https://codeload.github.com/zerotier/ZeroTierOne/tar.gz/$(PKG_VERSION)?
@@ -71,6 +71,7 @@ define Package/zerotier/install
 	$(INSTALL_BIN) $(PKG_BUILD_DIR)/zerotier-one $(1)/usr/bin/
 	$(LN) zerotier-one $(1)/usr/bin/zerotier-cli
 	$(LN) zerotier-one $(1)/usr/bin/zerotier-idtool
+	$(INSTALL_DIR) $(1)/etc/uci-defaults
 
 ifeq ($(CONFIG_ZEROTIER_ENABLE_SELFTEST),y)
 	$(INSTALL_BIN) $(PKG_BUILD_DIR)/zerotier-selftest $(1)/usr/bin/
diff --git a/net/zerotier/files/etc/config/zerotier b/net/zerotier/files/etc/config/zerotier
index 34b1ad347e..00238c48a1 100644
--- a/net/zerotier/files/etc/config/zerotier
+++ b/net/zerotier/files/etc/config/zerotier
@@ -1,20 +1,39 @@
 
-config zerotier sample_config
+config zerotier 'global'
+	# Sets whether ZeroTier is enabled or not
 	option enabled 0
-
-	# persistent configuration folder (for ZT controller mode)
+	# Sets the ZeroTier listening port (default 9993; set to 0 for random)
+	#option port '9993'
+	# Client secret (leave blank to generate a secret on first run)
+	option secret ''
+	# Path of the optional file local.conf (see documentation at
+	# https://docs.zerotier.com/config#local-configuration-options)
+	#option local_conf_path '/etc/zerotier.conf'
+	# Persistent configuration directory (to perform other configurations such
+	# as controller mode or moons, etc.)
 	#option config_path '/etc/zerotier'
-	# copy <config_path> to RAM to prevent writing to flash (for ZT controller mode)
+	# Copy the contents of the persistent configuration directory to memory
+	# instead of linking it, this avoids writing to flash
 	#option copy_config_path '1'
 
-	#option port '9993'
-
-	# path to the local.conf
-	#option local_conf '/etc/zerotier.conf'
+# Network configuration, you can have as many configurations as networks you
+# want to join (the network name is optional)
+config network 'mynet'
+	# Identifier of the network you wish to join
+	option id '8056c2e21c000001'
+	# Network configuration parameters (all are optional, if not indicated the
+	# default values are set, see documentation at
+	# https://docs.zerotier.com/config/#network-specific-configuration)
+	option allow_managed '1'
+	option allow_global '0'
+	option allow_default '0'
+	option allow_dns '0'
 
-	# Generate secret on first start
-	option secret ''
+# Example of a second network (unnamed as it is optional)
+#config network
+#	option id '1234567890123456'
+#	option allow_managed '1'
+#	option allow_global '0'
+#	option allow_default '0'
+#	option allow_dns '0'
 
-	# Join a public network called Earth
-	list join '8056c2e21c000001'
-	#list join '<other_network>'
diff --git a/net/zerotier/files/etc/init.d/zerotier b/net/zerotier/files/etc/init.d/zerotier
index 84002f9526..910709859d 100755
--- a/net/zerotier/files/etc/init.d/zerotier
+++ b/net/zerotier/files/etc/init.d/zerotier
@@ -7,113 +7,94 @@ USE_PROCD=1
 PROG=/usr/bin/zerotier-one
 CONFIG_PATH=/var/lib/zerotier-one
 
-section_enabled() {
-	config_get_bool enabled "$1" 'enabled' 0
-	[ $enabled -ne 0 ]
+join_network() {
+	local section="${1}"
+	local id allow_managed allow_global allow_default allow_dns
+
+	config_get id "${section}" 'id'
+	config_get_bool allow_managed "${section}" 'allow_managed' 1
+	config_get_bool allow_global "${section}" 'allow_global' 0
+	config_get_bool allow_default "${section}" 'allow_default' 0
+	config_get_bool allow_dns "${section}" 'allow_dns' 0
+
+	if [ -n "${id}" ]; then
+		# an (empty) config file will cause ZT to join a network
+		touch "${CONFIG_PATH}"/networks.d/"${id}".conf
+		echo "allowManaged=${allow_managed}" > "${CONFIG_PATH}"/networks.d/"${id}".local.conf
+		echo "allowGlobal=${allow_global}" >> "${CONFIG_PATH}"/networks.d/"${id}".local.conf
+		echo "allowDefault=${allow_default}" >> "${CONFIG_PATH}"/networks.d/"${id}".local.conf
+		echo "allowDNS=${allow_dns}" >> "${CONFIG_PATH}"/networks.d/"${id}".local.conf
+	fi
 }
 
-start_instance() {
-	local cfg="$1"
-	local port secret config_path local_conf copy_config_path path
+start_service() {
+	config_load zerotier
+	local enabled port secret local_conf_path config_path copy_config_path
 	local args=""
 
-	if ! section_enabled "$cfg"; then
+	config_get_bool enabled 'global' 'enabled' 0
+	config_get port 'global' 'port'
+	config_get secret 'global' 'secret'
+	config_get local_conf_path 'global' 'local_conf_path'
+	config_get config_path 'global' 'config_path'
+	config_get_bool copy_config_path 'global' 'copy_config_path' 0
+
+	if [ ${enabled} -eq 0 ]; then
 		echo "disabled in /etc/config/zerotier"
-		return 1
 	fi
 
-	config_get config_path $cfg 'config_path'
-	config_get port $cfg 'port'
-	config_get secret $cfg 'secret'
-	config_get local_conf $cfg 'local_conf'
-	config_get_bool copy_config_path $cfg 'copy_config_path' 0
-
-	path=${CONFIG_PATH}_$cfg
-
 	# Remove existing link or folder
-	rm -rf $path
+	rm -rf "${CONFIG_PATH}"
 
-	# Create link or copy files from CONFIG_PATH to config_path
-	if [ -n "$config_path" -a "$config_path" != "$path" ]; then
-		if [ ! -d "$config_path" ]; then
-			echo "ZeroTier config_path does not exist: $config_path" 1>&2
+	# Create link or copy files from config_path to CONFIG_PATH
+	if [ -n "${config_path}" ]; then
+		if [ ! -d "${config_path}" ]; then
+			echo "ZeroTier config_path does not exist: ${config_path}" 1>&2
 			return
 		fi
 
-		# ensure that the target exists
-		mkdir -p $(dirname $path)
-
-		if [ "$copy_config_path" = "1" ]; then
-			cp -r $config_path $path
+		if [ ${copy_config_path} -eq 1 ]; then
+			cp -r "${config_path}" "${CONFIG_PATH}"
 		else
-			ln -s $config_path $path
+			ln -s "${config_path}" "${CONFIG_PATH}"
 		fi
 	fi
 
-	mkdir -p $path/networks.d
-
-	# link latest default config path to latest config path
-	rm -f $CONFIG_PATH
-	ln -s $path $CONFIG_PATH
+	mkdir -p "${CONFIG_PATH}"/networks.d
+	config_foreach join_network network
 
-	if [ -n "$port" ]; then
-		args="$args -p${port}"
+	if [ -f "${local_conf_path}" ]; then
+		ln -s "${local_conf_path}" "${CONFIG_PATH}"/local.conf
 	fi
 
-	if [ -z "$secret" ]; then
-		echo "Generate secret - please wait..."
-		local sf="/tmp/zt.$cfg.secret"
-
-		zerotier-idtool generate "$sf" > /dev/null
-		[ $? -ne 0 ] && return 1
-
-		secret="$(cat $sf)"
-		rm "$sf"
+	if [ -n "${port}" ]; then
+		args="${args} -p${port}"
+	fi
 
-		uci set zerotier.$cfg.secret="$secret"
+	if [ -z "${secret}" ]; then
+		echo -n "Generating secret - please wait... "
+		secret="$(zerotier-idtool generate)"
+		[ ${?} -ne 0 ] && return 1
+		uci set zerotier.global.secret="${secret}"
 		uci commit zerotier
+		echo "done."
 	fi
 
-	if [ -n "$secret" ]; then
-		echo "$secret" > $path/identity.secret
+	if [ -n "${secret}" ]; then
+		echo "${secret}" > "${CONFIG_PATH}"/identity.secret
 		# make sure there is not previous identity.public
-		rm -f $path/identity.public
+		rm -f "${CONFIG_PATH}"/identity.public
 	fi
 
-	if [ -f "$local_conf" ]; then
-		ln -s "$local_conf" $path/local.conf
-	fi
-
-	add_join() {
-		# an (empty) config file will cause ZT to join a network
-		touch $path/networks.d/$1.conf
-	}
-
-	config_list_foreach $cfg 'join' add_join
-
 	procd_open_instance
-	procd_set_param command $PROG $args $path
+	procd_set_param command ${PROG} ${args}
 	procd_set_param stderr 1
 	procd_set_param respawn
 	procd_close_instance
 }
 
-start_service() {
-	config_load 'zerotier'
-	config_foreach start_instance 'zerotier'
-}
-
-stop_instance() {
-	local cfg="$1"
-
-	# Remove existing link or folder
-	rm -rf ${CONFIG_PATH}_${cfg}
-}
-
 stop_service() {
-	config_load 'zerotier'
-	config_foreach stop_instance 'zerotier'
-	rm -f ${CONFIG_PATH}
+	rm -rf "${CONFIG_PATH}"
 }
 
 reload_service() {
diff --git a/net/zerotier/files/etc/uci-defaults/80-zt-migration b/net/zerotier/files/etc/uci-defaults/80-zt-migration
new file mode 100644
index 0000000000..1cfedb5838
--- /dev/null
+++ b/net/zerotier/files/etc/uci-defaults/80-zt-migration
@@ -0,0 +1,19 @@
+# Convert the join list into networks
+nets=$(uci -q get zerotier.@zerotier[0].join)
+
+if [ -n "$nets" ]; then
+  for net in ${nets}; do
+    sid=$(uci add zerotier network)
+    uci set zerotier.${sid}.id=${net}
+  done
+  uci delete zerotier.@zerotier[0].join
+
+  # Rename local conf (only if defined)
+  uci -q rename zerotier.@zerotier[0].local_conf='local_conf_path' || true
+
+  # Rename configuration to global
+  uci rename zerotier.@zerotier[0]='global'
+
+  # Commit all changes
+  uci commit zerotier
+fi

From 04b4e6af66e0d4c941e000a045b1526affd56eff Mon Sep 17 00:00:00 2001
From: Moritz Warning <moritzwarning@web.de>
Date: Wed, 18 Sep 2024 11:59:52 +0200
Subject: [PATCH 06/11] zerotier: update to 1.14.1

Signed-off-by: Moritz Warning <moritzwarning@web.de>
(cherry picked from commit 660b10f0dc9ca81ec2f7b9f7d30ba1a745925d77)
---
 net/zerotier/Makefile                         |  6 +++---
 ...1-fix-miniupnpc-natpmp-include-path.patch} |  7 +++----
 .../patches/0002-remove-PIE-options.patch     | 20 +++++--------------
 ...x-compilation-for-arm_cortex-a7-neon.patch |  9 +++------
 .../patches/0004-add-missing-libatomic.patch  |  5 ++---
 .../patches/0005-remove-noexecstack.patch     |  9 ++++-----
 6 files changed, 20 insertions(+), 36 deletions(-)
 rename net/zerotier/patches/{0001-fix-miniupnpc-natpmp-include-paths.patch => 0001-fix-miniupnpc-natpmp-include-path.patch} (89%)

diff --git a/net/zerotier/Makefile b/net/zerotier/Makefile
index 5ecb76f217..bce1255421 100644
--- a/net/zerotier/Makefile
+++ b/net/zerotier/Makefile
@@ -6,12 +6,12 @@
 include $(TOPDIR)/rules.mk
 
 PKG_NAME:=zerotier
-PKG_VERSION:=1.14.0
-PKG_RELEASE:=2
+PKG_VERSION:=1.14.1
+PKG_RELEASE:=1
 
 PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
 PKG_SOURCE_URL:=https://codeload.github.com/zerotier/ZeroTierOne/tar.gz/$(PKG_VERSION)?
-PKG_HASH:=7191623a81b0d1b552b9431e8864dd3420783ee518394ac1376cee6aaf033291
+PKG_HASH:=4f9f40b27c5a78389ed3f3216c850921f6298749e5819e9f2edabb2672ce9ca0
 PKG_BUILD_DIR:=$(BUILD_DIR)/ZeroTierOne-$(PKG_VERSION)
 
 PKG_MAINTAINER:=Moritz Warning <moritzwarning@web.de>
diff --git a/net/zerotier/patches/0001-fix-miniupnpc-natpmp-include-paths.patch b/net/zerotier/patches/0001-fix-miniupnpc-natpmp-include-path.patch
similarity index 89%
rename from net/zerotier/patches/0001-fix-miniupnpc-natpmp-include-paths.patch
rename to net/zerotier/patches/0001-fix-miniupnpc-natpmp-include-path.patch
index a4a129ae3c..eeab33905f 100644
--- a/net/zerotier/patches/0001-fix-miniupnpc-natpmp-include-paths.patch
+++ b/net/zerotier/patches/0001-fix-miniupnpc-natpmp-include-path.patch
@@ -1,9 +1,8 @@
-From f53004bd22365900a1dbb120dae62ce8b614d31d Mon Sep 17 00:00:00 2001
+From ec02787ae7c5b6e906ab50bcebcd676d4219c812 Mon Sep 17 00:00:00 2001
 From: Moritz Warning <moritzwarning@web.de>
-Date: Mon, 6 May 2024 22:31:57 +0200
-Subject: [PATCH 1/5] fix miniupnpc/natpmp include paths
+Date: Tue, 17 Sep 2024 14:17:08 +0200
+Subject: [PATCH 1/5] fix miniupnpc natpmp include path
 
-Signed-off-by: Moritz Warning <moritzwarning@web.de>
 ---
  make-linux.mk | 6 +++---
  1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/net/zerotier/patches/0002-remove-PIE-options.patch b/net/zerotier/patches/0002-remove-PIE-options.patch
index 4e44f2ef45..26e7e87481 100644
--- a/net/zerotier/patches/0002-remove-PIE-options.patch
+++ b/net/zerotier/patches/0002-remove-PIE-options.patch
@@ -1,24 +1,14 @@
-From c10b5ed4c6c44e36178b0a5a82da9e8eaa957008 Mon Sep 17 00:00:00 2001
+From 81a632c99b581790344729ad327eb473c4c05260 Mon Sep 17 00:00:00 2001
 From: Moritz Warning <moritzwarning@web.de>
-Date: Mon, 6 May 2024 22:34:15 +0200
+Date: Tue, 17 Sep 2024 15:36:36 +0200
 Subject: [PATCH 2/5] remove PIE options
 
-Signed-off-by: Moritz Warning <moritzwarning@web.de>
 ---
- make-linux.mk | 8 ++++----
- 1 file changed, 4 insertions(+), 4 deletions(-)
+ make-linux.mk | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
 
 --- a/make-linux.mk
 +++ b/make-linux.mk
-@@ -71,7 +71,7 @@ else
- 	override CFLAGS+=-Wall -Wno-deprecated -pthread $(INCLUDES) -DNDEBUG $(DEFS)
- 	CXXFLAGS?=-O3 -fstack-protector
- 	override CXXFLAGS+=-Wall -Wno-deprecated -std=c++17 -pthread $(INCLUDES) -DNDEBUG $(DEFS)
--	LDFLAGS=-pie -Wl,-z,relro,-z,now
-+	LDFLAGS=-Wl,-z,relro,-z,now
- 	ZT_CARGO_FLAGS=--release
- endif
- 
 @@ -333,7 +333,7 @@ ifeq ($(ZT_CONTROLLER),1)
  endif
  
@@ -38,4 +28,4 @@ Signed-off-by: Moritz Warning <moritzwarning@web.de>
 +#override CXXFLAGS+=-fPIC -fPIE
  
  # Non-executable stack
- override ASFLAGS+=--noexecstack
+ override LDFLAGS+=-Wl,-z,noexecstack
diff --git a/net/zerotier/patches/0003-fix-compilation-for-arm_cortex-a7-neon.patch b/net/zerotier/patches/0003-fix-compilation-for-arm_cortex-a7-neon.patch
index 34b336196b..14e6b46ea1 100644
--- a/net/zerotier/patches/0003-fix-compilation-for-arm_cortex-a7-neon.patch
+++ b/net/zerotier/patches/0003-fix-compilation-for-arm_cortex-a7-neon.patch
@@ -1,11 +1,8 @@
-From fee674d5a5c7cc847d7e1925ddf41eea89d915c4 Mon Sep 17 00:00:00 2001
+From 71ed5b791fb0f7bfe1f564726fdc979b71313fbe Mon Sep 17 00:00:00 2001
 From: Moritz Warning <moritzwarning@web.de>
-Date: Mon, 4 Jul 2022 00:10:52 +0200
-Subject: [PATCH 3/5] fix compilation for arm_cortex-a7+neon
+Date: Tue, 17 Sep 2024 15:38:01 +0200
+Subject: [PATCH 3/5] fix compilation for arm_cortex a7 neon
 
-Fixes "error: 'vrbitq_u8' was not declared in this scope"
-
-Signed-off-by: Rosen Penev <rosenp@gmail.com>
 ---
  node/Constants.hpp | 2 +-
  1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/net/zerotier/patches/0004-add-missing-libatomic.patch b/net/zerotier/patches/0004-add-missing-libatomic.patch
index 5b7cb80c28..68c033e96a 100644
--- a/net/zerotier/patches/0004-add-missing-libatomic.patch
+++ b/net/zerotier/patches/0004-add-missing-libatomic.patch
@@ -1,9 +1,8 @@
-From f8b4c4a045a9711c316a5c48b238c24cc0948da1 Mon Sep 17 00:00:00 2001
+From d6197554b3f52ee9d8d81374141aa82014b4fc7b Mon Sep 17 00:00:00 2001
 From: Moritz Warning <moritzwarning@web.de>
-Date: Mon, 6 May 2024 22:35:41 +0200
+Date: Tue, 17 Sep 2024 15:38:34 +0200
 Subject: [PATCH 4/5] add missing libatomic
 
-Signed-off-by: Moritz Warning <moritzwarning@web.de>
 ---
  make-linux.mk | 2 +-
  1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/net/zerotier/patches/0005-remove-noexecstack.patch b/net/zerotier/patches/0005-remove-noexecstack.patch
index 8569fa8e8b..c4fb0f3fb5 100644
--- a/net/zerotier/patches/0005-remove-noexecstack.patch
+++ b/net/zerotier/patches/0005-remove-noexecstack.patch
@@ -1,9 +1,8 @@
-From 2a5a279ac0192bc444cd1c3059169f576817d8b9 Mon Sep 17 00:00:00 2001
+From 8e89af98ac00b1c9c019865faca7479fa0de6084 Mon Sep 17 00:00:00 2001
 From: Moritz Warning <moritzwarning@web.de>
-Date: Mon, 28 Aug 2023 09:48:28 +0200
+Date: Tue, 17 Sep 2024 21:26:08 +0200
 Subject: [PATCH 5/5] remove noexecstack
 
-The compilers for arm_cortex-a9 do not recognize this argument.
 ---
  make-linux.mk | 2 +-
  1 file changed, 1 insertion(+), 1 deletion(-)
@@ -14,8 +13,8 @@ The compilers for arm_cortex-a9 do not recognize this argument.
  #override CXXFLAGS+=-fPIC -fPIE
  
  # Non-executable stack
--override ASFLAGS+=--noexecstack
-+# override ASFLAGS+=--noexecstack
+-override LDFLAGS+=-Wl,-z,noexecstack
++#override LDFLAGS+=-Wl,-z,noexecstack
  
  .PHONY: all
  all:	one

From 940d45f414033b3b7feb302c727c11c3ef9d7701 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?G=C3=A1bor=20De=C3=A9?= <dee.gabor@gmail.com>
Date: Mon, 28 Oct 2024 14:49:40 +0100
Subject: [PATCH 07/11] libdeflate: update to 1.22
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Release note:
https://github.com/ebiggers/libdeflate/blob/master/NEWS.md#version-122

Signed-off-by: Gábor Deé <dee.gabor@gmail.com>
---
 libs/libdeflate/Makefile | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libs/libdeflate/Makefile b/libs/libdeflate/Makefile
index d0a9ed8058..3c1eef34bf 100644
--- a/libs/libdeflate/Makefile
+++ b/libs/libdeflate/Makefile
@@ -1,12 +1,12 @@
 include $(TOPDIR)/rules.mk
 
 PKG_NAME:=libdeflate
-PKG_VERSION:=1.18
+PKG_VERSION:=1.22
 PKG_RELEASE:=1
 
 PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
 PKG_SOURCE_URL:=https://codeload.github.com/ebiggers/libdeflate/tar.gz/v$(PKG_VERSION)?
-PKG_HASH:=225d982bcaf553221c76726358d2ea139bb34913180b20823c782cede060affd
+PKG_HASH:=7834d9adbc9a809e0fb0d7b486060a9ae5f7819eb7f55bb8c22b10d7b3bed8da
 
 PKG_LICENSE:=COPYING
 PKG_LICENSE_FILES:=MIT

From 98a3c211feb6f63b3042e568205e3f5293e9236b Mon Sep 17 00:00:00 2001
From: Andrea Pesaresi <andreapesaresi82@gmail.com>
Date: Sat, 6 Apr 2024 14:18:32 +0200
Subject: [PATCH 08/11] ksmbd-tools: update to version 3.5.2

Major changes are:
 - Add durable handles parameter to ksmbd.conf.
 - Add payload_sz in ksmbd_share_config_response to validate ipc
   response.
 - Fix UAF and cleanups.

Signed-off-by: Andrea Pesaresi <andreapesaresi82@gmail.com>
(cherry picked from commit 9cf0eae9bca7447354bcb96759f213ae407c9532)
---
 net/ksmbd-tools/Makefile | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/net/ksmbd-tools/Makefile b/net/ksmbd-tools/Makefile
index a0f939dbe0..4c1b85663b 100644
--- a/net/ksmbd-tools/Makefile
+++ b/net/ksmbd-tools/Makefile
@@ -1,12 +1,12 @@
 include $(TOPDIR)/rules.mk
 
 PKG_NAME:=ksmbd-tools
-PKG_VERSION:=3.5.1
-PKG_RELEASE:=2
+PKG_VERSION:=3.5.2
+PKG_RELEASE:=1
 
 PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
 PKG_SOURCE_URL:=https://github.com/cifsd-team/ksmbd-tools/releases/download/$(PKG_VERSION)
-PKG_HASH:=ab377b3044c48382303f3f7ec95f2e1a17592c774d70b2a11f32952099dbb214
+PKG_HASH:=5da7fb4cb4368f9abf56f6f9fbc17b25e387876bed9ff7ee0d6f1140ef07c8d7
 
 PKG_LICENSE:=GPL-2.0-or-later
 PKG_LICENSE_FILES:=COPYING

From 1627d875b1fb71bd686c96388a47659fe22ae0cc Mon Sep 17 00:00:00 2001
From: Andrea Pesaresi <andreapesaresi82@gmail.com>
Date: Sat, 7 Dec 2024 10:29:09 +0100
Subject: [PATCH 09/11] ksmbd-tools: update to version 3.5.3

- manually refresh patch 030-glib.patch

Major changes are:
    fix adduser / addshare prompting on musl libc
    fix use of veto files as global share parameter
    lookup primary group and don't recurse in ksmbd.conf @group handling
    fix a leak and an intermittent auth failure in Kerberos 5
    add global parameter kerberos support

detailed changelog here: https://github.com/cifsd-team/ksmbd-tools/releases/tag/3.5.3

Signed-off-by: Andrea Pesaresi <andreapesaresi82@gmail.com>
(cherry picked from commit 5b058c9949b3e3adcbc35347aa1acc40c3442e2c)
---
 net/ksmbd-tools/Makefile               | 4 ++--
 net/ksmbd-tools/patches/030-glib.patch | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/net/ksmbd-tools/Makefile b/net/ksmbd-tools/Makefile
index 4c1b85663b..a08fe368cb 100644
--- a/net/ksmbd-tools/Makefile
+++ b/net/ksmbd-tools/Makefile
@@ -1,12 +1,12 @@
 include $(TOPDIR)/rules.mk
 
 PKG_NAME:=ksmbd-tools
-PKG_VERSION:=3.5.2
+PKG_VERSION:=3.5.3
 PKG_RELEASE:=1
 
 PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
 PKG_SOURCE_URL:=https://github.com/cifsd-team/ksmbd-tools/releases/download/$(PKG_VERSION)
-PKG_HASH:=5da7fb4cb4368f9abf56f6f9fbc17b25e387876bed9ff7ee0d6f1140ef07c8d7
+PKG_HASH:=e8d55cc53825170d7e5213d48a92b8251dc0d1351601283f6d0995cfd789b4d0
 
 PKG_LICENSE:=GPL-2.0-or-later
 PKG_LICENSE_FILES:=COPYING
diff --git a/net/ksmbd-tools/patches/030-glib.patch b/net/ksmbd-tools/patches/030-glib.patch
index 49809e9c60..4b9d7a4518 100644
--- a/net/ksmbd-tools/patches/030-glib.patch
+++ b/net/ksmbd-tools/patches/030-glib.patch
@@ -3,7 +3,7 @@
 @@ -21,6 +21,7 @@ include_dirs = include_directories(
  glib_dep = dependency(
    'glib-2.0',
-   version: '>= 2.44',
+   version: '>= 2.58',
 +  static: true,
  )
  libnl_dep = dependency(

From 93b41c74e0a51f9a763b3d621e5ee35c7319bffe Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ozan=20G=C3=B6ktan?= <ozan@goktan.site>
Date: Sun, 10 Mar 2024 14:39:46 +0100
Subject: [PATCH 10/11] microsocks: update to 1.0.4
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Ozan Göktan <ozan@goktan.site>
(cherry picked from commit 544e4a90619bc6bf39a6845d8e5b7b3c0b2e160a)
---
 net/microsocks/Makefile                | 4 ++--
 net/microsocks/files/microsocks.config | 1 +
 net/microsocks/files/microsocks.init   | 3 +++
 3 files changed, 6 insertions(+), 2 deletions(-)

diff --git a/net/microsocks/Makefile b/net/microsocks/Makefile
index 82abec56ac..79eea2e6af 100644
--- a/net/microsocks/Makefile
+++ b/net/microsocks/Makefile
@@ -1,12 +1,12 @@
 include $(TOPDIR)/rules.mk
 
 PKG_NAME:=microsocks
-PKG_VERSION:=1.0.3
+PKG_VERSION:=1.0.4
 PKG_RELEASE:=1
 
 PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
 PKG_SOURCE_URL:=https://codeload.github.com/rofl0r/microsocks/tar.gz/v$(PKG_VERSION)?
-PKG_HASH:=6801559b6f8e17240ed8eef17a36eea8643412b5a7476980fd4e24b02a021b82
+PKG_HASH:=130127a87f55870f18fbe47a64d9b9533020e2900802d36a0f6fd2b074313deb
 
 PKG_MAINTAINER:=Mateusz Korniak <matkorgithubcom@ant.gliwice.pl>
 PKG_LICENSE:=MIT
diff --git a/net/microsocks/files/microsocks.config b/net/microsocks/files/microsocks.config
index b85d740844..d834a7aacb 100644
--- a/net/microsocks/files/microsocks.config
+++ b/net/microsocks/files/microsocks.config
@@ -7,3 +7,4 @@ config microsocks 'config'
 	option user ''
 	option password ''
 	option auth_once '0' # Boolean, must be used together with user/pass
+	option quiet '1'
diff --git a/net/microsocks/files/microsocks.init b/net/microsocks/files/microsocks.init
index e775d89d7e..74dc03ff5f 100755
--- a/net/microsocks/files/microsocks.init
+++ b/net/microsocks/files/microsocks.init
@@ -18,6 +18,7 @@ start_service() {
 	local _user
 	local _format
 	local _auth_once
+	local _quiet
 
 	config_get _port "config" "port"
 	config_get _listenip "config" "listenip"
@@ -25,6 +26,7 @@ start_service() {
 	config_get _user "config" "user"
 	config_get _password "config" "password"
 	config_get_bool _auth_once "config" "auth_once" 0
+	config_get_bool _quiet "config" "quiet" 0
     
 	procd_open_instance "$CONF"
 	procd_set_param command /usr/bin/microsocks
@@ -34,6 +36,7 @@ start_service() {
 	[ -z "$_user" ] || procd_append_param command -u "${_user}"
 	[ -z "$_password" ] || procd_append_param command -P "${_password}"
 	[ "$_auth_once" -eq "0" ] || procd_append_param command -1
+	[ "$_quiet" -eq "0" ] || procd_append_param command -q
 	
 	procd_set_param respawn
 	procd_set_param stderr 1

From 571510a1491882d5b52403f2a39eab961ba1ee04 Mon Sep 17 00:00:00 2001
From: Tianling Shen <cnsztl@immortalwrt.org>
Date: Wed, 4 Dec 2024 13:33:13 +0800
Subject: [PATCH 11/11] microsocks: backport upstream fixes

Fix segmentation fault with newer musl and improve throughput.

Signed-off-by: Tianling Shen <cnsztl@immortalwrt.org>
(cherry picked from commit 77a7324de54e175029fa7ad051a3a17d1806e03e)
---
 net/microsocks/Makefile                       |   2 +-
 ...use-a-bigger-thread-stack-by-default.patch |  24 ++++
 ...bout-shadow-declaration-of-bind_addr.patch |  22 ++++
 ...hput-in-copyloop-using-bigger-buffer.patch | 114 ++++++++++++++++++
 4 files changed, 161 insertions(+), 1 deletion(-)
 create mode 100644 net/microsocks/patches/001-use-a-bigger-thread-stack-by-default.patch
 create mode 100644 net/microsocks/patches/002-mute-warning-about-shadow-declaration-of-bind_addr.patch
 create mode 100644 net/microsocks/patches/003-improve-throughput-in-copyloop-using-bigger-buffer.patch

diff --git a/net/microsocks/Makefile b/net/microsocks/Makefile
index 79eea2e6af..b2adbc20c1 100644
--- a/net/microsocks/Makefile
+++ b/net/microsocks/Makefile
@@ -2,7 +2,7 @@ include $(TOPDIR)/rules.mk
 
 PKG_NAME:=microsocks
 PKG_VERSION:=1.0.4
-PKG_RELEASE:=1
+PKG_RELEASE:=2
 
 PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
 PKG_SOURCE_URL:=https://codeload.github.com/rofl0r/microsocks/tar.gz/v$(PKG_VERSION)?
diff --git a/net/microsocks/patches/001-use-a-bigger-thread-stack-by-default.patch b/net/microsocks/patches/001-use-a-bigger-thread-stack-by-default.patch
new file mode 100644
index 0000000000..3defe5db23
--- /dev/null
+++ b/net/microsocks/patches/001-use-a-bigger-thread-stack-by-default.patch
@@ -0,0 +1,24 @@
+From c81760cc3f1b6db22c7c9694efe7f3be115c2caf Mon Sep 17 00:00:00 2001
+From: rofl0r <rofl0r@users.noreply.github.com>
+Date: Fri, 17 May 2024 14:38:16 +0000
+Subject: [PATCH] use a bigger thread stack by default
+
+apparently newer musl versions require more stack for the TCP-based
+DNS resolver.
+
+closes #73
+---
+ sockssrv.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/sockssrv.c
++++ b/sockssrv.c
+@@ -47,7 +47,7 @@
+ #endif
+ 
+ #ifdef PTHREAD_STACK_MIN
+-#define THREAD_STACK_SIZE MAX(8*1024, PTHREAD_STACK_MIN)
++#define THREAD_STACK_SIZE MAX(16*1024, PTHREAD_STACK_MIN)
+ #else
+ #define THREAD_STACK_SIZE 64*1024
+ #endif
diff --git a/net/microsocks/patches/002-mute-warning-about-shadow-declaration-of-bind_addr.patch b/net/microsocks/patches/002-mute-warning-about-shadow-declaration-of-bind_addr.patch
new file mode 100644
index 0000000000..8d298bd7fa
--- /dev/null
+++ b/net/microsocks/patches/002-mute-warning-about-shadow-declaration-of-bind_addr.patch
@@ -0,0 +1,22 @@
+From 0343813e0410b469d591bc61b9a546ee2c2c15f6 Mon Sep 17 00:00:00 2001
+From: rofl0r <rofl0r@users.noreply.github.com>
+Date: Fri, 17 May 2024 14:40:11 +0000
+Subject: [PATCH] mute warning about shadow declaration of bind_addr
+
+---
+ sockssrv.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/sockssrv.c
++++ b/sockssrv.c
+@@ -112,8 +112,8 @@ struct thread {
+ static void dolog(const char* fmt, ...) { }
+ #endif
+ 
+-static struct addrinfo* addr_choose(struct addrinfo* list, union sockaddr_union* bind_addr) {
+-	int af = SOCKADDR_UNION_AF(bind_addr);
++static struct addrinfo* addr_choose(struct addrinfo* list, union sockaddr_union* bindaddr) {
++	int af = SOCKADDR_UNION_AF(bindaddr);
+ 	if(af == AF_UNSPEC) return list;
+ 	struct addrinfo* p;
+ 	for(p=list; p; p=p->ai_next)
diff --git a/net/microsocks/patches/003-improve-throughput-in-copyloop-using-bigger-buffer.patch b/net/microsocks/patches/003-improve-throughput-in-copyloop-using-bigger-buffer.patch
new file mode 100644
index 0000000000..dfee5c933e
--- /dev/null
+++ b/net/microsocks/patches/003-improve-throughput-in-copyloop-using-bigger-buffer.patch
@@ -0,0 +1,114 @@
+From 98421a21c4adc4c77c0cf3a5d650cc28ad3e0107 Mon Sep 17 00:00:00 2001
+From: rofl0r <rofl0r@users.noreply.github.com>
+Date: Fri, 24 May 2024 23:02:34 +0000
+Subject: [PATCH] improve throughput in copyloop() using bigger buffer
+
+- refactored clientthread to put the handshake code into its own
+  function, since it used its own 1K stack buffer.
+  by returning from the function before calling copyloop, we have
+  that space available in the new stackframe.
+- since getaddrinfo() was the main stack consumer in the entire
+  code, we can safely use at least half the available thread
+  stack size for the copyloop to achieve higher throughput.
+  in my testing with pyhttpd it turned out that 64k is the sweet
+  spot to have minimal syscall overhead, but 16k is very close,
+  and it allows us to keep the minimal memory usage profile.
+
+this is in response to https://github.com/rofl0r/microsocks/issues/58#issuecomment-2118389063
+which links to a repo that tests different socks5 servers
+performance on gigabit links.
+
+also closes #10
+---
+ sockssrv.c | 35 +++++++++++++++++++----------------
+ 1 file changed, 19 insertions(+), 16 deletions(-)
+
+--- a/sockssrv.c
++++ b/sockssrv.c
+@@ -44,6 +44,7 @@
+ 
+ #ifndef MAX
+ #define MAX(x, y) ((x) > (y) ? (x) : (y))
++#define MIN(x, y) ((x) < (y) ? (x) : (y))
+ #endif
+ 
+ #ifdef PTHREAD_STACK_MIN
+@@ -282,7 +283,10 @@ static void copyloop(int fd1, int fd2) {
+ 		}
+ 		int infd = (fds[0].revents & POLLIN) ? fd1 : fd2;
+ 		int outfd = infd == fd2 ? fd1 : fd2;
+-		char buf[1024];
++		/* since the biggest stack consumer in the entire code is
++		   libc's getaddrinfo(), we can safely use at least half the
++		   available stacksize to improve throughput. */
++		char buf[MIN(16*1024, THREAD_STACK_SIZE/2)];
+ 		ssize_t sent = 0, n = read(infd, buf, sizeof buf);
+ 		if(n <= 0) return;
+ 		while(sent < n) {
+@@ -310,14 +314,12 @@ static enum errorcode check_credentials(
+ 	return EC_NOT_ALLOWED;
+ }
+ 
+-static void* clientthread(void *data) {
+-	struct thread *t = data;
+-	t->state = SS_1_CONNECTED;
++static int handshake(struct thread *t) {
+ 	unsigned char buf[1024];
+ 	ssize_t n;
+ 	int ret;
+-	int remotefd = -1;
+ 	enum authmethod am;
++	t->state = SS_1_CONNECTED;
+ 	while((n = recv(t->client.fd, buf, sizeof buf, 0)) > 0) {
+ 		switch(t->state) {
+ 			case SS_1_CONNECTED:
+@@ -325,13 +327,13 @@ static void* clientthread(void *data) {
+ 				if(am == AM_NO_AUTH) t->state = SS_3_AUTHED;
+ 				else if (am == AM_USERNAME) t->state = SS_2_NEED_AUTH;
+ 				send_auth_response(t->client.fd, 5, am);
+-				if(am == AM_INVALID) goto breakloop;
++				if(am == AM_INVALID) return -1;
+ 				break;
+ 			case SS_2_NEED_AUTH:
+ 				ret = check_credentials(buf, n);
+ 				send_auth_response(t->client.fd, 1, ret);
+ 				if(ret != EC_SUCCESS)
+-					goto breakloop;
++					return -1;
+ 				t->state = SS_3_AUTHED;
+ 				if(auth_ips && !pthread_rwlock_wrlock(&auth_ips_lock)) {
+ 					if(!is_in_authed_list(&t->client.addr))
+@@ -343,23 +345,24 @@ static void* clientthread(void *data) {
+ 				ret = connect_socks_target(buf, n, &t->client);
+ 				if(ret < 0) {
+ 					send_error(t->client.fd, ret*-1);
+-					goto breakloop;
++					return -1;
+ 				}
+-				remotefd = ret;
+ 				send_error(t->client.fd, EC_SUCCESS);
+-				copyloop(t->client.fd, remotefd);
+-				goto breakloop;
+-
++				return ret;
+ 		}
+ 	}
+-breakloop:
++	return -1;
++}
+ 
+-	if(remotefd != -1)
++static void* clientthread(void *data) {
++	struct thread *t = data;
++	int remotefd = handshake(t);
++	if(remotefd != -1) {
++		copyloop(t->client.fd, remotefd);
+ 		close(remotefd);
+-
++	}
+ 	close(t->client.fd);
+ 	t->done = 1;
+-
+ 	return 0;
+ }
+