We found some anomalies
Something has been going on with DNS in Russia, and it’s been going on for a while.
For example, on September 8, 2021, Roskomnadzor (Russia’s federal media and communications regulator) blocked the public DNS resolvers 1.1.1.1 and 8.8.8.8 for an hour, from 21:00 to 22:00 Moscow time, at several major carriers using TSPU (technical means for combating threats). Independent researchers documented this fact. A few days later, telecom provider Rostelecom circulated an internal proposal to make this block permanent. In October 2024, Cloudflare enabled the TLS ECH extension by default, which hides the destination domain (SNI) during connection setup. Starting November 5, 2024, researchers documented blocking of TLS sessions carrying this signature, not through a TCP RST but through a silent packet drop after detection, which forces the browser to fall back to a regular, unencrypted connection.
We decided to test how different DNS resolvers behave in Russia across different resources, and what role TSPU plays in restricting them.
Our measurements
Filtering nodes
To isolate the filtering nodes (TSPU), we deployed our measurement infrastructure across five independent vantage points:
- Reference node (Spain, residential broadband).
- Reference node (Germany, Tier-1 backbone level).
- Measurement nodes inside Russia (Moscow and Saint Petersburg, Tier-2/Tier-3 data center level).
- Measurement node inside Russia (Saint Petersburg, residential end-user broadband level).
Domains
We queried resolvers across several groups of domains:
- Blocked messengers: whatsapp.com, telegram.org, signal.org.
- Blocked media outlets: meduza.io, bbc.com, novayagazeta.eu.
- Technical resources: protonvpn.com, te-st.org, github.com.
- Reference foreign domains (not blocked): microsoft.com, nvidia.com.
- Degraded: youtube.com, apple.com.
- Local Russian (working): ya.ru, vk.ru.
- CDN infrastructure: cdnjs.cloudflare.com (Cloudflare), fastly.net, ajax.googleapis.com (Google), cdn.jsdelivr.net (goal: detect hosting-provider subnet blocks at the L3/L4 level, as opposed to targeted L7 filtering.)
DNS servers (resolvers)
- BigTech & public resolvers: Cloudflare, Google, Quad9, Cisco Umbrella, OpenDNS.
- Specialized and private DoH/DoT: NextDNS, Control D, AdGuard DNS, Mullvad DNS, LibreDNS.
- Russian: Yandex DNS, NSDI (National System of Domain Names), included not as “external” DNS under test but as a reference point for how a DNS resolver known to operate under Russian jurisdiction responds.
Measurement tools
- kdig: for queries over plain DNS, DoT, DoH, and DoQ;
- dig: for manual control of the EDNS Client Subnet field when testing the geographic-routing hypothesis;
- curl: to check directly what a specific IP address returns over HTTPS with a given SNI, independent of the resolver and its response.
Our targets
- L3/L4: IP blackholing of known resolvers’ subnets, blanket blocking of port 853, and injection of forged TCP RST packets to reset connections during session setup. (Port 853 is shared by DoT and DoQ; comparing how these two protocols behave on the same port lets us tell whether the port gets cut off entirely or the filter distinguishes a specific protocol/signature within it.)
- Active intercept: spoofing of A/AAAA responses by TSPU servers (DNS hijacking). To verify the interception point and prove that spoofing happens specifically at TSPU, we used network route tracing.
A “corrupted” DNS answer in this research can arise from one of two distinct causes, and we can’t lump them together in one table:
- In-path interception (TSPU). Modification or dropping of traffic along the route between the client and the resolver. This only occurs if the request physically passes through a Russian carrier’s network. Signature: the result changes depending on the observation point for the same resolver (a Russian node sees a spoofed/dropped response, while a foreign node querying the same resolver gets a genuine answer).
- Resolver-side compliance. The resolver operator itself builds a blocklist into its own response logic on its servers. Signature: the result is identical regardless of the client’s geolocation, because what gets filtered isn’t the route but the response point itself.
Telling these two cases apart matters, because each requires a different countermeasure. A VPN or traffic obfuscation solves the first case, but is powerless against the second: that one requires switching your DNS provider instead.
Our findings
We found no classic blocking, connection resets, or unreachable subnets at our test nodes. What we did find was an unexplained DNS response substitution at some resolvers for some domains. We covered these addresses in an earlier piece.
At the time, we called these addresses “RKN stubs.” But a closer look showed that description wasn’t quite accurate. These addresses serve real, working Cloudflare content, not an empty blackhole response. We verified this directly with curl, using an explicit SNI for signal.org through the address 8.47.69.6, and not just from a VPS but also over an ordinary home internet connection in Russia.
How we told “noise” from real anomalies
A first pass through the logs showed that for some domains, the response at the Russian node differed from the response at every foreign node we checked. Before interpreting anything, we had to clean this sample of noise; otherwise, the “repeated IP where it shouldn’t be” method produces false positives on any anycast/CDN service.
An important methodological observation: stability and high repeatability alone aren’t signs of interference. For example, whatsapp.com and youtube.com produce reproducible, node-dependent responses with 100% repeatability, and that’s exactly what you’d expect from ordinary geographic load balancing at the providers themselves, not from spoofing. The only way to tell an “anomaly” from “noise” is to compare against a control group. If the same thing happens with nvidia.com or microsoft.com, which aren’t currently blocked and show no signs of being blocked, the cause is probably not censorship.
After subtracting the “noise,” one notable family of responses remains in the data: 8.47.69.N / 8.6.112.N, where N is 0, 6, or 8. According to ARIN, 8.6.112.0/24 is registered to Cloudflare, Inc. (origin AS13335). That said, this network isn’t part of Cloudflare’s published IP Ranges list, which is meant for configuring allowlists on origin servers and doesn’t cover the company’s entire internal/product address space. In other words, finding an address outside the public list doesn’t mean the address belongs to someone else.
Signs of spoofing
We used a simple principle to detect spoofing automatically: if the same IP response repeats across several domains that have no shared physical infrastructure (for example, signal.org, which runs on Signal’s own servers, versus meduza.io and te-st.org, which sit behind Cloudflare), that’s statistically anomalous and points to a common source of spoofing rather than coincidence.
This method doesn’t rely on external IP-ownership databases and makes no assumptions about “correct” addresses in advance. It simply looks for repeats where none should exist, which lets us find anomalies without knowing in advance exactly what to look for. We manually checked whether each match we found was legitimate. Domains behind the same CDN provider can legitimately share an IP, and we counted those cases as coincidence, not spoofing. What helped us tell one from the other was looking at exactly which domains ended up grouped together.
How we ruled out AITM
A TLS certificate check showed that the connection to 8.8.8.8/1.1.1.1 is established with genuine Google/Cloudflare infrastructure (the certificates are valid and issued by legitimate CAs, Google Trust Services and SSL.com respectively), which rules out a classic AITM attack with a spoofed certificate on the path to the resolver itself. Route tracing confirms that traffic from the Russian nodes does in fact reach these providers’ own networks (AS15169 for Google), rather than being redirected to a third-party address.
This leads to the conclusion: the anomalous response at addresses 8.47.69.x/8.6.112.x isn’t the result of traffic interception along the route between the client and the Google/Cloudflare DNS; the connection to the resolver itself is genuine. If spoofing is happening at all, it’s built into the resolver’s own response logic (see the classification in section 2.4), or it’s legitimate infrastructure behavior that merely looks like spoofing from the outside.
The response looks like genuine Cloudflare
Cloudflare’s addressing scheme for domains under its protection uses two A records in two adjacent /24 blocks, sharing the same last octet:
signal.org 104.18.10.47 , 104.18.11.47 → octet 47
meduza.io 104.18.0.79 , 104.18.1.79 → octet 79
cdnjs.cloudflare.com 104.17.24.14 , 104.17.25.14 → octet 14
аномалия (класс B/C) 8.47.69.0 , 8.6.112.0 → octet 0
аномалия (класс A) 8.47.69.6 , 8.6.112.6 → octet 6
аномалия (AdGuard) 8.47.69.8 , 8.6.112.8 → octet 8
In all three variants, the anomalous response reproduces Cloudflare’s internal convention (paired /24 blocks with a shared last octet), rather than looking like the arbitrary IP substitution typical of an on-path injector (in such cases, TSPU usually returns a single fixed stub IP with no structure like this). This is an argument in favor of the response originating from Cloudflare’s own infrastructure rather than a third-party interceptor, but it isn’t conclusive proof.
Three different scenarios
Class A. A differential signal, blocked domains only, regardless of the encryption protocol
| resolver | nodes | protocols | response | blocked domains hit | control domains hit |
| RU-msk, RU-spb, EU-vpn-SPB | udp_53, dot_853, doh_443 | 8.47.69.6, 8.6.112.6 | 4 | 0 | |
| AdGuard | RU-spb | udp_53, dot_853, doq_853, doh_443 | 8.47.69.8, 8.6.112.8 | 4 | 0 |
Repeatability within class A ranges from 53.3% to 100%, reaching 100% for Google on RU-spb/doh_443. The key detail: for Google, this response shows up on RU-msk, RU-spb, and EU-vpn-spb (a VPN exit through home broadband in Saint Petersburg) and doesn’t show up on any node without a Russian route, meaning the effect depends specifically on the route, not on DNS as such. At the same time, cdnjs.cloudflare.com, included in the same test matrix, shows no change; only the blocked set is affected. This is the only class where the differential test (a reaction limited to blocked domains, with a clean control group) passes completely.
The DoT and DoH data matter here on their own: the 8.47.69.6 response also arrives over channels where the request content is encrypted (DoT/DoH). Inside a TLS session, TSPU can’t spoof a DNS answer without breaking the session (that would cause a validation error on the client, not a silent IP substitution), which means this particular response gets generated by whoever holds the session’s private key: Google’s own resolver (or whatever answers as the source for that resolver). This narrows the possible causes of the class A anomaly down to Google/Cloudflare actions responding to the request’s route. But why cdnjs.cloudflare.com would be excluded under this same logic remains an open question.
Class B. Resolver-side, the response doesn’t depend on the observing node, but it hits a control domain
Yandex and NSDI return this same response, 8.47.69.0, 8.6.112.0, from every measurement node. This affects 4 blocked domains as well as cdnjs.cloudflare.com, which isn’t on any blocklist. Repeatability ranges from 53.3% to 100% (maxing out at 100% for NSDI/FI-VPS).
The lack of route dependence here is expected and explainable at the infrastructure level. Yandex and NSDI always query their servers from within Russia, regardless of where you’re asking them from, so “the same answer from any node” doesn’t yet distinguish resolver-side censorship from a resolver-side quirk that has nothing to do with censorship (say, ECS/anycast routing at the upstream server itself). The one thing that doesn’t fit the neutral explanation is the involvement of cdnjs.cloudflare.com, a domain that isn’t blocked.
The “RKN list” hypothesis isn’t confirmed. This may be an infrastructure quirk in how Cloudflare responds to upstream queries from Russia. An important caveat: we haven’t yet directly tested whether Yandex’s/NSDI’s upstream server behaves the same way Cloudflare does.
Class C. Russia-specific, route-dependent, but hits the same control domain
Cloudflare (RU-msk, all protocols including DoH/DoT) and NextDNS (RU-msk + RU-spb, all 4 protocols) return that same 8.47.69.0, 8.6.112.0 only from the Russian nodes, and cdnjs.cloudflare.com gets hit again. Repeatability for Cloudflare/RU-msk is 100% across all three protocols.
Formally, class C is closer to our methodology’s “signature of in-path interception” (the result changes depending on the observation point), but the structure of the response and the involvement of DoH/DoT make the in-path injection theory unlikely, for the same reason as in class A. TSPU can’t modify an encrypted response without the client noticing. A more likely explanation is that geographic routing of the request to the upstream server (via EDNS Client Subnet or an anycast node) produces the same result as with Yandex and NSDI, but only shows up when the client’s address is Russian, not when only the resolver’s address is Russian.
What it means
- Class A (Google, AdGuard). The differential test passes completely, route dependence is confirmed, and the response arrives over encrypted transports too. The most likely explanation is logic on the resolver side, or at whatever source answers on behalf of the resolver, that depends on the request’s route rather than in-path injection.
- Class B (Yandex, NSDI). Reproduces Cloudflare’s addressing structure, but it affects cdnjs.cloudflare.com, which contradicts the Roskomnadzor-list theory. We haven’t yet had a chance to check whether these resolvers’ responses depend on the ECS field the same way Cloudflare’s do.
- Class C (Cloudflare, NextDNS). The same response, but only from Russian nodes, and it affects cdnjs.cloudflare.com too. A direct test showed that changing the declared subnet (Russian versus German) doesn’t change the response, so the cause isn’t the ECS content but the specific network path from the node to Cloudflare. For Cloudflare, the anomaly only shows up at the Moscow node. This differs from class A, where the anomaly hits both Russian nodes equally. Google and Cloudflare most likely handle this differently, and the two aren’t related.
What we haven’t studied yet
- Protocol coverage is uneven. NSDI doesn’t support encrypted transports (NOT_SUPPORTED for DoH/DoT/DoQ); comparisons involving NSDI are valid only for udp_53. Yandex’s DoH returns a 100% timeout rate, and DoQ doesn’t work either; comparisons involving Yandex are valid for udp_53 and dot_853, but not for DoH/DoQ.
- We only partly understand why spoofing happens specifically for these domains. For Cloudflare (class C), we identified the cause in section 3.7. We haven’t checked whether Google, AdGuard, Yandex, and NSDI show the same insensitivity to ECS content, so why they select the domains they do remains unclear.
- cdnjs.cloudflare.com remains an unexplained inconsistency across classes. If a single common cause were behind all this, the control domain’s involvement should be either constant or absent everywhere; instead, it depends on which DNS is used. This is the one finding that doesn’t fit either the “pure censorship” theory or the “pure infrastructure quirk” theory. Explaining it is a question for further research.
So what’s the bottom line? Is there blocking?
We still don’t know, and we’re continuing to dig. The three classes of anomaly we found each have a different explanation, and we haven’t found one explanation that covers all three: for Google, the effect hits both Russian nodes equally regardless of encryption protocol. For Cloudflare as a resolver, it hits only one of the two Russian nodes, and changing the client’s declared subnet doesn’t change the response.
The content at the substituted address turned out to be genuine and complete. That said, loading the same page normally in a browser, over the same home connection (without manually specifying the IP), was slow and lost some images and layout, so something is interfering with the site’s operation, just not completely and not for every request.
It looks like some requests don’t go through during a normal page load, even though the main address and main document load in full. We can’t say with 100% certainty whether this shows signs of blocking. We’ve written before about similar behavior (blocking of the genuine address at the SNI level), and the anomaly we found here backs up that earlier piece.
Decentralized systems (OpenNIC, Namecoin, Emercoin, GNU Name System), alternative protocols (DNSCrypt, ODoH), and lesser-known regional resolvers fall outside the scope of this study; they require different tooling and a separate investigation.
Technical recommendations
- Self-hosted DoH via a reverse proxy. Route requests through your own VPS. Traffic gets proxied through a web server (Nginx or Caddy) on port 443, with a legitimate TLS certificate, to a locally running dnscrypt-proxy. For TSPU’s signature analysis, this connection is indistinguishable from ordinary web browsing.
- Alternative protocols (ODoH). Deploy Oblivious DoH. This technology uses an intermediary proxy server to guarantee cryptographic separation: the proxy knows the client’s IP address but never sees the DNS request itself, while the final resolver sees the request but doesn’t know the client’s IP address.
- Traffic encapsulation (XTLS/obfuscation). Wrap system DNS requests (port 53) inside tunnels disguised as a TLS 1.3 handshake (VLESS+Reality protocols). This hides not just the destination domain from DPI but the DNS protocol’s own fingerprint (ALPN), making L7 filtering impossible.
- Local resolvers and split tunneling. To work around geo-blocks on Russian resources (banks, government services) while simultaneously bypassing censorship, we recommend using dnsmasq on home routers (OpenWrt, Keenetic). Direct requests to the .ru zone to go through the provider’s own DNS, and route everything else through an encrypted local tunnel (DoH/DNSCrypt).
- Drop plain DNS. Basic requests over port 53 are intercepted by TSPU for response spoofing (Layer 3). Configuring DoH (DNS over HTTPS) in modern browsers, or at the OS level, is the minimal baseline step. It doesn’t hide the SNI, but it does rule out getting served stub IPs.