#!/usr/bin/env python3
import dns.name
import dns.resolver
import http.client
import ipaddress
import socket
import ssl
import sys


def resolve_name(name):
    zone = name
    while True:
        try:
            ns_recs = dns.resolver.query(zone, dns.rdatatype.NS)
        except dns.resolver.NoAnswer:
            zone = zone.parent()
            continue
        break

    for ns_rec in ns_recs:
        resolver = dns.resolver.Resolver()
        resolver.nameservers = [
            record.address
            for rdtype in [dns.rdatatype.AAAA, dns.rdatatype.A]
            for record in dns.resolver.query(
                ns_rec.target, rdtype, raise_on_no_answer=False
            )
        ]

        try:
            return [
                record.address
                for rdtype in [dns.rdatatype.AAAA, dns.rdatatype.A]
                for record in resolver.query(name, rdtype, raise_on_no_answer=False)
            ]
        except dns.resolver.NoNameservers:
            continue

    raise dns.resolver.NoNameservers(name)


origin = dns.name.from_text("mit.edu")


def resolve(s):
    try:
        return [ipaddress.ip_address(s).compressed]
    except ValueError:
        try:
            return resolve_name(dns.name.from_text(s, origin=origin))
        except dns.resolver.NXDOMAIN:
            return resolve_name(dns.name.from_text(s))


[old_addr] = resolve("scripts-vhosts-old.mit.edu")
[new_addr] = resolve("scripts-vhosts.mit.edu")


def check_http(host, addr, h):
    h._create_connection = lambda address_, *args, **kwargs: socket.create_connection(
        (addr, h.port), *args, **kwargs
    )
    h.request("GET", "/__scripts/server.shtml")
    res = h.getresponse()
    if res.status == 200:
        body = res.read()
        for line in body.splitlines():
            if line.startswith(b"SERVER_ADDR="):
                sa = line[len(b"SERVER_ADDR=") :].decode()
                if sa == old_addr:
                    yield h.port, "needs update"
                elif sa == new_addr:
                    pass
                else:
                    yield h.port, "has a weird SERVER_ADDR", sa
                break
        else:
            yield h.port, "gives no SERVER_ADDR",
    else:
        yield h.port, "not on Scripts", res.status


def check_addr(host, addr):
    if addr == old_addr:
        yield "needs update",
    elif addr == new_addr:
        pass
    else:
        try:
            yield from check_http(
                host, addr, http.client.HTTPConnection(host, http.client.HTTP_PORT)
            )
        except OSError as e:
            yield http.client.HTTP_PORT, e
        try:
            context = ssl.create_default_context()
            context.check_hostname = False
            yield from check_http(
                host,
                addr,
                http.client.HTTPSConnection(
                    host, http.client.HTTPS_PORT, context=context
                ),
            )
        except OSError as e:
            yield http.client.HTTPS_PORT, e


def check_host(host):
    try:
        host2 = (
            "scripts-wildcard-test-25681." + host[len("*.") :]
            if host.startswith("*.")
            else host
        )
        for addr in resolve(host2):
            for msg in check_addr(host2, addr):
                yield (addr, *msg)
    except (dns.exception.DNSException, OSError) as e:
        yield e,


def needs_update(host):
    msgs = list(check_host(host))
    for msg in msgs:
        print(host, *msg, file=sys.stderr)
    return any(msg[-1] == "needs update" for msg in msgs)


def run():
    fi = open("tickets.txt", "r")

    for line in fi.read().splitlines():
        line = line.split(" ")
        u = [h for h in line[3:] if needs_update(h)]
        if u:
            print(" ".join(line[:3] + u))


if __name__ == "__main__":
    run()
