Browse Source

add timeout support in DNS

auth
clowwindy 11 years ago
parent
commit
d4458bceb7
  1. 33
      shadowsocks/asyncdns.py
  2. 7
      shadowsocks/lru_cache.py

33
shadowsocks/asyncdns.py

@ -32,7 +32,7 @@ import lru_cache
import eventloop import eventloop
CACHE_SWEEP_INTERVAL = 30 CACHE_SWEEP_INTERVAL = 10
VALID_HOSTNAME = re.compile("(?!-)[A-Z\d-]{1,63}(?<!-)$", re.IGNORECASE) VALID_HOSTNAME = re.compile("(?!-)[A-Z\d-]{1,63}(?<!-)$", re.IGNORECASE)
@ -267,19 +267,23 @@ class DNSResolver(object):
self._hosts = {} self._hosts = {}
self._hostname_status = {} self._hostname_status = {}
self._hostname_to_cb = {} self._hostname_to_cb = {}
self._cb_to_hostname = {} self._cb_to_hostname = lru_cache.LRUCache(timeout=15,
close_callback=self._timedout)
self._cache = lru_cache.LRUCache(timeout=300) self._cache = lru_cache.LRUCache(timeout=300)
self._last_time = time.time() self._last_time = time.time()
self._sock = None self._sock = None
self._servers = None
self._server_index = 0
self._parse_resolv() self._parse_resolv()
self._parse_hosts() self._parse_hosts()
# TODO monitor hosts change and reload hosts # TODO monitor hosts change and reload hosts
# TODO parse /etc/gai.conf and follow its rules # TODO parse /etc/gai.conf and follow its rules
def _parse_resolv(self): def _parse_resolv(self):
servers = []
self._servers = servers
try: try:
with open('/etc/resolv.conf', 'rb') as f: with open('/etc/resolv.conf', 'rb') as f:
servers = []
content = f.readlines() content = f.readlines()
for line in content: for line in content:
line = line.strip() line = line.strip()
@ -291,12 +295,20 @@ class DNSResolver(object):
if is_ip(server): if is_ip(server):
servers.append(server) servers.append(server)
# TODO support more servers # TODO support more servers
if servers:
self._dns_server = (servers[0], 53)
return
except IOError: except IOError:
pass pass
self._dns_server = ('8.8.8.8', 53) if not servers:
servers.append('8.8.8.8')
self._dns_server = (servers[0], 53)
def _timedout(self, hostname):
error = Exception('timed out when resolving %s with DNS %s' %
(hostname, self._dns_server))
self._server_index += 1
self._server_index %= len(self._servers)
self._dns_server = (self._servers[self._server_index], 53)
self._call_callback(hostname, None, error=error)
def _parse_hosts(self): def _parse_hosts(self):
etc_path = '/etc/hosts' etc_path = '/etc/hosts'
@ -328,13 +340,13 @@ class DNSResolver(object):
loop.add(self._sock, eventloop.POLL_IN) loop.add(self._sock, eventloop.POLL_IN)
loop.add_handler(self.handle_events) loop.add_handler(self.handle_events)
def _call_callback(self, hostname, ip): def _call_callback(self, hostname, ip, error=None):
callbacks = self._hostname_to_cb.get(hostname, []) callbacks = self._hostname_to_cb.get(hostname, [])
for callback in callbacks: for callback in callbacks:
if self._cb_to_hostname.__contains__(callback): if self._cb_to_hostname.__contains__(callback):
del self._cb_to_hostname[callback] del self._cb_to_hostname[callback]
if ip: if ip or error:
callback((hostname, ip), None) callback((hostname, ip), error)
else: else:
callback((hostname, None), callback((hostname, None),
Exception('unknown hostname %s' % hostname)) Exception('unknown hostname %s' % hostname))
@ -385,6 +397,7 @@ class DNSResolver(object):
now = time.time() now = time.time()
if now - self._last_time > CACHE_SWEEP_INTERVAL: if now - self._last_time > CACHE_SWEEP_INTERVAL:
self._cache.sweep() self._cache.sweep()
self._cb_to_hostname.sweep()
self._last_time = now self._last_time = now
def remove_callback(self, callback): def remove_callback(self, callback):

7
shadowsocks/lru_cache.py

@ -50,13 +50,14 @@ class LRUCache(collections.MutableMapping):
least = self._last_visits[0] least = self._last_visits[0]
if now - least <= self.timeout: if now - least <= self.timeout:
break break
if self.close_callback is not None:
for key in self._time_to_keys[least]: for key in self._time_to_keys[least]:
heapq.heappop(self._last_visits)
if self._store.__contains__(key): if self._store.__contains__(key):
value = self._store[key] value = self._store[key]
if self.close_callback is not None:
self.close_callback(value) self.close_callback(value)
for key in self._time_to_keys[least]:
heapq.heappop(self._last_visits)
if self._store.__contains__(key):
del self._store[key] del self._store[key]
c += 1 c += 1
del self._time_to_keys[least] del self._time_to_keys[least]

Loading…
Cancel
Save