xdp-dns-evadeproxy README

The project's README from github.com/Oihalitz/xdp-dns-evadeproxy. As one markdown file: README.md.

xdp.es

evade-proxy

Cuando un operador bloquea IPs de forma indiscriminada, esto se lo salta.

Proxy DNS del resolver público xdp.es

Usar el DNS · Estadísticas · Cómo funciona · IPs bloqueadas

Desarrollado por Oihalitz con mateodd1


Usar el DNS de xdp.es

No hace falta clonar este repo. Guía y copiar-y-pegar en xdp.es.

Adblock

publicidad y malware fuera · DNSSEC · zero-logs

Webhttps://xdp.es
DNS85.208.114.51   2a0e:97c0:c40::51
DoHhttps://dns.xdp.es/dns-query
DoTdns.xdp.es · puerto 853
DoQquic://dns.xdp.es:853
iOS / macOSperfil DoH · perfil DoT

Standard

sin adblock · la misma evasión · DNSSEC · zero-logs

Webhttps://xdp.es
DNS85.208.114.52   2a0e:97c0:c40::52
DoHhttps://lite.xdp.es/dns-query
DoTlite.xdp.es · puerto 853
DoQquic://lite.xdp.es:853
iOS / macOSperfil DoH · perfil DoT

Qué es esto

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í.

Cómo se lo salta

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)
  1. Unbound responde con la IP real (DNSSEC).
  2. Si un A, AAAA o hint HTTPS/SVCB está en la blocklist y en un prefijo Cloudflare, el proxy la cambia por un vecino del mismo prefijo que no esté listado, y pone TTL 30 s (EVADE_REWRITE_TTL; 0 restaura el comportamiento anterior, sin caché en el cliente).
  3. Fuera de Cloudflare no se toca nada. En CDNs no-anycast la sonda fija un 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.

Compilar

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.

Instalar el servidor

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.

Docker

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.

Lista de IPs bloqueadas

scripts/update-blocked-ips.sh, timer cada 15 s:

  1. Descarga el listado público IPv4 (BLOCKLIST_URL)
  2. Se queda solo con direcciones IPv4
  3. Une las IPs que confirmó la sonda
  4. Escribe /etc/unbound/blocked_ips.txt — evade-proxy lo recarga cada 5 s
  5. Une IPv6 de la sonda en /etc/unbound/blocked_ipv6.txt
  6. Actualiza prefijos Cloudflare AS13335 (una vez al día)

No hace falta vaciar la caché del resolver: el proxy reescribe a la salida, también las respuestas ya cacheadas.

Sonda residencial

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:

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.

Puertos

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

Layout

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

Oihalitz con mateodd1 · xdp.es · MIT

xdp-dns-evadeproxy (Oihalitz/xdp-dns-evadeproxy)

proxy-server
Searching Database…

What is this?A DNS proxy in Rust that sidesteps blanket ISP IP blocks: when an operator cuts off an anycast address shared by hundreds of Cloudflare sites, it rewrites that IP in the DNS response to an unblocked neighbor of the same prefix.
Is this something I install on my own server, or a hosted service I can point my devices at?Both. The authors run public resolvers — dns.xdp.es and lite.xdp.es — reachable by plain IP, DoH, DoT and DoQ on port 853, so you can just use them; this repo is the server side if you prefer to self-host.
What does running my own instance involve?Build with Rust 1.82+ and, on a Debian/Ubuntu server, run the install script as root: it puts the binary in /usr/local/bin/evade-proxy, the tree in /opt/xdp-dns-evadeproxy and starts the proxy plus the blocklist timer. A Docker compose stack is the alternative.
Besides the proxy itself, what else does a full setup need?The example stack pairs it with an unbound recursive resolver doing DNSSEC validation and a blocklist-updater loop. Full coverage of IPv6 and non-anycast CDNs also wants the residential probe reporting from a home connection.
Does the rewriting rescue every blocked site?No — only Cloudflare anycast IPs are rewritten, everything outside those prefixes is left untouched. Non-anycast CDNs like GitHub, Fastly or Akamai go through probe-verified redirects instead, and you can't run this alongside the older Python evadeproxy because they share ports.
Where does all of this actually run?The server side targets Debian/Ubuntu (Docker works too) and listens only on localhost: 5335 and 5337 for DNS, 5339 for metrics, 8090 for probe ingest. The residential probe is meant for a Raspberry Pi or mini-PC behind your home NAT, making outgoing connections only.
What happens to my queries on the hosted resolvers?The xdp.es resolvers advertise DNSSEC validation, zero logs and built-in ad and malware filtering. Self-hosting keeps it all on your own machine, with the proxy listening on localhost only.
What exactly gets changed in a DNS response?If an A, AAAA or HTTPS/SVCB hint is both on the blocklist and inside a Cloudflare prefix, the proxy swaps the IP for an unblocked neighbor of the same prefix and sets TTL 30 s — tunable via EVADEREWRITETTL, where 0 restores the old behavior. The rewrite is done in-place, preserving compression pointers, flags and record order.
Why is there a residential probe on top of the blocklist?Because a server in a datacenter cannot see what a subscriber line sees. A small box at home probes targets over TCP:443, TLS(SNI) and HTTP HEAD and reports back; the server flags a block only when a site serves fine from the datacenter but fails from home, with hysteresis of 3 rounds to add an IP and 2 to remove it.
Why rewrite IPs instead of just pointing at another resolver?Because the block hits the address, not the domain: any resolver still returns the same blocked anycast IP that hundreds of sites share. Swapping it for a live neighbor in the same Cloudflare prefix restores access for everything riding on that address.
How does it keep up when operators add or lift blocks?A script runs on a 15-second timer: it downloads the public blocked-IP list, keeps the IPv4 entries, merges whatever your home probe confirmed and writes /etc/unbound/blockedips.txt, which the proxy reloads every 5 s. Cloudflare prefix data refreshes once a day.
Creator
Oihalitz
Size
0.2 MB
Stars
131
Updated
CommitOct 15
Age
1mo
Commits · 90 Days