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
c527f2f4
Commit
c527f2f4
authored
Apr 14, 2020
by
Alexandre
Browse files
Merge branch 'master' into streams
parents
657a5eb5
40322ce6
Changes
3
Hide whitespace changes
Inline
Side-by-side
README.md
View file @
c527f2f4
...
...
@@ -45,6 +45,7 @@ Total elapsed time: 0.07 seconds (66.72 ms/request )
Possible options, besides
`--dot`
:
*
--verbose or -v: Makes the program more talkative
*
--debug: Makes the program very talkative (sets verbose to true)
*
--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)
...
...
@@ -124,7 +125,8 @@ one):
```
When repeating tests, you can add a delay between tests, with
`--delay
N`
, where N is the (possibly fractional) number of seconds to wait.
N`
or
`-d N`
, where N is the (possibly fractional) number of seconds
to wait.
### Mulitstreams
...
...
homer.py
View file @
c527f2f4
...
...
@@ -36,6 +36,7 @@ import signal
# Values that can be changed from the command line
dot
=
False
# DoH by default
verbose
=
False
debug
=
False
insecure
=
False
post
=
False
head
=
False
...
...
@@ -212,6 +213,12 @@ def check_ip_address(addr, dot=dot):
family
=
0
return
(
family
,
repraddress
)
def
dump_data
(
data
,
text
=
"data"
):
pref
=
' '
*
(
len
(
text
)
-
4
)
print
(
f
'
{
text
}
: '
,
data
)
print
(
pref
,
'hex:'
,
" "
.
join
(
format
(
c
,
'02x'
)
for
c
in
data
))
print
(
pref
,
'bin:'
,
" "
.
join
(
format
(
c
,
'08b'
)
for
c
in
data
))
def
timeout_connection
(
signum
,
frame
):
raise
TimeoutConnectionError
(
'Connection timeout'
)
...
...
@@ -245,13 +252,15 @@ class Request:
class
RequestDoT
(
Request
):
def
check_response
(
self
):
def
check_response
(
self
,
debug
=
False
):
ok
=
self
.
ok
if
not
self
.
rcode
:
self
.
ok
=
False
return
False
if
self
.
response
.
id
!=
self
.
message
.
id
:
self
.
response
=
"The ID in the answer does not match the one in the query"
if
debug
:
self
.
response
+=
f
'"(query id:
{
self
.
message
.
id
}
) (response id:
{
self
.
response
.
id
}
)'
self
.
ok
=
False
return
False
return
self
.
ok
...
...
@@ -264,7 +273,7 @@ class RequestDoH(Request):
self
.
post
=
False
self
.
head
=
False
def
check_response
(
self
):
def
check_response
(
self
,
debug
=
False
):
ok
=
self
.
ok
if
self
.
rcode
==
200
:
if
self
.
ctype
!=
"application/dns-message"
:
...
...
@@ -273,20 +282,29 @@ class RequestDoH(Request):
else
:
if
not
self
.
head
:
try
:
self
.
response
=
dns
.
message
.
from_wire
(
self
.
response
)
response
=
dns
.
message
.
from_wire
(
self
.
response
)
except
dns
.
message
.
TrailingJunk
:
# Not DNS. Should
# not happen for a content type
# application/dns-message but who knows?
self
.
response
=
"ERROR Not proper DNS data, trailing junk
\"
%s
\"
"
%
self
.
response
self
.
response
=
"ERROR Not proper DNS data, trailing junk"
if
debug
:
self
.
response
+=
"
\"
%s
\"
"
%
response
ok
=
False
except
dns
.
name
.
BadLabelType
:
# Not DNS.
self
.
response
=
"ERROR Not proper DNS data (wrong path in the URL?)
\"
%s
\"
"
%
self
.
response
[:
100
]
self
.
response
=
"ERROR Not proper DNS data (wrong path in the URL?)"
if
debug
:
self
.
response
+=
"
\"
%s
\"
"
%
response
[:
100
]
ok
=
False
else
:
self
.
response
=
response
else
:
if
self
.
response_size
==
0
:
self
.
response
=
"HEAD successful"
else
:
self
.
response
=
"ERROR Body length is not null
\"
%s
\"
"
%
self
.
response
[:
100
]
data
=
self
.
response
self
.
response
=
"ERROR Body length is not null"
if
debug
:
self
.
response
+=
"
\"
%s
\"
"
%
data
[:
100
]
ok
=
False
else
:
ok
=
False
...
...
@@ -300,7 +318,7 @@ class RequestDoH(Request):
class
Connection
:
def
__init__
(
self
,
server
,
servername
=
None
,
connect
=
None
,
forceIPv4
=
False
,
forceIPv6
=
False
,
dot
=
dot
,
verbose
=
verbose
,
insecure
=
insecure
):
dot
=
dot
,
verbose
=
verbose
,
debug
=
debug
,
insecure
=
insecure
):
if
dot
and
not
is_valid_hostname
(
server
):
error
(
"DoT requires a host name or IP address, not
\"
%s
\"
"
%
server
)
if
not
dot
and
not
is_valid_url
(
server
):
...
...
@@ -316,6 +334,7 @@ class Connection:
self
.
check
=
self
.
server
self
.
dot
=
dot
self
.
verbose
=
verbose
self
.
debug
=
debug
self
.
insecure
=
insecure
self
.
forceIPv4
=
forceIPv4
self
.
forceIPv6
=
forceIPv6
...
...
@@ -331,10 +350,10 @@ class Connection:
class
ConnectionDoT
(
Connection
):
def
__init__
(
self
,
server
,
servername
=
None
,
connect
=
None
,
forceIPv4
=
False
,
forceIPv6
=
False
,
verbose
=
verbose
,
insecure
=
insecure
):
verbose
=
verbose
,
debug
=
debug
,
insecure
=
insecure
):
Connection
.
__init__
(
self
,
server
,
servername
=
servername
,
connect
=
connect
,
forceIPv4
=
forceIPv4
,
forceIPv6
=
forceIPv6
,
dot
=
True
,
verbose
=
verbose
,
insecure
=
insecure
)
verbose
=
verbose
,
debug
=
debug
,
insecure
=
insecure
)
if
connect
is
not
None
:
addr
=
connect
else
:
...
...
@@ -400,12 +419,12 @@ class ConnectionDoT(Connection):
# RFC 7858, section 4.2 and appendix A
self
.
cert
=
self
.
session
.
get_peer_certificate
()
self
.
publickey
=
self
.
cert
.
get_pubkey
()
if
verbose
or
key
is
not
None
:
if
debug
or
key
is
not
None
:
self
.
hasher
.
update
(
OpenSSL
.
crypto
.
dump_publickey
(
OpenSSL
.
crypto
.
FILETYPE_ASN1
,
self
.
publickey
))
self
.
digest
=
self
.
hasher
.
digest
()
key_string
=
base64
.
standard_b64encode
(
self
.
digest
).
decode
()
if
verbose
:
if
debug
:
print
(
"Certificate #%x for
\"
%s
\"
, delivered by
\"
%s
\"
"
%
\
(
self
.
cert
.
get_serial_number
(),
self
.
cert
.
get_subject
().
commonName
,
...
...
@@ -427,24 +446,28 @@ class ConnectionDoT(Connection):
self
.
session
.
shutdown
()
self
.
session
.
close
()
def
send_data
(
self
,
data
):
def
send_data
(
self
,
data
,
dump
=
False
):
if
dump
:
dump_data
(
data
,
'data sent'
)
length
=
len
(
data
)
self
.
session
.
send
(
length
.
to_bytes
(
2
,
byteorder
=
'big'
)
+
data
)
def
receive_data
(
self
,
request
):
def
receive_data
(
self
,
request
,
dump
=
False
):
buf
=
self
.
session
.
recv
(
2
)
request
.
response_size
=
int
.
from_bytes
(
buf
,
byteorder
=
'big'
)
buf
=
self
.
session
.
recv
(
request
.
response_size
)
if
dump
:
dump_data
(
buf
,
'data recv'
)
request
.
response
=
dns
.
message
.
from_wire
(
buf
)
request
.
rcode
=
True
def
send_and_receive
(
self
,
request
):
self
.
send_data
(
request
.
data
)
self
.
receive_data
(
request
)
def
send_and_receive
(
self
,
request
,
dump
=
False
):
self
.
send_data
(
request
.
data
,
dump
=
dump
)
self
.
receive_data
(
request
,
dump
=
dump
)
def
do_test
(
self
,
request
,
synchronous
=
True
):
self
.
send_and_receive
(
request
)
request
.
check_response
()
request
.
check_response
(
self
.
debug
)
def
create_handle
(
connection
):
...
...
@@ -479,7 +502,7 @@ def create_handle(connection):
# Does not work if pycurl was not compiled with nghttp2 (recent Debian
# packages are OK) https://github.com/pycurl/pycurl/issues/477
handle
.
setopt
(
pycurl
.
HTTP_VERSION
,
pycurl
.
CURL_HTTP_VERSION_2
)
if
connection
.
verbose
:
if
connection
.
debug
:
handle
.
setopt
(
pycurl
.
VERBOSE
,
True
)
if
connection
.
insecure
:
handle
.
setopt
(
pycurl
.
SSL_VERIFYPEER
,
False
)
...
...
@@ -500,10 +523,10 @@ def create_handle(connection):
class
ConnectionDoH
(
Connection
):
def
__init__
(
self
,
server
,
servername
=
None
,
connect
=
None
,
forceIPv4
=
False
,
forceIPv6
=
False
,
multistreams
=
False
,
verbose
=
verbose
,
insecure
=
insecure
):
multistreams
=
False
,
verbose
=
verbose
,
debug
=
debug
,
insecure
=
insecure
):
Connection
.
__init__
(
self
,
server
,
servername
=
servername
,
connect
=
connect
,
forceIPv4
=
forceIPv4
,
forceIPv6
=
forceIPv6
,
dot
=
False
,
verbose
=
verbose
,
insecure
=
insecure
)
verbose
=
verbose
,
debug
=
debug
,
insecure
=
insecure
)
self
.
url
=
server
self
.
connect
=
connect
self
.
multistreams
=
multistreams
...
...
@@ -579,7 +602,7 @@ class ConnectionDoH(Connection):
request
.
ctype
=
content_type
handle
.
buffer
.
close
()
def
send_and_receive
(
self
,
handle
):
def
send_and_receive
(
self
,
handle
,
dump
=
False
):
self
.
send
(
handle
)
self
.
receive
(
handle
)
...
...
@@ -611,7 +634,7 @@ class ConnectionDoH(Connection):
handle
.
prepare
(
handle
,
self
,
request
)
if
synchronous
:
self
.
send_and_receive
(
handle
)
request
.
check_response
()
request
.
check_response
(
self
.
debug
)
else
:
self
.
multi
.
add_handle
(
handle
)
...
...
@@ -731,12 +754,16 @@ def run_check_default(connection):
ok
=
False
error
(
e
)
break
request
.
check_response
()
request
.
check_response
(
debug
)
if
not
print_result
(
connection
,
request
,
prefix
=
test_name
,
display_err
=
False
):
if
mandatory
>=
mandatory_level
:
print_result
(
connection
,
request
,
prefix
=
test_name
,
display_err
=
True
)
ok
=
False
if
verbose
:
print
()
break
if
verbose
:
print
()
return
ok
def
run_check_mime
(
connection
,
accept
=
"application/dns-message"
,
content_type
=
"application/dns-message"
):
...
...
@@ -744,6 +771,9 @@ def run_check_mime(connection, accept="application/dns-message", content_type="a
return
True
ok
=
True
header
=
[
f
"Accept:
{
accept
}
"
,
f
"Content-type:
{
content_type
}
"
]
if
verbose
:
test_name
=
f
'Test mime:
{
", "
.
join
(
h
for
h
in
header
)
}
'
print
(
test_name
)
req_args
=
{
'qname'
:
name
,
'qtype'
:
rtype
,
'use_edns'
:
edns
,
'want_dnssec'
:
dnssec
}
request
=
create_request
(
**
req_args
)
handle
=
connection
.
curl_handle
...
...
@@ -754,12 +784,14 @@ def run_check_mime(connection, accept="application/dns-message", content_type="a
except
CustomException
as
e
:
ok
=
False
error
(
e
)
request
.
check_response
()
request
.
check_response
(
debug
)
if
not
print_result
(
connection
,
request
,
prefix
=
f
"Test Header
{
', '
.
join
(
header
)
}
"
):
ok
=
False
default
=
"application/dns-message"
default_header
=
[
f
"Accept:
{
default
}
"
,
f
"Content-type:
{
default
}
"
]
handle
.
setopt
(
pycurl
.
HTTPHEADER
,
default_header
)
if
verbose
:
print
()
return
ok
def
run_check_trunc
(
connection
):
...
...
@@ -779,7 +811,7 @@ def run_check_trunc(connection):
bundle
=
handle
try
:
# 8.8.8.8 replies FORMERR but most DoT servers violently shut down the connection (which is legal)
connection
.
send_and_receive
(
bundle
)
connection
.
send_and_receive
(
bundle
,
dump
=
debug
)
except
CustomException
as
e
:
ok
=
False
error
(
e
)
...
...
@@ -790,7 +822,7 @@ def run_check_trunc(connection):
# the RCODE set to FORMERR
# so response can not be parsed in this case
return
ok
if
request
.
check_response
():
# FORMERR is expected
if
request
.
check_response
(
debug
):
# FORMERR is expected
if
dot
:
ok
=
request
.
rcode
==
dns
.
rcode
.
FORMERR
else
:
...
...
@@ -801,6 +833,8 @@ def run_check_trunc(connection):
else
:
# a 400 response's status is acceptable
ok
=
(
request
.
rcode
>=
400
and
request
.
rcode
<
500
)
print_result
(
connection
,
request
,
prefix
=
test_name
,
display_err
=
not
ok
)
if
verbose
:
print
()
return
ok
def
run_check_additionals
(
connection
):
...
...
@@ -836,7 +870,7 @@ if not monitoring:
message
=
None
try
:
optlist
,
args
=
getopt
.
getopt
(
sys
.
argv
[
1
:],
"hvPkeV:r:f:d:t46"
,
[
"help"
,
"verbose"
,
"dot"
,
"head"
,
[
"help"
,
"verbose"
,
"debug"
,
"dot"
,
"head"
,
"insecure"
,
"POST"
,
"vhost="
,
"multistreams"
,
"sync"
,
"no-display-results"
,
"time"
,
"dnssec"
,
"noedns"
,
"ecs"
,
"repeat="
,
"file="
,
"delay="
,
...
...
@@ -851,6 +885,9 @@ if not monitoring:
dot
=
True
elif
option
==
"--verbose"
or
option
==
"-v"
:
verbose
=
True
elif
option
==
"--debug"
:
debug
=
True
verbose
=
True
elif
option
==
"--HEAD"
or
option
==
"--head"
or
option
==
"-e"
:
head
=
True
elif
option
==
"--POST"
or
option
==
"--post"
or
option
==
"-P"
:
...
...
@@ -1066,11 +1103,11 @@ for connectTo in ip_set:
try
:
if
dot
:
conn
=
ConnectionDoT
(
url
,
servername
=
extracheck
,
connect
=
connectTo
,
verbose
=
verbose
,
forceIPv4
=
forceIPv4
,
forceIPv6
=
forceIPv6
,
debug
=
debug
,
forceIPv4
=
forceIPv4
,
forceIPv6
=
forceIPv6
,
insecure
=
insecure
)
else
:
conn
=
ConnectionDoH
(
url
,
servername
=
extracheck
,
connect
=
connectTo
,
verbose
=
verbose
,
forceIPv4
=
forceIPv4
,
forceIPv6
=
forceIPv6
,
debug
=
debug
,
forceIPv4
=
forceIPv4
,
forceIPv6
=
forceIPv6
,
multistreams
=
multistreams
,
insecure
=
insecure
)
except
TimeoutError
:
error
(
"timeout"
)
...
...
tests.yaml
View file @
c527f2f4
...
...
@@ -145,7 +145,7 @@ tests:
-
'
doh'
-
'
check'
args
:
-
'
-
v
'
-
'
-
-debug
'
-
'
--check'
-
'
https://doh.bortzmeyer.fr'
-
'
www.afnic.fr'
...
...
@@ -157,7 +157,7 @@ tests:
-
'
doh'
-
'
check'
args
:
-
'
-
v
'
-
'
-
-debug
'
-
'
--check'
-
'
https://doh.bortzmeyer.fr'
-
'
curl.haxx.se'
...
...
@@ -256,7 +256,7 @@ tests:
-
'
check'
-
'
forceIPv4'
args
:
-
'
-
v
'
-
'
-
-debug
'
-
'
-4'
-
'
--check'
-
'
https://dns.google/dns-query'
...
...
@@ -270,7 +270,7 @@ tests:
-
'
check'
-
'
forceIPv4'
args
:
-
'
-
v
'
-
'
-
-debug
'
-
'
-4'
-
'
--check'
-
'
https://dns.google/dns-query'
...
...
@@ -284,7 +284,7 @@ tests:
-
'
check'
-
'
forceIPv6'
args
:
-
'
-
v
'
-
'
-
-debug
'
-
'
-6'
-
'
--check'
-
'
https://dns.google/dns-query'
...
...
@@ -298,7 +298,7 @@ tests:
-
'
check'
-
'
forceIPv6'
args
:
-
'
-
v
'
-
'
-
-debug
'
-
'
-6'
-
'
--check'
-
'
https://dns.google/dns-query'
...
...
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