PageRenderTime 50ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/desktop/core/ext-py/Twisted/twisted/names/test/test_client.py

https://github.com/jcrobak/hue
Python | 635 lines | 329 code | 117 blank | 189 comment | 3 complexity | 5bb35b6d57257bcf384bb38523aa30ea MD5 | raw file
  1. # Copyright (c) 2001-2008 Twisted Matrix Laboratories.
  2. # See LICENSE for details.
  3. """
  4. Test cases for L{twisted.names.client}.
  5. """
  6. from twisted.names import client, dns
  7. from twisted.names.error import DNSQueryTimeoutError
  8. from twisted.trial import unittest
  9. from twisted.names.common import ResolverBase
  10. from twisted.internet import defer, error
  11. from twisted.python import failure
  12. from twisted.python.deprecate import getWarningMethod, setWarningMethod
  13. from twisted.python.compat import set
  14. class FakeResolver(ResolverBase):
  15. def _lookup(self, name, cls, qtype, timeout):
  16. """
  17. The getHostByNameTest does a different type of query that requires it
  18. return an A record from an ALL_RECORDS lookup, so we accomodate that
  19. here.
  20. """
  21. if name == 'getHostByNameTest':
  22. rr = dns.RRHeader(name=name, type=dns.A, cls=cls, ttl=60,
  23. payload=dns.Record_A(address='127.0.0.1', ttl=60))
  24. else:
  25. rr = dns.RRHeader(name=name, type=qtype, cls=cls, ttl=60)
  26. results = [rr]
  27. authority = []
  28. addtional = []
  29. return defer.succeed((results, authority, addtional))
  30. class StubPort(object):
  31. """
  32. A partial implementation of L{IListeningPort} which only keeps track of
  33. whether it has been stopped.
  34. @ivar disconnected: A C{bool} which is C{False} until C{stopListening} is
  35. called, C{True} afterwards.
  36. """
  37. disconnected = False
  38. def stopListening(self):
  39. self.disconnected = True
  40. class StubDNSDatagramProtocol(object):
  41. """
  42. L{dns.DNSDatagramProtocol}-alike.
  43. @ivar queries: A C{list} of tuples giving the arguments passed to
  44. C{query} along with the L{defer.Deferred} which was returned from
  45. the call.
  46. """
  47. def __init__(self):
  48. self.queries = []
  49. self.transport = StubPort()
  50. def query(self, address, queries, timeout=10, id=None):
  51. """
  52. Record the given arguments and return a Deferred which will not be
  53. called back by this code.
  54. """
  55. result = defer.Deferred()
  56. self.queries.append((address, queries, timeout, id, result))
  57. return result
  58. class ResolverTests(unittest.TestCase):
  59. """
  60. Tests for L{client.Resolver}.
  61. """
  62. def test_resolverProtocol(self):
  63. """
  64. Reading L{client.Resolver.protocol} causes a deprecation warning to be
  65. emitted and evaluates to an instance of L{DNSDatagramProtocol}.
  66. """
  67. resolver = client.Resolver(servers=[('example.com', 53)])
  68. self.addCleanup(setWarningMethod, getWarningMethod())
  69. warnings = []
  70. setWarningMethod(
  71. lambda message, category, stacklevel:
  72. warnings.append((message, category, stacklevel)))
  73. protocol = resolver.protocol
  74. self.assertIsInstance(protocol, dns.DNSDatagramProtocol)
  75. self.assertEqual(
  76. warnings, [("Resolver.protocol is deprecated; use "
  77. "Resolver.queryUDP instead.",
  78. PendingDeprecationWarning, 0)])
  79. self.assertIdentical(protocol, resolver.protocol)
  80. def test_datagramQueryServerOrder(self):
  81. """
  82. L{client.Resolver.queryUDP} should issue queries to its
  83. L{dns.DNSDatagramProtocol} with server addresses taken from its own
  84. C{servers} and C{dynServers} lists, proceeding through them in order
  85. as L{DNSQueryTimeoutError}s occur.
  86. """
  87. protocol = StubDNSDatagramProtocol()
  88. servers = [object(), object()]
  89. dynServers = [object(), object()]
  90. resolver = client.Resolver(servers=servers)
  91. resolver.dynServers = dynServers
  92. resolver.protocol = protocol
  93. expectedResult = object()
  94. queryResult = resolver.queryUDP(None)
  95. queryResult.addCallback(self.assertEqual, expectedResult)
  96. self.assertEqual(len(protocol.queries), 1)
  97. self.assertIdentical(protocol.queries[0][0], servers[0])
  98. protocol.queries[0][-1].errback(DNSQueryTimeoutError(0))
  99. self.assertEqual(len(protocol.queries), 2)
  100. self.assertIdentical(protocol.queries[1][0], servers[1])
  101. protocol.queries[1][-1].errback(DNSQueryTimeoutError(1))
  102. self.assertEqual(len(protocol.queries), 3)
  103. self.assertIdentical(protocol.queries[2][0], dynServers[0])
  104. protocol.queries[2][-1].errback(DNSQueryTimeoutError(2))
  105. self.assertEqual(len(protocol.queries), 4)
  106. self.assertIdentical(protocol.queries[3][0], dynServers[1])
  107. protocol.queries[3][-1].callback(expectedResult)
  108. return queryResult
  109. def test_singleConcurrentRequest(self):
  110. """
  111. L{client.Resolver.query} only issues one request at a time per query.
  112. Subsequent requests made before responses to prior ones are received
  113. are queued and given the same response as is given to the first one.
  114. """
  115. resolver = client.Resolver(servers=[('example.com', 53)])
  116. resolver.protocol = StubDNSDatagramProtocol()
  117. queries = resolver.protocol.queries
  118. query = dns.Query('foo.example.com', dns.A, dns.IN)
  119. # The first query should be passed to the underlying protocol.
  120. firstResult = resolver.query(query)
  121. self.assertEqual(len(queries), 1)
  122. # The same query again should not be passed to the underlying protocol.
  123. secondResult = resolver.query(query)
  124. self.assertEqual(len(queries), 1)
  125. # The response to the first query should be sent in response to both
  126. # queries.
  127. answer = object()
  128. response = dns.Message()
  129. response.answers.append(answer)
  130. queries.pop()[-1].callback(response)
  131. d = defer.gatherResults([firstResult, secondResult])
  132. def cbFinished((firstResponse, secondResponse)):
  133. self.assertEqual(firstResponse, ([answer], [], []))
  134. self.assertEqual(secondResponse, ([answer], [], []))
  135. d.addCallback(cbFinished)
  136. return d
  137. def test_multipleConcurrentRequests(self):
  138. """
  139. L{client.Resolver.query} issues a request for each different concurrent
  140. query.
  141. """
  142. resolver = client.Resolver(servers=[('example.com', 53)])
  143. resolver.protocol = StubDNSDatagramProtocol()
  144. queries = resolver.protocol.queries
  145. # The first query should be passed to the underlying protocol.
  146. firstQuery = dns.Query('foo.example.com', dns.A)
  147. resolver.query(firstQuery)
  148. self.assertEqual(len(queries), 1)
  149. # A query for a different name is also passed to the underlying
  150. # protocol.
  151. secondQuery = dns.Query('bar.example.com', dns.A)
  152. resolver.query(secondQuery)
  153. self.assertEqual(len(queries), 2)
  154. # A query for a different type is also passed to the underlying
  155. # protocol.
  156. thirdQuery = dns.Query('foo.example.com', dns.A6)
  157. resolver.query(thirdQuery)
  158. self.assertEqual(len(queries), 3)
  159. def test_multipleSequentialRequests(self):
  160. """
  161. After a response is received to a query issued with
  162. L{client.Resolver.query}, another query with the same parameters
  163. results in a new network request.
  164. """
  165. resolver = client.Resolver(servers=[('example.com', 53)])
  166. resolver.protocol = StubDNSDatagramProtocol()
  167. queries = resolver.protocol.queries
  168. query = dns.Query('foo.example.com', dns.A)
  169. # The first query should be passed to the underlying protocol.
  170. resolver.query(query)
  171. self.assertEqual(len(queries), 1)
  172. # Deliver the response.
  173. queries.pop()[-1].callback(dns.Message())
  174. # Repeating the first query should touch the protocol again.
  175. resolver.query(query)
  176. self.assertEqual(len(queries), 1)
  177. def test_multipleConcurrentFailure(self):
  178. """
  179. If the result of a request is an error response, the Deferreds for all
  180. concurrently issued requests associated with that result fire with the
  181. L{Failure}.
  182. """
  183. resolver = client.Resolver(servers=[('example.com', 53)])
  184. resolver.protocol = StubDNSDatagramProtocol()
  185. queries = resolver.protocol.queries
  186. query = dns.Query('foo.example.com', dns.A)
  187. firstResult = resolver.query(query)
  188. secondResult = resolver.query(query)
  189. class ExpectedException(Exception):
  190. pass
  191. queries.pop()[-1].errback(failure.Failure(ExpectedException()))
  192. return defer.gatherResults([
  193. self.assertFailure(firstResult, ExpectedException),
  194. self.assertFailure(secondResult, ExpectedException)])
  195. def test_connectedProtocol(self):
  196. """
  197. L{client.Resolver._connectedProtocol} returns a new
  198. L{DNSDatagramProtocol} connected to a new address with a
  199. cryptographically secure random port number.
  200. """
  201. resolver = client.Resolver(servers=[('example.com', 53)])
  202. firstProto = resolver._connectedProtocol()
  203. secondProto = resolver._connectedProtocol()
  204. self.assertNotIdentical(firstProto.transport, None)
  205. self.assertNotIdentical(secondProto.transport, None)
  206. self.assertNotEqual(
  207. firstProto.transport.getHost().port,
  208. secondProto.transport.getHost().port)
  209. return defer.gatherResults([
  210. defer.maybeDeferred(firstProto.transport.stopListening),
  211. defer.maybeDeferred(secondProto.transport.stopListening)])
  212. def test_differentProtocol(self):
  213. """
  214. L{client.Resolver._connectedProtocol} is called once each time a UDP
  215. request needs to be issued and the resulting protocol instance is used
  216. for that request.
  217. """
  218. resolver = client.Resolver(servers=[('example.com', 53)])
  219. protocols = []
  220. class FakeProtocol(object):
  221. def __init__(self):
  222. self.transport = StubPort()
  223. def query(self, address, query, timeout=10, id=None):
  224. protocols.append(self)
  225. return defer.succeed(dns.Message())
  226. resolver._connectedProtocol = FakeProtocol
  227. resolver.query(dns.Query('foo.example.com'))
  228. resolver.query(dns.Query('bar.example.com'))
  229. self.assertEqual(len(set(protocols)), 2)
  230. def test_disallowedPort(self):
  231. """
  232. If a port number is initially selected which cannot be bound, the
  233. L{CannotListenError} is handled and another port number is attempted.
  234. """
  235. ports = []
  236. class FakeReactor(object):
  237. def listenUDP(self, port, *args):
  238. ports.append(port)
  239. if len(ports) == 1:
  240. raise error.CannotListenError(None, port, None)
  241. resolver = client.Resolver(servers=[('example.com', 53)])
  242. resolver._reactor = FakeReactor()
  243. proto = resolver._connectedProtocol()
  244. self.assertEqual(len(set(ports)), 2)
  245. def test_differentProtocolAfterTimeout(self):
  246. """
  247. When a query issued by L{client.Resolver.query} times out, the retry
  248. uses a new protocol instance.
  249. """
  250. resolver = client.Resolver(servers=[('example.com', 53)])
  251. protocols = []
  252. results = [defer.fail(failure.Failure(DNSQueryTimeoutError(None))),
  253. defer.succeed(dns.Message())]
  254. class FakeProtocol(object):
  255. def __init__(self):
  256. self.transport = StubPort()
  257. def query(self, address, query, timeout=10, id=None):
  258. protocols.append(self)
  259. return results.pop(0)
  260. resolver._connectedProtocol = FakeProtocol
  261. resolver.query(dns.Query('foo.example.com'))
  262. self.assertEqual(len(set(protocols)), 2)
  263. def test_protocolShutDown(self):
  264. """
  265. After the L{Deferred} returned by L{DNSDatagramProtocol.query} is
  266. called back, the L{DNSDatagramProtocol} is disconnected from its
  267. transport.
  268. """
  269. resolver = client.Resolver(servers=[('example.com', 53)])
  270. protocols = []
  271. result = defer.Deferred()
  272. class FakeProtocol(object):
  273. def __init__(self):
  274. self.transport = StubPort()
  275. def query(self, address, query, timeout=10, id=None):
  276. protocols.append(self)
  277. return result
  278. resolver._connectedProtocol = FakeProtocol
  279. resolver.query(dns.Query('foo.example.com'))
  280. self.assertFalse(protocols[0].transport.disconnected)
  281. result.callback(dns.Message())
  282. self.assertTrue(protocols[0].transport.disconnected)
  283. def test_protocolShutDownAfterTimeout(self):
  284. """
  285. The L{DNSDatagramProtocol} created when an interim timeout occurs is
  286. also disconnected from its transport after the Deferred returned by its
  287. query method completes.
  288. """
  289. resolver = client.Resolver(servers=[('example.com', 53)])
  290. protocols = []
  291. result = defer.Deferred()
  292. results = [defer.fail(failure.Failure(DNSQueryTimeoutError(None))),
  293. result]
  294. class FakeProtocol(object):
  295. def __init__(self):
  296. self.transport = StubPort()
  297. def query(self, address, query, timeout=10, id=None):
  298. protocols.append(self)
  299. return results.pop(0)
  300. resolver._connectedProtocol = FakeProtocol
  301. resolver.query(dns.Query('foo.example.com'))
  302. self.assertFalse(protocols[1].transport.disconnected)
  303. result.callback(dns.Message())
  304. self.assertTrue(protocols[1].transport.disconnected)
  305. def test_protocolShutDownAfterFailure(self):
  306. """
  307. If the L{Deferred} returned by L{DNSDatagramProtocol.query} fires with
  308. a failure, the L{DNSDatagramProtocol} is still disconnected from its
  309. transport.
  310. """
  311. class ExpectedException(Exception):
  312. pass
  313. resolver = client.Resolver(servers=[('example.com', 53)])
  314. protocols = []
  315. result = defer.Deferred()
  316. class FakeProtocol(object):
  317. def __init__(self):
  318. self.transport = StubPort()
  319. def query(self, address, query, timeout=10, id=None):
  320. protocols.append(self)
  321. return result
  322. resolver._connectedProtocol = FakeProtocol
  323. queryResult = resolver.query(dns.Query('foo.example.com'))
  324. self.assertFalse(protocols[0].transport.disconnected)
  325. result.errback(failure.Failure(ExpectedException()))
  326. self.assertTrue(protocols[0].transport.disconnected)
  327. return self.assertFailure(queryResult, ExpectedException)
  328. class ClientTestCase(unittest.TestCase):
  329. def setUp(self):
  330. """
  331. Replace the resolver with a FakeResolver
  332. """
  333. client.theResolver = FakeResolver()
  334. self.hostname = 'example.com'
  335. self.ghbntest = 'getHostByNameTest'
  336. def tearDown(self):
  337. """
  338. By setting the resolver to None, it will be recreated next time a name
  339. lookup is done.
  340. """
  341. client.theResolver = None
  342. def checkResult(self, (results, authority, additional), qtype):
  343. """
  344. Verify that the result is the same query type as what is expected.
  345. """
  346. result = results[0]
  347. self.assertEquals(str(result.name), self.hostname)
  348. self.assertEquals(result.type, qtype)
  349. def checkGetHostByName(self, result):
  350. """
  351. Test that the getHostByName query returns the 127.0.0.1 address.
  352. """
  353. self.assertEquals(result, '127.0.0.1')
  354. def test_getHostByName(self):
  355. """
  356. do a getHostByName of a value that should return 127.0.0.1.
  357. """
  358. d = client.getHostByName(self.ghbntest)
  359. d.addCallback(self.checkGetHostByName)
  360. return d
  361. def test_lookupAddress(self):
  362. """
  363. Do a lookup and test that the resolver will issue the correct type of
  364. query type. We do this by checking that FakeResolver returns a result
  365. record with the same query type as what we issued.
  366. """
  367. d = client.lookupAddress(self.hostname)
  368. d.addCallback(self.checkResult, dns.A)
  369. return d
  370. def test_lookupIPV6Address(self):
  371. """
  372. See L{test_lookupAddress}
  373. """
  374. d = client.lookupIPV6Address(self.hostname)
  375. d.addCallback(self.checkResult, dns.AAAA)
  376. return d
  377. def test_lookupAddress6(self):
  378. """
  379. See L{test_lookupAddress}
  380. """
  381. d = client.lookupAddress6(self.hostname)
  382. d.addCallback(self.checkResult, dns.A6)
  383. return d
  384. def test_lookupNameservers(self):
  385. """
  386. See L{test_lookupAddress}
  387. """
  388. d = client.lookupNameservers(self.hostname)
  389. d.addCallback(self.checkResult, dns.NS)
  390. return d
  391. def test_lookupCanonicalName(self):
  392. """
  393. See L{test_lookupAddress}
  394. """
  395. d = client.lookupCanonicalName(self.hostname)
  396. d.addCallback(self.checkResult, dns.CNAME)
  397. return d
  398. def test_lookupAuthority(self):
  399. """
  400. See L{test_lookupAddress}
  401. """
  402. d = client.lookupAuthority(self.hostname)
  403. d.addCallback(self.checkResult, dns.SOA)
  404. return d
  405. def test_lookupMailBox(self):
  406. """
  407. See L{test_lookupAddress}
  408. """
  409. d = client.lookupMailBox(self.hostname)
  410. d.addCallback(self.checkResult, dns.MB)
  411. return d
  412. def test_lookupMailGroup(self):
  413. """
  414. See L{test_lookupAddress}
  415. """
  416. d = client.lookupMailGroup(self.hostname)
  417. d.addCallback(self.checkResult, dns.MG)
  418. return d
  419. def test_lookupMailRename(self):
  420. """
  421. See L{test_lookupAddress}
  422. """
  423. d = client.lookupMailRename(self.hostname)
  424. d.addCallback(self.checkResult, dns.MR)
  425. return d
  426. def test_lookupNull(self):
  427. """
  428. See L{test_lookupAddress}
  429. """
  430. d = client.lookupNull(self.hostname)
  431. d.addCallback(self.checkResult, dns.NULL)
  432. return d
  433. def test_lookupWellKnownServices(self):
  434. """
  435. See L{test_lookupAddress}
  436. """
  437. d = client.lookupWellKnownServices(self.hostname)
  438. d.addCallback(self.checkResult, dns.WKS)
  439. return d
  440. def test_lookupPointer(self):
  441. """
  442. See L{test_lookupAddress}
  443. """
  444. d = client.lookupPointer(self.hostname)
  445. d.addCallback(self.checkResult, dns.PTR)
  446. return d
  447. def test_lookupHostInfo(self):
  448. """
  449. See L{test_lookupAddress}
  450. """
  451. d = client.lookupHostInfo(self.hostname)
  452. d.addCallback(self.checkResult, dns.HINFO)
  453. return d
  454. def test_lookupMailboxInfo(self):
  455. """
  456. See L{test_lookupAddress}
  457. """
  458. d = client.lookupMailboxInfo(self.hostname)
  459. d.addCallback(self.checkResult, dns.MINFO)
  460. return d
  461. def test_lookupMailExchange(self):
  462. """
  463. See L{test_lookupAddress}
  464. """
  465. d = client.lookupMailExchange(self.hostname)
  466. d.addCallback(self.checkResult, dns.MX)
  467. return d
  468. def test_lookupText(self):
  469. """
  470. See L{test_lookupAddress}
  471. """
  472. d = client.lookupText(self.hostname)
  473. d.addCallback(self.checkResult, dns.TXT)
  474. return d
  475. def test_lookupResponsibility(self):
  476. """
  477. See L{test_lookupAddress}
  478. """
  479. d = client.lookupResponsibility(self.hostname)
  480. d.addCallback(self.checkResult, dns.RP)
  481. return d
  482. def test_lookupAFSDatabase(self):
  483. """
  484. See L{test_lookupAddress}
  485. """
  486. d = client.lookupAFSDatabase(self.hostname)
  487. d.addCallback(self.checkResult, dns.AFSDB)
  488. return d
  489. def test_lookupService(self):
  490. """
  491. See L{test_lookupAddress}
  492. """
  493. d = client.lookupService(self.hostname)
  494. d.addCallback(self.checkResult, dns.SRV)
  495. return d
  496. def test_lookupZone(self):
  497. """
  498. See L{test_lookupAddress}
  499. """
  500. d = client.lookupZone(self.hostname)
  501. d.addCallback(self.checkResult, dns.AXFR)
  502. return d
  503. def test_lookupAllRecords(self):
  504. """
  505. See L{test_lookupAddress}
  506. """
  507. d = client.lookupAllRecords(self.hostname)
  508. d.addCallback(self.checkResult, dns.ALL_RECORDS)
  509. return d
  510. def test_lookupNamingAuthorityPointer(self):
  511. """
  512. See L{test_lookupAddress}
  513. """
  514. d = client.lookupNamingAuthorityPointer(self.hostname)
  515. d.addCallback(self.checkResult, dns.NAPTR)
  516. return d