hosts.py 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. # -*- test-case-name: twisted.names.test.test_hosts -*-
  2. # Copyright (c) Twisted Matrix Laboratories.
  3. # See LICENSE for details.
  4. """
  5. hosts(5) support.
  6. """
  7. from __future__ import division, absolute_import
  8. from twisted.python.compat import nativeString
  9. from twisted.names import dns
  10. from twisted.python import failure
  11. from twisted.python.filepath import FilePath
  12. from twisted.internet import defer
  13. from twisted.internet.abstract import isIPAddress
  14. from twisted.names import common
  15. def searchFileForAll(hostsFile, name):
  16. """
  17. Search the given file, which is in hosts(5) standard format, for an address
  18. entry with a given name.
  19. @param hostsFile: The name of the hosts(5)-format file to search.
  20. @type hostsFile: L{FilePath}
  21. @param name: The name to search for.
  22. @type name: C{str}
  23. @return: L{None} if the name is not found in the file, otherwise a
  24. C{str} giving the address in the file associated with the name.
  25. """
  26. results = []
  27. try:
  28. lines = hostsFile.getContent().splitlines()
  29. except:
  30. return results
  31. name = name.lower()
  32. for line in lines:
  33. idx = line.find(b'#')
  34. if idx != -1:
  35. line = line[:idx]
  36. if not line:
  37. continue
  38. parts = line.split()
  39. if name.lower() in [s.lower() for s in parts[1:]]:
  40. results.append(nativeString(parts[0]))
  41. return results
  42. def searchFileFor(file, name):
  43. """
  44. Grep given file, which is in hosts(5) standard format, for an address
  45. entry with a given name.
  46. @param file: The name of the hosts(5)-format file to search.
  47. @param name: The name to search for.
  48. @type name: C{str}
  49. @return: L{None} if the name is not found in the file, otherwise a
  50. C{str} giving the address in the file associated with the name.
  51. """
  52. addresses = searchFileForAll(FilePath(file), name)
  53. if addresses:
  54. return addresses[0]
  55. return None
  56. class Resolver(common.ResolverBase):
  57. """
  58. A resolver that services hosts(5) format files.
  59. """
  60. def __init__(self, file=b'/etc/hosts', ttl = 60 * 60):
  61. common.ResolverBase.__init__(self)
  62. self.file = file
  63. self.ttl = ttl
  64. def _aRecords(self, name):
  65. """
  66. Return a tuple of L{dns.RRHeader} instances for all of the IPv4
  67. addresses in the hosts file.
  68. """
  69. return tuple([
  70. dns.RRHeader(name, dns.A, dns.IN, self.ttl,
  71. dns.Record_A(addr, self.ttl))
  72. for addr
  73. in searchFileForAll(FilePath(self.file), name)
  74. if isIPAddress(addr)])
  75. def _aaaaRecords(self, name):
  76. """
  77. Return a tuple of L{dns.RRHeader} instances for all of the IPv6
  78. addresses in the hosts file.
  79. """
  80. return tuple([
  81. dns.RRHeader(name, dns.AAAA, dns.IN, self.ttl,
  82. dns.Record_AAAA(addr, self.ttl))
  83. for addr
  84. in searchFileForAll(FilePath(self.file), name)
  85. if not isIPAddress(addr)])
  86. def _respond(self, name, records):
  87. """
  88. Generate a response for the given name containing the given result
  89. records, or a failure if there are no result records.
  90. @param name: The DNS name the response is for.
  91. @type name: C{str}
  92. @param records: A tuple of L{dns.RRHeader} instances giving the results
  93. that will go into the response.
  94. @return: A L{Deferred} which will fire with a three-tuple of result
  95. records, authority records, and additional records, or which will
  96. fail with L{dns.DomainError} if there are no result records.
  97. """
  98. if records:
  99. return defer.succeed((records, (), ()))
  100. return defer.fail(failure.Failure(dns.DomainError(name)))
  101. def lookupAddress(self, name, timeout=None):
  102. """
  103. Read any IPv4 addresses from C{self.file} and return them as L{Record_A}
  104. instances.
  105. """
  106. return self._respond(name, self._aRecords(name))
  107. def lookupIPV6Address(self, name, timeout=None):
  108. """
  109. Read any IPv6 addresses from C{self.file} and return them as
  110. L{Record_AAAA} instances.
  111. """
  112. return self._respond(name, self._aaaaRecords(name))
  113. # Someday this should include IPv6 addresses too, but that will cause
  114. # problems if users of the API (mainly via getHostByName) aren't updated to
  115. # know about IPv6 first.
  116. lookupAllRecords = lookupAddress