DNS Firewall: Timeout while downloading large zones

Hi Guys

Upgraded to Core 201 and tried to get DNS Firewall to replace my Adguard but not having much luck.

I can get the zones:

  • Dating
  • Smart-TV
  • Violence
  • Piracy

To download zones files and block content but the larger zones never download the zones files into /var/cache/unbound/

I have run a few dig commands to see if I can get them to download manually but they always exit with

;; Communications error to 81.3.27.55#53: end of file
;; no servers could be reached

it will get the first 3759 lines of ads.rpz.ipfire.org every time before giving those error lines

it looks to be rate or timeout limited is this the case?

any help would be greatly appreciated.

Hello,

thanks for giving the DNS Firewall a try.

Do you potentially have a very slow connection to our service? Can you see the bandwidth that is being used? After how long does the timeout happen?

Hi

Thanks for your quick reply I have a 50/20 connection not slow but not blinding fast

less than 5s and it alway stops at the same amount of lines downloaded

@ms Thanks for having a look at this.

I attempted to download the zone using dig from my workplace and encountered the same limitation — the ad-block zone returns the same number of lines as it does from my home network. I’m not sure if this is directly relevant, but I thought it was worth mentioning.

After further investigation, the only workaround I’ve found is to create a script that downloads the enabled zones, places them into the cache folder, and restarts Unbound. I’ve included the script below in case anyone else runs into this problem.

That said, I believe the root cause should be addressed — either through a more reliable initial download of the blocklist, or at the very least, a more visible notification in the web UI when a list fails to load.

#!/bin/bash
#
# IPFire DBL to Unbound RPZ Zone Converter
#
# Downloads IPFire domain blocklists and converts them to RPZ zone files.
# Designed to be run via cron for automatic updates.
#
# Usage: ./download-rpz.sh
#
# Reads /etc/unbound/dnsbl.conf to find enabled RPZ zones.
# If unable to parse, falls back to downloading all lists.
#

# ---- CONFIGURATION ----

# Unbound DNSBL config file to parse for enabled lists
DNSBL_CONF="/etc/unbound/dnsbl.conf"

# All available IPFire lists (used as fallback)
ALL_LISTS="ads dating doh gambling games malware phishing piracy porn shopping smart-tv social streaming violence"

# Where to store zone files
ZONE_DIR="/var/cache/unbound"

# Base URL
BASE_URL="https://dbl.ipfire.org/lists"

# RPZ action: CNAME . = NXDOMAIN, CNAME *.= NODATA
RPZ_ACTION="CNAME ."

# ---- END CONFIGURATION ----

# Parse enabled lists from dnsbl.conf
if [ -f "$DNSBL_CONF" ]; then
    LISTS=$(grep -oP '(?<=name:\s)[\w-]+(?=\.rpz\.ipfire\.org)' "$DNSBL_CONF" | sort -u | tr '\n' ' ')
    if [ -z "$LISTS" ]; then
        # Fallback: try alternative grep without -P
        LISTS=$(grep 'name:' "$DNSBL_CONF" | grep 'rpz.ipfire.org' | sed 's/.*name:[[:space:]]*//' | sed 's/\.rpz\.ipfire\.org.*//' | sort -u | tr '\n' ' ')
    fi
fi

if [ -z "$LISTS" ]; then
    echo "WARNING: Could not parse $DNSBL_CONF or file not found."
    echo "Falling back to downloading ALL lists."
    LISTS="$ALL_LISTS"
else
    echo "Found enabled lists in $DNSBL_CONF:"
    echo "  $LISTS"
fi

TIMESTAMP=$(date -u +%Y-%m-%dT%H:%M:%S+00:00)
SERIAL=$(date +%s)

for LIST in $LISTS; do
    echo "Processing: $LIST"

    DOMAIN_URL="${BASE_URL}/${LIST}/domains.txt"
    ZONE_FILE="${ZONE_DIR}/${LIST}.rpz.ipfire.org.zone"
    ZONE_NAME="${LIST}.rpz.ipfire.org"
    TMP_FILE=$(mktemp)

    # Download the domain list
    HTTP_CODE=$(curl -s -w '%{http_code}' -o "$TMP_FILE" "$DOMAIN_URL")

    if [ "$HTTP_CODE" != "200" ]; then
        echo "  ERROR: Failed to download $LIST (HTTP $HTTP_CODE)"
        rm -f "$TMP_FILE"
        continue
    fi

    # Count domains
    TOTAL=$(grep -cv '^#\|^$' "$TMP_FILE")
    echo "  Downloaded $TOTAL domains"

    # Extract description from the file header
    DESCRIPTION=$(grep '^#  Blocks\|^#  Contains' "$TMP_FILE" | head -1 | sed 's/^#  *//')

    # Build zone file
    {
        # SOA record
        echo "${ZONE_NAME}.  60  IN  SOA  primary.dbl.ipfire.org. hostmaster.ipfire.org. ${SERIAL} 3600 600 3600000 60"

        # NS record
        echo "${ZONE_NAME}.  60  IN  NS  primary.dbl.ipfire.org."

        # TXT info records
        echo "_info.${ZONE_NAME}. 60  IN  TXT  \"license=CC BY-SA 4.0\""
        echo "_info.${ZONE_NAME}. 60  IN  TXT  \"total-domains=${TOTAL}\""
        echo "_info.${ZONE_NAME}. 60  IN  TXT  \"updated-at=${TIMESTAMP}\""
        [ -n "$DESCRIPTION" ] && echo "_info.${ZONE_NAME}. 60  IN  TXT  \"description=${DESCRIPTION}\""

        # Convert domains to RPZ entries (append zone name to match AXFR format)
        grep -v '^#\|^$' "$TMP_FILE" | while IFS= read -r domain; do
            echo "${domain}.${ZONE_NAME}. 60 IN ${RPZ_ACTION}"
            echo "*.${domain}.${ZONE_NAME}. 60 IN ${RPZ_ACTION}"
        done
    } > "${ZONE_FILE}.tmp"

    # Atomic replace and set permissions
    mv "${ZONE_FILE}.tmp" "$ZONE_FILE"
    chmod 644 "$ZONE_FILE"
    chown nobody:nobody "$ZONE_FILE"
    echo "  Written to $ZONE_FILE"

    rm -f "$TMP_FILE"
done

# Reload Unbound
echo "Reloading Unbound..."
if unbound-control reload 2>/dev/null; then
    echo "Done - all zones loaded."
else
    echo "WARNING: unbound-control reload failed. You may need to restart unbound manually."
fi