chiark / gitweb /
peerdb/tripe-newpeers.in: Split out a class for a host's resolved names.
authorMark Wooding <mdw@distorted.org.uk>
Tue, 26 Sep 2017 21:37:16 +0000 (22:37 +0100)
committerMark Wooding <mdw@distorted.org.uk>
Sat, 16 Jun 2018 18:14:10 +0000 (19:14 +0100)
This becomes the primary kind of object maintained in the resolver's
dictionary.

Also formalize the arrangements for reporting whether name resolution
failed.

This is an initial step towards introducing IPv6 support, initially in
this program, and later across the entire project.

peerdb/tripe-newpeers.in

index 81e62f75fa8c2727f226a10c4e6ae563151b678d..502492d9673b904c2c990a5536a59a4050d0cba6 100644 (file)
@@ -60,6 +60,32 @@ class ResolverFailure (ExpectedError):
   def __str__(me):
     return "failed to resolve `%s': %s" % (me.host, me.msg)
 
+class ResolvingHost (object):
+  """
+  A host name which is being looked up by a bulk-resolver instance.
+  """
+
+  def __init__(me, name):
+    """Make a new resolving-host object for the host NAME."""
+    me.name = name
+    me.addr = None
+    me.failure = None
+
+  def setaddr(me, addr):
+    """Add the address ADDR."""
+    me.addr = addr
+
+  def failed(me, msg):
+    """
+    Report that resolution of this host failed, with a human-readable MSG.
+    """
+    me.failure = msg
+
+  def get(me):
+    """Return the resolved address."""
+    if me.failure is not None: raise ResolverFailure(me.name, me.failure)
+    return me.addr
+
 class BulkResolver (object):
   """
   Resolve a number of DNS names in parallel.
@@ -78,36 +104,35 @@ class BulkResolver (object):
 
   def __init__(me):
     """Initialize the resolver."""
-    me._resolvers = {}
     me._namemap = {}
-
-  def prepare(me, host):
-    """Prime the resolver to resolve the name HOST."""
-    if host not in me._resolvers:
-      me._resolvers[host] = M.SelResolveByName \
-                            (host,
-                             lambda name, alias, addr:
-                               me._resolved(host, addr[0]),
-                             lambda: me._resolved(host, None))
+    me._noutstand = 0
+
+  def prepare(me, name):
+    """Prime the resolver to resolve the given host NAME."""
+    if name not in me._namemap:
+      me._namemap[name] = host = ResolvingHost(name)
+      host._resolv = M.SelResolveByName(
+        name,
+        lambda cname, alias, addr: me._resolved(host, addr[0]),
+        lambda: me._resolved(host, None))
+      me._noutstand += 1
 
   def run(me):
     """Run the background DNS resolver until it's finished."""
-    while me._resolvers:
-      M.select()
+    while me._noutstand: M.select()
 
-  def lookup(me, host):
-    """
-    Fetch the address corresponding to HOST.
-    """
-    addr = me._namemap[host]
-    if addr is None:
-      raise ResolverFailure(host, '(unknown failure)')
-    return addr
+  def lookup(me, name):
+    """Fetch the address corresponding to the host NAME."""
+    return me._namemap[name].get()
 
   def _resolved(me, host, addr):
     """Callback function: remember that ADDR is the address for HOST."""
-    me._namemap[host] = addr
-    del me._resolvers[host]
+    if addr is None:
+      host.failed('(unknown failure)')
+    else:
+      host.setaddr(addr)
+    host._resolv = None
+    me._noutstand -= 1
 
 ###--------------------------------------------------------------------------
 ### The configuration parser.