Commit 9cf37711 authored by Stéphane Bortzmeyer's avatar Stéphane Bortzmeyer
Browse files

Merge branch 'dot-change_ip_on_timeout' into 'master'

[DoT] Try another ip on connection timeout

Closes #13

See merge request bortzmeyer/homer!8
parents ace9028f 8baa1f49
......@@ -31,6 +31,7 @@ import re
import os.path
import hashlib
import base64
import signal
# Values that can be changed from the command line
dot = False # DoH by default
......@@ -62,6 +63,8 @@ STATE_CRITICAL = 2
STATE_UNKNOWN = 3
STATE_DEPENDENT = 4
TIMEOUT_CONN = 2
def error(msg=None):
if msg is None:
msg = "Unknown error"
......@@ -168,6 +171,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
......@@ -280,12 +289,26 @@ class ConnectionDoT(Connection):
forceIPv4=forceIPv4, forceIPv6=forceIPv6, dot=True,
verbose=verbose, insecure=insecure)
self.check_ip_address(self.server)
self.hasher = hashlib.sha256()
addrinfo = socket.getaddrinfo(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(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,
......@@ -302,9 +325,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()
......@@ -322,6 +350,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()
......@@ -630,6 +660,7 @@ else: # Monitoring plugin
if path.startswith("/"):
path = path[1:]
url += path
ok = True
start = time.time()
try:
......
......@@ -152,6 +152,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