lib.cuckoo.common.dns

Package lib :: Package cuckoo :: Package common :: Module dns |
[hide private]
[frames] | no frames]

# Source Code for Module lib.cuckoo.common.dns

      1  # Copyright (C) 2010-2013 Claudio Guarnieri.
      2  # Copyright (C) 2014-2016 Cuckoo Foundation.
      3  # This file is part of Cuckoo Sandbox - http://www.cuckoosandbox.org
      4  # See the file 'docs/LICENSE' for copying permission.
      5
      6  import select
      7  import socket
      8  import threading
      9
     10  try:
     11      import pycares
     12      HAVE_CARES = True
     13  except:
     14      HAVE_CARES = False
     15
     16  #try:
     17  #    import gevent, gevent.socket
     18  #    HAVE_GEVENT = True
     19  #except:
     20  HAVE_GEVENT = False
     21
     22
     23  # these are used by all resolvers
     24  DNS_TIMEOUT = 5
     25  DNS_TIMEOUT_VALUE = ""
     26
     27 -def set_timeout(value):
     28      global DNS_TIMEOUT
     29      DNS_TIMEOUT = value
     30
     31 -def set_timeout_value(value):
     32      global DNS_TIMEOUT_VALUE
     33      DNS_TIMEOUT_VALUE = value
     34
     35
     36  # standard gethostbyname in thread
     37  # http://code.activestate.com/recipes/473878/
     38 -def with_timeout(func, args=(), kwargs={}):
     39      """This function will spawn a thread and run the given function
     40      using the args, kwargs and return the given default value if the
     41      timeout_duration is exceeded.
     42      """
     43      class ResultThread(threading.Thread):
     44          daemon = True
     45          def __init__(self):
     46              threading.Thread.__init__(self)
     47              self.result, self.error = None, None
     48          def run(self):
     49              try:
     50                  self.result = func(*args, **kwargs)
     51              except Exception, e:
     52                  self.error = e
     53
     54      it = ResultThread()
     55      it.start()
     56      it.join(DNS_TIMEOUT)
     57      if it.isAlive():
     58          return DNS_TIMEOUT_VALUE
     59      else:
     60          if it.error:
     61              raise it.error
     62          return it.result
     63
     64 -def resolve_thread(name):
     65      return with_timeout(gethostbyname, (name,))
     66
     67 -def gethostbyname(name):
     68      try:
     69          ip = socket.gethostbyname(name)
     70      except socket.gaierror:
     71          ip = ""
     72      return ip
     73
     74
     75  # C-ARES (http://c-ares.haxx.se/)
     76 -def resolve_cares(name):
     77      # create new c-ares channel
     78      careschan = pycares.Channel(timeout=DNS_TIMEOUT, tries=1)
     79
     80      # if we don't get a response we return the default value
     81      result = Resultholder()
     82      result.value = DNS_TIMEOUT_VALUE
     83
     84      def setresult_cb(res, error):
     85          # ignore error and just take first result ip (randomized anyway)
     86          if res and res.addresses:
     87              result.value = res.addresses[0]
     88
     89      # resolve with cb
     90      careschan.gethostbyname(name, socket.AF_INET, setresult_cb)
     91
     92      # now do the actual work
     93      readfds, writefds = careschan.getsock()
     94      canreadfds, canwritefds, _ = select.select(readfds, writefds, [],
     95                                                 DNS_TIMEOUT)
     96      for rfd in canreadfds:
     97          careschan.process_fd(rfd, -1)
     98
     99      # if the query did not succeed, setresult was not called and we just
    100      # return result destroy the channel first to not leak anything
    101      careschan.destroy()
    102      return result.value
    103
    104  # workaround until py3 nonlocal (for c-ares and gevent)
    105 -class Resultholder:
    106      pass
    107
    108
    109  # gevent based resolver with timeout
    110  """def resolve_gevent(name):
    111      result = resolve_gevent_real(name)
    112      # if it failed, do this a second time because of strange libevent behavior
    113      # basically sometimes the Timeout fires immediately instead of after
    114      # DNS_TIMEOUT
    115      if result == DNS_TIMEOUT_VALUE:
    116          result = resolve_gevent_real(name)
    117      return result
    118
    119  def resolve_gevent_real(name):
    120      result = DNS_TIMEOUT_VALUE
    121      with gevent.Timeout(DNS_TIMEOUT, False):
    122          try:
    123              result = gevent.socket.gethostbyname(name)
    124          except socket.gaierror:
    125              pass
    126
    127      return result
    128  """
    129
    130  # choose resolver automatically
    131 -def resolve(name):
    132      if HAVE_CARES:
    133          return resolve_cares(name)
    134      #elif HAVE_GEVENT:
    135      #    return resolve_gevent(name)
    136      else:
    137          return resolve_thread(name)
    138
    139  # another alias
    140  resolve_best = resolve
    141