Commit 66ea1c09 authored by Alexandre's avatar Alexandre
Browse files

Merge branch 'fix-exit-on-error'

Do not exit on error with --check

See merge request bortzmeyer/homer!28
parents bf5940ad 84267e39
...@@ -60,10 +60,12 @@ def error(msg=None, exit=True): ...@@ -60,10 +60,12 @@ def error(msg=None, exit=True):
if msg is None: if msg is None:
msg = "Unknown error" msg = "Unknown error"
print(msg, file=sys.stderr) print(msg, file=sys.stderr)
def error_and_exit(msg=None):
error(msg)
if opts.check: if opts.check:
print('KO') print('KO')
if exit: sys.exit(1)
sys.exit(1)
def usage(msg=None): def usage(msg=None):
if msg: if msg:
...@@ -360,7 +362,7 @@ def resolved_ips(host, port, family, dot=False): ...@@ -360,7 +362,7 @@ def resolved_ips(host, port, family, dot=False):
try: try:
addr_list = socket.getaddrinfo(host, port, family) addr_list = socket.getaddrinfo(host, port, family)
except socket.gaierror: except socket.gaierror:
error("Could not resolve \"%s\"" % host) error_and_exit("Could not resolve \"%s\"" % host)
ip_set = { addr[4][0] for addr in addr_list } ip_set = { addr[4][0] for addr in addr_list }
return ip_set return ip_set
...@@ -418,11 +420,11 @@ def parse_opts(opts): ...@@ -418,11 +420,11 @@ def parse_opts(opts):
elif option == "--repeat" or option == "-r": elif option == "--repeat" or option == "-r":
opts.tests = int(value) opts.tests = int(value)
if opts.tests <= 1: if opts.tests <= 1:
error("--repeat needs a value > 1") error_and_exit("--repeat needs a value > 1")
elif option == "--delay" or option == "-d": elif option == "--delay" or option == "-d":
opts.delay = float(value) opts.delay = float(value)
if delay <= 0: if delay <= 0:
error("--delay needs a value > 0") error_and_exit("--delay needs a value > 0")
elif option == "--file" or option == "-f": elif option == "--file" or option == "-f":
opts.ifile = value opts.ifile = value
elif option == "--key": elif option == "--key":
...@@ -436,67 +438,53 @@ def parse_opts(opts): ...@@ -436,67 +438,53 @@ def parse_opts(opts):
elif option == "--max-in-flight": elif option == "--max-in-flight":
opts.max_in_flight = int(value) opts.max_in_flight = int(value)
if max_in_flight <= 0: if max_in_flight <= 0:
error("--max_in_flight but be > 0") error_and_exit("--max_in_flight but be > 0")
if max_in_flight >= 65536: if max_in_flight >= 65536:
error("Because of a limit of the DNS protocol (the size of the query ID) --max_in_flight must be < 65 536") error_and_exit("Because of a limit of the DNS protocol (the size of the query ID) --max_in_flight must be < 65 536")
elif option == "--check": elif option == "--check":
opts.check = True opts.check = True
opts.display_results = False opts.display_results = False
elif option == "--mandatory-level": elif option == "--mandatory-level":
opts.mandatory_level = value opts.mandatory_level = value
else: else:
error("Unknown option %s" % option) error_and_exit("Unknown option %s" % option)
except (getopt.error, ValueError) as reason: except (getopt.error, ValueError) as reason:
error(reason) error_and_exit(reason)
sys.exit(1)
if opts.delay is not None and opts.multistreams: if opts.delay is not None and opts.multistreams:
error("--delay makes no sense with multistreams") error_and_exit("--delay makes no sense with multistreams")
if opts.tests <= 1 and opts.delay is not None: if opts.tests <= 1 and opts.delay is not None:
error("--delay makes no sense if there is no repetition") error_and_exit("--delay makes no sense if there is no repetition")
if not opts.dot and opts.pipelining: if not opts.dot and opts.pipelining:
error("Pipelining is only accepted for DoT") error_and_exit("Pipelining is only accepted for DoT")
sys.exit(1)
if opts.dot and (opts.post or opts.head): if opts.dot and (opts.post or opts.head):
error("POST or HEAD makes non sense for DoT") error_and_exit("POST or HEAD makes non sense for DoT")
sys.exit(1)
if opts.post and opts.head: if opts.post and opts.head:
error("POST or HEAD but not both") error_and_exit("POST or HEAD but not both")
sys.exit(1)
if opts.pipelining and opts.ifile is None: if opts.pipelining and opts.ifile is None:
error("Pipelining requires an input file") error_and_exit("Pipelining requires an input file")
sys.exit(1)
if opts.check and opts.multistreams: if opts.check and opts.multistreams:
error("--check and --multistreams are not compatible") error_and_exit("--check and --multistreams are not compatible")
sys.exit(1)
if opts.dot and opts.multistreams: if opts.dot and opts.multistreams:
error("Multi-streams makes no sense for DoT") error_and_exit("Multi-streams makes no sense for DoT")
sys.exit(1)
if opts.multistreams and opts.ifile is None: if opts.multistreams and opts.ifile is None:
error("Multi-streams requires an input file") error_and_exit("Multi-streams requires an input file")
sys.exit(1)
if opts.show_time and opts.dot: if opts.show_time and opts.dot:
error("--time cannot be used with --dot") error_and_exit("--time cannot be used with --dot")
sys.exit(1)
if not opts.edns and not opts.no_ecs: if not opts.edns and not opts.no_ecs:
error("ECS requires EDNS") error_and_exit("ECS requires EDNS")
sys.exit(1)
if opts.mandatory_level is not None and \ if opts.mandatory_level is not None and \
opts.mandatory_level not in homer.mandatory_levels.keys(): opts.mandatory_level not in homer.mandatory_levels.keys():
error("Unknown mandatory level \"%s\"" % opts.mandatory_level) error_and_exit("Unknown mandatory level \"%s\"" % opts.mandatory_level)
sys.exit(1)
if opts.mandatory_level is not None and not opts.check: if opts.mandatory_level is not None and not opts.check:
error("--mandatory-level only makes sense with --check") error_and_exit("--mandatory-level only makes sense with --check")
sys.exit(1)
if opts.mandatory_level is None: if opts.mandatory_level is None:
opts.mandatory_level = "necessary" opts.mandatory_level = "necessary"
opts.mandatory_level = homer.mandatory_levels[opts.mandatory_level] opts.mandatory_level = homer.mandatory_levels[opts.mandatory_level]
if opts.ifile is None and (len(args) != 2 and len(args) != 3): if opts.ifile is None and (len(args) != 2 and len(args) != 3):
error("Wrong number of arguments") error_and_exit("Wrong number of arguments")
sys.exit(1)
if opts.ifile is not None and len(args) != 1: if opts.ifile is not None and len(args) != 1:
error("Wrong number of arguments (if --file is used, do not indicate the domain name)") error_and_exit("Wrong number of arguments (if --file is used, do not indicate the domain name)")
sys.exit(1)
url = args[0] url = args[0]
if opts.ifile is None: if opts.ifile is None:
name = args[1] name = args[1]
...@@ -604,18 +592,18 @@ else: ...@@ -604,18 +592,18 @@ else:
if opts.dot: if opts.dot:
port = homer.PORT_DOT port = homer.PORT_DOT
if not homer.is_valid_hostname(url): if not homer.is_valid_hostname(url):
error("DoT requires a host name or IP address, not \"%s\"" % url) error_and_exit("DoT requires a host name or IP address, not \"%s\"" % url)
netloc = url netloc = url
else: else:
port = homer.PORT_DOH port = homer.PORT_DOH
if not homer.is_valid_url(url): if not homer.is_valid_url(url):
error("DoH requires a valid HTTPS URL, not \"%s\"" % url) error_and_exit("DoH requires a valid HTTPS URL, not \"%s\"" % url)
try: try:
url_parts = urllib.parse.urlparse(url) # A very poor validation, many url_parts = urllib.parse.urlparse(url) # A very poor validation, many
# errors (for instance whitespaces, IPv6 address litterals without # errors (for instance whitespaces, IPv6 address litterals without
# brackets...) are ignored. # brackets...) are ignored.
except ValueError: except ValueError:
error("The provided url \"%s\" could not be parsed" % url) error_and_exit("The provided url \"%s\" could not be parsed" % url)
netloc = url_parts.netloc netloc = url_parts.netloc
if opts.forceIPv4: if opts.forceIPv4:
family = socket.AF_INET family = socket.AF_INET
...@@ -631,8 +619,9 @@ if opts.verbose and opts.check: ...@@ -631,8 +619,9 @@ if opts.verbose and opts.check:
print("%d IP found : %s" % (len(ip_set), ', '.join(ip_set))) print("%d IP found : %s" % (len(ip_set), ', '.join(ip_set)))
ok = True ok = True
i = 1 # ip counter i = 0 # ip counter
for ip in ip_set: for ip in ip_set:
i += 1
if opts.dot and opts.vhostname is not None: if opts.dot and opts.vhostname is not None:
extracheck = opts.vhostname extracheck = opts.vhostname
else: else:
...@@ -652,12 +641,20 @@ for ip in ip_set: ...@@ -652,12 +641,20 @@ for ip in ip_set:
multistreams=opts.multistreams) multistreams=opts.multistreams)
except TimeoutError: except TimeoutError:
error("timeout") error("timeout")
ok = False
continue
except ConnectionRefusedError: except ConnectionRefusedError:
error("Connection to server refused") error("Connection to server refused")
ok = False
continue
except ValueError: except ValueError:
error("\"%s\" not a name or an IP address" % url) error("\"%s\" not a name or an IP address" % url)
ok = False
continue
except socket.gaierror: except socket.gaierror:
error("Could not resolve \"%s\"" % url) error("Could not resolve \"%s\"" % url)
ok = False
continue
except homer.ConnectionDOTException as e: except homer.ConnectionDOTException as e:
print(e, file=sys.stderr) print(e, file=sys.stderr)
err = "Could not connect to \"%s\"" % url err = "Could not connect to \"%s\"" % url
...@@ -666,27 +663,35 @@ for ip in ip_set: ...@@ -666,27 +663,35 @@ for ip in ip_set:
elif ip is not None: elif ip is not None:
err += " on %s" % ip err += " on %s" % ip
error(err) error(err)
ok = False
continue
except (homer.ConnectionException, homer.DOHException) as e: except (homer.ConnectionException, homer.DOHException) as e:
error(e) error(e)
ok = False
continue
if conn.dot and not conn.success: if conn.dot and not conn.success:
ok = False ok = False
continue continue
if opts.ifile is not None: if opts.ifile is not None:
input = open(opts.ifile) input = open(opts.ifile)
if not opts.check: if not opts.check:
ok = run_default(name, conn, opts) ok = run_default(name, conn, opts)
else: else:
ok = run_check(conn) and ok # need to run run_check first ok = run_check(conn) and ok # need to run run_check first
if opts.ifile is not None: if opts.ifile is not None:
input.close() input.close()
conn.end()
i += 1 conn.end()
if ok: if ok:
if opts.check or opts.pipelining: if opts.check or opts.pipelining:
print('OK') print('OK')
sys.exit(0) sys.exit(0)
else: else:
print('KO') if opts.check:
print('KO')
sys.exit(1) sys.exit(1)
...@@ -311,6 +311,22 @@ tests: ...@@ -311,6 +311,22 @@ tests:
retcode: 0 retcode: 0
partstdout: "checking IP : 2001:4860:4860::8888\nConnecting to 2001:4860:4860::8888 ..." partstdout: "checking IP : 2001:4860:4860::8888\nConnecting to 2001:4860:4860::8888 ..."
- name: '[dot][check] Verify that all IPs are tried even when one is failing'
exe: './homer.py'
timeout: 6
markers:
- 'dot'
- 'check'
args:
- '-v'
- '-k'
- '--dot'
- '--check'
- 'brok.sources.org'
- 'tm'
retcode: 1
partstdout: "(3/3) checking IP"
- name: '[doh][check] Test all the IPs, force IPv4' - name: '[doh][check] Test all the IPs, force IPv4'
exe: './homer.py' exe: './homer.py'
markers: markers:
......
Markdown is supported
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