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
3f75d576
Commit
3f75d576
authored
Sep 15, 2020
by
Alexandre
Browse files
Move code for monitoring in new file
parent
53f34611
Changes
4
Hide whitespace changes
Inline
Side-by-side
check_doh
View file @
3f75d576
homer.py
\ No newline at end of file
monitor.py
\ No newline at end of file
check_dot
View file @
3f75d576
./homer.py
\ No newline at end of file
monitor.py
\ No newline at end of file
homer.py
View file @
3f75d576
...
...
@@ -59,31 +59,15 @@ class opts:
check
=
False
mandatory_level
=
None
check_additional
=
True
# Monitoring plugin only:
host
=
None
path
=
None
expect
=
None
# For the monitoring plugin
STATE_OK
=
0
STATE_WARNING
=
1
STATE_CRITICAL
=
2
STATE_UNKNOWN
=
3
STATE_DEPENDENT
=
4
def
error
(
msg
=
None
,
exit
=
True
):
if
msg
is
None
:
msg
=
"Unknown error"
if
monitoring
:
print
(
"%s: %s"
%
(
url
,
msg
))
if
exit
:
sys
.
exit
(
STATE_CRITICAL
)
else
:
print
(
msg
,
file
=
sys
.
stderr
)
if
opts
.
check
:
print
(
'KO'
)
if
exit
:
sys
.
exit
(
1
)
print
(
msg
,
file
=
sys
.
stderr
)
if
opts
.
check
:
print
(
'KO'
)
if
exit
:
sys
.
exit
(
1
)
def
usage
(
msg
=
None
):
if
msg
:
...
...
@@ -159,42 +143,24 @@ def print_result(connection, request, prefix=None, display_err=True):
msg
=
request
.
response
size
=
request
.
response_size
if
(
dot
and
rcode
)
or
(
not
dot
and
rcode
==
200
):
if
not
monitoring
:
if
not
opts
.
dot
and
opts
.
show_time
:
connection
.
print_time
(
connection
.
curl_handle
)
if
opts
.
display_results
and
(
not
opts
.
check
or
opts
.
verbose
):
print
(
msg
)
else
:
if
opts
.
expect
is
not
None
and
opts
.
expect
not
in
str
(
request
.
response
):
ok
=
False
print
(
"%s Cannot find
\"
%s
\"
in response"
%
(
server
,
opts
.
expect
))
sys
.
exit
(
STATE_CRITICAL
)
if
ok
and
size
is
not
None
and
size
>
0
:
print
(
"%s OK - %s"
%
(
server
,
"No error for %s/%s, %i bytes received"
%
(
name
,
opts
.
rtype
,
size
)))
else
:
print
(
"%s OK - %s"
%
(
server
,
"No error"
))
sys
.
exit
(
STATE_OK
)
if
not
opts
.
dot
and
opts
.
show_time
:
connection
.
print_time
(
connection
.
curl_handle
)
if
opts
.
display_results
and
(
not
opts
.
check
or
opts
.
verbose
):
print
(
msg
)
else
:
if
not
monitoring
:
if
display_err
:
if
opts
.
check
:
print
(
connection
.
connect_to
,
end
=
': '
,
file
=
sys
.
stderr
)
if
prefix
:
print
(
prefix
,
end
=
': '
,
file
=
sys
.
stderr
)
if
dot
:
print
(
"Error: %s"
%
msg
,
file
=
sys
.
stderr
)
else
:
try
:
msg
=
msg
.
decode
()
except
(
UnicodeDecodeError
,
AttributeError
):
pass
# Sometimes, msg can be binary, or Latin-1
print
(
"HTTP error %i: %s"
%
(
rcode
,
msg
),
file
=
sys
.
stderr
)
else
:
if
not
dot
:
print
(
"%s HTTP error - %i: %s"
%
(
server
,
rcode
,
msg
))
if
display_err
:
if
opts
.
check
:
print
(
connection
.
connect_to
,
end
=
': '
,
file
=
sys
.
stderr
)
if
prefix
:
print
(
prefix
,
end
=
': '
,
file
=
sys
.
stderr
)
if
dot
:
print
(
"Error: %s"
%
msg
,
file
=
sys
.
stderr
)
else
:
print
(
"%s Error - %i: %s"
%
(
server
,
rcode
,
msg
))
sys
.
exit
(
STATE_CRITICAL
)
try
:
msg
=
msg
.
decode
()
except
(
UnicodeDecodeError
,
AttributeError
):
pass
# Sometimes, msg can be binary, or Latin-1
print
(
"HTTP error %i: %s"
%
(
rcode
,
msg
),
file
=
sys
.
stderr
)
ok
=
False
return
ok
...
...
@@ -489,79 +455,6 @@ def parse_opts(opts):
return
(
url
,
name
)
def
parse_opts_monitoring
(
me
,
opts
):
name
=
None
opts
.
dot
=
(
me
==
"check_dot"
)
rtype
=
opts
.
rtype
try
:
optlist
,
args
=
getopt
.
getopt
(
sys
.
argv
[
1
:],
"H:n:p:V:t:e:Pih46k:x"
)
for
option
,
value
in
optlist
:
if
option
==
"-H"
:
opts
.
host
=
value
elif
option
==
"-V"
:
opts
.
vhostname
=
value
elif
option
==
"-n"
:
name
=
value
elif
option
==
"-t"
:
opts
.
rtype
=
value
elif
option
==
"-e"
:
opts
.
expect
=
value
elif
option
==
"-p"
:
opts
.
path
=
value
elif
option
==
"-P"
:
opts
.
post
=
True
elif
option
==
"-h"
:
opts
.
head
=
True
elif
option
==
"-i"
:
opts
.
insecure
=
True
elif
option
==
"-x"
:
opts
.
sni
=
False
elif
option
==
"-4"
:
opts
.
forceIPv4
=
True
elif
option
==
"-6"
:
opts
.
forceIPv6
=
True
elif
option
==
"-k"
:
opts
.
key
=
value
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
opts
.
host
is
None
or
name
is
None
:
print
(
"Host (-H) and name to lookup (-n) are necessary"
)
sys
.
exit
(
STATE_UNKNOWN
)
if
opts
.
post
and
opts
.
head
:
print
(
"POST or HEAD but not both"
)
sys
.
exit
(
STATE_UNKNOWN
)
if
opts
.
dot
and
(
opts
.
post
or
opts
.
head
):
print
(
"POST or HEAD makes no sense for DoT"
)
sys
.
exit
(
STATE_UNKNOWN
)
if
opts
.
dot
and
opts
.
path
:
print
(
"URL path makes no sense for DoT"
)
sys
.
exit
(
STATE_UNKNOWN
)
if
opts
.
dot
:
url
=
opts
.
host
else
:
if
opts
.
vhostname
is
None
or
opts
.
vhostname
==
opts
.
host
:
opts
.
connectTo
=
None
url
=
"https://%s/"
%
opts
.
host
else
:
opts
.
connectTo
=
opts
.
host
url
=
"https://%s/"
%
opts
.
vhostname
if
opts
.
path
is
not
None
:
if
opts
.
path
.
startswith
(
"/"
):
opts
.
path
=
opts
.
path
[
1
:]
url
+=
opts
.
path
return
(
url
,
name
)
def
run_default
(
name
,
connection
,
opts
):
ok
=
True
start
=
time
.
time
()
...
...
@@ -626,7 +519,7 @@ def run_default(name, connection, opts):
extra
=
", %.2f ms/request if we ignore the first one"
%
((
stop
-
start2
)
*
1000
/
(
opts
.
tests
-
1
))
else
:
extra
=
""
if
not
monitoring
and
(
not
opts
.
check
or
opts
.
verbose
)
:
if
not
opts
.
check
or
opts
.
verbose
:
time_tot
=
stop
-
start
time_per_request
=
time_tot
/
opts
.
tests
*
1000
print
(
"
\n
Total elapsed time: %.2f seconds (%.2f ms/request%s)"
%
(
time_tot
,
time_per_request
,
extra
))
...
...
@@ -638,18 +531,10 @@ def run_default(name, connection, opts):
# Main program
me
=
os
.
path
.
basename
(
sys
.
argv
[
0
])
# Are we using the script as monitor ?
# the monitoring code should move somewhere else
monitoring
=
(
me
==
"check_doh"
or
me
==
"check_dot"
)
if
not
monitoring
:
url
,
name
=
parse_opts
(
opts
)
else
:
# Monitoring plugin
url
,
name
=
parse_opts_monitoring
(
me
,
opts
)
url
,
name
=
parse_opts
(
opts
)
# retrieve all ips when using --check
# not necessary if connectTo is already defined
# as it is the case with --monitoring
if
not
opts
.
check
or
opts
.
connectTo
is
not
None
:
ip_set
=
{
opts
.
connectTo
,
}
else
:
...
...
@@ -705,14 +590,11 @@ for ip in ip_set:
except
socket
.
gaierror
:
error
(
"Could not resolve
\"
%s
\"
"
%
url
)
except
homer
.
ConnectionDOTException
as
e
:
if
not
monitoring
:
print
(
e
,
file
=
sys
.
stderr
)
err
=
"Could not connect to
\"
%s
\"
"
%
url
if
opts
.
connectTo
is
not
None
:
err
+=
" on %s"
%
opts
.
connectTo
error
(
err
)
else
:
error
(
e
)
print
(
e
,
file
=
sys
.
stderr
)
err
=
"Could not connect to
\"
%s
\"
"
%
url
if
opts
.
connectTo
is
not
None
:
err
+=
" on %s"
%
opts
.
connectTo
error
(
err
)
except
(
homer
.
ConnectionException
,
homer
.
DOHException
)
as
e
:
error
(
e
)
if
conn
.
dot
and
not
conn
.
success
:
...
...
monitor.py
0 → 100755
View file @
3f75d576
#!/usr/bin/env python3
# Homer is a DoH (DNS-over-HTTPS) and DoT (DNS-over-TLS) client. Its
# main purpose is to test DoH and DoT resolvers. Reference site is
# <https://framagit.org/bortzmeyer/homer/> See author, documentation,
# etc, there, or in the README.md included with the distribution.
import
sys
import
getopt
import
urllib.parse
import
time
import
socket
import
os.path
try
:
# http://pycurl.io/docs/latest
import
pycurl
# http://www.dnspython.org/
import
dns.message
# Octobre 2019: the Python GnuTLS bindings don't work with Python 3. So we use OpenSSL.
# https://www.pyopenssl.org/
# https://pyopenssl.readthedocs.io/
import
OpenSSL
except
ImportError
as
e
:
print
(
"Error: missing module"
)
print
(
e
)
sys
.
exit
(
1
)
import
homer
# Values that can be changed from the command line
class
opts
:
#"H:n:p:V:t:e:Pih46k:x"
dot
=
False
# DoH by default
dnssec
=
False
edns
=
True
no_ecs
=
True
connectTo
=
None
# Monitoring plugin only:
host
=
None
vhostname
=
None
rtype
=
'AAAA'
expect
=
None
path
=
None
post
=
False
head
=
False
insecure
=
False
sni
=
True
forceIPv4
=
False
forceIPv6
=
False
key
=
None
# SPKI
# For the monitoring plugin
STATE_OK
=
0
STATE_WARNING
=
1
STATE_CRITICAL
=
2
STATE_UNKNOWN
=
3
STATE_DEPENDENT
=
4
def
error
(
msg
=
None
):
if
msg
is
None
:
msg
=
"Unknown error"
print
(
"%s: %s"
%
(
url
,
msg
))
sys
.
exit
(
STATE_CRITICAL
)
def
print_result
(
connection
,
request
,
prefix
=
None
,
display_err
=
True
):
ok
=
request
.
ok
dot
=
connection
.
dot
server
=
connection
.
server
rcode
=
request
.
rcode
msg
=
request
.
response
size
=
request
.
response_size
if
(
dot
and
rcode
)
or
(
not
dot
and
rcode
==
200
):
if
opts
.
expect
is
not
None
and
opts
.
expect
not
in
str
(
request
.
response
):
ok
=
False
print
(
"%s Cannot find
\"
%s
\"
in response"
%
(
server
,
opts
.
expect
))
sys
.
exit
(
STATE_CRITICAL
)
if
ok
and
size
is
not
None
and
size
>
0
:
print
(
"%s OK - %s"
%
(
server
,
"No error for %s/%s, %i bytes received"
%
(
name
,
opts
.
rtype
,
size
)))
else
:
print
(
"%s OK - %s"
%
(
server
,
"No error"
))
sys
.
exit
(
STATE_OK
)
else
:
if
not
dot
:
print
(
"%s HTTP error - %i: %s"
%
(
server
,
rcode
,
msg
))
else
:
print
(
"%s Error - %i: %s"
%
(
server
,
rcode
,
msg
))
sys
.
exit
(
STATE_CRITICAL
)
ok
=
False
return
ok
def
resolved_ips
(
host
,
port
,
family
,
dot
=
False
):
try
:
addr_list
=
socket
.
getaddrinfo
(
host
,
port
,
family
)
except
socket
.
gaierror
as
e
:
error
(
"Could not resolve
\"
%s
\"
(%s)"
%
(
host
,
e
))
ip_set
=
{
addr
[
4
][
0
]
for
addr
in
addr_list
}
return
ip_set
def
parse_opts_monitoring
(
me
,
opts
):
name
=
None
opts
.
dot
=
(
me
==
"check_dot"
)
rtype
=
opts
.
rtype
try
:
optlist
,
args
=
getopt
.
getopt
(
sys
.
argv
[
1
:],
"H:n:p:V:t:e:Pih46k:x"
)
for
option
,
value
in
optlist
:
if
option
==
"-H"
:
opts
.
host
=
value
elif
option
==
"-V"
:
opts
.
vhostname
=
value
elif
option
==
"-n"
:
name
=
value
elif
option
==
"-t"
:
opts
.
rtype
=
value
elif
option
==
"-e"
:
opts
.
expect
=
value
elif
option
==
"-p"
:
opts
.
path
=
value
elif
option
==
"-P"
:
opts
.
post
=
True
elif
option
==
"-h"
:
opts
.
head
=
True
elif
option
==
"-i"
:
opts
.
insecure
=
True
elif
option
==
"-x"
:
opts
.
sni
=
False
elif
option
==
"-4"
:
opts
.
forceIPv4
=
True
elif
option
==
"-6"
:
opts
.
forceIPv6
=
True
elif
option
==
"-k"
:
opts
.
key
=
value
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
opts
.
host
is
None
or
name
is
None
:
print
(
"Host (-H) and name to lookup (-n) are necessary"
)
sys
.
exit
(
STATE_UNKNOWN
)
if
opts
.
post
and
opts
.
head
:
print
(
"POST or HEAD but not both"
)
sys
.
exit
(
STATE_UNKNOWN
)
if
opts
.
dot
and
(
opts
.
post
or
opts
.
head
):
print
(
"POST or HEAD makes no sense for DoT"
)
sys
.
exit
(
STATE_UNKNOWN
)
if
opts
.
dot
and
opts
.
path
:
print
(
"URL path makes no sense for DoT"
)
sys
.
exit
(
STATE_UNKNOWN
)
if
opts
.
dot
:
url
=
opts
.
host
else
:
if
opts
.
vhostname
is
None
or
opts
.
vhostname
==
opts
.
host
:
opts
.
connectTo
=
None
url
=
"https://%s/"
%
opts
.
host
else
:
opts
.
connectTo
=
opts
.
host
url
=
"https://%s/"
%
opts
.
vhostname
if
opts
.
path
is
not
None
:
if
opts
.
path
.
startswith
(
"/"
):
opts
.
path
=
opts
.
path
[
1
:]
url
+=
opts
.
path
return
(
url
,
name
)
def
run_default
(
name
,
connection
,
opts
):
request
=
homer
.
create_request
(
name
,
qtype
=
opts
.
rtype
,
use_edns
=
opts
.
edns
,
want_dnssec
=
opts
.
dnssec
,
no_ecs
=
opts
.
no_ecs
,
dot
=
opts
.
dot
)
if
not
opts
.
dot
:
request
.
head
=
opts
.
head
request
.
post
=
opts
.
post
try
:
connection
.
do_test
(
request
)
except
(
OpenSSL
.
SSL
.
Error
,
homer
.
DOHException
)
as
e
:
error
(
e
)
return
False
return
print_result
(
connection
,
request
)
# Main program
me
=
os
.
path
.
basename
(
sys
.
argv
[
0
])
url
,
name
=
parse_opts_monitoring
(
me
,
opts
)
if
homer
.
is_valid_ip_address
(
opts
.
host
)[
0
]:
opts
.
connectTo
=
opts
.
host
# retrieve all ips when using --check
# not necessary if connectTo is already defined
# as it is the case with --monitoring
if
opts
.
connectTo
is
not
None
:
ip_set
=
{
opts
.
connectTo
,
}
else
:
if
opts
.
dot
:
port
=
homer
.
PORT_DOT
if
not
homer
.
is_valid_hostname
(
url
):
error
(
"DoT requires a host name or IP address, not
\"
%s
\"
"
%
url
)
netloc
=
url
else
:
port
=
homer
.
PORT_DOH
if
not
homer
.
is_valid_url
(
url
):
error
(
"DoH requires a valid HTTPS URL, not
\"
%s
\"
"
%
url
)
try
:
url_parts
=
urllib
.
parse
.
urlparse
(
url
)
# A very poor validation, many
# errors (for instance whitespaces, IPv6 address litterals without
# brackets...) are ignored.
except
ValueError
:
error
(
"The provided url
\"
%s
\"
could not be parsed"
%
url
)
netloc
=
url_parts
.
netloc
if
opts
.
forceIPv4
:
family
=
socket
.
AF_INET
elif
opts
.
forceIPv6
:
family
=
socket
.
AF_INET6
else
:
family
=
0
ip_set
=
resolved_ips
(
netloc
,
port
,
family
,
opts
.
dot
)
ok
=
True
# todo no loop needed here when using monitoring plugin,
# we know where to connect to, we monitor one machine therefore the IP is known
for
ip
in
ip_set
:
if
opts
.
dot
and
opts
.
vhostname
is
not
None
:
extracheck
=
opts
.
vhostname
else
:
extracheck
=
None
try
:
if
opts
.
dot
:
conn
=
homer
.
ConnectionDOT
(
url
,
servername
=
extracheck
,
connect_to
=
opts
.
connectTo
,
forceIPv4
=
opts
.
forceIPv4
,
forceIPv6
=
opts
.
forceIPv6
,
insecure
=
opts
.
insecure
,
sni
=
opts
.
sni
,
key
=
opts
.
key
)
else
:
conn
=
homer
.
ConnectionDOH
(
url
,
servername
=
extracheck
,
connect_to
=
opts
.
connectTo
,
forceIPv4
=
opts
.
forceIPv4
,
forceIPv6
=
opts
.
forceIPv6
,
insecure
=
opts
.
insecure
)
except
TimeoutError
:
error
(
"timeout"
)
except
ConnectionRefusedError
:
error
(
"Connection to server refused"
)
except
ValueError
:
error
(
"
\"
%s
\"
not a name or an IP address"
%
url
)
except
socket
.
gaierror
:
error
(
"Could not resolve
\"
%s
\"
"
%
url
)
except
(
homer
.
ConnectionException
,
homer
.
DOHException
)
as
e
:
error
(
e
)
if
conn
.
dot
and
not
conn
.
success
:
ok
=
False
continue
ok
=
run_default
(
name
,
conn
,
opts
)
conn
.
end
()
if
ok
:
sys
.
exit
(
STATE_OK
)
else
:
sys
.
exit
(
STATE_CRITICAL
)
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