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
c5601734
Commit
c5601734
authored
Apr 14, 2020
by
Stéphane Bortzmeyer
Browse files
Merge branch 'option-debug' into 'master'
Option debug Closes #25 See merge request bortzmeyer/homer!14
parents
2244deb3
203247a6
Changes
3
Hide whitespace changes
Inline
Side-by-side
README.md
View file @
c5601734
...
...
@@ -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 or -d: 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)
...
...
homer.py
View file @
c5601734
...
...
@@ -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
...
...
@@ -189,6 +190,12 @@ def validate_hostname(hostname, cert):
return
True
return
False
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'
)
...
...
@@ -222,13 +229,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
...
...
@@ -241,7 +250,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"
:
...
...
@@ -250,20 +259,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
...
...
@@ -277,7 +295,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
):
...
...
@@ -293,6 +311,7 @@ class Connection:
self
.
check
=
self
.
server
self
.
dot
=
dot
self
.
verbose
=
verbose
self
.
debug
=
debug
self
.
insecure
=
insecure
self
.
connect_to
=
connect
...
...
@@ -323,10 +342,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
:
...
...
@@ -392,12 +411,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
,
...
...
@@ -419,35 +438,39 @@ 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
,
qname
,
qtype
=
rtype
):
request
=
RequestDoT
(
qname
,
qtype
,
want_dnssec
=
dnssec
,
use_edns
=
edns
)
request
.
to_wire
()
self
.
send_and_receive
(
request
)
request
.
check_response
()
request
.
check_response
(
self
.
debug
)
return
request
class
ConnectionDoH
(
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
=
False
,
verbose
=
verbose
,
insecure
=
insecure
)
verbose
=
verbose
,
debug
=
debug
,
insecure
=
insecure
)
self
.
url
=
server
self
.
connect
=
connect
...
...
@@ -456,7 +479,7 @@ class ConnectionDoH(Connection):
# Does not work if pycurl was not compiled with nghttp2 (recent Debian
# packages are OK) https://github.com/pycurl/pycurl/issues/477
self
.
curl
.
setopt
(
pycurl
.
HTTP_VERSION
,
pycurl
.
CURL_HTTP_VERSION_2
)
if
self
.
verbose
:
if
self
.
debug
:
self
.
curl
.
setopt
(
pycurl
.
VERBOSE
,
True
)
if
self
.
insecure
:
self
.
curl
.
setopt
(
pycurl
.
SSL_VERIFYPEER
,
False
)
...
...
@@ -536,7 +559,7 @@ class ConnectionDoH(Connection):
request
.
ctype
=
content_type
self
.
buffer
.
close
()
def
send_and_receive
(
self
,
request
):
def
send_and_receive
(
self
,
request
,
dump
=
False
):
self
.
prepare
(
request
)
self
.
perform
()
self
.
receive
(
request
)
...
...
@@ -547,7 +570,7 @@ class ConnectionDoH(Connection):
request
.
post
=
post
request
.
to_wire
()
self
.
send_and_receive
(
request
)
request
.
check_response
()
request
.
check_response
(
self
.
debug
)
return
request
...
...
@@ -661,12 +684,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"
):
...
...
@@ -674,6 +701,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
)
connection
.
curl
.
setopt
(
pycurl
.
HTTPHEADER
,
header
)
...
...
@@ -682,12 +712,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
}
"
]
connection
.
curl
.
setopt
(
pycurl
.
HTTPHEADER
,
default_header
)
if
verbose
:
print
()
return
ok
def
run_check_trunc
(
connection
):
...
...
@@ -703,7 +735,7 @@ def run_check_trunc(connection):
request
.
post
=
True
try
:
# 8.8.8.8 replies FORMERR but most DoT servers violently shut down the connection (which is legal)
connection
.
send_and_receive
(
request
)
connection
.
send_and_receive
(
request
,
dump
=
debug
)
except
CustomException
as
e
:
ok
=
False
error
(
e
)
...
...
@@ -714,7 +746,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
:
...
...
@@ -725,6 +757,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
):
...
...
@@ -759,8 +793,8 @@ if not monitoring:
name
=
None
message
=
None
try
:
optlist
,
args
=
getopt
.
getopt
(
sys
.
argv
[
1
:],
"hvPkeV:r:f:d:t46"
,
[
"help"
,
"verbose"
,
"dot"
,
"head"
,
optlist
,
args
=
getopt
.
getopt
(
sys
.
argv
[
1
:],
"hv
d
PkeV:r:f:d:t46"
,
[
"help"
,
"verbose"
,
"debug"
,
"dot"
,
"head"
,
"insecure"
,
"POST"
,
"vhost="
,
"dnssec"
,
"noedns"
,
"ecs"
,
"repeat="
,
"file="
,
"delay="
,
"key="
,
"nosni"
,
...
...
@@ -774,6 +808,9 @@ if not monitoring:
dot
=
True
elif
option
==
"--verbose"
or
option
==
"-v"
:
verbose
=
True
elif
option
==
"--debug"
or
option
==
"-d"
:
debug
=
True
verbose
=
True
elif
option
==
"--HEAD"
or
option
==
"--head"
or
option
==
"-e"
:
head
=
True
elif
option
==
"--POST"
or
option
==
"--post"
or
option
==
"-P"
:
...
...
@@ -960,11 +997,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
,
insecure
=
insecure
)
except
TimeoutError
:
error
(
"timeout"
)
...
...
tests.yaml
View file @
c5601734
...
...
@@ -144,7 +144,7 @@ tests:
-
'
doh'
-
'
check'
args
:
-
'
-
v
'
-
'
-
d
'
-
'
--check'
-
'
https://doh.bortzmeyer.fr'
-
'
www.afnic.fr'
...
...
@@ -156,7 +156,7 @@ tests:
-
'
doh'
-
'
check'
args
:
-
'
-
v
'
-
'
-
d
'
-
'
--check'
-
'
https://doh.bortzmeyer.fr'
-
'
curl.haxx.se'
...
...
@@ -255,7 +255,7 @@ tests:
-
'
check'
-
'
forceIPv4'
args
:
-
'
-
v
'
-
'
-
d
'
-
'
-4'
-
'
--check'
-
'
https://dns.google/dns-query'
...
...
@@ -269,7 +269,7 @@ tests:
-
'
check'
-
'
forceIPv4'
args
:
-
'
-
v
'
-
'
-
d
'
-
'
-4'
-
'
--check'
-
'
https://dns.google/dns-query'
...
...
@@ -283,7 +283,7 @@ tests:
-
'
check'
-
'
forceIPv6'
args
:
-
'
-
v
'
-
'
-
d
'
-
'
-6'
-
'
--check'
-
'
https://dns.google/dns-query'
...
...
@@ -297,7 +297,7 @@ tests:
-
'
check'
-
'
forceIPv6'
args
:
-
'
-
v
'
-
'
-
d
'
-
'
-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