Enterprise-Grade Domain Validation, IDN Punycode Normalization & Blocklist Aggregation Pipeline
[!NOTE] Production Status: Tested and verified on multi-million domain feeds from TrustPositif/Komdigi Indonesia, AdGuard, and custom threat feeds. Fully compatible with enterprise recursive resolvers (BIND 9 RPZ, Unbound, PowerDNS, Knot), edge hardware firewalls (MikroTik RouterOS), and local DNS appliances (Pi-hole, AdGuard Home).
DOMAINS_TO_CLEAN.txt)idn2)TrustPositif Validator is a specialized, zero-dependency Bash automation pipeline that converts messy, multi-source domain blacklists into deterministic, deduplicated, RFC-compliant, and DNS-ready blocklist files.
Government and public domain blocklists (such as the raw Komdigi TrustPositif database) are aggregated from diverse reporting portals, crawling robots, and legacy ISP submissions. These raw sources contain massive volumes of invalid data that cause DNS servers to reject zone files, crash during parsing, or exhaust RAM tables:
https://), port numbers (:8080), web paths (/login), and query strings (?ref=spam).||bad.com^ or @@safe.com$document which standard nameservers cannot compile.127.0.0.1, 0.0.0.0, or IPv6 loopback addresses prepended to domain names..corp, .local, .internal, .xyz123).TrustPositif Validator strips out all noise, validates every TLD against the authoritative IANA Root Zone Database, standardizes internationalized labels into IDNA2008 Punycode (xn--), removes wild subdomains while retaining root domain apex rules, and writes clean, sorted results with atomic filesystem safety.
| Raw Input from Upstream Feed | Transformation Mechanics | Final Clean Output |
|---|---|---|
https://evil-malware.com:8443/steal.php?id=1#frag |
Strips scheme, port, query string, path, and fragment | evil-malware.com |
||tracking-network.net^$third-party |
Strips AdGuard/uBlock filter tokens (||, ^, $) |
tracking-network.net |
127.0.0.1 telemetry.adservice.org |
Strips hosts file IPv4 prefix and whitespace | telemetry.adservice.org |
::1 spyware.analytic.io |
Strips IPv6 loopback prefix (::1, fe80::) |
spyware.analytic.io |
contoh-domain.Π΄ΠΎΠΌΠ΅Π½.ΡΡ |
Converts Unicode IDN via GNU Libidn2 (idn2) |
contoh-domain.xn--d1acufc.xn--p1ai |
login.phishing-bank.co.id |
Stream-cleaned if phishing-bank.co.id in DOMAINS_TO_CLEAN.txt |
(Subdomain stripped; apex preserved) |
bogus-site.invalidcorp |
Filtered: .invalidcorp is not registered in IANA Root Database |
(Discarded) |
-illegal-leading-hyphen.com |
Filtered: RFC 1035 disallows labels starting with - |
(Discarded) |
| Capability | Technical Implementation | Practical Advantage |
|---|---|---|
| Multi-Source Ingestion | Ingests all URLs defined in TRUSTPOSITIF_URLS |
Combines government blocklists with custom enterprise threat feeds |
| Live IANA TLD Verification | Downloads & caches data.iana.org/TLD/tlds-alpha-by-domain.txt |
Eliminates typo TLDs, deprecated TLDs, and fake local domains |
| IDNA2008 Punycode Engine | Per-chunk GNU Libidn2 (idn2) processing with auto-fallback |
Converts internationalized Unicode domains into RFC 5890 xn-- format |
| Strict RFC Enforcement | RFC 1034, 1035, 1123, 3490, 5890 structural validation | Guarantees syntax compliance with BIND, Unbound, PowerDNS, and Knot |
| AWK Tri-Engine Fallback | Adaptive detection: mawk (fastest) β gawk β system awk |
Blazing-fast execution without requiring manual compiler configuration |
| Subdomain Cleaning Engine | Stream-based AWK hash table lookup for DOMAINS_TO_CLEAN.txt |
Removes thousands of wild subdomains in <0.3s with zero memory overhead |
| Apex Guard (Root Protection) | Retains the registered root domain when stripping its subdomains | Maintains RPZ zone blocking for parent domains without wasting cache RAM |
| Parent Domain Collapse | Optional ccTLD-aware collapse (CUT_SUBDOMAINS=1) |
Consolidates deep subdomains to parent SLD/TLD (.co.id, .com.au, etc.) |
| Adaptive Resource Auto-Tuning | Auto-detects physical RAM, cgroups v1/v2, Darwin sysctl, FreeBSD sysctl | Prevents Out-Of-Memory (OOM) crashes on budget 512MB VPS servers |
| Parallel Worker Pool | GNU Parallel with adaptive core clamping (4β32 workers) | Utilizes 100% of host CPU threads for maximum throughput |
| Atomic Inode Replacement | Staging write in target directory + atomic mv + chmod 644 |
Zero lock contention or partial read risk for DNS daemons and web servers |
| Resilient Networking | Wget primary with Curl fallback, SSL bypass toggle, and auto-retry | Survives legacy server TLS quirks, handshake timeouts, and network blips |
| Cross-Platform Portability | Debian, Ubuntu, RHEL, CentOS, Rocky, Alma, Alpine, Arch, macOS, FreeBSD | Identical performance and output formatting across all UNIX environments |
| ShellCheck Certified | 100% zero-warning compliance on ShellCheck v0.11+ | Clean, defensive, bug-free, and production-ready enterprise shell code |
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β TRUSTPOSITIF VALIDATOR β
β 7-Phase High-Performance Pipeline β
ββββββββββββββββββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββββ
β
βββββββββββββββββββββββββββββββββββββββββΌββββββββββββββββββββββββββββββββββββββββ
β PHASE 1: INGESTION & DOWNLOAD β
β ββ Download authoritative IANA Root Zone TLD list β
β ββ Ingest all threat feeds configured in TRUSTPOSITIF_URLS β
β ββ Automatic retry engine (5 tries, exponential delay, TLS bypass fallback) β
β ββ Payload verification (rejects HTTP 403, 404, and HTML error pages) β
βββββββββββββββββββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββββββββββ
β
βββββββββββββββββββββββββββββββββββββββββΌββββββββββββββββββββββββββββββββββββββββ
β PHASE 2: RESOURCE PROFILING & ADAPTIVE CHUNKING β
β ββ Detect available CPU cores and memory limits (physical RAM & cgroups v1/v2)β
β ββ Auto-tune CHUNK_SIZE = 20,000 + (CORES Γ 1,000), clamped [1,000 β 50,000] β
β ββ Partition raw domain corpus into isolated disk chunks in secure /tmp β
βββββββββββββββββββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββββββββββ
β
βββββββββββββββββββββββββββββββββββββββββΌββββββββββββββββββββββββββββββββββββββββ
β PHASE 3: PARALLEL WORKER POOL (GNU Parallel + AWK Engine) β
β ββ Strip schemes (http://, ftp://), ports, query strings, paths, and anchors β
β ββ Strip AdGuard tokens (||, ^, $options), comments (#, ;), and IP prefixes β
β ββ IDN Punycode Engine: Convert non-ASCII domains via idn2 (IDNA2008) β
β ββ Structural RFC validator: FQDN β€ 253 octets, labels β€ 63, hyphen rules β
β ββ IANA TLD Verification: Match trailing label against normalized TLD hash β
βββββββββββββββββββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββββββββββ
β
βββββββββββββββββββββββββββββββββββββββββΌββββββββββββββββββββββββββββββββββββββββ
β PHASE 4: SUBDOMAIN STREAM CLEANING (Optional: CLEAN_SUBDOMAINS=1) β
β ββ Stream-load DOMAINS_TO_CLEAN.txt into an in-memory AWK hash table β
β ββ Strip *.target.com subdomains while preserving apex target.com (Apex Guard)β
β ββ Sub-second execution (<0.3s) with zero Bash memory array overhead β
βββββββββββββββββββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββββββββββ
β
βββββββββββββββββββββββββββββββββββββββββΌββββββββββββββββββββββββββββββββββββββββ
β PHASE 5: SORTING & GLOBAL DEDUPLICATION β
β ββ Concatenate all *.processed worker chunk files β
β ββ Execute sort -u with dynamically calculated SORT_BUFFER β
β ββ Full BSD sort & GNU sort portability (automatic percentage to MiB conversion)
βββββββββββββββββββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββββββββββ
β
βββββββββββββββββββββββββββββββββββββββββΌββββββββββββββββββββββββββββββββββββββββ
β PHASE 6: ATOMIC WRITE & PERMISSION HARDENING β
β ββ Write staging file inside target OUTPUT_DIR (prevents cross-device rename) β
β ββ Enforce deterministic file mode (chmod 0644) for DNS/Web service access β
β ββ Atomic inode swap via mv -f β
βββββββββββββββββββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββββββββββ
β
βββββββββββββββββββββββββββββββββββββββββΌββββββββββββββββββββββββββββββββββββββββ
β PHASE 7: TRAP CLEANUP & AUDIT REPORTING β
β ββ Signals trapped: EXIT, SIGINT (130), SIGTERM (143) β
β ββ Terminate background worker processes & delete staging temporary folders β
β ββ Render complete performance metrics, throughput, and memory consumption β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Run TrustPositif Validator using any method suitable for your environment:
Ideal for quick evaluation or lightweight scheduled cron jobs:
# Via curl
bash <(curl -fsSL https://raw.githubusercontent.com/alsyundawy/TrustPositif-Validator/main/trustpositif-validator.sh)
# Via wget
bash <(wget -qO- https://raw.githubusercontent.com/alsyundawy/TrustPositif-Validator/main/trustpositif-validator.sh)
Allows custom configurations, local list files, and offline execution:
git clone https://github.com/alsyundawy/TrustPositif-Validator.git
cd TrustPositif-Validator
chmod +x trustpositif-validator.sh
# Run with standard options
bash trustpositif-validator.sh
# Run with subdomain cleanup enabled
bash trustpositif-validator.sh --clean-subdomains
The script features a flexible argument parser that supports combining multiple flags:
Usage: bash trustpositif-validator.sh [OPTIONS]
| Flag | Long Form | Description |
|---|---|---|
-h |
--help |
Display the interactive manual using the system pager (less -R / more). |
-v |
--version |
Display build version string and release timestamp. |
| Β | --force-cleanup |
Terminate any stale validator processes and purge leftover temp folders in /tmp and $TMPDIR. |
| Β | --clean-subdomains |
Enable subdomain cleaning using DOMAINS_TO_CLEAN.txt. |
| Β | --no-clean-subdomains |
Disable subdomain cleaning (default behavior). |
| Β | --clean-file=<path> |
Specify an alternative path for the domains cleanup list. |
| Β | --punycode, --idn |
Enable IDN Punycode conversion using GNU Libidn2 (idn2). Enabled by default. |
| Β | --no-punycode, --no-idn |
Disable Punycode conversion (skip non-ASCII Unicode translation). |
| Β | --cut-subdomains |
Collapse subdomains to their registered parent domain (aggressive mode). |
| Β | --no-cut-subdomains |
Keep distinct subdomains intact (default mode). |
# 1. Clean subdomains using default DOMAINS_TO_CLEAN.txt
bash trustpositif-validator.sh --clean-subdomains
# 2. Specify a custom target cleanup list with Punycode active
bash trustpositif-validator.sh --clean-file=/etc/trustpositif/custom_targets.txt --punycode
# 3. Emergency maintenance: kill stale processes and purge scratch folders
bash trustpositif-validator.sh --force-cleanup
Override any configuration parameter without editing code:
| Environment Variable | Default Value | Parameter Description |
|---|---|---|
OUTPUT_DIR |
/var/www/html/trustpositif |
Destination directory where the final validated blocklist is published. |
CLEAN_SUBDOMAINS |
0 |
Set to 1 to activate subdomain pruning via DOMAINS_TO_CLEAN_FILE. |
DOMAINS_TO_CLEAN_FILE |
DOMAINS_TO_CLEAN.txt |
Target file path containing domains whose subdomains must be stripped. |
USE_IDN2 |
1 |
Set to 0 to disable Punycode normalization via GNU Libidn2 (idn2). |
CUT_SUBDOMAINS |
0 |
Set to 1 to collapse subdomains to their registered parent domain. |
NUM_CORES |
Auto (4β32) | Worker thread count for GNU Parallel. |
CHUNK_SIZE |
20000 + (CORESΓ1000) |
Line count per chunk partition for parallel AWK processing. |
SORT_BUFFER |
Auto (128Mβ2G) | Memory allocated for sort -S. Converted to absolute MiB on BSD/macOS. |
AWK_CMD |
Auto (mawkβgawkβawk) |
Explicit path to an AWK binary. |
CURL_INSECURE |
1 |
Set to 0 to require valid TLS certificates on all download endpoints. |
DOWNLOAD_MAX_TIME |
300 |
Maximum network timeout in seconds for downloading each feed. |
DOWNLOAD_RETRY |
5 |
Retry attempts on network timeout or HTTP error. |
NO_COLOR |
Unset | Set to any value to disable ANSI colors (per no-color.org). |
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β HOST RESOURCE DISCOVERY ENGINE β
ββββββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββ
β
βββββββββββββββββ΄ββββββββββββββββ
βΌ βΌ
βββββββββββββββββ βββββββββββββββββ
β RAM < 2 GiB β β RAM β₯ 16 GiB β
βββββββββ¬ββββββββ βββββββββ¬ββββββββ
β β
βΌ βΌ
β’ NUM_CORES = 1 β’ NUM_CORES = Max CPU Cores (up to 32)
β’ SORT_BUFFER = 128M β’ SORT_BUFFER = 2 GiB
β’ Prevents OOM-killer β’ Maximum in-memory sort speed
DOMAINS_TO_CLEAN.txt)Large blocklists often contain thousands of redundant subdomains for targeted root domains (e.g. gambling, malware, phishing). Including all these subdomains bloats DNS RPZ zones without providing additional security.
TrustPositif Validator includes a specialized stream processing engine for subdomain cleanup:
The DOMAINS_TO_CLEAN.txt file supports all common formatting conventions:
# Plain format (one per line)
target1.com
target2.org
# Bash array format
DOMAINS_TO_CLEAN=(
"target3.com"
"target4.net"
)
The AWK tokenizer automatically ignores bash variable declarations, parentheses, quotes (" / '), and commas.
When subdomain cleaning is enabled:
Target Domain: evil-site.com
βββ evil-site.com <-- PRESERVED in blocklist (Apex domain rule)
βββ sub1.evil-site.com <-- STRIPPED (Redundant subdomain)
βββ api.evil-site.com <-- STRIPPED (Redundant subdomain)
βββ cdn.evil-site.com <-- STRIPPED (Redundant subdomain)
[!TIP] Why Apex Guard Matters: Keeping the root domain (
evil-site.com) in the blocklist ensures that DNS sinkholes and RPZ wildcard policies (*.evil-site.com CNAME .) continue to block all current and future subdomains, while saving megabytes of RAM on the nameserver.
idn2)The modern Internet supports internationalized domain names (IDNs) in regional alphabets (Arabic, Cyrillic, Chinese, Indonesian regional scripts, and accented Latin).
TrustPositif Validator implements an automated IDNA2008 conversion layer:
idn2 --quiet --no-tr46 (with fallback to idn).xn--...):
Π΄ΠΎΠΌΠ΅Π½.ΡΡ β xn--d1acufc.xn--p1aimΓΌnchen.de β xn--mnchen-3ya.dexn--p1ai) is validated against the official IANA IDN ccTLD database.The final validated output file is published to:
/var/www/html/trustpositif/domain-trustpositif_valid.txt
Below are production-ready deployment configurations:
BIND Configuration (/etc/bind/named.conf.local):
zone "rpz.trustpositif" {
type master;
file "/var/lib/bind/rpz.trustpositif.zone";
allow-query { localhost; 192.168.0.0/16; };
};
Automated RPZ Zone Compiler:
#!/usr/bin/env bash
INPUT="/var/www/html/trustpositif/domain-trustpositif_valid.txt"
ZONE="/var/lib/bind/rpz.trustpositif.zone"
cat <<'EOF' > "${ZONE}"
$TTL 300
@ IN SOA localhost. root.localhost. ( 2026100301 3600 600 604800 300 )
@ IN NS localhost.
EOF
awk '{
print $1 " CNAME ."
print "*." $1 " CNAME ."
}' "${INPUT}" >> "${ZONE}"
rndc reload rpz.trustpositif
Convert the validated output into Unbound local-zone redirection rules:
awk '{
print "local-zone: \"" $1 "\" always_nxdomain"
}' /var/www/html/trustpositif/domain-trustpositif_valid.txt > /etc/unbound/trustpositif.conf
unbound-control reload
http://localhost/trustpositif/domain-trustpositif_valid.txt.Rebuild gravity:
pihole -g
TrustPositif Komdigi and URL to:
http://127.0.0.1/trustpositif/domain-trustpositif_valid.txt
(or filesystem path: /var/www/html/trustpositif/domain-trustpositif_valid.txt).12 hours.Generate Dnsmasq redirection rules:
awk '{
print "address=/" $1 "/0.0.0.0"
}' /var/www/html/trustpositif/domain-trustpositif_valid.txt > /etc/dnsmasq.d/trustpositif.conf
systemctl restart dnsmasq
Export domains into a RouterOS DNS static import file:
awk '{
print "/ip dns static add name=" $1 " address=127.0.0.1 type=FWD comment=TrustPositif"
}' /var/www/html/trustpositif/domain-trustpositif_valid.txt | head -n 5000 > /tmp/mikrotik_dns.rsc
Update the blocklist nightly at 03:00 AM with log rotation and TTY protection:
# Add to /etc/crontab or crontab -e
0 3 * * * /usr/bin/env bash /root/TrustPositif-Validator/trustpositif-validator.sh --clean-subdomains >> /var/log/trustpositif-validator.log 2>&1
Conducted on an enterprise Linux server (AMD EPYC 7763, 8 vCPUs, 16 GiB RAM, NVMe SSD):
| Pipeline Stage | Elapsed Duration | Processing Metrics |
|---|---|---|
| IANA TLD Sync | 1.8 seconds | Ingests official TLD list into lookup hash |
| TrustPositif Feed Ingestion | 9.4 seconds | Multi-source download with HTTP retry engine |
| Adaptive Chunking | 1.1 seconds | Dynamically sized partition chunks |
| Parallel AWK + Punycode | 34.2 seconds | 1,480,000+ records processed across 8 workers |
| Subdomain Cleaning Engine | 0.28 seconds | AWK hash table stream prune |
| Global Sorting & Deduplication | 6.8 seconds | Memory-bounded sort -u |
| Atomic Output Swap | 0.05 seconds | Inode replacement (chmod 644) |
| Total Wall-Clock Time | ~53.6 seconds | ~43,200 domains / second |
| Peak Resident RAM (RSS) | ~94 MiB | Zero memory array bloat |
set -Eeuo pipefail and IFS=$'\n\t' to eliminate silent failures.OUTPUT_DIR before invoking mv -f, guaranteeing atomic inode updates across distinct mounts.0644): Guarantees that DNS daemons (BIND, Unbound) and Web servers (Nginx, Apache) can read the list regardless of restrictive parent umasks (027 / 077).EXIT, SIGINT (Ctrl+C), and SIGTERM, killing all spawned background jobs (jobs -pr) and scrubbing temporary directories.[[ -t 1 ]] before issuing terminal commands...), leading/trailing hyphens, and labels exceeding 63 octets.Run the built-in quality verification suite before deploying:
# 1. ShellCheck static analysis (zero warnings guarantee)
shellcheck trustpositif-validator.sh
# 2. Bash syntax check
bash -n trustpositif-validator.sh
# 3. Dry run test with custom temporary output
OUTPUT_DIR=/tmp/test_tp bash trustpositif-validator.sh --clean-subdomains
# 4. Verify output integrity
head -n 20 /tmp/test_tp/domain-trustpositif_valid.txt
wc -l /tmp/test_tp/domain-trustpositif_valid.txt
| Symptom | Probable Cause | Corrective Action |
|---|---|---|
| Script hangs or locks up | Zombie worker process from previous interrupted run | Execute bash trustpositif-validator.sh --force-cleanup to kill stale workers and purge /tmp. |
| Missing GNU Parallel | Package not installed | Run sudo apt install -y parallel or sudo dnf install -y parallel. |
idn2: command not found |
GNU Libidn2 missing | Run sudo apt install -y idn2 or sudo dnf install -y libidn2. IDN falls back to idn or raw domain if absent. |
| Download fails with SSL error | Upstream feed has outdated or self-signed cert | Ensure CURL_INSECURE=1 is set (default). |
| OOM on 512MB VPS | Sort buffer or chunk size too large | Run with CHUNK_SIZE=5000 SORT_BUFFER=64M bash trustpositif-validator.sh. |
| Permission denied on output | User lacks write permissions to /var/www/html |
Run with sudo or override output path: OUTPUT_DIR=$HOME/blocklists bash trustpositif-validator.sh. |
Detailed release history is documented in CHANGELOG.md.