Commit 86ef3293 authored by Alexandre's avatar Alexandre
Browse files

Merge branch 'master' into option-check

parents 8deb62a1 9cf37711
......@@ -31,6 +31,7 @@ import re
import os.path
import hashlib
import base64
import signal
check_additional = False
......@@ -70,6 +71,8 @@ DOH_GET = 0
DOH_POST = 1
DOH_HEAD = 2
TIMEOUT_CONN = 2
def error(msg=None):
if msg is None:
msg = "Unknown error"
......@@ -178,6 +181,12 @@ def validate_hostname(hostname, cert):
return True
return False
def timeout_connection(signum, frame):
raise TimeoutConnectionError('Connection timeout')
class TimeoutConnectionError(Exception):
pass
class CustomException(Exception):
pass
......@@ -185,7 +194,8 @@ class CustomException(Exception):
class Request:
def __init__(self, qname, qtype=rtype, use_edns=edns, want_dnssec=dnssec):
self.message = dns.message.make_query(qname, dns.rdatatype.from_text(qtype), use_edns=use_edns, want_dnssec=want_dnssec)
self.message = dns.message.make_query(qname, dns.rdatatype.from_text(qtype),
use_edns=use_edns, want_dnssec=want_dnssec)
self.message.flags |= dns.flags.AD # Ask for validation
self.ok = True
......@@ -220,27 +230,27 @@ class RequestDoH(Request):
else:
if not self.head:
try:
self.response = dns.message.from_wire(self.body)
self.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.body
self.response = "ERROR Not proper DNS data, trailing junk \"%s\"" % self.response
ok = False
except dns.name.BadLabelType: # Not DNS.
self.response = "ERROR Not proper DNS data (wrong path in the URL?) \"%s\"" % self.body[:100]
self.response = "ERROR Not proper DNS data (wrong path in the URL?) \"%s\"" % self.response[:100]
ok = False
else:
if self.response_size == 0:
self.response = "HEAD successful"
else:
self.response = "ERROR Body length is not null \"%s\"" % self.body[:100]
self.response = "ERROR Body length is not null \"%s\"" % self.response[:100]
ok = False
else:
ok = False
if self.response_size == 0:
self.response = "[No details]"
else:
self.response = self.body
self.response = self.response
self.ok = ok
return ok
......@@ -286,7 +296,7 @@ class Connection:
self.family = 0
def do_test(self, qname, qtype=rtype):
# Routine doing one actual test. Returns a Request
# Routine doing one actual test. Returns a Request object
pass
......@@ -294,14 +304,29 @@ class ConnectionDoT(Connection):
def __init__(self, server, servername=None, connect=None, forceIPv4=False, forceIPv6=False,
verbose=verbose, insecure=insecure):
Connection.__init__(self, server, servername=servername, connect=connect,
forceIPv4=forceIPv4, forceIPv6=forceIPv6, dot=True, verbose=verbose, insecure=insecure)
forceIPv4=forceIPv4, forceIPv6=forceIPv6, dot=True,
verbose=verbose, insecure=insecure)
self.check_ip_address(self.server)
self.hasher = hashlib.sha256()
addrinfo = socket.getaddrinfo(self.server, 853, self.family)
# May be loop over the results of getaddrinfo, to test all
# the IP addresses? See #13.
self.sock = socket.socket(addrinfo[0][0], socket.SOCK_STREAM)
self.addr = addrinfo[0][4]
addrinfo_list = socket.getaddrinfo(self.server, 853, self.family)
addrinfo_set = { (addrinfo[4], addrinfo[0]) for addrinfo in addrinfo_list }
signal.signal(signal.SIGALRM, timeout_connection)
connection_success = False
for addrinfo in addrinfo_set:
self.hasher = hashlib.sha256()
if self.connect(addrinfo[0], addrinfo[1]):
connection_success = True
break
if self.verbose:
print("Trying another IP address")
if not connection_success:
if self.verbose:
print("No other IP address")
error(f'Could not connect to "{server}"')
def connect(self, addr, sock_family):
signal.alarm(TIMEOUT_CONN)
self.addr = addr
self.sock = socket.socket(sock_family, socket.SOCK_STREAM)
if self.verbose:
print("Connecting to %s ..." % str(self.addr))
# With typical DoT servers, we *must* use TLS 1.2 (otherwise,
......@@ -318,9 +343,14 @@ class ConnectionDoT(Connection):
lambda conn, cert, errno, depth, preverify_ok: preverify_ok)
self.session = OpenSSL.SSL.Connection(self.context, self.sock)
self.session.set_tlsext_host_name(canonicalize(self.check).encode()) # Server Name Indication (SNI)
self.session.connect((self.addr))
# TODO We may here have exceptions such as OpenSSL.SSL.ZeroReturnError
self.session.do_handshake()
try:
self.session.connect((self.addr))
# TODO We may here have exceptions such as OpenSSL.SSL.ZeroReturnError
self.session.do_handshake()
except TimeoutConnectionError:
if self.verbose:
print("Timeout")
return False
self.cert = self.session.get_peer_certificate()
# RFC 7858, section 4.2 and appendix A
self.publickey = self.cert.get_pubkey()
......@@ -338,6 +368,8 @@ class ConnectionDoT(Connection):
valid = validate_hostname(self.check, self.cert)
if not valid:
error("Certificate error: \"%s\" is not in the certificate" % (self.check))
signal.alarm(0)
return True
def end(self):
self.session.shutdown()
......@@ -419,7 +451,6 @@ class ConnectionDoH(Connection):
self.prepare_post(request)
elif request.head:
self.prepare_head(request)
request.head = True
else:
self.prepare_get(request)
......@@ -452,7 +483,7 @@ class ConnectionDoH(Connection):
body_size = len(body)
http_code = self.curl.getinfo(pycurl.RESPONSE_CODE)
content_type = self.curl.getinfo(pycurl.CONTENT_TYPE)
request.body = body
request.response = body
request.response_size = body_size
request.rcode = http_code
request.ctype = content_type
......@@ -764,6 +795,7 @@ else: # Monitoring plugin
if path.startswith("/"):
path = path[1:]
url += path
ok = True
start = time.time()
try:
......
......@@ -163,6 +163,48 @@ tests:
partstderr: 'litteral IPv6'
stdout: ''
- exe: './homer.py'
name: '[dot] Loop on all ips on connection error (brok.sources.org)'
markers:
- 'dot'
timeout: 6
args:
- '--dot'
- '--insecure'
- 'brok.sources.org'
- 'framagit.org'
retcode: 0
stderr: ''
partstdout: '2a01:4f8:'
- exe: './homer.py'
name: '[dot] Force IPv6 on brok.sources.org'
markers:
- 'dot'
timeout: 6
args:
- '-6'
- '--insecure'
- '--dot'
- 'brok.sources.org'
- 'framagit.org'
retcode: 1
partstderr: 'not connect to'
stdout: ''
- exe: './homer.py'
name: '[dot] Not a DoT server'
markers:
- 'dot'
timeout: 5
args:
- '--dot'
- 'afnic.fr'
- 'framagit.org'
retcode: 1
partstderr: 'not connect to'
stdout: ''
################################################################################
# check_dot
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment