+ while me._noutstand: M.select()
+
+ def _resolved(me, host, cname, addr):
+ """Callback function: remember that ADDRs are the addresses for HOST."""
+ if not addr:
+ host.failed('(unknown failure)')
+ else:
+ if cname is not None: host.name = cname
+ for a in addr: host.addaddr('INET', a)
+ host._resolv = None
+ me._noutstand -= 1
+
+class AdnsBulkResolver (BaseBulkResolver):
+ """
+ A BulkResolver using ADNS, via the `adnshost' command-line tool.
+
+ This can do simultaneous IPv4 and IPv6 lookups and is quite shiny.
+ """
+
+ def __init__(me):
+ """Initialize the resolver."""
+
+ super(AdnsBulkResolver, me).__init__()
+
+ ## Start the external resolver process.
+ me._kid = SUB.Popen(['adnshost', '-afs'],
+ stdin = SUB.PIPE, stdout = SUB.PIPE)
+
+ ## Set up the machinery for feeding input to the resolver.
+ me._in = me._kid.stdin
+ M.fdflags(me._in, fbic = OS.O_NONBLOCK, fxor = OS.O_NONBLOCK)
+ me._insel = M.SelFile(me._in.fileno(), M.SEL_WRITE, me._write)
+ me._inbuf, me._inoff, me._inlen = '', 0, 0
+ me._idmap = {}
+ me._nextid = 0
+
+ ## Set up the machinery for collecting the resolver's output.
+ me._out = me._kid.stdout
+ M.fdflags(me._out, fbic = OS.O_NONBLOCK, fxor = OS.O_NONBLOCK)
+ me._outline = M.SelLineBuffer(me._out,
+ lineproc = me._hostline, eofproc = me._eof)
+ me._outline.enable()
+
+ ## It's not finished yet.
+ me._done = False
+
+ def _prepare(me, host, name):
+ """Arrange for the resolver to resolve the name NAME."""
+
+ ## Work out the next job id, and associate that with the host record.
+ host.id = me._nextid; me._nextid += 1
+ me._namemap[name] = me._idmap[host.id] = host
+
+ ## Feed the name to the resolver process.
+ me._inbuf += name + '\n'
+ me._inlen += len(name) + 1
+ if not me._insel.activep: me._insel.enable()
+ while me._inoff < me._inlen: M.select()
+
+ def _write(me):
+ """Write material from `_inbuf' to the resolver when it's ready."""
+
+ ## Try to feed some more material to the resolver.
+ try: n = OS.write(me._in.fileno(), me._inbuf[me._inoff:])
+ except OSError, e:
+ if e.errno == E.EAGAIN or e.errno == E.EWOULDBLOCK: return
+ else: raise
+
+ ## If we're done, then clear the buffer.
+ me._inoff += n
+ if me._inoff >= me._inlen:
+ me._insel.disable()
+ me._inbuf, me._inoff, me._inlen = '', 0, 0
+
+ def _eof(me):
+ """Notice that the resolver has finished."""
+ me._outline.disable()
+ me._done = True
+ me._kid.wait()