peerdb/tripe-newpeers.in: Split out a resolver base class.
[tripe] / peerdb / tripe-newpeers.in
index 88d3154..202cea8 100644 (file)
@@ -34,6 +34,7 @@ import cdb as CDB
 from sys import stdin, stdout, exit, argv
 import re as RX
 import os as OS
 from sys import stdin, stdout, exit, argv
 import re as RX
 import os as OS
+import socket as S
 from cStringIO import StringIO
 
 ###--------------------------------------------------------------------------
 from cStringIO import StringIO
 
 ###--------------------------------------------------------------------------
@@ -71,14 +72,14 @@ class ResolvingHost (object):
   def __init__(me, name):
     """Make a new resolving-host object for the host NAME."""
     me.name = name
   def __init__(me, name):
     """Make a new resolving-host object for the host NAME."""
     me.name = name
-    me.addr = { 'INET': [] }
+    me.addr = { 'INET': [], 'INET6': [] }
     me.failure = None
 
   def addaddr(me, af, addr):
     """
     Add the address ADDR with address family AF.
 
     me.failure = None
 
   def addaddr(me, af, addr):
     """
     Add the address ADDR with address family AF.
 
-    The address family must currently be `INET'.
+    The address family may be `INET' or `INET6'.
     """
     me.addr[af].append(addr)
 
     """
     me.addr[af].append(addr)
 
@@ -93,17 +94,19 @@ class ResolvingHost (object):
     if me.failure is not None: raise ResolverFailure(me.name, me.failure)
     aa = []
     a4 = me.addr['INET']
     if me.failure is not None: raise ResolverFailure(me.name, me.failure)
     aa = []
     a4 = me.addr['INET']
+    a6 = me.addr['INET6']
     all, any = False, False
     for ch in flags:
       if ch == '*': all = True
       elif ch == '4': aa += a4; any = True
     all, any = False, False
     for ch in flags:
       if ch == '*': all = True
       elif ch == '4': aa += a4; any = True
+      elif ch == '6': aa += a6; any = True
       else: raise ValueError("unknown address-resolution flag `%s'" % ch)
       else: raise ValueError("unknown address-resolution flag `%s'" % ch)
-    if not any: aa = a4
+    if not any: aa = a4 + a6
     if not aa: raise ResolverFailure(me.name, 'no matching addresses found')
     if not all: aa = [aa[0]]
     return aa
 
     if not aa: raise ResolverFailure(me.name, 'no matching addresses found')
     if not all: aa = [aa[0]]
     return aa
 
-class BulkResolver (object):
+class BaseBulkResolver (object):
   """
   Resolve a number of DNS names in parallel.
 
   """
   Resolve a number of DNS names in parallel.
 
@@ -122,26 +125,49 @@ class BulkResolver (object):
   def __init__(me):
     """Initialize the resolver."""
     me._namemap = {}
   def __init__(me):
     """Initialize the resolver."""
     me._namemap = {}
-    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)
 
   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, cname, addr),
-        lambda: me._resolved(host, None, []))
-      me._noutstand += 1
-
-  def run(me):
-    """Run the background DNS resolver until it's finished."""
-    while me._noutstand: M.select()
+      try:
+        ailist = S.getaddrinfo(name, None, S.AF_UNSPEC, S.SOCK_DGRAM, 0,
+                               S.AI_NUMERICHOST | S.AI_NUMERICSERV)
+      except S.gaierror:
+        me._prepare(host, name)
+      else:
+        for af, skty, proto, cname, sa in ailist:
+          if af == S.AF_INET: host.addaddr('INET', sa[0])
+          elif af == S.AF_INET6: host.addaddr('INET6', sa[0])
 
   def lookup(me, name, flags):
     """Fetch the address corresponding to the host NAME."""
     return me._namemap[name].get(flags)
 
 
   def lookup(me, name, flags):
     """Fetch the address corresponding to the host NAME."""
     return me._namemap[name].get(flags)
 
+class BresBulkResolver (BaseBulkResolver):
+  """
+  A BulkResolver using mLib's `bres' background resolver.
+
+  This is always available (and might use ADNS), but only does IPv4.
+  """
+
+  def __init__(me):
+    super(BresBulkResolver, me).__init__()
+    """Initialize the resolver."""
+    me._noutstand = 0
+
+  def _prepare(me, host, name):
+    """Arrange to resolve a NAME, reporting the results to HOST."""
+    host._resolv = M.SelResolveByName(
+      name,
+      lambda cname, alias, addr: me._resolved(host, cname, addr),
+      lambda: me._resolved(host, None, []))
+    me._noutstand += 1
+
+  def run(me):
+    """Run the background DNS resolver until it's finished."""
+    while me._noutstand: M.select()
+
   def _resolved(me, host, cname, addr):
     """Callback function: remember that ADDRs are the addresses for HOST."""
     if not addr:
   def _resolved(me, host, cname, addr):
     """Callback function: remember that ADDRs are the addresses for HOST."""
     if not addr:
@@ -152,6 +178,9 @@ class BulkResolver (object):
     host._resolv = None
     me._noutstand -= 1
 
     host._resolv = None
     me._noutstand -= 1
 
+## Select a bulk resolver.  Currently, there's only one choice.
+BulkResolver = BresBulkResolver
+
 ###--------------------------------------------------------------------------
 ### The configuration parser.
 
 ###--------------------------------------------------------------------------
 ### The configuration parser.
 
@@ -178,7 +207,7 @@ RX_REF = RX.compile(r'(?x) \$ \( ([^)]+) \)')
 
 ## Match a $FLAGS[HOST] name resolution reference; group 1 are the flags;
 ## group 2 is the HOST.
 
 ## Match a $FLAGS[HOST] name resolution reference; group 1 are the flags;
 ## group 2 is the HOST.
-RX_RESOLVE = RX.compile(r'(?x) \$ ([4*]*) \[ ([^]]+) \]')
+RX_RESOLVE = RX.compile(r'(?x) \$ ([46*]*) \[ ([^]]+) \]')
 
 class ConfigSyntaxError (ExpectedError):
   def __init__(me, fname, lno, msg):
 
 class ConfigSyntaxError (ExpectedError):
   def __init__(me, fname, lno, msg):
@@ -397,8 +426,9 @@ class MyConfigParser (object):
       expansion and processes them correctly.
 
     * It recognizes `$FLAGS[HOST]' name-resolver requests and handles them
       expansion and processes them correctly.
 
     * It recognizes `$FLAGS[HOST]' name-resolver requests and handles them
-      correctly.  FLAGS consists of characters `4' (IPv4 addresses), and `*'
-      (all addresses, space-separated, rather than just the first).
+      correctly.  FLAGS consists of characters `4' (IPv4 addresses), `6'
+      (IPv6 addresses), and `*' (all, space-separated, rather than just the
+      first).
 
     * Its parsing behaviour is well-defined.
 
 
     * Its parsing behaviour is well-defined.