Proxy DNS en Rust para evitar bloqueos de operadores mediante reescritura transparente de IPs anycast.
93
stars
21
commits
Rust
primary language
Sep 6, 2026
updated
Cuando un operador bloquea IPs de forma indiscriminada, esto se lo salta.
Proxy DNS del resolver público xdp.es
No hace falta clonar este repo. Guía y copiar-y-pegar en xdp.es.
publicidad y malware fuera · DNSSEC · zero-logs
| Web | https://xdp.es |
| DNS | 85.208.114.51 2a0e:97c0:c40::51 |
| DoH | https://dns.xdp.es/dns-query |
| DoT | dns.xdp.es · puerto 853 |
| DoQ | quic://dns.xdp.es:853 |
| iOS / macOS | perfil DoH · perfil DoT |
sin adblock · la misma evasión · DNSSEC · zero-logs
| Web | https://xdp.es |
| DNS | 85.208.114.52 2a0e:97c0:c40::52 |
| DoH | https://lite.xdp.es/dns-query |
| DoT | lite.xdp.es · puerto 853 |
| DoQ | quic://lite.xdp.es:853 |
| iOS / macOS | perfil DoH · perfil DoT |
A veces un operador no bloquea un dominio: bloquea la IP. En Cloudflare anycast esa misma dirección la comparten cientos de sitios, así que el corte es indiscriminado — cae lo que tocaba y lo que no. evade-proxy detecta esas IPs y, en la respuesta DNS, las sustituye por otra del mismo prefijo que no está cortada.
Tres piezas:
| evade-proxy | Proxy DNS en Rust. Reescribe en el paquete las IPs anycast bloqueadas. |
| Lista de IPs | Cada pocos segundos actualiza qué direcciones están cortadas y se lo pasa al proxy. |
| Sonda residencial | Un equipo en casa confirma IPv6 y CDNs que no son anycast. El servidor en datacenter no ve el bloqueo; la línea del abonado sí. |
cliente → Blocky / DoH / DoT → evade-proxy :5335 → Unbound :5336
│
├─ blocked_ips.txt ← lista pública + sonda IPv4
├─ blocked_ipv6.txt ← sonda IPv6
├─ cloudflare_prefixes ← AS13335
└─ redirects.txt ← sonda (GitHub / Fastly / Akamai)
EVADE_REWRITE_TTL; 0 restaura el comportamiento anterior, sin caché en el cliente).redirect a una IP verificada desde casa y desde el servidor.La reescritura es in-place: punteros de compresión, flags y orden de registros se conservan.
Rust estable (1.82+):
cargo test --locked
cargo build --release --locked
./target/release/evade-proxy --help
Modo prueba (convive con producción: :15335 / :15337, métricas :15339):
./target/release/evade-proxy --test \
--redirect example.com=203.0.113.7
--redirect solo se acepta con --test.
Debian/Ubuntu, como root, desde el clone:
sudo ./scripts/install.sh
Instala el binario en /usr/local/bin/evade-proxy, el árbol en /opt/xdp-dns-evadeproxy, escribe /etc/evade-proxy.env y arranca el proxy + el timer de la lista de IPs.
sudo editor /etc/evade-proxy.env # PROBE_TOKEN=…
sudo systemctl enable --now xdp-probe-server
Exponer el ingest de la sonda con TLS (Caddy):
handle /probe/* {
reverse_proxy 127.0.0.1:8090
}
No ejecutar a la vez que un evade_proxy en Python: mismos puertos.
Alternativa a install.sh para quien prefiera contenedores. El stack de ejemplo
levanta evade-proxy, un unbound recursivo con validación DNSSEC (su upstream
en 127.0.0.1:5336/:5338) y el bucle que sincroniza blocklist + prefijos
Cloudflare:
cp evade-proxy.env.example evade-proxy.env # PROBE_TOKEN=… si usas la sonda
docker compose up -d --build
unbound y blocklist-updater se unen al network namespace de
evade-proxy (network_mode: "service:evade-proxy") porque el proxy siempre
resuelve su upstream en 127.0.0.1 (ver tabla de puertos más abajo) — no hay
forma de apuntarlo a otro contenedor por nombre de servicio.
Para ponerlo delante de un servidor DNS que ya use network_mode: host en el
mismo host (p. ej. AdGuard Home), añade network_mode: host al servicio
evade-proxy, quita su bloque ports:, y apunta el DNS upstream de ese
servidor a 127.0.0.1:5335.
scripts/update-blocked-ips.sh, timer cada 15 s:
BLOCKLIST_URL)/etc/unbound/blocked_ips.txt — evade-proxy lo recarga cada 5 s/etc/unbound/blocked_ipv6.txtNo hace falta vaciar la caché del resolver: el proxy reescribe a la salida, también las respuestas ya cacheadas.
Cubre IPv6 y CDNs no-anycast. Pide objetivos, sondea TCP:443 + TLS(SNI) + HTTP HEAD y reporta. El servidor solo marca bloqueo si sirve en el datacenter y no en casa, con histéresis (3 rondas para añadir, 2 para quitar).
En probe/domains.json:
cf-blocklist — Cloudflare. La IP entra en la blocklist; el proxy salta al vecino.verified-pool — GitHub / Fastly / Akamai. Se fija dominio=IP_sana en redirects.txt.En casa (Raspberry Pi / mini-PC):
sudo ./probe/agent/install-agent.sh https://dns.xdp.es/probe <TOKEN> mi-casa
Solo conexiones salientes (funciona detrás del NAT). Detalle: probe/README.md.
| Escucha | Upstream | Uso |
|---|---|---|
127.0.0.1:5335 UDP/TCP | :5336 | camino principal |
127.0.0.1:5337 UDP/TCP | :5338 | segundo camino (Lite) |
127.0.0.1:5339 HTTP | — | /metrics y stats JSON |
127.0.0.1:8090 HTTP | — | ingest de la sonda |
Variables: evade-proxy.env.example (EVADE_REWRITE_TTL incluido). Redirects temporales de producción (/run/evade-proxy/redirects.txt):
ejemplo.es=104.18.13.102 1787609000
src/main.rs evade-proxy
scripts/update-blocked-ips.sh lista de IPs + sonda
scripts/update-cloudflare-prefixes.py AS13335
probe/probe-server.py ingest + diferencial
probe/domains.json objetivos
probe/agent/ sonda de casa
systemd/ unidades
Rust
55.1%
Python
31.1%
Shell
11.2%
Dockerfile
2.7%
Proxy DNS en Rust para evitar bloqueos de operadores mediante reescritura transparente de IPs anycast.
93
stars
21
commits
Rust
primary language
Sep 6, 2026
updated
Cuando un operador bloquea IPs de forma indiscriminada, esto se lo salta.
Proxy DNS del resolver público xdp.es
No hace falta clonar este repo. Guía y copiar-y-pegar en xdp.es.
publicidad y malware fuera · DNSSEC · zero-logs
| Web | https://xdp.es |
| DNS | 85.208.114.51 2a0e:97c0:c40::51 |
| DoH | https://dns.xdp.es/dns-query |
| DoT | dns.xdp.es · puerto 853 |
| DoQ | quic://dns.xdp.es:853 |
| iOS / macOS | perfil DoH · perfil DoT |
sin adblock · la misma evasión · DNSSEC · zero-logs
| Web | https://xdp.es |
| DNS | 85.208.114.52 2a0e:97c0:c40::52 |
| DoH | https://lite.xdp.es/dns-query |
| DoT | lite.xdp.es · puerto 853 |
| DoQ | quic://lite.xdp.es:853 |
| iOS / macOS | perfil DoH · perfil DoT |
A veces un operador no bloquea un dominio: bloquea la IP. En Cloudflare anycast esa misma dirección la comparten cientos de sitios, así que el corte es indiscriminado — cae lo que tocaba y lo que no. evade-proxy detecta esas IPs y, en la respuesta DNS, las sustituye por otra del mismo prefijo que no está cortada.
Tres piezas:
| evade-proxy | Proxy DNS en Rust. Reescribe en el paquete las IPs anycast bloqueadas. |
| Lista de IPs | Cada pocos segundos actualiza qué direcciones están cortadas y se lo pasa al proxy. |
| Sonda residencial | Un equipo en casa confirma IPv6 y CDNs que no son anycast. El servidor en datacenter no ve el bloqueo; la línea del abonado sí. |
cliente → Blocky / DoH / DoT → evade-proxy :5335 → Unbound :5336
│
├─ blocked_ips.txt ← lista pública + sonda IPv4
├─ blocked_ipv6.txt ← sonda IPv6
├─ cloudflare_prefixes ← AS13335
└─ redirects.txt ← sonda (GitHub / Fastly / Akamai)
EVADE_REWRITE_TTL; 0 restaura el comportamiento anterior, sin caché en el cliente).redirect a una IP verificada desde casa y desde el servidor.La reescritura es in-place: punteros de compresión, flags y orden de registros se conservan.
Rust estable (1.82+):
cargo test --locked
cargo build --release --locked
./target/release/evade-proxy --help
Modo prueba (convive con producción: :15335 / :15337, métricas :15339):
./target/release/evade-proxy --test \
--redirect example.com=203.0.113.7
--redirect solo se acepta con --test.
Debian/Ubuntu, como root, desde el clone:
sudo ./scripts/install.sh
Instala el binario en /usr/local/bin/evade-proxy, el árbol en /opt/xdp-dns-evadeproxy, escribe /etc/evade-proxy.env y arranca el proxy + el timer de la lista de IPs.
sudo editor /etc/evade-proxy.env # PROBE_TOKEN=…
sudo systemctl enable --now xdp-probe-server
Exponer el ingest de la sonda con TLS (Caddy):
handle /probe/* {
reverse_proxy 127.0.0.1:8090
}
No ejecutar a la vez que un evade_proxy en Python: mismos puertos.
Alternativa a install.sh para quien prefiera contenedores. El stack de ejemplo
levanta evade-proxy, un unbound recursivo con validación DNSSEC (su upstream
en 127.0.0.1:5336/:5338) y el bucle que sincroniza blocklist + prefijos
Cloudflare:
cp evade-proxy.env.example evade-proxy.env # PROBE_TOKEN=… si usas la sonda
docker compose up -d --build
unbound y blocklist-updater se unen al network namespace de
evade-proxy (network_mode: "service:evade-proxy") porque el proxy siempre
resuelve su upstream en 127.0.0.1 (ver tabla de puertos más abajo) — no hay
forma de apuntarlo a otro contenedor por nombre de servicio.
Para ponerlo delante de un servidor DNS que ya use network_mode: host en el
mismo host (p. ej. AdGuard Home), añade network_mode: host al servicio
evade-proxy, quita su bloque ports:, y apunta el DNS upstream de ese
servidor a 127.0.0.1:5335.
scripts/update-blocked-ips.sh, timer cada 15 s:
BLOCKLIST_URL)/etc/unbound/blocked_ips.txt — evade-proxy lo recarga cada 5 s/etc/unbound/blocked_ipv6.txtNo hace falta vaciar la caché del resolver: el proxy reescribe a la salida, también las respuestas ya cacheadas.
Cubre IPv6 y CDNs no-anycast. Pide objetivos, sondea TCP:443 + TLS(SNI) + HTTP HEAD y reporta. El servidor solo marca bloqueo si sirve en el datacenter y no en casa, con histéresis (3 rondas para añadir, 2 para quitar).
En probe/domains.json:
cf-blocklist — Cloudflare. La IP entra en la blocklist; el proxy salta al vecino.verified-pool — GitHub / Fastly / Akamai. Se fija dominio=IP_sana en redirects.txt.En casa (Raspberry Pi / mini-PC):
sudo ./probe/agent/install-agent.sh https://dns.xdp.es/probe <TOKEN> mi-casa
Solo conexiones salientes (funciona detrás del NAT). Detalle: probe/README.md.
| Escucha | Upstream | Uso |
|---|---|---|
127.0.0.1:5335 UDP/TCP | :5336 | camino principal |
127.0.0.1:5337 UDP/TCP | :5338 | segundo camino (Lite) |
127.0.0.1:5339 HTTP | — | /metrics y stats JSON |
127.0.0.1:8090 HTTP | — | ingest de la sonda |
Variables: evade-proxy.env.example (EVADE_REWRITE_TTL incluido). Redirects temporales de producción (/run/evade-proxy/redirects.txt):
ejemplo.es=104.18.13.102 1787609000
src/main.rs evade-proxy
scripts/update-blocked-ips.sh lista de IPs + sonda
scripts/update-cloudflare-prefixes.py AS13335
probe/probe-server.py ingest + diferencial
probe/domains.json objetivos
probe/agent/ sonda de casa
systemd/ unidades
Rust
55.1%
Python
31.1%
Shell
11.2%
Dockerfile
2.7%