unbound-adblock 0.5 errata 002, January 19, 2021:

This is a hotfix release to address errata in the portability glue and input sanitization functions.

Issues fixed:

* Allow script to function on FreeBSD with 'security.bsd.see_other_uids=0' and 'security.bsd.see_other_gids=0'
# Note: The above fix requires updating FreeBSD's doas permissions. Please see updated install documentation.

* Fix FreeBSD and NetBSD path handling issues.

* Accomodate certain shells that have a stricter interpretation of what constitutes a "tested function" as it relates to 'set -e'.

* Implement 'IS_DOMAIN" function to be exceedingly paranoid about domain/blocklist validity.

* Invalid domains are now logged.

Apply by doing: (Note: Provided file path is for OpenBSD, other platforms path may differ)

patch /usr/local/bin/unbound-adblock < p1.patch

NOTE: This patch is dependent on patch 001 released on January 15th 2021. If you installed unbound-adblock after this date, then you are already running a patched version.
To check which patch/version of unbound-adblock you're running call unbound-adblock with the '-h' switch; the second line of output will show the version info:
$ unbound-adblock -h
...
# unbound-adblock 0.5p0 (Gaslight Republic)  Released on: 2021-01-10
...

'0.5p0' means you have the first patch installed, if there is no "p*" suffix, then that means you have no patches installed. 

--- unbound-adblock.sh.orig1	Fri Jan 15 14:27:30 2021
+++ unbound-adblock.sh	Tue Jan 19 00:33:09 2021
@@ -23,7 +23,7 @@
 # Fetch, parse and generate domain blocklists data into format suitable 
 # for ingestion by RPZ compatible DNS servers and/or unbound/unwind.
 
-version='0.5p0'
+version='0.5p1'
 release_date='2021-01-10'
 release_name='Gaslight Republic'
 
@@ -256,11 +256,11 @@
 # ------------------------------------------------------------------------------
 
 FORMAT_DOMAIN() {
-	myawk -- 'BEGIN { OFS = "" } (length($1) < 254 && length($1) > 3) && ($1 != "" && $1 ~ "\\.[[:alnum:]]" && $1 ~ "[[:alpha:]]" && $1 !~ "#|/|^\\.|\\.$|\\.\\.") { print $1 }'
+	myawk -- 'BEGIN { OFS = "" } (length($1) < 254 && length($1) > 3) && ($1 != "" && $1 !~ "#|\\.\\." && $1 ~ "\\.[[:alnum:]]" && $1 ~ "[[:alpha:]]") { print $1 }'
 }
 
 FORMAT_HOSTS() {
-	myawk -- 'BEGIN { OFS = "" } ($1 == "0.0.0.0" || $1 == "127.0.0.1") && (length($2) < 254 && length($2) > 3) && ($2 ~ "\\.[[:alnum:]]" && $2 ~ "[[:alpha:]]" && $2 !~ "/|^\\.|\\.$|\\.\\.") { print $2 }'
+	myawk -- 'BEGIN { OFS = "" } ($1 == "0.0.0.0" || $1 == "127.0.0.1") && (length($2) < 254 && length($2) > 3) && ($2 !~ "\\.\\." && $2 ~ "\\.[[:alnum:]]" && $2 ~ "[[:alpha:]]") { print $2 }'
 }
 
 RAW_TO_UNBOUND() {
@@ -268,8 +268,9 @@
 }
 
 LIST_GEN() {
+	typeset _i
+
 	# Delete empty files
-	typeset _file
 	find "${tmpdir_domain}" "${tmpdir_hosts}" -type f -size 0 -delete || WARN_ERR 'ERROR: Failed to delete temporary files!'
 
 	# Domain-only format data
@@ -279,7 +280,9 @@
 	{ find "${tmpdir_hosts}" -type f -exec gunzip -dcf -- {} + | tr -cd -- '[:alnum:][:blank:]%#.~\n_/\-' | FORMAT_HOSTS ;} > "${tmpfile_hosts}" || TMP_FILE_ABORT
 
 	# Merge and sort lists
-	{ cat -- "${tmpfile_domain}" "${tmpfile_hosts}" | tr '[:upper:]' '[:lower:]' | WHITELIST_FILTER | mysort -u ;} > "${rawout}" || TMP_FILE_ABORT
+	cat -- "${tmpfile_domain}" "${tmpfile_hosts}" | while IFS= read -r _i ; do
+		IS_DOMAIN "${_i}" || true
+	done | WHITELIST_FILTER | mysort -u > "${rawout}" || TMP_FILE_ABORT
 }
 
 
@@ -456,6 +459,26 @@
 	fi
 }
 
+IS_DOMAIN() {
+	typeset -l domain="${1}"
+	typeset prefix suffix
+	prefix="${domain%"${domain#?}"}"
+	suffix="${domain#"${domain%?}"}"
+
+	case "${prefix}" in
+	    .|-) WARNING "Skipping invalid domain: '${1}'" ; return 1 ;;
+	esac
+
+	case "${suffix}" in
+	    .|-) WARNING "Skipping invalid domain: '${1}'" ; return 1 ;;
+	esac
+
+	case "${domain}" in
+	    ''|*[!0-9A-Za-z._-]*) WARNING "Skipping invalid domain: '${1}'" ; return 1 ;;
+	    *) echo "${domain}" ; return 0 ;;
+	esac
+}
+
 IS_INT() {
 	case "$1" in
 	    ''|*[!0-9]*) return 1 ;;
@@ -498,17 +521,17 @@
 UNBOUND_STATUS_CHECK() {
 	case "${_OS_TYPE}" in
 	alpine)
-		rc-service unbound status >/dev/null 2>&1 ; return ;;
+		"${rcd}" unbound status >/dev/null 2>&1 ; return ;;
 	dragonflybsd)
-		service unbound onestatus >/dev/null 2>&1 ; return ;;
+		"${rcd}" unbound onestatus >/dev/null 2>&1 ; return ;;
 	freebsd)
-		service unbound onestatus >/dev/null 2>&1 ; return ;;
+		"${getroot}" "${rcd}" unbound onestatus >/dev/null 2>&1 ; return ;;
 	linux)
 		systemctl is-active --quiet unbound >/dev/null 2>&1 ; return ;;
 	netbsd)
-		"${getroot}" service unbound onestatus >/dev/null 2>&1 ; return ;;
+		"${getroot}" "${rcd}" unbound onestatus >/dev/null 2>&1 ; return ;;
 	openbsd)
-		rcctl check unbound >/dev/null 2>&1 ; return ;;
+		"${rcd}" check unbound >/dev/null 2>&1 ; return ;;
 	*)
 		ERR "Operating system type '${_OS_TYPE}' not recognized..."
 	esac
@@ -682,7 +705,7 @@
 	readonly version release_date release_name
 
 	# Initialize counters
-	typeset -i _array_index=0 _d_counter=0 _g_counter=0 _l_counter=0
+	typeset -i _array_index=0 _d_counter=0 _l_counter=0 _r_counter=0
 	# Initialize case (in)sensitive vars
 	typeset -l _opt_arg
 	# Initialize global configuration vars
@@ -774,29 +797,29 @@
 	alpine)
 	  test -n "${getroot}" || getroot="$(CHECK_CMD doas)"
 	  test -n "${netget}" || netget='wget'
-	  test -n "${rcd}" || rcd='rc-service'
+	  test -n "${rcd}" || rcd="$(CHECK_CMD rc-service)"
 	  test -n "${rcdarg1}" || rcdarg1='unbound'
 	  test -n "${rcdarg2}" || rcdarg2='restart'
 	  test -n "${confpath}" || confpath='/etc/unbound/adblock.rpz'
-	  test "${_ALT_UNBOUND}" -eq 1 && confpath='/etc/unbound/adblock.conf'
+	  if [ "${_ALT_UNBOUND}" -eq 1 ]; then confpath='/etc/unbound/adblock.conf' ; fi
 	  ;;
 	dragonflybsd)
 	  test -n "${getroot}" || getroot="$(CHECK_CMD doas)"
 	  test -n "${netget}" || netget='fetch'
-	  test -n "${rcd}" || rcd='service'
+	  test -n "${rcd}" || rcd="$(CHECK_CMD service)"
 	  test -n "${rcdarg1}" || rcdarg1='unbound'
 	  test -n "${rcdarg2}" || rcdarg2='restart'
 	  test -n "${confpath}" || confpath='/usr/local/etc/unbound/adblock.rpz'
-	  test "${_ALT_UNBOUND}" -eq 1 && confpath='/usr/local/etc/unbound/adblock.conf'
+	  if [ "${_ALT_UNBOUND}" -eq 1 ]; then confpath='/usr/local/etc/unbound/adblock.conf' ; fi
 	  ;;
 	freebsd)
 	  test -n "${getroot}" || getroot="$(CHECK_CMD doas)"
 	  test -n "${netget}" || netget='fetch'
-	  test -n "${rcd}" || rcd='service'
+	  test -n "${rcd}" || rcd="$(CHECK_CMD service)"
 	  test -n "${rcdarg1}" || rcdarg1='unbound'
 	  test -n "${rcdarg2}" || rcdarg2='restart'
-	  test -n "${confpath}" || confpath='/etc/unbound/adblock.rpz'
-	  test "${_ALT_UNBOUND}" -eq 1 && confpath='/etc/unbound/adblock.conf'
+	  test -n "${confpath}" || confpath='/usr/local/etc/unbound/adblock.rpz'
+	  if [ "${_ALT_UNBOUND}" -eq 1 ]; then confpath='/usr/local/etc/unbound/adblock.conf' ; fi
 	  ;;
 	linux)
 	  test -n "${getroot}" || getroot="$(CHECK_CMD sudo)"
@@ -805,18 +828,18 @@
 	  test -n "${rcdarg1}" || rcdarg1='restart'
 	  test -n "${rcdarg2}" || rcdarg2='unbound'
 	  test -n "${confpath}" || confpath='/etc/unbound/adblock.rpz'
-	  test "${_ALT_UNBOUND}" -eq 1 && confpath='/etc/unbound/adblock.conf'
+	  if [ "${_ALT_UNBOUND}" -eq 1 ]; then confpath='/etc/unbound/adblock.conf' ; fi
 	  ;;
 	netbsd)
 	  # NetBSD does annoying things with their $PATH, so make sure we set what we need
-	  PATH='/usr/pkg/bin:/usr/pkg/sbin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin'
+	  PATH="/usr/pkg/bin:/usr/pkg/sbin:${PATH}"
 	  test -n "${getroot}" || getroot="$(CHECK_CMD doas)"
 	  test -n "${netget}" || netget='curl'
-	  test -n "${rcd}" || rcd='service'
+	  test -n "${rcd}" || rcd="$(CHECK_CMD service)"
 	  test -n "${rcdarg1}" || rcdarg1='unbound'
 	  test -n "${rcdarg2}" || rcdarg2='restart'
 	  test -n "${confpath}" || confpath='/usr/pkg/etc/unbound/adblock.rpz'
-	  test "${_ALT_UNBOUND}" -eq 1 && confpath='/etc/unbound/adblock.conf'
+	  if [ "${_ALT_UNBOUND}" -eq 1 ]; then confpath='/etc/unbound/adblock.conf' ; fi
 	  ;;
 	openbsd)
 	  test -n "${getroot}" || getroot="$(CHECK_CMD doas)"
@@ -825,7 +848,7 @@
 	  test -n "${rcdarg1}" || rcdarg1='reload'
 	  test -n "${rcdarg2}" || rcdarg2='unbound'
 	  test -n "${confpath}" || confpath='/var/unbound/db/adblock.rpz'
-	  test "${_ALT_UNBOUND}" -eq 1 && confpath='/var/unbound/db/adblock.conf'
+	  if [ "${_ALT_UNBOUND}" -eq 1 ]; then confpath='/var/unbound/db/adblock.conf' ; fi
 	  ;;
 	custom)
 	  test -n "${getroot}" || ERR "Custom OS type specified - please set doas/sudo preference with '-Z' option"
