test_resolver.py 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590
  1. # Copyright (c) Twisted Matrix Laboratories.
  2. # See LICENSE for details.
  3. """
  4. Tests for implementations of L{IHostnameResolver} and their interactions with
  5. reactor implementations.
  6. """
  7. from __future__ import division, absolute_import
  8. __metaclass__ = type
  9. from collections import defaultdict
  10. from socket import (
  11. getaddrinfo, gaierror, EAI_NONAME, AF_INET, AF_INET6, AF_UNSPEC,
  12. SOCK_STREAM, SOCK_DGRAM, IPPROTO_TCP
  13. )
  14. from threading import local, Lock
  15. from zope.interface import implementer
  16. from zope.interface.verify import verifyObject
  17. from twisted.internet.interfaces import (
  18. IResolutionReceiver, IResolverSimple, IReactorPluggableNameResolver,
  19. IHostnameResolver,
  20. )
  21. from twisted.trial.unittest import (
  22. SynchronousTestCase as UnitTest
  23. )
  24. from twisted.python.threadpool import ThreadPool
  25. from twisted._threads import createMemoryWorker, Team, LockWorker
  26. from twisted.internet.address import IPv4Address, IPv6Address
  27. from twisted.internet._resolver import (
  28. GAIResolver, SimpleResolverComplexifier, ComplexResolverSimplifier
  29. )
  30. from twisted.internet.defer import Deferred
  31. from twisted.internet.error import DNSLookupError
  32. from twisted.internet.base import ReactorBase
  33. class DeterministicThreadPool(ThreadPool, object):
  34. """
  35. Create a deterministic L{ThreadPool} object.
  36. """
  37. def __init__(self, team):
  38. """
  39. Create a L{DeterministicThreadPool} from a L{Team}.
  40. """
  41. self.min = 1
  42. self.max = 1
  43. self.name = None
  44. self.threads = []
  45. self._team = team
  46. def deterministicPool():
  47. """
  48. Create a deterministic threadpool.
  49. @return: 2-tuple of L{ThreadPool}, 0-argument C{work} callable; when
  50. C{work} is called, do the work.
  51. """
  52. worker, doer = createMemoryWorker()
  53. return (
  54. DeterministicThreadPool(Team(LockWorker(Lock(), local()),
  55. (lambda: worker), lambda: None)),
  56. doer
  57. )
  58. def deterministicReactorThreads():
  59. """
  60. Create a deterministic L{IReactorThreads}
  61. @return: a 2-tuple consisting of an L{IReactorThreads}-like object and a
  62. 0-argument callable that will perform one unit of work invoked via that
  63. object's C{callFromThread} method.
  64. """
  65. worker, doer = createMemoryWorker()
  66. class CFT(object):
  67. def callFromThread(self, f, *a, **k):
  68. worker.do(lambda: f(*a, **k))
  69. return CFT(), doer
  70. class FakeAddrInfoGetter(object):
  71. """
  72. Test object implementing getaddrinfo.
  73. """
  74. def __init__(self):
  75. """
  76. Create a L{FakeAddrInfoGetter}.
  77. """
  78. self.calls = []
  79. self.results = defaultdict(list)
  80. def getaddrinfo(self, host, port, family=0, socktype=0, proto=0, flags=0):
  81. """
  82. Mock for L{socket.getaddrinfo}.
  83. @param host: see L{socket.getaddrinfo}
  84. @param port: see L{socket.getaddrinfo}
  85. @param family: see L{socket.getaddrinfo}
  86. @param socktype: see L{socket.getaddrinfo}
  87. @param proto: see L{socket.getaddrinfo}
  88. @param flags: see L{socket.getaddrinfo}
  89. @return: L{socket.getaddrinfo}
  90. """
  91. self.calls.append((host, port, family, socktype, proto, flags))
  92. results = self.results[host]
  93. if results:
  94. return results
  95. else:
  96. raise gaierror(EAI_NONAME,
  97. 'nodename nor servname provided, or not known')
  98. def addResultForHost(self, host, sockaddr, family=AF_INET,
  99. socktype=SOCK_STREAM, proto=IPPROTO_TCP,
  100. canonname=b""):
  101. """
  102. Add a result for a given hostname. When this hostname is resolved, the
  103. result will be a L{list} of all results C{addResultForHost} has been
  104. called with using that hostname so far.
  105. @param host: The hostname to give this result for. This will be the
  106. next result from L{FakeAddrInfoGetter.getaddrinfo} when passed this
  107. host.
  108. @type canonname: native L{str}
  109. @param sockaddr: The resulting socket address; should be a 2-tuple for
  110. IPv4 or a 4-tuple for IPv6.
  111. @param family: An C{AF_*} constant that will be returned from
  112. C{getaddrinfo}.
  113. @param socktype: A C{SOCK_*} constant that will be returned from
  114. C{getaddrinfo}.
  115. @param proto: An C{IPPROTO_*} constant that will be returned from
  116. C{getaddrinfo}.
  117. @param canonname: A canonical name that will be returned from
  118. C{getaddrinfo}.
  119. @type canonname: native L{str}
  120. """
  121. self.results[host].append(
  122. (family, socktype, proto, canonname, sockaddr)
  123. )
  124. @implementer(IResolutionReceiver)
  125. class ResultHolder(object):
  126. """
  127. A resolution receiver which holds onto the results it received.
  128. """
  129. _started = False
  130. _ended = False
  131. def __init__(self, testCase):
  132. """
  133. Create a L{ResultHolder} with a L{UnitTest}.
  134. """
  135. self._testCase = testCase
  136. def resolutionBegan(self, hostResolution):
  137. """
  138. Hostname resolution began.
  139. @param hostResolution: see L{IResolutionReceiver}
  140. """
  141. self._started = True
  142. self._resolution = hostResolution
  143. self._addresses = []
  144. def addressResolved(self, address):
  145. """
  146. An address was resolved.
  147. @param address: see L{IResolutionReceiver}
  148. """
  149. self._addresses.append(address)
  150. def resolutionComplete(self):
  151. """
  152. Hostname resolution is complete.
  153. """
  154. self._ended = True
  155. class HelperTests(UnitTest):
  156. """
  157. Tests for error cases of helpers used in this module.
  158. """
  159. def test_logErrorsInThreads(self):
  160. """
  161. L{DeterministicThreadPool} will log any exceptions that its "thread"
  162. workers encounter.
  163. """
  164. self.pool, self.doThreadWork = deterministicPool()
  165. def divideByZero():
  166. return 1 / 0
  167. self.pool.callInThread(divideByZero)
  168. self.doThreadWork()
  169. self.assertEqual(len(self.flushLoggedErrors(ZeroDivisionError)), 1)
  170. class HostnameResolutionTests(UnitTest):
  171. """
  172. Tests for hostname resolution.
  173. """
  174. def setUp(self):
  175. """
  176. Set up a L{GAIResolver}.
  177. """
  178. self.pool, self.doThreadWork = deterministicPool()
  179. self.reactor, self.doReactorWork = deterministicReactorThreads()
  180. self.getter = FakeAddrInfoGetter()
  181. self.resolver = GAIResolver(self.reactor, lambda: self.pool,
  182. self.getter.getaddrinfo)
  183. def test_resolveOneHost(self):
  184. """
  185. Resolving an individual hostname that results in one address from
  186. getaddrinfo results in a single call each to C{resolutionBegan},
  187. C{addressResolved}, and C{resolutionComplete}.
  188. """
  189. receiver = ResultHolder(self)
  190. self.getter.addResultForHost(u"sample.example.com", ("4.3.2.1", 0))
  191. resolution = self.resolver.resolveHostName(receiver,
  192. u"sample.example.com")
  193. self.assertIs(receiver._resolution, resolution)
  194. self.assertEqual(receiver._started, True)
  195. self.assertEqual(receiver._ended, False)
  196. self.doThreadWork()
  197. self.doReactorWork()
  198. self.assertEqual(receiver._ended, True)
  199. self.assertEqual(receiver._addresses,
  200. [IPv4Address('TCP', '4.3.2.1', 0)])
  201. def test_resolveOneIPv6Host(self):
  202. """
  203. Resolving an individual hostname that results in one address from
  204. getaddrinfo results in a single call each to C{resolutionBegan},
  205. C{addressResolved}, and C{resolutionComplete}; C{addressResolved} will
  206. receive an L{IPv6Address}.
  207. """
  208. receiver = ResultHolder(self)
  209. flowInfo = 1
  210. scopeID = 2
  211. self.getter.addResultForHost(u"sample.example.com",
  212. ("::1", 0, flowInfo, scopeID),
  213. family=AF_INET6)
  214. resolution = self.resolver.resolveHostName(receiver,
  215. u"sample.example.com")
  216. self.assertIs(receiver._resolution, resolution)
  217. self.assertEqual(receiver._started, True)
  218. self.assertEqual(receiver._ended, False)
  219. self.doThreadWork()
  220. self.doReactorWork()
  221. self.assertEqual(receiver._ended, True)
  222. self.assertEqual(receiver._addresses,
  223. [IPv6Address('TCP', '::1', 0, flowInfo, scopeID)])
  224. def test_gaierror(self):
  225. """
  226. Resolving a hostname that results in C{getaddrinfo} raising a
  227. L{gaierror} will result in the L{IResolutionReceiver} receiving a call
  228. to C{resolutionComplete} with no C{addressResolved} calls in between;
  229. no failure is logged.
  230. """
  231. receiver = ResultHolder(self)
  232. resolution = self.resolver.resolveHostName(receiver,
  233. u"sample.example.com")
  234. self.assertIs(receiver._resolution, resolution)
  235. self.doThreadWork()
  236. self.doReactorWork()
  237. self.assertEqual(receiver._started, True)
  238. self.assertEqual(receiver._ended, True)
  239. self.assertEqual(receiver._addresses, [])
  240. def _resolveOnlyTest(self, addrTypes, expectedAF):
  241. """
  242. Verify that the given set of address types results in the given C{AF_}
  243. constant being passed to C{getaddrinfo}.
  244. @param addrTypes: iterable of L{IAddress} implementers
  245. @param expectedAF: an C{AF_*} constant
  246. """
  247. receiver = ResultHolder(self)
  248. resolution = self.resolver.resolveHostName(
  249. receiver, u"sample.example.com", addressTypes=addrTypes
  250. )
  251. self.assertIs(receiver._resolution, resolution)
  252. self.doThreadWork()
  253. self.doReactorWork()
  254. host, port, family, socktype, proto, flags = self.getter.calls[0]
  255. self.assertEqual(family, expectedAF)
  256. def test_resolveOnlyIPv4(self):
  257. """
  258. When passed an C{addressTypes} parameter containing only
  259. L{IPv4Address}, L{GAIResolver} will pass C{AF_INET} to C{getaddrinfo}.
  260. """
  261. self._resolveOnlyTest([IPv4Address], AF_INET)
  262. def test_resolveOnlyIPv6(self):
  263. """
  264. When passed an C{addressTypes} parameter containing only
  265. L{IPv6Address}, L{GAIResolver} will pass C{AF_INET6} to C{getaddrinfo}.
  266. """
  267. self._resolveOnlyTest([IPv6Address], AF_INET6)
  268. def test_resolveBoth(self):
  269. """
  270. When passed an C{addressTypes} parameter containing both L{IPv4Address}
  271. and L{IPv6Address} (or the default of C{None}, which carries the same
  272. meaning), L{GAIResolver} will pass C{AF_UNSPEC} to C{getaddrinfo}.
  273. """
  274. self._resolveOnlyTest([IPv4Address, IPv6Address], AF_UNSPEC)
  275. self._resolveOnlyTest(None, AF_UNSPEC)
  276. def test_transportSemanticsToSocketType(self):
  277. """
  278. When passed a C{transportSemantics} paramter, C{'TCP'} (the value
  279. present in L{IPv4Address.type} to indicate a stream transport) maps to
  280. C{SOCK_STREAM} and C{'UDP'} maps to C{SOCK_DGRAM}.
  281. """
  282. receiver = ResultHolder(self)
  283. self.resolver.resolveHostName(receiver, u"example.com",
  284. transportSemantics='TCP')
  285. receiver2 = ResultHolder(self)
  286. self.resolver.resolveHostName(receiver2, u"example.com",
  287. transportSemantics='UDP')
  288. self.doThreadWork()
  289. self.doReactorWork()
  290. self.doThreadWork()
  291. self.doReactorWork()
  292. host, port, family, socktypeT, proto, flags = self.getter.calls[0]
  293. host, port, family, socktypeU, proto, flags = self.getter.calls[1]
  294. self.assertEqual(socktypeT, SOCK_STREAM)
  295. self.assertEqual(socktypeU, SOCK_DGRAM)
  296. def test_socketTypeToAddressType(self):
  297. """
  298. When L{GAIResolver} receives a C{SOCK_DGRAM} result from
  299. C{getaddrinfo}, it returns a C{'TCP'} L{IPv4Address} or L{IPv6Address};
  300. if it receives C{SOCK_STREAM} then it returns a C{'UDP'} type of same.
  301. """
  302. receiver = ResultHolder(self)
  303. flowInfo = 1
  304. scopeID = 2
  305. for socktype in SOCK_STREAM, SOCK_DGRAM:
  306. self.getter.addResultForHost(
  307. "example.com", ("::1", 0, flowInfo, scopeID), family=AF_INET6,
  308. socktype=socktype
  309. )
  310. self.getter.addResultForHost(
  311. "example.com", ("127.0.0.3", 0), family=AF_INET,
  312. socktype=socktype
  313. )
  314. self.resolver.resolveHostName(receiver, u"example.com")
  315. self.doThreadWork()
  316. self.doReactorWork()
  317. stream4, stream6, dgram4, dgram6 = receiver._addresses
  318. self.assertEqual(stream4.type, 'TCP')
  319. self.assertEqual(stream6.type, 'TCP')
  320. self.assertEqual(dgram4.type, 'UDP')
  321. self.assertEqual(dgram6.type, 'UDP')
  322. @implementer(IResolverSimple)
  323. class SillyResolverSimple(object):
  324. """
  325. Trivial implementation of L{IResolverSimple}
  326. """
  327. def __init__(self):
  328. """
  329. Create a L{SillyResolverSimple} with a queue of requests it is working
  330. on.
  331. """
  332. self._requests = []
  333. def getHostByName(self, name, timeout=()):
  334. """
  335. Implement L{IResolverSimple.getHostByName}.
  336. @param name: see L{IResolverSimple.getHostByName}.
  337. @param timeout: see L{IResolverSimple.getHostByName}.
  338. @return: see L{IResolverSimple.getHostByName}.
  339. """
  340. self._requests.append(Deferred())
  341. return self._requests[-1]
  342. class LegacyCompatibilityTests(UnitTest, object):
  343. """
  344. Older applications may supply an object to the reactor via
  345. C{installResolver} that only provides L{IResolverSimple}.
  346. L{SimpleResolverComplexifier} is a wrapper for an L{IResolverSimple}.
  347. """
  348. def test_success(self):
  349. """
  350. L{SimpleResolverComplexifier} translates C{resolveHostName} into
  351. L{IResolutionReceiver.addressResolved}.
  352. """
  353. simple = SillyResolverSimple()
  354. complex = SimpleResolverComplexifier(simple)
  355. receiver = ResultHolder(self)
  356. self.assertEqual(receiver._started, False)
  357. complex.resolveHostName(receiver, u"example.com")
  358. self.assertEqual(receiver._started, True)
  359. self.assertEqual(receiver._ended, False)
  360. self.assertEqual(receiver._addresses, [])
  361. simple._requests[0].callback("192.168.1.1")
  362. self.assertEqual(receiver._addresses,
  363. [IPv4Address('TCP', '192.168.1.1', 0)])
  364. self.assertEqual(receiver._ended, True)
  365. def test_failure(self):
  366. """
  367. L{SimpleResolverComplexifier} translates a known error result from
  368. L{IResolverSimple.resolveHostName} into an empty result.
  369. """
  370. simple = SillyResolverSimple()
  371. complex = SimpleResolverComplexifier(simple)
  372. receiver = ResultHolder(self)
  373. self.assertEqual(receiver._started, False)
  374. complex.resolveHostName(receiver, u"example.com")
  375. self.assertEqual(receiver._started, True)
  376. self.assertEqual(receiver._ended, False)
  377. self.assertEqual(receiver._addresses, [])
  378. simple._requests[0].errback(DNSLookupError("nope"))
  379. self.assertEqual(receiver._ended, True)
  380. self.assertEqual(receiver._addresses, [])
  381. def test_error(self):
  382. """
  383. L{SimpleResolverComplexifier} translates an unknown error result from
  384. L{IResolverSimple.resolveHostName} into an empty result and a logged
  385. error.
  386. """
  387. simple = SillyResolverSimple()
  388. complex = SimpleResolverComplexifier(simple)
  389. receiver = ResultHolder(self)
  390. self.assertEqual(receiver._started, False)
  391. complex.resolveHostName(receiver, u"example.com")
  392. self.assertEqual(receiver._started, True)
  393. self.assertEqual(receiver._ended, False)
  394. self.assertEqual(receiver._addresses, [])
  395. simple._requests[0].errback(ZeroDivisionError("zow"))
  396. self.assertEqual(len(self.flushLoggedErrors(ZeroDivisionError)), 1)
  397. self.assertEqual(receiver._ended, True)
  398. self.assertEqual(receiver._addresses, [])
  399. def test_simplifier(self):
  400. """
  401. L{ComplexResolverSimplifier} translates an L{IHostnameResolver} into an
  402. L{IResolverSimple} for applications that still expect the old
  403. interfaces to be in place.
  404. """
  405. self.pool, self.doThreadWork = deterministicPool()
  406. self.reactor, self.doReactorWork = deterministicReactorThreads()
  407. self.getter = FakeAddrInfoGetter()
  408. self.resolver = GAIResolver(self.reactor, lambda: self.pool,
  409. self.getter.getaddrinfo)
  410. simpleResolver = ComplexResolverSimplifier(self.resolver)
  411. self.getter.addResultForHost('example.com', ('192.168.3.4', 4321))
  412. success = simpleResolver.getHostByName('example.com')
  413. failure = simpleResolver.getHostByName('nx.example.com')
  414. self.doThreadWork()
  415. self.doReactorWork()
  416. self.doThreadWork()
  417. self.doReactorWork()
  418. self.assertEqual(self.failureResultOf(failure).type, DNSLookupError)
  419. self.assertEqual(self.successResultOf(success), '192.168.3.4')
  420. def test_portNumber(self):
  421. """
  422. L{SimpleResolverComplexifier} preserves the C{port} argument passed to
  423. C{resolveHostName} in its returned addresses.
  424. """
  425. simple = SillyResolverSimple()
  426. complex = SimpleResolverComplexifier(simple)
  427. receiver = ResultHolder(self)
  428. complex.resolveHostName(receiver, u"example.com", 4321)
  429. self.assertEqual(receiver._started, True)
  430. self.assertEqual(receiver._ended, False)
  431. self.assertEqual(receiver._addresses, [])
  432. simple._requests[0].callback("192.168.1.1")
  433. self.assertEqual(receiver._addresses,
  434. [IPv4Address('TCP', '192.168.1.1', 4321)])
  435. self.assertEqual(receiver._ended, True)
  436. class JustEnoughReactor(ReactorBase, object):
  437. """
  438. Just enough subclass implementation to be a valid L{ReactorBase} subclass.
  439. """
  440. def installWaker(self):
  441. """
  442. Do nothing.
  443. """
  444. class ReactorInstallationTests(UnitTest, object):
  445. """
  446. Tests for installing old and new resolvers onto a L{ReactorBase} (from
  447. which all of Twisted's reactor implementations derive).
  448. """
  449. def test_interfaceCompliance(self):
  450. """
  451. L{ReactorBase} (and its subclasses) provide both
  452. L{IReactorPluggableNameResolver} and L{IReactorPluggableResolver}.
  453. """
  454. reactor = JustEnoughReactor()
  455. verifyObject(IReactorPluggableNameResolver, reactor)
  456. verifyObject(IResolverSimple, reactor.resolver)
  457. verifyObject(IHostnameResolver, reactor.nameResolver)
  458. def test_defaultToGAIResolver(self):
  459. """
  460. L{ReactorBase} defaults to using a L{GAIResolver}.
  461. """
  462. reactor = JustEnoughReactor()
  463. self.assertIsInstance(reactor.nameResolver, GAIResolver)
  464. self.assertIs(reactor.nameResolver._getaddrinfo, getaddrinfo)
  465. self.assertIsInstance(reactor.resolver, ComplexResolverSimplifier)
  466. self.assertIs(reactor.nameResolver._reactor, reactor)
  467. self.assertIs(reactor.resolver._nameResolver, reactor.nameResolver)
  468. def test_installingOldStyleResolver(self):
  469. """
  470. L{ReactorBase} will wrap an L{IResolverSimple} in a complexifier.
  471. """
  472. reactor = JustEnoughReactor()
  473. it = SillyResolverSimple()
  474. verifyObject(IResolverSimple, reactor.installResolver(it))
  475. self.assertIsInstance(reactor.nameResolver, SimpleResolverComplexifier)
  476. self.assertIs(reactor.nameResolver._simpleResolver, it)