README.md 10.5 KB
Newer Older
Stéphane Bortzmeyer's avatar
Stéphane Bortzmeyer committed
1
2
# Homer

3
4
Homer is a DoH (DNS-over-HTTPS) and DoT (DNS-over-TLS) client. Its
main purpose is to test DoH and DoT resolvers.
Stephane Bortzmeyer's avatar
Stephane Bortzmeyer committed
5
6
7
8
9

It is currently quite experimental.

## Usage

10
11
12
13
14
Two mandatory arguments, the URL of the DoH server (or name/address of
the DoT resolver), and a domain name to query. By default, Homer uses
DoH. Also by defaut, the type of data is AAAA (IP address). You can
add a third argument to use another type, as in the second example
below.
Stephane Bortzmeyer's avatar
Stephane Bortzmeyer committed
15
16

```
17
% homer https://doh.powerdns.org/ framagit.org
Stephane Bortzmeyer's avatar
Stephane Bortzmeyer committed
18
19
20
21
22
23
24
25
26
27
28
id 0
opcode QUERY
rcode NOERROR
flags QR RD RA
;QUESTION
framagit.org. IN AAAA
;ANSWER
framagit.org. 10800 IN AAAA 2a01:4f8:200:1302::42
;AUTHORITY
;ADDITIONAL
Total elapsed time: 0.40 seconds (402.28 ms/request)
29
30
31
32
33
34
35
36
37
38
39
40
41
42

% homer --dot 9.9.9.9 cocca.fr A
id 42545
opcode QUERY
rcode NOERROR
flags QR RD RA
;QUESTION
cocca.fr. IN A
;ANSWER
cocca.fr. 43200 IN A 185.17.236.69
;AUTHORITY
;ADDITIONAL

Total elapsed time: 0.07 seconds (66.72 ms/request )
Stephane Bortzmeyer's avatar
Stephane Bortzmeyer committed
43
44
```

45
46
47
Possible options, besides `--dot`:

* --verbose or -v: Makes the program more talkative
Alexandre's avatar
Alexandre committed
48
* --debug: Makes the program very talkative (sets verbose to true)
49
50
51
52
* --head or -e: (DoH) Uses only the HEAD HTTP method. Since the RFC
  does not mention it, result is probably indefinite.
* --POST or -P: (DoH) Uses the POST HTTP method (default is GET)
* --insecure or -k: Does not check the certificate
53
54
* -4: Uses only IPv4
* -6: Uses only IPv6
55
56
* --dnssec: requests DNSSEC data (signatures)
* --noedns: no EDNS (default is to indicate EDNS support)
57
58
59
60
* --ecs: send ECS, my subnet to auth. servers (default is to refuse
  it)
* --key KEYINBASE64: authentifies a DoT resolver with its public
  key. Example: `homer.py --key "62lKu9HsDVbyiPenApnc4sfmSYTHOVfFgL3pyB+cBL4=" --dot 145.100.185.15 IN NS`
Alexandre's avatar
Alexandre committed
61
* --check: Run a set of tests (see below)
Alexandre's avatar
Alexandre committed
62
63
64
65
* --multistreams: (DoH) Uses HTTP/2 streams (requires the --file option)
* --file INPUT_FILE: provide an input file with a list of domain name to query
  (read the first line only, use --repeat N to read up to N lines of the file)
* --repeat N: repeat a test N times or read up to N lines of a file
66
* --no-display-results: do not output DNS response
Alexandre's avatar
Alexandre committed
67
68
69
70
71
72
73
74
75
76
77
78
79

### Check

The `--check` option allows to run several defined tests on a connection.

Homer displays `OK` on success and `KO` on failure.
The program stops after the first failed test.

```
% homer --check https://doh.bortzmeyer.fr framagit.org
OK
```

80
81
82
83
84
85
86
87
Each test is marked with a level of compliance. There are three
levels, "legal" (compliant with the strict requirments of the RFCs),
"necessary" (in a typical setup) and "nicetohave". The default level
is "necessary" but you can change it with option
`--mandatory-check`. For instance, sending a reply when the request
uses the HEAD method is "nicetohave" for a DoH server (the RFC does
not mandate it). The tests are always performed but are not fatal if
the choosen level is lower than the level of the test.
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128

### Repetition of tests

You can repeat the tests several times, for instance for performance
measurements. This is done with option `--repeat N` where N is the
number of repetitions.

```
% homer --repeat 3 https://doh.bortzmeyer.fr ça.fr SOA
Test 0
...
Test 1
...
Test 2

Total elapsed time: 0.10 seconds (33.56 ms/request , 7.88 ms/request if we ignore the first one)
```

Homer reuses the same connection for all requests, both for DoH and
DoT, which explains why the first request is often longer.

Repetition is often combined with the use of an external file, where
Homer reads the domain names (and types) to query. Here is a sample
file:

```
truc.fr
chose.fr
machin.fr
trucmachin.fr NS
```

Assuming the file is named `list.txt`, this command will run four
tests, with the above names (and the query type `NS` for the last
one):

```
% homer --repeat 4 --file list.txt https://doh.42l.fr/dns-query
```

When repeating tests, you can add a delay between tests, with `--delay
Alexandre's avatar
Alexandre committed
129
130
N` or `-d N`, where N is the (possibly fractional) number of seconds
to wait.
131

Alexandre's avatar
Alexandre committed
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
### Mulitstreams

When using Homer with DoH, the option `--multistreams` can be used
to specify that you want to take advantage of the HTTP/2 streams
when sending several requests.

This option requires an input file provided with the `--file` option.
By default only the first line of the file is read. You need to
specify a number of line with `--repeat` to read more lines from
the file.

For example :
```
% ./homer.py --multistreams --file input_file --repeat 5 https://doh.powerdns.org
```

In order to focus on the time per request, you can suppress the
149
output by using the option `--no-display-results`.
Alexandre's avatar
Alexandre committed
150

151
Two modes are available. By default each response is read,
Alexandre's avatar
Alexandre committed
152
153
154
155
156
checked and displayed as soon as it is received.
You can use `--sync` to delay this processing after the last transfer.
In that case the DNS responses are displayed in the same order as they
were sent.

157
158
159
160
161
162
An option `--time` allows to display the time taken by each transfer.
This is based on [libcurl time values](https://curl.haxx.se/libcurl/c/curl_easy_getinfo.html#TIMES)
[CURLINFO_TOTAL_TIME](https://curl.haxx.se/libcurl/c/curl_easy_getinfo.html#CURLINFOTOTALTIME)
and [CURLINFO_PRETRANSFER_TIME](https://curl.haxx.se/libcurl/c/curl_easy_getinfo.html#CURLINFOPRETRANSFERTIME)

```
163
% ./homer.py  --multistreams --file input_file --repeat 5 --no-display-results --time https://doh.powerdns.org
164
165
166
167
168
169
170
171
172
173
  0     36.165 ms    44.773 ms     8.608 ms
  1      0.142 ms     8.580 ms     8.438 ms
  3      0.095 ms     9.223 ms     9.128 ms
  2      0.103 ms    10.282 ms    10.179 ms
  4      0.104 ms    10.068 ms     9.964 ms

Total elapsed time: 0.07 seconds (9.26 ms/request)
OK
```

Alexandre's avatar
Alexandre committed
174

175
176
### Monitoring with Nagios, Icinga, or similar software

Winfried Angele's avatar
Winfried Angele committed
177
If the program is named `check_doh` or ` check_dot` (either from
178
179
180
181
182
183
184
185
186
187
copying or symbolic linking), it will behave as a [monitoring
plugin](https://www.monitoring-plugins.org/), suitable to be used from monitoring program like Nagios
or [Icinga](https://icinga.com/). The options are different in that case, and follow the
monitoring plugins conventions:

* -H: host name or address to monitor
* -V: virtual hostname (the certificate check will be based on that)
* -n: domain name to lookup
* -t: DNS type to query
* -p: (DoH) path in the URLx
188
* -e: a string to expect in the result
189
190
191
* -P: uses the HTTP method POST
* -h: uses the HTTP method HEAD
* -i: insecure (do not check the certificate)
192
* -k:  authenticated the DoT server with this public key
193
194
195
196
197
198
199
200
201
202
203

For Icinga, the following definition enables the plugin:

```
object CheckCommand "doh_monitor" {
  command = [ PluginContribDir + "/check_doh" ]

  arguments = {
      "-H" = "$address6$",
	  "-n" = "$doh_lookup$",
	  "-p" = "$doh_path$",
204
	  "-e" = "$doh_expect$",
205
206
	  "-V" = "$doh_vhost$",
	  "-t" = "$doh_type$",
Winfried Angele's avatar
Winfried Angele committed
207
	  "-P" = "$doh_post$",
208
209
210
211
212
213
214
215
216
217
218
219
	  "-i" = "$doh_insecure$",
	  "-h" = "$doh_head$"	
	  }
}

object CheckCommand "dot_monitor" {
  command = [ PluginContribDir + "/check_dot" ]

  arguments = {
      "-H" = "$address6$",
	  "-n" = "$dot_lookup$",
	  "-p" = "$dot_path$",
220
	  "-e" = "$dot_expect$",
221
222
	  "-V" = "$dot_vhost$",
	  "-t" = "$dot_type$",
Winfried Angele's avatar
Winfried Angele committed
223
	  "-P" = "$dot_post$",
224
	  "-i" = "$dot_insecure$",
225
226
	  "-h" = "$dot_head$",
	  "-k" = "$dot_key$"
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
	}
}

```

And a possible use is:

```
apply Service "doh" {
  import "generic-service"
  check_command = "doh_monitor"
    assign where (host.address || host.address6) && host.vars.doh
      vars.doh_lookup = "fr.wikipedia.org"

}

apply Service "dot" {
  import "generic-service"
  check_command = "dot_monitor"
    assign where (host.address || host.address6) && host.vars.dot
      vars.dot_lookup = "fr.wikipedia.org"

}

```

```
object Host "myserver" {
...
  vars.dot = true
  vars.dot_vhost = "dot.me.example"

  vars.doh = true
  vars.doh_vhost = "doh.me.example"
  vars.doh_post = true

```
264

Stephane Bortzmeyer's avatar
Stephane Bortzmeyer committed
265
266
## Installation

267
268
269
You need Python 3, [DNSpython](http://www.dnspython.org/),
[PyOpenSSL](https://www.pyopenssl.org/),
[netaddr](https://github.com/drkjam/netaddr/) and
270
[pycurl](http://pycurl.io/docs/latest). You can install them with pip
271
272
`pip3 install dnspython pyOpenSSL netaddr pycurl`. Then, just run the
script `homer` (or `homer.py`).
273
274

On Debian, if you prefer regular operating system packages to pip,
275
276
`apt install python3 python3-dnspython python3-openssl python3-netaddr
python3-pycurl` will install everything you need.
Stephane Bortzmeyer's avatar
Stephane Bortzmeyer committed
277

Stephane Bortzmeyer's avatar
Stephane Bortzmeyer committed
278
279
### Testing

280
281
282
The tests configured in `tests.yaml` require
https://framagit.org/feth/test_exe_matrix. Then, just `test_exe_matrix
tests.yaml`.
Stephane Bortzmeyer's avatar
Stephane Bortzmeyer committed
283

284
## Public servers
Stephane Bortzmeyer's avatar
Stephane Bortzmeyer committed
285

Stephane Bortzmeyer's avatar
Stephane Bortzmeyer committed
286
287
(Managed by non-profit organisations. I may trim this list in the
future, to remove servers that do not validate with DNSSEC.)
Stephane Bortzmeyer's avatar
Stephane Bortzmeyer committed
288

289
290
### DoH

Stephane Bortzmeyer's avatar
Stephane Bortzmeyer committed
291
* `https://doh.powerdns.org/`
292
* `https://doh.bortzmeyer.fr/` ([Documentation](https://doh.bortzmeyer.fr/about)) 
Stephane Bortzmeyer's avatar
Stephane Bortzmeyer committed
293
* `https://doh.42l.fr/dns-query` ([Documentation](https://42l.fr/DoH-service))
294
* `https://odvr.nic.cz/doh` ([Documentation](https://www.nic.cz/odvr/))
295
* `https://dns.hostux.net/dns-query`
296
  ([Documentation](https://dns.hostux.net/))
297
* `https://ldn-fai.net/dns-query` ([Documentation in french](https://ldn-fai.net/serveur-dns-recursif-ouvert/))
298
* `https://dns.digitale-gesellschaft.ch/dns-query` ([Documentation in german](https://www.digitale-gesellschaft.ch/dns/))
Stephane Bortzmeyer's avatar
Stephane Bortzmeyer committed
299
300
301
302
303
* `https://doh.ffmuc.net`
  ([Documentation](https://ffmuc.net/wiki/doku.php?id=knb:dohdot_en))
* `https://doh.libredns.gr/dns-query`
  ([Documentation](https://libredns.gr/); Also,
  `https://doh.libredns.gr/ads` is a lying resolver, blocking ads and trackers)
304
* `https://dns.switch.ch/dns-query` ([Documentation](https://www.switch.ch/security/info/public-dns/))
Stephane Bortzmeyer's avatar
Stephane Bortzmeyer committed
305
306
307
* `https://nat64.tuxis.nl`
  ([Documentation](https://www.tuxis.nl/blog/public-doh-dot-dns64-nat64-service-20191021/);
  NAT64, and no IPv4 address)
308
309
310
311
312
313
314

### DoT

* `dot.bortzmeyer.fr` ([Documentation](https://doh.bortzmeyer.fr/about)) 
* `dns.digitale-gesellschaft.ch` ([Documentation in german](https://www.digitale-gesellschaft.ch/dns/))
* `dot.ffmuc.net` ([Documentation](https://ffmuc.net/wiki/doku.php?id=knb:dohdot_en)) 
* `ns0.ldn-fai.net` ([Documentation in french](https://ldn-fai.net/serveur-dns-recursif-ouvert/))
Stephane Bortzmeyer's avatar
Stephane Bortzmeyer committed
315
* `dot.libredns.gr` ([Documentation](https://libredns.gr/))
Stephane Bortzmeyer's avatar
Stephane Bortzmeyer committed
316
317
318
319
320
321
* `dns.switch.ch` ([Documentation](https://www.switch.ch/security/info/public-dns/))
* `nat64.tuxis.net`
  ([Documentation](https://www.tuxis.nl/blog/public-doh-dot-dns64-nat64-service-20191021/);
  NAT64, and no IPv4 address)
* `anycast.censurfridns.dk` ([Documentation](https://blog.uncensoreddns.org/))
  
Stephane Bortzmeyer's avatar
Stephane Bortzmeyer committed
322
323
## License 

324
GPL. See LICENSE.
Stephane Bortzmeyer's avatar
Stephane Bortzmeyer committed
325
326
327

## Author

328
Stéphane Bortzmeyer <stephane+framagit@bortzmeyer.org> and Alexandre Pion
Stephane Bortzmeyer's avatar
Stephane Bortzmeyer committed
329

330
## Reference site
Stephane Bortzmeyer's avatar
Stephane Bortzmeyer committed
331

332
333
https://framagit.org/bortzmeyer/homer/ Use the Gitlab issue tracker to
report bugs or wishes.
Stephane Bortzmeyer's avatar
Stephane Bortzmeyer committed
334
335
336
337
338
339
340

## See also

* A [simple DoH client](https://github.com/curl/doh), from the author
  of curl.