def __init__(self, loop): self._loop = loop self._hosts = load_hosts_conf() self._servers = load_resolv_conf() self._callbacks = {} self._cache = LRUCache(timeout=300, **self._hosts) self._sock = None # TODO monitor hosts change and reload hosts # TODO parse /etc/gai.conf and follow its rules self._register_to_loop()
class DNSResolver(object): def __init__(self, loop): self._loop = loop self._hosts = load_hosts_conf() self._servers = load_resolv_conf() self._callbacks = {} self._cache = LRUCache(timeout=300, **self._hosts) self._sock = None # TODO monitor hosts change and reload hosts # TODO parse /etc/gai.conf and follow its rules self._register_to_loop() def _register_to_loop(self): self._sock = Socket(self._servers) self._sock.setblocking(False) self._loop.register(self._sock, MainLoop.EVENT_READ, self.handle_event) self._loop.add_timeout(self._handle_timeout, 20) def _call_callback(self, hostname, ipaddr, error=None): """domain resolved, execute the callbacks""" DEBUG('DNS callback %s:%s' % (hostname, ipaddr)) for callback in self._callbacks.get(hostname, []): if ipaddr or error: callback((hostname, ipaddr), error) else: callback((hostname, None), Exception('unknown hostname %s' % hostname)) if hostname in self._callbacks: del self._callbacks[hostname] def _send_request(self, hostname): DEBUG('query DNS %s' % hostname) self._sock.send_dns_request(hostname) def _handle_response(self, response): if response is None: """wait another response""" return if response.is_valid(): self._cache[response.hostname] = response.answer self._call_callback(response.hostname, response.answer) def handle_event(self, sock, event): if sock != self._sock: return if event & MainLoop.EVENT_ERROR: ERROR('dns socket error') self._loop.remove(self._sock) self._sock.close() self._refresh() elif event & MainLoop.EVENT_READ: response = sock.recv_dns_response() self._handle_response(response) def _handle_timeout(self): self._cache.sweep() def resolve(self, host, callback): """resolve a domain names""" hostname = tobytes(host) if not hostname or not check_hostname(hostname): callback(None, Exception('invalid hostname: %s' % hostname)) elif ip_address(hostname): callback((hostname, hostname), None) elif hostname in self._cache: DEBUG('hit cache: %s' % host) ip = self._cache[hostname] callback((hostname, ip), None) else: arr = self._callbacks.get(hostname, None) if not arr: self._callbacks[hostname] = [callback] self._send_request(hostname) else: arr.append(callback) # TODO send again only if waited too long self._send_request(hostname) def close(self): if self._sock: if self._loop: self._loop.remove_timeout(self._handle_timeout) self._loop.unregister(self._sock) self._sock.close() self._sock = None