Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
Menu
Open sidebar
DNS testing tools
Remoh
Commits
855d949f
Commit
855d949f
authored
Oct 21, 2019
by
Stephane Bortzmeyer
Browse files
Initial import
parent
c83dc6eb
Changes
3
Hide whitespace changes
Inline
Side-by-side
README.md
View file @
855d949f
# Homer
Homer is a DoH (DNS-over-HTTPS) client. Its main purpose is to test DoH resolvers.
\ No newline at end of file
Homer is a DoH (DNS-over-HTTPS) client. Its main purpose is to test
DoH resolvers.
It is currently quite experimental.
## Usage
Two mandatory arguments, the URL of the DoH server, and a domain name
to query.
```
% ./homer.py https://doh.powerdns.org/ framagit.org
Test 0
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)
```
## Installation
You need (DNSpython)[http://www.dnspython.org/] and
(pycurl)[http://pycurl.io/docs/latest].
## License
See LICENSE.
## Author
Stéphane Bortzmeyer
<stephane+framagit@bortzmeyer.org>
check_doh.py
0 → 100755
View file @
855d949f
#!/usr/bin/env python3
"""Monitoring plugin (Nagios-compatible) for watching a DoH resolver.
The monitoring plugin API is documented at
<https://www.monitoring-plugins.org/doc/guidelines.html>.
"""
# Do not touch
# https://www.monitoring-plugins.org/doc/guidelines.html#AEN78
STATE_OK
=
0
STATE_WARNING
=
1
STATE_CRITICAL
=
2
STATE_UNKNOWN
=
3
STATE_DEPENDENT
=
4
# http://pycurl.io/docs/latest
import
pycurl
# http://www.dnspython.org/
import
dns.message
import
io
import
sys
import
base64
import
urllib.parse
import
socket
import
re
import
getopt
host
=
None
vhostname
=
None
path
=
None
lookup
=
None
ltype
=
'AAAA'
post
=
False
insecure
=
False
head
=
False
# TODO add an option: a string which is expected in the DNS response
try
:
optlist
,
args
=
getopt
.
getopt
(
sys
.
argv
[
1
:],
"H:n:p:V:t:Pih"
)
for
option
,
value
in
optlist
:
if
option
==
"-H"
:
host
=
value
elif
option
==
"-V"
:
vhostname
=
value
elif
option
==
"-n"
:
lookup
=
value
elif
option
==
"-t"
:
ltype
=
value
elif
option
==
"-p"
:
path
=
value
elif
option
==
"-P"
:
post
=
True
elif
option
==
"-i"
:
insecure
=
True
elif
option
==
"-h"
:
head
=
True
else
:
# Should never occur, it is trapped by getopt
print
(
"Unknown option %s"
%
option
)
sys
.
exit
(
STATE_UNKNOWN
)
except
getopt
.
error
as
reason
:
print
(
"Option parsing problem %s"
%
reason
)
sys
.
exit
(
STATE_UNKNOWN
)
if
len
(
args
)
>
0
:
print
(
"Too many arguments (
\"
%s
\"
)"
%
args
)
sys
.
exit
(
STATE_UNKNOWN
)
if
host
is
None
or
lookup
is
None
:
print
(
"Host and name to lookup are necessary"
)
sys
.
exit
(
STATE_UNKNOWN
)
if
post
and
head
:
print
(
"POST or HEAD but not both"
)
sys
.
exit
(
STATE_UNKNOWN
)
if
vhostname
is
not
None
:
url
=
"https://%s/"
%
vhostname
# host is ignored in that case, which is a bit strange
else
:
url
=
"https://%s/"
%
host
if
path
is
not
None
:
url
+=
path
try
:
buffer
=
io
.
BytesIO
()
c
=
pycurl
.
Curl
()
message
=
dns
.
message
.
make_query
(
lookup
,
dns
.
rdatatype
.
from_text
(
ltype
))
message
.
id
=
0
# DoH requests that
if
head
:
c
.
setopt
(
pycurl
.
NOBODY
,
True
)
if
post
:
c
.
setopt
(
c
.
URL
,
url
)
data
=
message
.
to_wire
()
c
.
setopt
(
pycurl
.
POST
,
True
)
c
.
setopt
(
pycurl
.
POSTFIELDS
,
data
)
else
:
dns_req
=
base64
.
urlsafe_b64encode
(
message
.
to_wire
()).
decode
(
'UTF8'
).
rstrip
(
'='
)
c
.
setopt
(
c
.
URL
,
url
+
(
"?dns=%s"
%
dns_req
))
c
.
setopt
(
pycurl
.
HTTPHEADER
,
[
"Content-type: application/dns-message"
])
c
.
setopt
(
c
.
WRITEDATA
,
buffer
)
if
insecure
:
c
.
setopt
(
pycurl
.
SSL_VERIFYPEER
,
False
)
c
.
setopt
(
pycurl
.
SSL_VERIFYHOST
,
False
)
# Does not work if pycurl was not compiled with nghttp2 (recent Debian
# packages are OK) https://github.com/pycurl/pycurl/issues/477
c
.
setopt
(
pycurl
.
HTTP_VERSION
,
pycurl
.
CURL_HTTP_VERSION_2
)
try
:
c
.
perform
()
except
Exception
as
e
:
print
(
"%s ERROR - %s"
%
(
url
,
"Cannot connect:
\"
%s
\"
"
%
e
))
sys
.
exit
(
STATE_CRITICAL
)
rcode
=
c
.
getinfo
(
pycurl
.
RESPONSE_CODE
)
c
.
close
()
if
rcode
==
200
:
if
not
head
:
body
=
buffer
.
getvalue
()
try
:
response
=
dns
.
message
.
from_wire
(
body
)
# May be we should test the DNS response code as well?
except
dns
.
name
.
BadLabelType
as
e
:
print
(
"%s ERROR - %s"
%
(
url
,
"Not a DNS reply, is it a DoH server?
\"
%s
\"
"
%
e
))
sys
.
exit
(
STATE_CRITICAL
)
print
(
"%s OK - %s"
%
(
url
,
"No error for %s/%s, %i bytes received"
%
(
lookup
,
ltype
,
sys
.
getsizeof
(
body
))))
else
:
print
(
"%s OK - %s"
%
(
url
,
"No error"
))
sys
.
exit
(
STATE_OK
)
else
:
body
=
buffer
.
getvalue
()
if
len
(
body
)
==
0
:
body
=
b
"[No details]"
print
(
"%s HTTP error - %i: %s"
%
(
url
,
rcode
,
body
.
decode
()))
sys
.
exit
(
STATE_CRITICAL
)
except
Exception
as
e
:
print
(
"%s UNKNOWN - %s"
%
(
url
,
"Unknown internal error:
\"
%s
\"
"
%
e
))
sys
.
exit
(
STATE_UNKNOWN
)
homer.py
0 → 100755
View file @
855d949f
#!/usr/bin/env python3
# http://pycurl.io/docs/latest
import
pycurl
# http://www.dnspython.org/
import
dns.message
import
io
import
sys
import
base64
import
getopt
import
urllib.parse
import
time
post
=
False
verbose
=
False
insecure
=
False
head
=
False
rtype
=
'AAAA'
tests
=
1
# Number of repeated tests
def
error
(
msg
=
None
):
if
msg
is
None
:
msg
=
"Unknown error"
print
(
msg
,
file
=
sys
.
stderr
)
sys
.
exit
(
1
)
def
usage
(
msg
=
None
):
if
msg
:
print
(
msg
,
file
=
sys
.
stderr
)
print
(
"Usage: %s [-P] [-k] url domain-name [DNS type]"
%
sys
.
argv
[
0
],
file
=
sys
.
stderr
)
try
:
optlist
,
args
=
getopt
.
getopt
(
sys
.
argv
[
1
:],
"hvPker:"
,
[
"help"
,
"verbose"
,
"head"
,
"insecure"
,
"POST"
,
"repeat="
])
for
option
,
value
in
optlist
:
if
option
==
"--help"
or
option
==
"-h"
:
usage
()
sys
.
exit
(
0
)
elif
option
==
"--verbose"
or
option
==
"-v"
:
verbose
=
True
elif
option
==
"--head"
or
option
==
"-e"
:
head
=
True
elif
option
==
"--insecure"
or
option
==
"-k"
:
insecure
=
True
elif
option
==
"--POST"
or
option
==
"-P"
:
post
=
True
elif
option
==
"--repeat"
or
option
==
"-r"
:
tests
=
int
(
value
)
if
tests
<=
1
:
error
(
"--repeat needs a value > 1"
)
else
:
error
(
"Unknown option %s"
%
option
)
except
getopt
.
error
as
reason
:
usage
(
reason
)
sys
.
exit
(
1
)
if
post
and
head
:
usage
(
"POST or HEAD but not both"
)
sys
.
exit
(
1
)
if
len
(
args
)
!=
2
and
len
(
args
)
!=
3
:
usage
(
"Wrong number of arguments"
)
sys
.
exit
(
1
)
url
=
args
[
0
]
name
=
args
[
1
]
if
len
(
args
)
==
3
:
rtype
=
args
[
2
]
c
=
pycurl
.
Curl
()
message
=
dns
.
message
.
make_query
(
name
,
dns
.
rdatatype
.
from_text
(
rtype
))
message
.
id
=
0
# DoH requests that
if
head
:
c
.
setopt
(
pycurl
.
NOBODY
,
True
)
if
post
:
c
.
setopt
(
c
.
URL
,
url
)
data
=
message
.
to_wire
()
c
.
setopt
(
pycurl
.
POST
,
True
)
c
.
setopt
(
pycurl
.
POSTFIELDS
,
data
)
else
:
dns_req
=
base64
.
urlsafe_b64encode
(
message
.
to_wire
()).
decode
(
'UTF8'
).
rstrip
(
'='
)
c
.
setopt
(
c
.
URL
,
url
+
(
"?dns=%s"
%
dns_req
))
c
.
setopt
(
pycurl
.
HTTPHEADER
,
[
"Content-type: application/dns-message"
])
# libcurl sets HTTP persistence automatically, thus handling the case if tests > 1
if
verbose
:
c
.
setopt
(
c
.
VERBOSE
,
True
)
if
insecure
:
c
.
setopt
(
pycurl
.
SSL_VERIFYPEER
,
False
)
c
.
setopt
(
pycurl
.
SSL_VERIFYHOST
,
False
)
# Does not work if pycurl was not compiled with nghttp2 (recent Debian
# packages are OK) https://github.com/pycurl/pycurl/issues/477
c
.
setopt
(
pycurl
.
HTTP_VERSION
,
pycurl
.
CURL_HTTP_VERSION_2
)
ok
=
True
start
=
time
.
time
()
for
i
in
range
(
0
,
tests
):
print
(
"Test %i"
%
i
)
buffer
=
io
.
BytesIO
()
c
.
setopt
(
c
.
WRITEDATA
,
buffer
)
c
.
perform
()
rcode
=
c
.
getinfo
(
pycurl
.
RESPONSE_CODE
)
if
rcode
==
200
:
if
not
head
:
body
=
buffer
.
getvalue
()
try
:
response
=
dns
.
message
.
from_wire
(
body
)
except
dns
.
message
.
TrailingJunk
:
# Not DNS.
response
=
"ERROR Not proper DNS data
\"
%s
\"
"
%
body
ok
=
False
print
(
response
)
else
:
print
(
"HEAD request successful"
)
else
:
body
=
buffer
.
getvalue
()
if
len
(
body
)
==
0
:
body
=
b
"[No details]"
print
(
"HTTP error %i: %s"
%
(
rcode
,
body
[
0
:
1000
].
decode
()),
file
=
sys
.
stderr
)
ok
=
False
buffer
.
close
()
c
.
close
()
stop
=
time
.
time
()
print
(
"Total elapsed time: %.2f seconds (%.2f ms/request)"
%
(
stop
-
start
,
(
stop
-
start
)
*
1000
/
tests
))
if
ok
:
sys
.
exit
(
0
)
else
:
sys
.
exit
(
1
)
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment