#!/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:
                        (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)