diff options
-rw-r--r-- | rpkid/rpki/adns.py | 30 | ||||
-rw-r--r-- | rpkid/rpki/https.py | 87 |
2 files changed, 72 insertions, 45 deletions
diff --git a/rpkid/rpki/adns.py b/rpkid/rpki/adns.py index bd01cc7b..018f0cfd 100644 --- a/rpkid/rpki/adns.py +++ b/rpkid/rpki/adns.py @@ -290,18 +290,19 @@ class getaddrinfo(object): typemap = { dns.rdatatype.A : socket.AF_INET, dns.rdatatype.AAAA : socket.AF_INET6 } - def __init__(self, cb, eb, host, port): + def __init__(self, cb, eb, host, address_families = typemap.values()): self.cb = cb self.eb = eb self.host = host - self.port = port self.result = [] - self.queries = [query(self.done, self.lose, host, qtype) for qtype in self.typemap] + self.queries = [query(self.done, self.lose, host, qtype) + for qtype in self.typemap + if self.typemap[qtype] in address_families] def done(self, q, answer): if answer is not None: for a in answer: - self.result.append((self.typemap[a.rdtype], (a.address, self.port))) + self.result.append((self.typemap[a.rdtype], a.address)) self.queries.remove(q) if not self.queries: self.cb(self.result) @@ -322,21 +323,18 @@ if __name__ == "__main__": for rdata in result: print rdata - def done2(q, result): - done(result) - def lose(e): print "DNS lookup failed: %r" % e - def lose2(q, e): - lose(e) - - for qtype in (dns.rdatatype.A, dns.rdatatype.AAAA, dns.rdatatype.HINFO): - query(done2, lose2, "www.hactrn.net", qtype) - query(done2, lose2, "wibble.hactrn.net") - query(done2, lose2, "www.hactrn.net", qclass = dns.rdataclass.CH) + if True: + def done2(q, result): done(result) + def lose2(q, e): lose(e) + for qtype in (dns.rdatatype.A, dns.rdatatype.AAAA, dns.rdatatype.HINFO): + query(done2, lose2, "subvert-rpki.hactrn.net", qtype) + query(done2, lose2, "wibble.hactrn.net") + query(done2, lose2, "subvert-rpki.hactrn.net", qclass = dns.rdataclass.CH) - for host in ("www.hactrn.net", "www.psg.com", "arin.rpki.net", "www.rpki.net"): - getaddrinfo(done, lose, host, 80) + for host in ("subvert-rpki.hactrn.net", "www.rpki.net"): + getaddrinfo(done, lose, host) rpki.async.event_loop() diff --git a/rpkid/rpki/https.py b/rpkid/rpki/https.py index f86d4f12..4ecb37ce 100644 --- a/rpkid/rpki/https.py +++ b/rpkid/rpki/https.py @@ -32,7 +32,7 @@ OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. """ -import time, socket, asyncore, asynchat, urlparse, sys +import time, socket, asyncore, asynchat, urlparse, sys, random import rpki.async, rpki.sundial, rpki.x509, rpki.exceptions, rpki.log, rpki.adns import POW @@ -75,26 +75,55 @@ default_http_version = (1, 0) # Default port for clients and servers that don't specify one. default_tcp_port = 443 -## @var supported_address_families -# -# IP address families to support. Almost all the code is in place for -# IPv6, the missing bits are DNS support that would let us figure out -# which address family to request, and configuration support to let us -# figure out which protocols are supported on the local machine. For -# now, leave code in place but disabled. -# -# Address families on which to listen; first entry is also the default -# for opening new connections. -if False: - supported_address_families = (socket.AF_INET, socket.AF_INET6) -else: - supported_address_families = (socket.AF_INET,) +## @var enable_ipv6_servers +# Whether to enable IPv6 listeners. Enabled by default, as it should +# be harmless. Has no effect if kernel doesn't support IPv6. +enable_ipv6_servers = True + +## @var enable_ipv6_clients +# Whether to consider IPv6 addresses when making connections. +# Disabled by default, as IPv6 connectivity is still a bad joke in +# far too much of the world. +enable_ipv6_clients = False ## @var use_adns # Whether to use rpki.adns code. This is still experimental, so it's # not (yet) enabled by default. use_adns = False +## @var have_ipv6 +# Whether the current machine claims to support IPv6. Note that just +# because the kernel supports it doesn't mean that the machine has +# usable IPv6 connectivity. I don't know of a simple portable way to +# probe for connectivity at runtime (the old test of "can you ping +# SRI-NIC.ARPA?" seems a bit dated...). Don't set this, it's set +# automatically by probing using the socket() system call at runtime. +try: + socket.socket(socket.AF_INET6).close() +except: + have_ipv6 = False +else: + have_ipv6 = True + +def supported_address_families(enable_ipv6): + """ + IP address families on which servers should listen, and to consider + when selecting addresses for client connections. + """ + if enable_ipv6 and have_ipv6: + return (socket.AF_INET, socket.AF_INET6) + else: + return (socket.AF_INET,) + +def localhost_addrinfo(): + """ + Return pseudo-getaddrinfo results for localhost. + """ + result = [(socket.AF_INET, "127.0.0.1")] + if enable_ipv6_clients and have_ipv6: + result.append((socket.AF_INET6, "::1")) + return result + class http_message(object): """ Virtual class representing of one HTTP message. @@ -723,7 +752,7 @@ class http_listener(asyncore.dispatcher): log = log_method - def __init__(self, handlers, port = default_tcp_port, host = "", cert = None, key = None, ta = None, dynamic_ta = None, af = supported_address_families[0]): + def __init__(self, handlers, port = default_tcp_port, host = "", cert = None, key = None, ta = None, dynamic_ta = None, af = socket.AF_INET): self.log("Listener cert %r key %r ta %r dynamic_ta %r" % (cert, key, ta, dynamic_ta)) asyncore.dispatcher.__init__(self) self.handlers = handlers @@ -781,7 +810,7 @@ class http_client(http_stream): # Use the default client timeout value set in the module header. timeout = default_client_timeout - def __init__(self, queue, hostport, cert = None, key = None, ta = (), af = supported_address_families[0]): + def __init__(self, queue, hostport, cert = None, key = None, ta = ()): self.log("Creating new connection to %r" % (hostport,)) self.log("cert %r key %r ta %r" % (cert, key, ta)) http_stream.__init__(self) @@ -793,31 +822,31 @@ class http_client(http_stream): self.cert = cert self.key = key self.ta = rpki.x509.X509.normalize_chain(ta) - self.af = af def start(self): """ Create socket and request a connection. """ if not use_adns: - self.do_connect(None, (self.host,)) + self.gotaddrinfo([(socket.AF_INET, self.host)]) elif self.host == "localhost": - self.do_connect(None, ("127.0.0.1",)) + self.gotaddrinfo(localhost_addrinfo()) else: - rpki.adns.query(self.do_connect, self.dns_error, self.host) + rpki.adns.getaddrinfo(self.gotaddrinfo, self.dns_error, self.host, supported_address_families(enable_ipv6_clients)) - def dns_error(self, query, e): + def dns_error(self, e): + """ + Handle DNS lookup errors. For now, just whack the connection. + Undoubtedly we should do something better with diagnostics here. + """ self.handle_error() - def do_connect(self, query, rdata): + def gotaddrinfo(self, addrinfo): """ Got address data from DNS, create socket and request connection. - - In the long run, this should receive (af, address) pairs from - rpki.adns, but I'm not quite there yet. """ try: - self.addr = rdata[0] + self.af, self.addr = random.choice(addrinfo) self.create_socket(self.af, socket.SOCK_STREAM) self.connect((self.addr, self.port)) except (rpki.async.ExitNow, SystemExit): @@ -1107,7 +1136,7 @@ def client(msg, client_key, client_cert, server_ta, url, callback, errback): rpki.log.debug("Scheduling connection startup for %r" % request) rpki.async.defer(client_queues[hostport].restart) -def server(handlers, server_key, server_cert, port, host ="", client_ta = (), dynamic_https_trust_anchor = None, address_families = supported_address_families): +def server(handlers, server_key, server_cert, port, host ="", client_ta = (), dynamic_https_trust_anchor = None): """ Run an HTTPS server and wait (forever) for connections. """ @@ -1118,7 +1147,7 @@ def server(handlers, server_key, server_cert, port, host ="", client_ta = (), dy if not isinstance(client_ta, (tuple, list)): client_ta = (client_ta,) - for af in address_families: + for af in supported_address_families(enable_ipv6_servers): http_listener(port = port, host = host, handlers = handlers, cert = server_cert, key = server_key, ta = client_ta, dynamic_ta = dynamic_https_trust_anchor, af = af) rpki.async.event_loop() |