132 lines
4.3 KiB
Plaintext
132 lines
4.3 KiB
Plaintext
|
#!/usr/bin/env python
|
||
|
|
||
|
import sys
|
||
|
import os
|
||
|
import socket
|
||
|
import subprocess
|
||
|
from optparse import OptionParser
|
||
|
|
||
|
|
||
|
def error(*arg):
|
||
|
print(*arg, file=sys.stderr)
|
||
|
|
||
|
|
||
|
def check_host_lookup(hostname, port):
|
||
|
try:
|
||
|
return socket.getaddrinfo(hostname, port)
|
||
|
except Exception as e:
|
||
|
error("DNS Lookup for {hostname} failed".format(hostname=hostname))
|
||
|
return []
|
||
|
|
||
|
|
||
|
def check_reachability(host, family):
|
||
|
if family is socket.AddressFamily.AF_INET:
|
||
|
process_name = 'ping'
|
||
|
else:
|
||
|
process_name = 'ping6'
|
||
|
child = subprocess.Popen([process_name, host, '-c', '1', '-W', '5'],
|
||
|
stdout=subprocess.PIPE)
|
||
|
child.communicate()
|
||
|
if child.returncode:
|
||
|
error("Host {host} is unreachable".format(host=host))
|
||
|
return 0 if child.returncode == 0 else 1
|
||
|
|
||
|
|
||
|
def get_hosts_data(srcdir):
|
||
|
for fname in sorted(list(set(os.listdir(srcdir)))):
|
||
|
if fname.startswith("."):
|
||
|
continue
|
||
|
|
||
|
fpath = os.path.join(srcdir, fname)
|
||
|
if os.path.isfile(fpath):
|
||
|
with open(fpath) as f:
|
||
|
ignore_key = False
|
||
|
addresses = []
|
||
|
port = 655 # tinc default port
|
||
|
|
||
|
for line in f.readlines():
|
||
|
|
||
|
if '-----BEGIN RSA PUBLIC KEY-----' in line:
|
||
|
ignore_key = True
|
||
|
elif '-----END RSA PUBLIC KEY-----' in line:
|
||
|
ignore_key = False
|
||
|
|
||
|
if line.startswith("#") or ignore_key:
|
||
|
continue
|
||
|
|
||
|
chunks = line.split("=")
|
||
|
if len(chunks) == 2:
|
||
|
import pdb
|
||
|
# pdb.set_trace()
|
||
|
(k, v) = (x.strip().lower() for x in chunks)
|
||
|
|
||
|
if k == "port":
|
||
|
try:
|
||
|
port = int(v)
|
||
|
except ValueError:
|
||
|
error("non-integer default port given")
|
||
|
elif k == "address":
|
||
|
if " " in v:
|
||
|
parts = v.split(' ')
|
||
|
if len(parts) != 2:
|
||
|
error("unknown address format")
|
||
|
try:
|
||
|
int(parts[1])
|
||
|
addresses.append(parts)
|
||
|
except ValueError:
|
||
|
error("non-integer port given")
|
||
|
else:
|
||
|
addresses.append((v, None))
|
||
|
elif k in ('ecdsapublickey'):
|
||
|
continue
|
||
|
else:
|
||
|
error("unknown key {key} with value {val}"
|
||
|
.format(key=k, val=v))
|
||
|
|
||
|
# set explicit port for address/port pairs
|
||
|
for i, addr in enumerate(addresses):
|
||
|
if addr[1] is None:
|
||
|
item = (addr[0], port)
|
||
|
addresses[i] = item
|
||
|
|
||
|
yield(dict(community=fname, addresses=addresses))
|
||
|
|
||
|
|
||
|
def do_checks(srcdir):
|
||
|
errcnt = 0
|
||
|
for host in get_hosts_data(srcdir):
|
||
|
print("Checking {community}".format(community=host['community']))
|
||
|
for address in host['addresses']:
|
||
|
host, port = address
|
||
|
|
||
|
# dns lookup
|
||
|
records = check_host_lookup(host, port)
|
||
|
if not records:
|
||
|
errcnt += 1
|
||
|
else:
|
||
|
for record in records:
|
||
|
if record[1] is not socket.SocketType.SOCK_DGRAM:
|
||
|
# we get SOCK_STREAM, SOCK_DGRAM and SOCK_RAW
|
||
|
# for every IP/Port pair, lets just pick one
|
||
|
# to have unique ip addresses
|
||
|
continue
|
||
|
errcnt += check_reachability(record[4][0], record[0])
|
||
|
|
||
|
print("{errcnt} errors".format(errcnt=errcnt))
|
||
|
|
||
|
return 0 if errcnt == 0 else 1
|
||
|
|
||
|
|
||
|
if __name__ == "__main__":
|
||
|
parser = OptionParser()
|
||
|
parser.add_option("-s", "--sourcedir", dest="src",
|
||
|
help="Location of tinc host files. Default: ../hosts",
|
||
|
metavar="DIR",
|
||
|
default="../hosts/")
|
||
|
|
||
|
(options, args) = parser.parse_args()
|
||
|
|
||
|
ret = do_checks(options.src)
|
||
|
|
||
|
sys.exit(ret)
|