test_hosts.py 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. # Copyright (c) Twisted Matrix Laboratories.
  2. # See LICENSE for details.
  3. """
  4. Tests for the I{hosts(5)}-based resolver, L{twisted.names.hosts}.
  5. """
  6. from __future__ import division, absolute_import
  7. from twisted.trial.unittest import TestCase
  8. from twisted.python.filepath import FilePath
  9. from twisted.internet.defer import gatherResults
  10. from twisted.names.dns import (
  11. A, AAAA, IN, DomainError, RRHeader, Query, Record_A, Record_AAAA)
  12. from twisted.names.hosts import Resolver, searchFileFor, searchFileForAll
  13. class GoodTempPathMixin(object):
  14. def path(self):
  15. return FilePath(self.mktemp().encode('utf-8'))
  16. class SearchHostsFileTests(TestCase, GoodTempPathMixin):
  17. """
  18. Tests for L{searchFileFor}, a helper which finds the first address for a
  19. particular hostname in a I{hosts(5)}-style file.
  20. """
  21. def test_findAddress(self):
  22. """
  23. If there is an IPv4 address for the hostname passed to L{searchFileFor},
  24. it is returned.
  25. """
  26. hosts = self.path()
  27. hosts.setContent(
  28. b"10.2.3.4 foo.example.com\n")
  29. self.assertEqual(
  30. "10.2.3.4", searchFileFor(hosts.path, b"foo.example.com"))
  31. def test_notFoundAddress(self):
  32. """
  33. If there is no address information for the hostname passed to
  34. L{searchFileFor}, L{None} is returned.
  35. """
  36. hosts = self.path()
  37. hosts.setContent(
  38. b"10.2.3.4 foo.example.com\n")
  39. self.assertIsNone(searchFileFor(hosts.path, b"bar.example.com"))
  40. def test_firstAddress(self):
  41. """
  42. The first address associated with the given hostname is returned.
  43. """
  44. hosts = self.path()
  45. hosts.setContent(
  46. b"::1 foo.example.com\n"
  47. b"10.1.2.3 foo.example.com\n"
  48. b"fe80::21b:fcff:feee:5a1d foo.example.com\n")
  49. self.assertEqual("::1", searchFileFor(hosts.path, b"foo.example.com"))
  50. def test_searchFileForAliases(self):
  51. """
  52. For a host with a canonical name and one or more aliases,
  53. L{searchFileFor} can find an address given any of the names.
  54. """
  55. hosts = self.path()
  56. hosts.setContent(
  57. b"127.0.1.1\thelmut.example.org\thelmut\n"
  58. b"# a comment\n"
  59. b"::1 localhost ip6-localhost ip6-loopback\n")
  60. self.assertEqual(searchFileFor(hosts.path, b'helmut'), '127.0.1.1')
  61. self.assertEqual(
  62. searchFileFor(hosts.path, b'helmut.example.org'), '127.0.1.1')
  63. self.assertEqual(searchFileFor(hosts.path, b'ip6-localhost'), '::1')
  64. self.assertEqual(searchFileFor(hosts.path, b'ip6-loopback'), '::1')
  65. self.assertEqual(searchFileFor(hosts.path, b'localhost'), '::1')
  66. class SearchHostsFileForAllTests(TestCase, GoodTempPathMixin):
  67. """
  68. Tests for L{searchFileForAll}, a helper which finds all addresses for a
  69. particular hostname in a I{hosts(5)}-style file.
  70. """
  71. def test_allAddresses(self):
  72. """
  73. L{searchFileForAll} returns a list of all addresses associated with the
  74. name passed to it.
  75. """
  76. hosts = self.path()
  77. hosts.setContent(
  78. b"127.0.0.1 foobar.example.com\n"
  79. b"127.0.0.2 foobar.example.com\n"
  80. b"::1 foobar.example.com\n")
  81. self.assertEqual(
  82. ["127.0.0.1", "127.0.0.2", "::1"],
  83. searchFileForAll(hosts, b"foobar.example.com"))
  84. def test_caseInsensitively(self):
  85. """
  86. L{searchFileForAll} searches for names case-insensitively.
  87. """
  88. hosts = self.path()
  89. hosts.setContent(b"127.0.0.1 foobar.EXAMPLE.com\n")
  90. self.assertEqual(
  91. ["127.0.0.1"], searchFileForAll(hosts, b"FOOBAR.example.com"))
  92. def test_readError(self):
  93. """
  94. If there is an error reading the contents of the hosts file,
  95. L{searchFileForAll} returns an empty list.
  96. """
  97. self.assertEqual(
  98. [], searchFileForAll(self.path(), b"example.com"))
  99. class HostsTests(TestCase, GoodTempPathMixin):
  100. """
  101. Tests for the I{hosts(5)}-based L{twisted.names.hosts.Resolver}.
  102. """
  103. def setUp(self):
  104. f = self.path()
  105. f.setContent(b'''
  106. 1.1.1.1 EXAMPLE EXAMPLE.EXAMPLETHING
  107. ::2 mixed
  108. 1.1.1.2 MIXED
  109. ::1 ip6thingy
  110. 1.1.1.3 multiple
  111. 1.1.1.4 multiple
  112. ::3 ip6-multiple
  113. ::4 ip6-multiple
  114. ''')
  115. self.ttl = 4200
  116. self.resolver = Resolver(f.path, self.ttl)
  117. def test_defaultPath(self):
  118. """
  119. The default hosts file used by L{Resolver} is I{/etc/hosts} if no value
  120. is given for the C{file} initializer parameter.
  121. """
  122. resolver = Resolver()
  123. self.assertEqual(b"/etc/hosts", resolver.file)
  124. def test_getHostByName(self):
  125. """
  126. L{hosts.Resolver.getHostByName} returns a L{Deferred} which fires with a
  127. string giving the address of the queried name as found in the resolver's
  128. hosts file.
  129. """
  130. data = [(b'EXAMPLE', '1.1.1.1'),
  131. (b'EXAMPLE.EXAMPLETHING', '1.1.1.1'),
  132. (b'MIXED', '1.1.1.2'),
  133. ]
  134. ds = [self.resolver.getHostByName(n).addCallback(self.assertEqual, ip)
  135. for n, ip in data]
  136. return gatherResults(ds)
  137. def test_lookupAddress(self):
  138. """
  139. L{hosts.Resolver.lookupAddress} returns a L{Deferred} which fires with A
  140. records from the hosts file.
  141. """
  142. d = self.resolver.lookupAddress(b'multiple')
  143. def resolved(results):
  144. answers, authority, additional = results
  145. self.assertEqual(
  146. (RRHeader(b"multiple", A, IN, self.ttl,
  147. Record_A("1.1.1.3", self.ttl)),
  148. RRHeader(b"multiple", A, IN, self.ttl,
  149. Record_A("1.1.1.4", self.ttl))),
  150. answers)
  151. d.addCallback(resolved)
  152. return d
  153. def test_lookupIPV6Address(self):
  154. """
  155. L{hosts.Resolver.lookupIPV6Address} returns a L{Deferred} which fires
  156. with AAAA records from the hosts file.
  157. """
  158. d = self.resolver.lookupIPV6Address(b'ip6-multiple')
  159. def resolved(results):
  160. answers, authority, additional = results
  161. self.assertEqual(
  162. (RRHeader(b"ip6-multiple", AAAA, IN, self.ttl,
  163. Record_AAAA("::3", self.ttl)),
  164. RRHeader(b"ip6-multiple", AAAA, IN, self.ttl,
  165. Record_AAAA("::4", self.ttl))),
  166. answers)
  167. d.addCallback(resolved)
  168. return d
  169. def test_lookupAllRecords(self):
  170. """
  171. L{hosts.Resolver.lookupAllRecords} returns a L{Deferred} which fires
  172. with A records from the hosts file.
  173. """
  174. d = self.resolver.lookupAllRecords(b'mixed')
  175. def resolved(results):
  176. answers, authority, additional = results
  177. self.assertEqual(
  178. (RRHeader(b"mixed", A, IN, self.ttl,
  179. Record_A("1.1.1.2", self.ttl)),),
  180. answers)
  181. d.addCallback(resolved)
  182. return d
  183. def test_notImplemented(self):
  184. return self.assertFailure(self.resolver.lookupMailExchange(b'EXAMPLE'),
  185. NotImplementedError)
  186. def test_query(self):
  187. d = self.resolver.query(Query(b'EXAMPLE'))
  188. d.addCallback(lambda x: self.assertEqual(x[0][0].payload.dottedQuad(),
  189. '1.1.1.1'))
  190. return d
  191. def test_lookupAddressNotFound(self):
  192. """
  193. L{hosts.Resolver.lookupAddress} returns a L{Deferred} which fires with
  194. L{dns.DomainError} if the name passed in has no addresses in the hosts
  195. file.
  196. """
  197. return self.assertFailure(self.resolver.lookupAddress(b'foueoa'),
  198. DomainError)
  199. def test_lookupIPV6AddressNotFound(self):
  200. """
  201. Like L{test_lookupAddressNotFound}, but for
  202. L{hosts.Resolver.lookupIPV6Address}.
  203. """
  204. return self.assertFailure(self.resolver.lookupIPV6Address(b'foueoa'),
  205. DomainError)
  206. def test_lookupAllRecordsNotFound(self):
  207. """
  208. Like L{test_lookupAddressNotFound}, but for
  209. L{hosts.Resolver.lookupAllRecords}.
  210. """
  211. return self.assertFailure(self.resolver.lookupAllRecords(b'foueoa'),
  212. DomainError)