DNS over TLS blocked in Iran

DNS over TLS (DoT) is a network protocol that allows one to use DNS over TLS (i.e. with encryption and authentication of the remote DNS server).

We investigated whether DoT works in Iran by gathering a list of 31 well-known DoT endpoints and running experiments from four distinct Iranian mobile and fixed-line Internet Service Providers (ISPs): MCI, TCI, Irancell, and Shatel.

We discovered that:

In this report, we share details from our experiments and findings.

Index

Introduction

Experiment Description

Analysis of Results

Conclusion

Introduction

DNS over TLS (DoT) uses port 853/tcp. In some cases, the DoT endpoint contains an IP address. For example, Cloudflare’s public DoT server uses the 1.1.1.1:853 endpoint. In this case, the TLS ClientHello message uses 1.1.1.1 as the SNI and the corresponding X.509 certificate, of course, lists 1.1.1.1 as a valid IP address for the domain. In other cases, the DoT endpoint contains a domain name. For example, Cloudflare’s public DoT server is also available using the one.one.one.one:853 endpoint and the one.one.one.one SNI.

When the endpoint contains a domain name, another DNS resolver is required to get the IP address for that domain. Such a resolver is typically the system resolver, which is the resolver used by default by the operating system. Unless the user changes the operating system configuration, such a resolver is usually the one advertised by the ISP using DHCP.

The following diagram illustrates what happens when you use the system resolver to lookup the IP addresses for the one.one.one.one domain name. In most cases, you will get two IPv4 addresses: 1.1.1.1 and 1.0.0.1 (as well as, possibly, IPv6 addresses), as illustrated below.

System resolver
Figure 1: Resolving DoT domain name using the system resolver.

Once you know the IP address or addresses for a domain, you run the same flow you would run if given a DoT endpoint containing an IP address rather than a domain name. That is, you connect to the TCP endpoint, establish a TLS connection, and use it to send DNS queries and receive DNS replies. This is illustrated by the following diagram.

DoT resolver
Figure 2: Establishing TLS connection with DoT server.

A censor that wishes to block DoT could thus act in two ways.

Firstly, it could censor the system resolver so that you cannot resolve, for example, one.one.one.one.

Secondly, it could prevent users from connecting to 1.1.1.1:853 and performing a TLS handshake with the correct SNI (one.one.one.one in this example). Of course, because a specific DoT server typically works as intended if you know the right IP address and SNI for the TLS handshake, it means that typically a censor needs to be concerned with preventing you from accessing the TLS endpoint on port 853, which cannot be done with DNS blocking alone.

Therefore, to investigate how DoT works in Iran, we checked whether we could resolve the DoT endpoint domain name (where available), whether we could perform a TLS handshake with the selected endpoint, and whether we could successfully use it to resolve domains.

In the rest of this report, we describe our experiment, discuss the results (providing a high level overview and investigating specific aspects, such as DNS based blocking and TLS blocking), and we share the conclusions.

Experiment Description

We prepared a list of 31 well-known DoT endpoints. We compiled miniooni from ooni/probe-engine@f3594e5a and tested each of these endpoints. We ran this experiment on 29th and 30th May, 2020.

We used the -O DNSCache="<domain> <ip>" option of miniooni to ensure we were checking the first two IP addresses associated with an endpoint, rather than just the first IP.

Because we were testing DoT, we didn’t bother with resolving specific domain names. We always queried for example.com, under the assumption that either DoT works for every domain, because we can query it, or it does not work for any domain, because it’s blocked.

The general command that we ran for a given $endpoint was like:

miniooni $extra_options -O ResolverURL=dot://$endpoint        \
                        -i dnslookup://example.com urlgetter

We also repeated some experiments forcing miniooni to use TLSv1.3, to check whether forcing the latest version of TLS would make a difference. (The default is instead that of using the version of TLS between v1.0 and v1.3 chosen by the server.)

We ran the following number of measurements in the following networks:

     ASN Network nameAccess link#Measurements
 AS24940Hetzner (VPN)        N/A          116
 AS44244     Irancell     mobile          116
AS197207          MCI     mobile          116
 AS58224          TCI fixed-line          116
 AS31549       Shatel  fixed-line           82

Hetzner is an Internet hosting company where we were running a VPN server; we used it as a control vantage point to compare other measurements to. Irancell is an Iranian TLC that operates a large mobile network in the country. MCI is the largest mobile operator. TCI is the incumbent fixed-line operator of Iran. Shatel is another large Iranian ISP, which provides both ADSL and wireless lines. We ran tests on Shatel using an ADSL connection.

Analysis of Results

We tested 31 DoT endpoints. One of them was not working on any network and its DNS record also appeared to be broken from Italy, so we excluded it. 13 endpoints out of 30 (i.e. 43% of them) worked on all the tested ISPs. Therefore, 57% of the tested endpoints were blocked.

The following table shows the frequency of measurement errors for all ISPs. The Failure column indicates the overall measurement failure, and the Frequency column indicates how many times such failure occurred. (The failures listed for the Hetzner VPN are respectively caused by experimental errors as well as by the aforementioned broken domain, as we will explain soon.)

 Network              Failure  Frequency
 Hetzner   dns_nxdomain_error            2
 Hetzner   unknown_failure…           34
 Hetzner                 null           80
Irancell   dns_nxdomain_error            2
Irancellgeneric_timeout_error           30
Irancell   unknown_failure…           34
Irancell                 null           50
     MCI   dns_nxdomain_error            2
     MCIgeneric_timeout_error           31
     MCI   unknown_failure…           34
     MCI                 null           49
     TCI            eof_error            8
     TCIgeneric_timeout_error           18
     TCI   unknown_failure…           34
     TCI                 null           56
  Shatel    dns_nxdomain_error            2
  Shatel             eof_error            5
  Shatel generic_timeout_error           27
  Shatel                  null           48

The failures indicated above have the following meaning:

The dns_nxdomain_error failure is, of course, clearly DNS related. This is the aforementioned endpoint that we could not resolve even from Italy. So we excluded it.

We classified the remaining errors and we attempted to correlate the overall failure with the first failure occurring during the TLS handshake. The following table shows the results. The MeasurementFailure column indicates the overall failure of a measurement. The set(TLSFailures) column indicates the set of errors observed during TLS handshakes. If such column value is null it means there was no TLS handshake; if it is [null] it means instead that all TLS handshakes succeeded. The Frequency column indicates how many times a specific set of TLS failures was observed along with a specific overall measurement failure.

 Network   MeasurementFailure                  set(TLSFailures)Frequency
Irancellgeneric_timeout_error           [generic_timeout_error]       30
     MCIgeneric_timeout_error           [generic_timeout_error]       30
     MCIgeneric_timeout_error                            [null]        1
     TCIgeneric_timeout_error           [generic_timeout_error]       16
     TCI            eof_error                       [eof_error]        8
     TCIgeneric_timeout_error                              null        2
  Shatelgeneric_timeout_error           [generic_timeout_error]       22
  Shatel            eof_error[generic_timeout_error, eof_error]        3
  Shatel            eof_error                      [eof_error]        2
  Shatelgeneric_timeout_error                            [null]        2
  Shatelgeneric_timeout_error[generic_timeout_error, eof_error]        3

We can conclude that in all cases except a few cases in Shatel, MCI, and TCI most generic_timeout_error and eof_error failures certainly are caused by TLS failures. For this reason, we are going to focus on the blocking of TLS handshakes. Yet, later we will also investigate the measurements where the failure does not seem to be directly and immediately related to what happened in the TLS handshake.

TLS Blocking

We grouped measurements by the network in which they were run. We ran a script on each measurement group. The purpose of this script was to obtain the number of successes and/or failures for each IP address that belongs to a specific DoT service. As far as this section is concerned, we say that a measurement is successful if we can complete a TLS handshake with the DoT endpoint. Otherwise, we say that the measurement failed.

Hetzner

All the tested DoT endpoints were of course working from the Hetzner-based VPN, which we used as a control vantage point to compare measurements.

Irancell

The following table shows the failures observed from Irancell. A null failure indicates that the experiment succeeded. A null TLS version indicates that no TLS version was negotiated. The Frequency column indicates how many times we observed a specific result.

           Endpoint                                  SNIFailureTLS versionFrequency
        1.0.0.1:853                              1.0.0.1   null    TLSv1.3    4
        1.0.0.1:853                      one.one.one.oneTIMEOUT    null    12
        1.0.0.1:853     1dot1dot1dot1.cloudflare-dns.comTIMEOUT       null   12
        1.1.1.1:853                              1.1.1.1TIMEOUT       null   12
        1.1.1.1:853                      one.one.one.oneTIMEOUT       null   12
        1.1.1.1:853     1dot1dot1dot1.cloudflare-dns.comTIMEOUT       null   12
        8.8.4.4:853                              8.8.4.4TIMEOUT       null   12
        8.8.4.4:853                           dns.googleTIMEOUT       null   12
        8.8.8.8:853                              8.8.8.8TIMEOUT       null   12
        8.8.8.8:853                           dns.googleTIMEOUT       null   12
        9.9.9.9:853                              9.9.9.9   null    TLSv1.3    4
        9.9.9.9:853                        dns.quad9.netTIMEOUT       null   12
       9.9.9.10:853                  dns-nosec.quad9.net   null    TLSv1.3    4
149.112.112.112:853                        dns.quad9.netTIMEOUT       null   12
 159.69.198.101:853                   dot-de.blahdns.comTIMEOUT       null   24
149.112.112.112:853                      149.112.112.112   null    TLSv1.3    4
176.103.130.130:853                      dns.adguard.comTIMEOUT       null   12
176.103.130.131:853                      dns.adguard.comTIMEOUT       null   12

We observe that some of the most popular services (e.g. Google’s DNS) are not accessible from this vantage point. In all cases, either the TLS handshake succeeds or terminates with a timeout. It is interesting to note that the same endpoint could or could not be accessible depending on the SNI being used. For example, 9.9.9.9:853 is accessible when using 9.9.9.9 or dns-nosec.quad9.net as SNI; inaccessible with dns.quad9.net. Likewise, 1.0.0.1:853 works with 1.0.0.1 as SNI and fails with one.one.one.one.

MCI

The following table shows the failures observed from MCI.

           Endpoint                                  SNIFailureTLS versionFrequency
        1.0.0.1:853                              1.0.0.1TIMEOUT       null   12
        1.0.0.1:853                      one.one.one.oneTIMEOUT       null   12
        1.0.0.1:853     1dot1dot1dot1.cloudflare-dns.comTIMEOUT       null   12
        1.1.1.1:853                              1.1.1.1   null    TLSv1.3    4
        1.1.1.1:853                      one.one.one.one   null    TLSv1.3    4
        1.1.1.1:853     1dot1dot1dot1.cloudflare-dns.com   null    TLSv1.3    4
        8.8.4.4:853                              8.8.4.4TIMEOUT       null   12
        8.8.4.4:853                           dns.googleTIMEOUT       null   12
        8.8.8.8:853                              8.8.8.8TIMEOUT       null   12
        8.8.8.8:853                           dns.googleTIMEOUT       null   12
        9.9.9.9:853                              9.9.9.9TIMEOUT       null   12
        9.9.9.9:853                        dns.quad9.netTIMEOUT       null   12
       9.9.9.10:853                  dns-nosec.quad9.netTIMEOUT       null   12
149.112.112.112:853                        dns.quad9.netTIMEOUT       null   12
149.112.112.112:853                      149.112.112.112TIMEOUT       null   12
 185.228.168.10:853   adult-filter-dns.cleanbrowsing.orgTIMEOUT       null   12
  185.228.168.9:853security-filter-dns.cleanbrowsing.orgTIMEOUT       null   12
185.228.168.168:853  family-filter-dns.cleanbrowsing.orgTIMEOUT       null   12

Also in this case we see consistent timeouts during TLS handshakes. While in the previous network the blocking of 9.9.9.9 was incomplete, here the blocking of Cloudflare DNS is incomplete: 1.0.0.1 is blocked but 1.1.1.1 is not. Notably, the blocking does not depend on the SNI alone, but it seems to depend on the IP: 1.0.0.1:853 which does not work with SNI equal to one.one.one.one, while 1.1.1.1:853 works as intended with such an SNI.

This is one of the measurements that failed:

{
  "input": "dnslookup://example.com",
  "probe_asn": "AS197207",
  "report_id": "20200529T184927Z_AS197207_CJSrZOavWNbfKOM3c1N6STCTIRxWiP8Hr9wSzaein4ZjlYHr9G",
  "resolver_asn": "AS197207",
  "test_keys": {
    "dns_cache": [
      ""
    ],
    "failure": "generic_timeout_error",
    "network_events": [
      {
        "failure": null,
        "operation": "resolve_start",
        "t": 0.000141754
      },
      {
        "failure": null,
        "operation": "dns_round_trip_start",
        "t": 0.000336556
      },
      {
        "address": "1.0.0.1:853",
        "failure": null,
        "operation": "connect",                     // 1
        "proto": "tcp",
        "t": 0.114608362
      },
      {
        "failure": null,
        "operation": "tls_handshake_start",         // 2
        "t": 0.114624373
      },
      {
        "failure": null,
        "num_bytes": 261,
        "operation": "write",                       // 3
        "t": 0.115186152
      },
      {
        "failure": "generic_timeout_error",         // 4
        "operation": "read",
        "t": 10.115200612
      },
      {
        "failure": "generic_timeout_error",
        "operation": "tls_handshake_done",
        "t": 10.115399137
      },
      {
        "failure": "generic_timeout_error",
        "operation": "dns_round_trip_done",
        "t": 10.115495971
      }
    ]
  }
}

We clearly see that (1) we connect successfully, (2) we start the TLS handshake, (3) we write the ClientHello message, and (4) we timeout waiting for the ServerHello message.

We were also wondering whether not forcing a specific version of TLS had an impact on this kind of blocking. We therefore repeated the measurement forcing TLSv1.3. The relevant part of the corresponding measurement is as follows:

{
  "report_id": "20200529T185034Z_AS197207_WAEHqumLvUEVe0906xJjqUaFsFOJdMXxoHRpOScar3fgN3Us3e",
  "test_keys": {
    "dns_cache": [
      ""
    ],
    "network_events": [
      {
        "failure": null,
        "operation": "resolve_start",
        "t": 0.000255566
      },
      {
        "failure": null,
        "operation": "dns_round_trip_start",
        "t": 0.00039169
      },
      {
        "address": "1.0.0.1:853",
        "failure": null,
        "operation": "connect",
        "proto": "tcp",
        "t": 0.114715378
      },
      {
        "failure": null,
        "operation": "tls_handshake_start",
        "t": 0.114749918
      },
      {
        "failure": null,
        "num_bytes": 261,
        "operation": "write",
        "t": 0.115099435
      },
      {
        "failure": "generic_timeout_error",
        "operation": "read",
        "t": 10.12390586
      },
      {
        "failure": "generic_timeout_error",
        "operation": "tls_handshake_done",
        "t": 10.124356902
      },
      {
        "failure": "generic_timeout_error",
        "operation": "dns_round_trip_done",
        "t": 10.12445338
      }

As you can see, the structure of the JSON snippet is the same. Overall, forcing TLSv1.3 did not change the result comparing to letting the server pick a version between v1.0 and v1.3.

TCI

The following table shows the failures observed from TCI.

EndpointSNIFailureTLS versionFrequency
1.0.0.1:8531.0.0.1nullTLSv1.34
1.0.0.1:853one.one.one.onenullTLSv1.34
1.0.0.1:8531dot1dot1dot1.cloudflare-dns.comnullTLSv1.35
1.1.1.1:8531.1.1.1TIMEOUTnull12
1.1.1.1:853one.one.one.oneTIMEOUTnull12
1.1.1.1:8531dot1dot1dot1.cloudflare-dns.comTIMEOUTnull12
8.8.4.4:8538.8.4.4TIMEOUTnull12
8.8.4.4:853dns.googleTIMEOUTnull12
8.8.8.8:8538.8.8.8TIMEOUTnull12
8.8.8.8:853dns.googleTIMEOUTnull12
9.9.9.9:8539.9.9.9nullTLSv1.34
9.9.9.9:853dns.quad9.netnullTLSv1.34
9.9.9.10:853dns-nosec.quad9.netnullTLSv1.34
149.112.112.112:853dns.quad9.netTIMEOUTnull12
159.69.198.101:853dot-de.blahdns.comEOFnull8
149.112.112.112:853149.112.112.112nullTLSv1.34
176.103.130.130:853dns.adguard.comEOFnull4
176.103.130.131:853dns.adguard.comEOFnull4

Also in this case, we see that some DoT endpoints work, while others are blocked. We again see that Cloudflare DNS blocking seems to be independent of the SNI being used. In fact, we see that with SNI one.one.one.one, 1.0.0.1:853 works, while 1.1.1.1:853 fails.

Another difference is that we see dot-de.blahdns.com and dns.adguard.com being blocked with the connection being closed (EOF), as opposed to with a timeout (TIMEOUT). This the relevant snippet of is one of the measurements regarding dns.adguard.com that failed:

{
  "probe_asn": "AS58224",
  "report_id": "20200529T180716Z_AS58224_XLxEoWh9ZK8ZEGza32TUMRFquB1BujsOI4nBLrY7bkHTf1EzTj",
  "resolver_asn": "AS58224",
  "test_keys": {
    "dns_cache": [
      ""
    ],
    "network_events": [
      {
        "failure": null,
        "operation": "resolve_start",
        "t": 0.000241651
      },
      {
        "failure": null,
        "operation": "dns_round_trip_start",
        "t": 0.000465461
      },
      {
        "failure": null,
        "operation": "resolve_start",
        "t": 0.000469994
      },
      {
        "failure": null,
        "operation": "resolve_done",
        "t": 0.241027373
      },
      {
        "address": "176.103.130.130:853",
        "failure": null,
        "operation": "connect",
        "proto": "tcp",
        "t": 0.484321535
      },
      {
        "failure": null,
        "operation": "tls_handshake_start",
        "t": 0.484337571
      },
      {
        "failure": null,
        "num_bytes": 285,
        "operation": "write",                              // 1
        "t": 0.484662834
      },
      {
        "failure": "eof_error",
        "operation": "read",                               // 2
        "t": 2.739924664
      },
      {
        "failure": "eof_error",
        "operation": "tls_handshake_done",
        "t": 2.740157413
      },
      {
        "failure": "eof_error",
        "operation": "dns_round_trip_done",
        "t": 2.740262478
      },

Like in previous networks, we observe that the anomaly (in this case an eof_error) occurs after we have written the ClientHello (1). It is also interesting to observe that the eof_error (which means that the connection is closed by the remote host, or a middlebox), occurs more than two seconds after we sent the ClientHello (2). This behaviour could possibly be explained by retransmissions. Yet we checked the whole dataset and noticed a consistent pattern: the eof_error always arrives after some seconds. For now, we will not investigate this behaviour further, but we will possibly do that as part of future work.

Shatel

The following table shows the failures observed from Shatel.

EndpointSNIFailureTLS versionFrequency
1.0.0.1:8531.0.0.1nullTLSv1.34
1.0.0.1:853one.one.one.oneTIMEOUTnull12
1.0.0.1:8531dot1dot1dot1.cloudflare-dns.comTIMEOUTnull12
1.1.1.1:8531.1.1.1TIMEOUTnull12
1.1.1.1:853one.one.one.oneTIMEOUTnull12
1.1.1.1:8531dot1dot1dot1.cloudflare-dns.comTIMEOUTnull12
8.8.4.4:8538.8.4.4TIMEOUTnull12
8.8.4.4:853dns.googleTIMEOUTnull12
8.8.8.8:8538.8.8.8TIMEOUTnull12
8.8.8.8:853dns.googleTIMEOUTnull12
9.9.9.9:8539.9.9.9nullTLSv1.34
9.9.9.9:853dns.quad9.netTIMEOUTnull12
9.9.9.10:853dns-nosec.quad9.netnullTLSv1.34
149.112.112.112:853dns.quad9.netTIMEOUTnull12
159.69.198.101:853dot-de.blahdns.comEOFnull5
159.69.198.101:853dot-de.blahdns.comTIMEOUTnull9
149.112.112.112:853149.112.112.112nullTLSv1.34
176.103.130.130:853dns.adguard.comEOFnull4
176.103.130.130:853dns.adguard.comTIMEOUTnull1
176.103.130.131:853dns.adguard.comEOFnull3
176.103.130.131:853dns.adguard.comTIMEOUTnull4

The results are similar to the ones on other networks. We see some timeouts and some cases where the connection is closed. We see that blocking is partial for some DoT providers. For example, the 9.9.9.9 SNI is not blocked, but dns.quad9.net is blocked.

Endpoint-Based TLS Blocking

On MCI and TCI we observed an interesting pattern where the blocking seemed to be independent of the SNI and dependent on the endpoint. For example, in TCI 1.0.0.1:853 works with one.one.one.one, while 1.1.1.1:853 is blocked with the same SNI.

To better understand what is happening on TCI, we used OpenSSL to connect to 1.0.0.1:853 using 1.1.1.1 as the SNI, as well as to 1.1.1.1:853 using 1.0.0.1 as the SNI.

This is what happens when we use the 1.0.0.1:853 endpoint with 1.1.1.1 as the SNI:

$ openssl s_client -servername 1.1.1.1 -connect 1.0.0.1:853
CONNECTED(00000006)
depth=2 C = US, O = DigiCert Inc, OU = www.digicert.com, CN = DigiCert Global Root CA
verify return:1
depth=1 C = US, O = DigiCert Inc, CN = DigiCert ECC Secure Server CA
verify return:1
depth=0 C = US, ST = California, L = San Francisco, O = "Cloudflare, Inc.", CN = cloudflare-dns.com
verify return:1
---
Certificate chain
0 s:C = US, ST = California, L = San Francisco, O = "Cloudflare, Inc.", CN = cloudflare-dns.com
  i:C = US, O = DigiCert Inc, CN = DigiCert ECC Secure Server CA
1 s:C = US, O = DigiCert Inc, CN = DigiCert ECC Secure Server CA
  i:C = US, O = DigiCert Inc, OU = www.digicert.com, CN = DigiCert Global Root CA
---
Server certificate
-----BEGIN CERTIFICATE-----
MIIFxjCCBUygAwIBAgIQAczjGN6fVn+rKySQH62nHTAKBggqhkjOPQQDAjBMMQsw
CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMSYwJAYDVQQDEx1EaWdp
Q2VydCBFQ0MgU2VjdXJlIFNlcnZlciBDQTAeFw0xOTAxMjgwMDAwMDBaFw0yMTAy
MDExMjAwMDBaMHIxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYw
FAYDVQQHEw1TYW4gRnJhbmNpc2NvMRkwFwYDVQQKExBDbG91ZGZsYXJlLCBJbmMu
MRswGQYDVQQDExJjbG91ZGZsYXJlLWRucy5jb20wWTATBgcqhkjOPQIBBggqhkjO
PQMBBwNCAATFIHCMIEJQKB59REF8MHkpHGNeHUSbxfdxOive0qKksWw9ash3uMuP
LlBT/fQYJn9hN+3/wr7pC125fuHfHOJ0o4ID6DCCA+QwHwYDVR0jBBgwFoAUo53m
H/naOU/AbuiRy5Wl2jHiCp8wHQYDVR0OBBYEFHCV3FyjjmYH28uBEMar58OoRX+g
MIGsBgNVHREEgaQwgaGCEmNsb3VkZmxhcmUtZG5zLmNvbYIUKi5jbG91ZGZsYXJl
LWRucy5jb22CD29uZS5vbmUub25lLm9uZYcEAQEBAYcEAQAAAYcEop+ENYcQJgZH
AEcAAAAAAAAAAAAREYcQJgZHAEcAAAAAAAAAAAAQAYcQJgZHAEcAAAAAAAAAAAAA
ZIcQJgZHAEcAAAAAAAAAAABkAIcEop8kAYcEop8uATAOBgNVHQ8BAf8EBAMCB4Aw
HQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMGkGA1UdHwRiMGAwLqAsoCqG
KGh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9zc2NhLWVjYy1nMS5jcmwwLqAsoCqG
KGh0dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9zc2NhLWVjYy1nMS5jcmwwTAYDVR0g
BEUwQzA3BglghkgBhv1sAQEwKjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cuZGln
aWNlcnQuY29tL0NQUzAIBgZngQwBAgIwewYIKwYBBQUHAQEEbzBtMCQGCCsGAQUF
BzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wRQYIKwYBBQUHMAKGOWh0dHA6
Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEVDQ1NlY3VyZVNlcnZlckNB
LmNydDAMBgNVHRMBAf8EAjAAMIIBfgYKKwYBBAHWeQIEAgSCAW4EggFqAWgAdgCk
uQmQtBhYFIe7E6LMZ3AKPDWYBPkb37jjd80OyA3cEAAAAWiVHhSLAAAEAwBHMEUC
IQDlnoPeMXtFkRsy3Vs0eovk3ILKt01x6bgUdMlmQTFIvAIgcAn0lFSjiGzHm2eO
jDZJzMiP5Uaj0Jwub9GO8RkxkkoAdQCHdb/nWXz4jEOZX73zbv9WjUdWNv9KtWDB
tOr/XqCDDwAAAWiVHhVsAAAEAwBGMEQCIFC0n0JModeol8b/Qicxd5Blf/o7xOs/
Bk0j9hdc5N7jAiAQocYnHL9iMqTtFkh0vmSsII5NbiakM/2yDEXnwkPRvAB3ALvZ
37wfinG1k5Qjl6qSe0c4V5UKq1LoGpCWZDaOHtGFAAABaJUeFJEAAAQDAEgwRgIh
AL3OPTBzOZpS5rS/uLzqMOiACCFQyY+mTJ+L0I9TcB3RAiEA4+SiPz0/5kFxvrk7
AKYKdvelgV1hiiPbM2YHY+/0BIkwCgYIKoZIzj0EAwIDaAAwZQIwez76hX2HTMur
/I3XRuwfdmVoa8J6ZVEVq+AZsE7DyQh7AV4WNLU+092BrPbnyVUFAjEAzUf5jdz1
pyc74lgOunC7LBE6cPtWbzfGpJiYyT/T+c5eIAwRYziKT0DKbaql7tiZ
-----END CERTIFICATE-----
subject=C = US, ST = California, L = San Francisco, O = "Cloudflare, Inc.", CN = cloudflare-dns.com
issuer=C = US, O = DigiCert Inc, CN = DigiCert ECC Secure Server CA
---
No client certificate CA names sent
Peer signing digest: SHA256
Peer signature type: ECDSA
Server Temp Key: X25519, 253 bits
---
SSL handshake has read 2740 bytes and written 379 bytes         // 1
Verification: OK
---
New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384                  // 2
Server public key is 256 bit
Secure Renegotiation IS NOT supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
Early data was not sent
Verify return code: 0 (ok)
---

The TLS handshake clearly completes successfully. In fact, we are able to read and write bytes during the TLS handshake (1) and (2) we agree on an encrypted channel.

This is, instead, what happens when using 1.1.1.1:853 with 1.0.0.1 as the SNI:

$ openssl s_client -servername 1.0.0.1 -connect 1.1.1.1:853
CONNECTED(00000006)
write:errno=0
---
no peer certificate available
---
No client certificate CA names sent
---
SSL handshake has read 0 bytes and written 299 bytes       // 1
Verification: OK
---
New, (NONE), Cipher is (NONE)
Secure Renegotiation IS NOT supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
Early data was not sent
Verify return code: 0 (ok)
---

Here we clearly see a failure where we can write the ClientHello but we cannot read anything back from the server (...has read 0 bytes...), as the handshake is interrupted (1).

The above experiments suggest that blocking is caused by handshaking with the 1.1.1.1:853 endpoint, rather than by the specific SNI. See also this follow-up experiment:

$ openssl s_client -servername example.com -connect 1.1.1.1:853
CONNECTED(00000006)
write:errno=0
---
no peer certificate available
---
No client certificate CA names sent
---
SSL handshake has read 0 bytes and written 303 bytes
Verification: OK
---
New, (NONE), Cipher is (NONE)
Secure Renegotiation IS NOT supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
Early data was not sent
Verify return code: 0 (ok)
---

Here we see that the connection is closed even with an unrelated SNI (example.com). Again, in fact, we see that OpenSSL has read 0 bytes (i.e. the handshake did not complete).

The final question is whether the blocking depends on the IP address or on the endpoint. The following OpenSSL experiment connects to 1.1.1.1:443 with SNI 1.1.1.1:

$ openssl s_client -servername 1.1.1.1 -connect 1.1.1.1:443
CONNECTED(00000006)
depth=2 C = US, O = DigiCert Inc, OU = www.digicert.com, CN = DigiCert Global Root CA
verify return:1
depth=1 C = US, O = DigiCert Inc, CN = DigiCert ECC Secure Server CA
verify return:1
depth=0 C = US, ST = California, L = San Francisco, O = "Cloudflare, Inc.", CN = cloudflare-dns.com
verify return:1
---
Certificate chain
0 s:C = US, ST = California, L = San Francisco, O = "Cloudflare, Inc.", CN = cloudflare-dns.com
  i:C = US, O = DigiCert Inc, CN = DigiCert ECC Secure Server CA
1 s:C = US, O = DigiCert Inc, CN = DigiCert ECC Secure Server CA
  i:C = US, O = DigiCert Inc, OU = www.digicert.com, CN = DigiCert Global Root CA
---
Server certificate
-----BEGIN CERTIFICATE-----
MIIFxjCCBUygAwIBAgIQAczjGN6fVn+rKySQH62nHTAKBggqhkjOPQQDAjBMMQsw
CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMSYwJAYDVQQDEx1EaWdp
Q2VydCBFQ0MgU2VjdXJlIFNlcnZlciBDQTAeFw0xOTAxMjgwMDAwMDBaFw0yMTAy
MDExMjAwMDBaMHIxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYw
FAYDVQQHEw1TYW4gRnJhbmNpc2NvMRkwFwYDVQQKExBDbG91ZGZsYXJlLCBJbmMu
MRswGQYDVQQDExJjbG91ZGZsYXJlLWRucy5jb20wWTATBgcqhkjOPQIBBggqhkjO
PQMBBwNCAATFIHCMIEJQKB59REF8MHkpHGNeHUSbxfdxOive0qKksWw9ash3uMuP
LlBT/fQYJn9hN+3/wr7pC125fuHfHOJ0o4ID6DCCA+QwHwYDVR0jBBgwFoAUo53m
H/naOU/AbuiRy5Wl2jHiCp8wHQYDVR0OBBYEFHCV3FyjjmYH28uBEMar58OoRX+g
MIGsBgNVHREEgaQwgaGCEmNsb3VkZmxhcmUtZG5zLmNvbYIUKi5jbG91ZGZsYXJl
LWRucy5jb22CD29uZS5vbmUub25lLm9uZYcEAQEBAYcEAQAAAYcEop+ENYcQJgZH
AEcAAAAAAAAAAAAREYcQJgZHAEcAAAAAAAAAAAAQAYcQJgZHAEcAAAAAAAAAAAAA
ZIcQJgZHAEcAAAAAAAAAAABkAIcEop8kAYcEop8uATAOBgNVHQ8BAf8EBAMCB4Aw
HQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMGkGA1UdHwRiMGAwLqAsoCqG
KGh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9zc2NhLWVjYy1nMS5jcmwwLqAsoCqG
KGh0dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9zc2NhLWVjYy1nMS5jcmwwTAYDVR0g
BEUwQzA3BglghkgBhv1sAQEwKjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cuZGln
aWNlcnQuY29tL0NQUzAIBgZngQwBAgIwewYIKwYBBQUHAQEEbzBtMCQGCCsGAQUF
BzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wRQYIKwYBBQUHMAKGOWh0dHA6
Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEVDQ1NlY3VyZVNlcnZlckNB
LmNydDAMBgNVHRMBAf8EAjAAMIIBfgYKKwYBBAHWeQIEAgSCAW4EggFqAWgAdgCk
uQmQtBhYFIe7E6LMZ3AKPDWYBPkb37jjd80OyA3cEAAAAWiVHhSLAAAEAwBHMEUC
IQDlnoPeMXtFkRsy3Vs0eovk3ILKt01x6bgUdMlmQTFIvAIgcAn0lFSjiGzHm2eO
jDZJzMiP5Uaj0Jwub9GO8RkxkkoAdQCHdb/nWXz4jEOZX73zbv9WjUdWNv9KtWDB
tOr/XqCDDwAAAWiVHhVsAAAEAwBGMEQCIFC0n0JModeol8b/Qicxd5Blf/o7xOs/
Bk0j9hdc5N7jAiAQocYnHL9iMqTtFkh0vmSsII5NbiakM/2yDEXnwkPRvAB3ALvZ
37wfinG1k5Qjl6qSe0c4V5UKq1LoGpCWZDaOHtGFAAABaJUeFJEAAAQDAEgwRgIh
AL3OPTBzOZpS5rS/uLzqMOiACCFQyY+mTJ+L0I9TcB3RAiEA4+SiPz0/5kFxvrk7
AKYKdvelgV1hiiPbM2YHY+/0BIkwCgYIKoZIzj0EAwIDaAAwZQIwez76hX2HTMur
/I3XRuwfdmVoa8J6ZVEVq+AZsE7DyQh7AV4WNLU+092BrPbnyVUFAjEAzUf5jdz1
pyc74lgOunC7LBE6cPtWbzfGpJiYyT/T+c5eIAwRYziKT0DKbaql7tiZ
-----END CERTIFICATE-----
subject=C = US, ST = California, L = San Francisco, O = "Cloudflare, Inc.", CN = cloudflare-dns.com
issuer=C = US, O = DigiCert Inc, CN = DigiCert ECC Secure Server CA
---
No client certificate CA names sent
Peer signing digest: SHA256
Peer signature type: ECDSA
Server Temp Key: X25519, 253 bits
---
SSL handshake has read 2740 bytes and written 379 bytes   // 1
Verification: OK
---
New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384            // 2
Server public key is 256 bit
Secure Renegotiation IS NOT supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
Early data was not sent
Verify return code: 0 (ok)
---
---
[snip]

Here we see that the handshake is able to progress (1) and we agree on a channel (2).

It seems that what is blocked is not 1.1.1.1:* in general, but 1.1.1.1:853 in particular. In this regard, it is interesting to note, however, that the blocking is not designed to prevent connecting to this TCP endpoint. Rather, the blocking only happens during the TLS handshake.

(It is also worth noting that miniooni wrongly used h2 http/1.1 as ALPN when performing DoT. Yet, we obtained the same results with OpenSSL and no ALPN.)

Analysis of Specific Measurements

We inspected all eleven measurements containing non-obvious causes of failure. Three of them were associated with the server misbehaving and were excluded. Two other measurements were associated with DNS failures and were also excluded. The following table lists the remaining measurements, along with the first 32 bytes of their report ID. All these measurements were performed on Shatel (AS31549).

ReportID[:32]MeasurementFailureset(TLSFailures)
20200530T174535Z_AS31549_rVgNqMNeof_error[eof_error, generic_timeout_error]
20200530T174555Z_AS31549_i04a7o8eof_error[eof_error, generic_timeout_error]
20200530T174623Z_AS31549_OmUGa28eof_error[eof_error, generic_timeout_error]
20200530T181031Z_AS31549_lAqvSEEgeneric_timeout_error[eof_error, generic_timeout_error]
20200530T181201Z_AS31549_o3qWCm4generic_timeout_error[eof_error, generic_timeout_error]
20200530T181248Z_AS31549_8fWDyk7generic_timeout_error[eof_error, generic_timeout_error]

These are cases in which we see a mixture of eof_error and generic_timeout_error when performing the TLS handshake. The following is, for example, a trimmed version of the network_events section of the last measurement in the table.

[
  {
    "failure": "generic_timeout_error",
    "operation": "read",
    "t": 10.141675515
  },
  {
    "failure": "generic_timeout_error",
    "operation": "tls_handshake_done",         // 1
    "t": 10.141765219
  },
  {
    "failure": "generic_timeout_error",
    "operation": "read",
    "t": 20.34222198
  },
  {
    "failure": "generic_timeout_error",
    "operation": "tls_handshake_done",         // 2
    "t": 20.342343819
  },
  {
    "failure": "generic_timeout_error",
    "operation": "read",
    "t": 30.4572344
  },
  {
    "failure": "generic_timeout_error",
    "operation": "tls_handshake_done",         // 3
    "t": 30.457394804
  },
  {
    "failure": null,
    "operation": "tls_handshake_start",        // 4
    "t": 30.570166418
  },
  {
    "failure": null,
    "num_bytes": 288,
    "operation": "write",
    "t": 30.570562975
  },
  {
    "failure": "eof_error",
    "operation": "read",
    "t": 38.809322017
  },
  {
    "failure": "eof_error",
    "operation": "tls_handshake_done",         // 5
    "t": 38.809483833
  }
]

In this JSON we see a single eof_error (5) and three timeouts (1,2,3). It is also interesting to note how the final handshake starts after 30 seconds (4) and it takes eight seconds for the network to close the connection (5). This is in line with a previous observation in which we reported that many eof_error happen several seconds after the beginning of the TLS handshake, rather than immediately after we send the ClientHello.

Results Summary

The following table shows what endpoints were blocked by which ISP. It is compiled by joining the ISP-specific results shown above.

A null result indicates that the specific endpoint worked with a specific SNI value. A TIMEOUT result indicates a timeout during the TLS handshake. An EOF result indicates that the connection was closed during the TLS handshake. In a few cases we observed a mixture of EOF and TIMEOUT on Shatel; in such cases we represented the results using a vector.

EndpointSNIIrancellMCITCIShatel
1.0.0.1:8531.0.0.1nullTIMEOUTnullnull
1.0.0.1:853one.one.one.oneTIMEOUTTIMEOUTnullTIMEOUT
1.0.0.1:8531dot1dot1dot1.cloudflare-dns.comTIMEOUTTIMEOUTnullTIMEOUT
1.1.1.1:8531.1.1.1TIMEOUTnullTIMEOUTTIMEOUT
1.1.1.1:853one.one.one.oneTIMEOUTnullTIMEOUTTIMEOUT
1.1.1.1:8531dot1dot1dot1.cloudflare-dns.comTIMEOUTnullTIMEOUTTIMEOUT
8.8.4.4:8538.8.4.4TIMEOUTTIMEOUTTIMEOUTTIMEOUT
8.8.4.4:853dns.googleTIMEOUTTIMEOUTTIMEOUTTIMEOUT
8.8.8.8:8538.8.8.8TIMEOUTTIMEOUTTIMEOUTTIMEOUT
8.8.8.8:853dns.googleTIMEOUTTIMEOUTTIMEOUTTIMEOUT
9.9.9.9:8539.9.9.9nullTIMEOUTnullnull
9.9.9.9:853dns.quad9.netTIMEOUTTIMEOUTnullTIMEOUT
9.9.9.10:853dns-nosec.quad9.netnullTIMEOUTnullnull
149.112.112.112:853dns.quad9.netTIMEOUTTIMEOUTTIMEOUTTIMEOUT
149.112.112.112:853149.112.112.112nullTIMEOUTnullnull
159.69.198.101:853dot-de.blahdns.comTIMEOUTnullEOF[EOF, TIMEOUT]
176.103.130.130:853dns.adguard.comTIMEOUTnullEOF[EOF, TIMEOUT]
176.103.130.131:853dns.adguard.comTIMEOUTnullEOF[EOF, TIMEOUT]
185.228.168.10:853adult-filter-dns.cleanbrowsing.orgnullTIMEOUTnullnull
185.228.168.9:853security-filter-dns.cleanbrowsing.orgnullTIMEOUTnullnull
185.228.168.168:853family-filter-dns.cleanbrowsing.orgnullTIMEOUTnullnull

The blocking pattern implemented by Irancell and Shatel is very similar. No specific combination of endpoint and SNI could work across all ISPs.

Conclusion

We performed experiments on four Iranian ISPs (MCI, TCI, Irancell, and Shatel) to check whether 31 well-known DNS over TLS (DoT) endpoints worked. As much as 57% of the endpoints appear to be blocked on at least one network. The blocking is not implemented uniformly across ISPs.

To perform these experiments we used OONI’s Engine experimental client, miniooni.

We only observed one case of DNS blocking. All the other cases involved tampering with the TLS handshake. We identified at least two cases where blocking depended on the SNI being used: 9.9.9.3:853 seems to be blocked using the SNI on Irancell and Shatel (using 9.9.9.9 as SNI works, dns.quad9.net does not); 1.0.0.1:853 seems to be blocked using the SNI on Irancell (1.0.0.1 works as SNI, one.one.one.one does not).

We also documented cases where TLS handshake blocking depends on the TCP endpoint being used. Cloudflare’s DoT resolver is blocked on an endpoint basis both on MCI and TCI. On MCI, 1.0.0.1:853 fails with several SNIs (including one.one.one.one) and 1.1.1.1.1:853 works with several SNIs (including one.one.one.one). On TCI, however, 1.1.1.1:853 fails and 1.0.0.1:853 works. Again, this is true for several SNIs, including one.one.one.one.

We also ran OpenSSL follow-up experiment to increase the confidence that the blocking (1) does not depend on the SNI and (2) does not depend on the port being used. Indeed, on TCI, we could not handshake with the 1.1.1.1:853 endpoint regardless of the SNI. We also tested an SNI as unrelated as example.com, but to no avail. Yet, 1.1.1.1:443 was working. This led us to conclude that the blocking is implemented per endpoint, rather than per IP address.

Also, the results of the experiment do not change when forcing TLSv1.3.

In conclusion, we were able to determine that Iran ISPs are interfering with DNS over TLS. We observed cases of SNI based filtering as well as cases in which the blocking applied to the port used by DNS over TLS regardless of the SNI being used.