| OLD | NEW |
| (Empty) |
| 1 # -*- test-case-name: twisted.names.test.test_names -*- | |
| 2 # Copyright (c) 2001-2004 Twisted Matrix Laboratories. | |
| 3 # See LICENSE for details. | |
| 4 | |
| 5 """ | |
| 6 Test cases for twisted.names. | |
| 7 """ | |
| 8 from __future__ import nested_scopes | |
| 9 | |
| 10 import socket, operator, copy | |
| 11 | |
| 12 from twisted.trial import unittest | |
| 13 | |
| 14 from twisted.internet import reactor, defer, error | |
| 15 from twisted.internet.defer import succeed | |
| 16 from twisted.names import client, server, common, authority, hosts, dns | |
| 17 from twisted.python import failure | |
| 18 from twisted.names.error import DNSFormatError, DNSServerError, DNSNameError | |
| 19 from twisted.names.error import DNSNotImplementedError, DNSQueryRefusedError | |
| 20 from twisted.names.error import DNSUnknownError | |
| 21 from twisted.names.dns import EFORMAT, ESERVER, ENAME, ENOTIMP, EREFUSED | |
| 22 from twisted.names.dns import Message | |
| 23 from twisted.names.client import Resolver | |
| 24 | |
| 25 | |
| 26 def justPayload(results): | |
| 27 return [r.payload for r in results[0]] | |
| 28 | |
| 29 class NoFileAuthority(authority.FileAuthority): | |
| 30 def __init__(self, soa, records): | |
| 31 # Yes, skip FileAuthority | |
| 32 common.ResolverBase.__init__(self) | |
| 33 self.soa, self.records = soa, records | |
| 34 | |
| 35 | |
| 36 soa_record = dns.Record_SOA( | |
| 37 mname = 'test-domain.com', | |
| 38 rname = 'root.test-domain.com', | |
| 39 serial = 100, | |
| 40 refresh = 1234, | |
| 41 minimum = 7654, | |
| 42 expire = 19283784, | |
| 43 retry = 15, | |
| 44 ttl=1 | |
| 45 ) | |
| 46 | |
| 47 reverse_soa = dns.Record_SOA( | |
| 48 mname = '93.84.28.in-addr.arpa', | |
| 49 rname = '93.84.28.in-addr.arpa', | |
| 50 serial = 120, | |
| 51 refresh = 54321, | |
| 52 minimum = 382, | |
| 53 expire = 11193983, | |
| 54 retry = 30, | |
| 55 ttl=3 | |
| 56 ) | |
| 57 | |
| 58 my_soa = dns.Record_SOA( | |
| 59 mname = 'my-domain.com', | |
| 60 rname = 'postmaster.test-domain.com', | |
| 61 serial = 130, | |
| 62 refresh = 12345, | |
| 63 minimum = 1, | |
| 64 expire = 999999, | |
| 65 retry = 100, | |
| 66 ) | |
| 67 | |
| 68 test_domain_com = NoFileAuthority( | |
| 69 soa = ('test-domain.com', soa_record), | |
| 70 records = { | |
| 71 'test-domain.com': [ | |
| 72 soa_record, | |
| 73 dns.Record_A('127.0.0.1'), | |
| 74 dns.Record_NS('39.28.189.39'), | |
| 75 dns.Record_MX(10, 'host.test-domain.com'), | |
| 76 dns.Record_HINFO(os='Linux', cpu='A Fast One, Dontcha know'), | |
| 77 dns.Record_CNAME('canonical.name.com'), | |
| 78 dns.Record_MB('mailbox.test-domain.com'), | |
| 79 dns.Record_MG('mail.group.someplace'), | |
| 80 dns.Record_TXT('A First piece of Text', 'a SecoNd piece'), | |
| 81 dns.Record_A6(0, 'ABCD::4321', ''), | |
| 82 dns.Record_A6(12, '0:0069::0', 'some.network.tld'), | |
| 83 dns.Record_A6(8, '0:5634:1294:AFCB:56AC:48EF:34C3:01FF', 'tra.la.la.
net'), | |
| 84 dns.Record_TXT('Some more text, haha! Yes. \0 Still here?'), | |
| 85 dns.Record_MR('mail.redirect.or.whatever'), | |
| 86 dns.Record_MINFO(rmailbx='r mail box', emailbx='e mail box'), | |
| 87 dns.Record_AFSDB(subtype=1, hostname='afsdb.test-domain.com'), | |
| 88 dns.Record_RP(mbox='whatever.i.dunno', txt='some.more.text'), | |
| 89 dns.Record_WKS('12.54.78.12', socket.IPPROTO_TCP, '\x12\x01\x16\xfe\
xc1\x00\x01'), | |
| 90 dns.Record_AAAA('AF43:5634:1294:AFCB:56AC:48EF:34C3:01FF')], | |
| 91 'http.tcp.test-domain.com': [ | |
| 92 dns.Record_SRV(257, 16383, 43690, 'some.other.place.fool') | |
| 93 ], | |
| 94 'host.test-domain.com': [ | |
| 95 dns.Record_A('123.242.1.5'), | |
| 96 dns.Record_A('0.255.0.255'), | |
| 97 ], | |
| 98 'host-two.test-domain.com': [ | |
| 99 # | |
| 100 # Python bug | |
| 101 # dns.Record_A('255.255.255.255'), | |
| 102 # | |
| 103 dns.Record_A('255.255.255.254'), | |
| 104 dns.Record_A('0.0.0.0') | |
| 105 ], | |
| 106 'cname.test-domain.com': [ | |
| 107 dns.Record_CNAME('test-domain.com') | |
| 108 ], | |
| 109 'anothertest-domain.com': [ | |
| 110 dns.Record_A('1.2.3.4')], | |
| 111 } | |
| 112 ) | |
| 113 | |
| 114 reverse_domain = NoFileAuthority( | |
| 115 soa = ('93.84.28.in-addr.arpa', reverse_soa), | |
| 116 records = { | |
| 117 '123.93.84.28.in-addr.arpa': [ | |
| 118 dns.Record_PTR('test.host-reverse.lookup.com'), | |
| 119 reverse_soa | |
| 120 ] | |
| 121 } | |
| 122 ) | |
| 123 | |
| 124 | |
| 125 my_domain_com = NoFileAuthority( | |
| 126 soa = ('my-domain.com', my_soa), | |
| 127 records = { | |
| 128 'my-domain.com': [ | |
| 129 my_soa, | |
| 130 dns.Record_A('1.2.3.4', ttl='1S'), | |
| 131 dns.Record_NS('ns1.domain', ttl='2M'), | |
| 132 dns.Record_NS('ns2.domain', ttl='3H'), | |
| 133 dns.Record_SRV(257, 16383, 43690, 'some.other.place.fool', ttl='4D') | |
| 134 ] | |
| 135 } | |
| 136 ) | |
| 137 | |
| 138 | |
| 139 class ServerDNSTestCase(unittest.TestCase): | |
| 140 """ | |
| 141 Test cases for DNS server and client. | |
| 142 """ | |
| 143 | |
| 144 def setUp(self): | |
| 145 self.factory = server.DNSServerFactory([ | |
| 146 test_domain_com, reverse_domain, my_domain_com | |
| 147 ], verbose=2) | |
| 148 | |
| 149 p = dns.DNSDatagramProtocol(self.factory) | |
| 150 | |
| 151 while 1: | |
| 152 self.listenerTCP = reactor.listenTCP(0, self.factory, interface="127
.0.0.1") | |
| 153 port = self.listenerTCP.getHost().port | |
| 154 | |
| 155 try: | |
| 156 self.listenerUDP = reactor.listenUDP(port, p, interface="127.0.0
.1") | |
| 157 except error.CannotListenError: | |
| 158 self.listenerTCP.stopListening() | |
| 159 else: | |
| 160 break | |
| 161 | |
| 162 self.resolver = client.Resolver(servers=[('127.0.0.1', port)]) | |
| 163 | |
| 164 | |
| 165 def tearDown(self): | |
| 166 """ | |
| 167 Asynchronously disconnect listenerTCP, listenerUDP and resolver. | |
| 168 """ | |
| 169 d1 = self.listenerTCP.loseConnection() | |
| 170 d2 = defer.maybeDeferred(self.listenerUDP.stopListening) | |
| 171 d = defer.gatherResults([d1, d2]) | |
| 172 def disconnectTransport(ignored): | |
| 173 if getattr(self.resolver.protocol, 'transport', None) is not None: | |
| 174 return self.resolver.protocol.transport.stopListening() | |
| 175 d.addCallback(disconnectTransport) | |
| 176 d.addCallback(lambda x : self.failUnless( | |
| 177 self.listenerUDP.disconnected | |
| 178 and self.listenerTCP.disconnected)) | |
| 179 return d | |
| 180 | |
| 181 | |
| 182 def namesTest(self, d, r): | |
| 183 self.response = None | |
| 184 def setDone(response): | |
| 185 self.response = response | |
| 186 | |
| 187 def checkResults(ignored): | |
| 188 if isinstance(self.response, failure.Failure): | |
| 189 raise self.response | |
| 190 results = justPayload(self.response) | |
| 191 assert len(results) == len(r), "%s != %s" % (map(str, results), map(
str, r)) | |
| 192 for rec in results: | |
| 193 assert rec in r, "%s not in %s" % (rec, map(str, r)) | |
| 194 | |
| 195 d.addBoth(setDone) | |
| 196 d.addCallback(checkResults) | |
| 197 return d | |
| 198 | |
| 199 def testAddressRecord1(self): | |
| 200 """Test simple DNS 'A' record queries""" | |
| 201 return self.namesTest( | |
| 202 self.resolver.lookupAddress('test-domain.com'), | |
| 203 [dns.Record_A('127.0.0.1', ttl=19283784)] | |
| 204 ) | |
| 205 | |
| 206 | |
| 207 def testAddressRecord2(self): | |
| 208 """Test DNS 'A' record queries with multiple answers""" | |
| 209 return self.namesTest( | |
| 210 self.resolver.lookupAddress('host.test-domain.com'), | |
| 211 [dns.Record_A('123.242.1.5', ttl=19283784), dns.Record_A('0.255.0.25
5', ttl=19283784)] | |
| 212 ) | |
| 213 | |
| 214 | |
| 215 def testAdressRecord3(self): | |
| 216 """Test DNS 'A' record queries with edge cases""" | |
| 217 return self.namesTest( | |
| 218 self.resolver.lookupAddress('host-two.test-domain.com'), | |
| 219 [dns.Record_A('255.255.255.254', ttl=19283784), dns.Record_A('0.0.0.
0', ttl=19283784)] | |
| 220 ) | |
| 221 | |
| 222 def testAuthority(self): | |
| 223 """Test DNS 'SOA' record queries""" | |
| 224 return self.namesTest( | |
| 225 self.resolver.lookupAuthority('test-domain.com'), | |
| 226 [soa_record] | |
| 227 ) | |
| 228 | |
| 229 | |
| 230 def testMailExchangeRecord(self): | |
| 231 """Test DNS 'MX' record queries""" | |
| 232 return self.namesTest( | |
| 233 self.resolver.lookupMailExchange('test-domain.com'), | |
| 234 [dns.Record_MX(10, 'host.test-domain.com', ttl=19283784)] | |
| 235 ) | |
| 236 | |
| 237 | |
| 238 def testNameserver(self): | |
| 239 """Test DNS 'NS' record queries""" | |
| 240 return self.namesTest( | |
| 241 self.resolver.lookupNameservers('test-domain.com'), | |
| 242 [dns.Record_NS('39.28.189.39', ttl=19283784)] | |
| 243 ) | |
| 244 | |
| 245 | |
| 246 def testHINFO(self): | |
| 247 """Test DNS 'HINFO' record queries""" | |
| 248 return self.namesTest( | |
| 249 self.resolver.lookupHostInfo('test-domain.com'), | |
| 250 [dns.Record_HINFO(os='Linux', cpu='A Fast One, Dontcha know', ttl=19
283784)] | |
| 251 ) | |
| 252 | |
| 253 def testPTR(self): | |
| 254 """Test DNS 'PTR' record queries""" | |
| 255 return self.namesTest( | |
| 256 self.resolver.lookupPointer('123.93.84.28.in-addr.arpa'), | |
| 257 [dns.Record_PTR('test.host-reverse.lookup.com', ttl=11193983)] | |
| 258 ) | |
| 259 | |
| 260 | |
| 261 def testCNAME(self): | |
| 262 """Test DNS 'CNAME' record queries""" | |
| 263 return self.namesTest( | |
| 264 self.resolver.lookupCanonicalName('test-domain.com'), | |
| 265 [dns.Record_CNAME('canonical.name.com', ttl=19283784)] | |
| 266 ) | |
| 267 | |
| 268 def testCNAMEAdditional(self): | |
| 269 """Test additional processing for CNAME records""" | |
| 270 return self.namesTest( | |
| 271 self.resolver.lookupAddress('cname.test-domain.com'), | |
| 272 [dns.Record_CNAME('test-domain.com', ttl=19283784), dns.Record_A('127.0.
0.1', ttl=19283784)] | |
| 273 ) | |
| 274 | |
| 275 def testMB(self): | |
| 276 """Test DNS 'MB' record queries""" | |
| 277 return self.namesTest( | |
| 278 self.resolver.lookupMailBox('test-domain.com'), | |
| 279 [dns.Record_MB('mailbox.test-domain.com', ttl=19283784)] | |
| 280 ) | |
| 281 | |
| 282 | |
| 283 def testMG(self): | |
| 284 """Test DNS 'MG' record queries""" | |
| 285 return self.namesTest( | |
| 286 self.resolver.lookupMailGroup('test-domain.com'), | |
| 287 [dns.Record_MG('mail.group.someplace', ttl=19283784)] | |
| 288 ) | |
| 289 | |
| 290 | |
| 291 def testMR(self): | |
| 292 """Test DNS 'MR' record queries""" | |
| 293 return self.namesTest( | |
| 294 self.resolver.lookupMailRename('test-domain.com'), | |
| 295 [dns.Record_MR('mail.redirect.or.whatever', ttl=19283784)] | |
| 296 ) | |
| 297 | |
| 298 | |
| 299 def testMINFO(self): | |
| 300 """Test DNS 'MINFO' record queries""" | |
| 301 return self.namesTest( | |
| 302 self.resolver.lookupMailboxInfo('test-domain.com'), | |
| 303 [dns.Record_MINFO(rmailbx='r mail box', emailbx='e mail box', ttl=19
283784)] | |
| 304 ) | |
| 305 | |
| 306 | |
| 307 def testSRV(self): | |
| 308 """Test DNS 'SRV' record queries""" | |
| 309 return self.namesTest( | |
| 310 self.resolver.lookupService('http.tcp.test-domain.com'), | |
| 311 [dns.Record_SRV(257, 16383, 43690, 'some.other.place.fool', ttl=1928
3784)] | |
| 312 ) | |
| 313 | |
| 314 def testAFSDB(self): | |
| 315 """Test DNS 'AFSDB' record queries""" | |
| 316 return self.namesTest( | |
| 317 self.resolver.lookupAFSDatabase('test-domain.com'), | |
| 318 [dns.Record_AFSDB(subtype=1, hostname='afsdb.test-domain.com', ttl=1
9283784)] | |
| 319 ) | |
| 320 | |
| 321 | |
| 322 def testRP(self): | |
| 323 """Test DNS 'RP' record queries""" | |
| 324 return self.namesTest( | |
| 325 self.resolver.lookupResponsibility('test-domain.com'), | |
| 326 [dns.Record_RP(mbox='whatever.i.dunno', txt='some.more.text', ttl=19
283784)] | |
| 327 ) | |
| 328 | |
| 329 | |
| 330 def testTXT(self): | |
| 331 """Test DNS 'TXT' record queries""" | |
| 332 return self.namesTest( | |
| 333 self.resolver.lookupText('test-domain.com'), | |
| 334 [dns.Record_TXT('A First piece of Text', 'a SecoNd piece', ttl=19283
784), | |
| 335 dns.Record_TXT('Some more text, haha! Yes. \0 Still here?', ttl=
19283784)] | |
| 336 ) | |
| 337 | |
| 338 | |
| 339 def testWKS(self): | |
| 340 """Test DNS 'WKS' record queries""" | |
| 341 return self.namesTest( | |
| 342 self.resolver.lookupWellKnownServices('test-domain.com'), | |
| 343 [dns.Record_WKS('12.54.78.12', socket.IPPROTO_TCP, '\x12\x01\x16\xfe
\xc1\x00\x01', ttl=19283784)] | |
| 344 ) | |
| 345 | |
| 346 | |
| 347 def testSomeRecordsWithTTLs(self): | |
| 348 result_soa = copy.copy(my_soa) | |
| 349 result_soa.ttl = my_soa.expire | |
| 350 return self.namesTest( | |
| 351 self.resolver.lookupAllRecords('my-domain.com'), | |
| 352 [result_soa, | |
| 353 dns.Record_A('1.2.3.4', ttl='1S'), | |
| 354 dns.Record_NS('ns1.domain', ttl='2M'), | |
| 355 dns.Record_NS('ns2.domain', ttl='3H'), | |
| 356 dns.Record_SRV(257, 16383, 43690, 'some.other.place.fool', ttl='4D'
)] | |
| 357 ) | |
| 358 | |
| 359 | |
| 360 def testAAAA(self): | |
| 361 """Test DNS 'AAAA' record queries (IPv6)""" | |
| 362 return self.namesTest( | |
| 363 self.resolver.lookupIPV6Address('test-domain.com'), | |
| 364 [dns.Record_AAAA('AF43:5634:1294:AFCB:56AC:48EF:34C3:01FF', ttl=1928
3784)] | |
| 365 ) | |
| 366 | |
| 367 def testA6(self): | |
| 368 """Test DNS 'A6' record queries (IPv6)""" | |
| 369 return self.namesTest( | |
| 370 self.resolver.lookupAddress6('test-domain.com'), | |
| 371 [dns.Record_A6(0, 'ABCD::4321', '', ttl=19283784), | |
| 372 dns.Record_A6(12, '0:0069::0', 'some.network.tld', ttl=19283784), | |
| 373 dns.Record_A6(8, '0:5634:1294:AFCB:56AC:48EF:34C3:01FF', 'tra.la.la
.net', ttl=19283784)] | |
| 374 ) | |
| 375 | |
| 376 | |
| 377 def testZoneTransfer(self): | |
| 378 """Test DNS 'AXFR' queries (Zone transfer)""" | |
| 379 default_ttl = soa_record.expire | |
| 380 results = [copy.copy(r) for r in reduce(operator.add, test_domain_com.re
cords.values())] | |
| 381 for r in results: | |
| 382 if r.ttl is None: | |
| 383 r.ttl = default_ttl | |
| 384 return self.namesTest( | |
| 385 self.resolver.lookupZone('test-domain.com').addCallback(lambda r: (r
[0][:-1],)), | |
| 386 results | |
| 387 ) | |
| 388 | |
| 389 def testSimilarZonesDontInterfere(self): | |
| 390 """Tests that unrelated zones don't mess with each other.""" | |
| 391 return self.namesTest( | |
| 392 self.resolver.lookupAddress("anothertest-domain.com"), | |
| 393 [dns.Record_A('1.2.3.4', ttl=19283784)] | |
| 394 ) | |
| 395 | |
| 396 | |
| 397 | |
| 398 class DNSServerFactoryTests(unittest.TestCase): | |
| 399 """ | |
| 400 Tests for L{server.DNSServerFactory}. | |
| 401 """ | |
| 402 def _messageReceivedTest(self, methodName, message): | |
| 403 """ | |
| 404 Assert that the named method is called with the given message when | |
| 405 it is passed to L{DNSServerFactory.messageReceived}. | |
| 406 """ | |
| 407 # Make it appear to have some queries so that | |
| 408 # DNSServerFactory.allowQuery allows it. | |
| 409 message.queries = [None] | |
| 410 | |
| 411 receivedMessages = [] | |
| 412 def fakeHandler(message, protocol, address): | |
| 413 receivedMessages.append((message, protocol, address)) | |
| 414 | |
| 415 class FakeProtocol(object): | |
| 416 def writeMessage(self, message): | |
| 417 pass | |
| 418 | |
| 419 protocol = FakeProtocol() | |
| 420 factory = server.DNSServerFactory(None) | |
| 421 setattr(factory, methodName, fakeHandler) | |
| 422 factory.messageReceived(message, protocol) | |
| 423 self.assertEqual(receivedMessages, [(message, protocol, None)]) | |
| 424 | |
| 425 | |
| 426 def test_notifyMessageReceived(self): | |
| 427 """ | |
| 428 L{DNSServerFactory.messageReceived} passes messages with an opcode | |
| 429 of C{OP_NOTIFY} on to L{DNSServerFactory.handleNotify}. | |
| 430 """ | |
| 431 # RFC 1996, section 4.5 | |
| 432 opCode = 4 | |
| 433 self._messageReceivedTest('handleNotify', Message(opCode=opCode)) | |
| 434 | |
| 435 | |
| 436 def test_updateMessageReceived(self): | |
| 437 """ | |
| 438 L{DNSServerFactory.messageReceived} passes messages with an opcode | |
| 439 of C{OP_UPDATE} on to L{DNSServerFactory.handleOther}. | |
| 440 | |
| 441 This may change if the implementation ever covers update messages. | |
| 442 """ | |
| 443 # RFC 2136, section 1.3 | |
| 444 opCode = 5 | |
| 445 self._messageReceivedTest('handleOther', Message(opCode=opCode)) | |
| 446 | |
| 447 | |
| 448 | |
| 449 class HelperTestCase(unittest.TestCase): | |
| 450 def testSerialGenerator(self): | |
| 451 f = self.mktemp() | |
| 452 a = authority.getSerial(f) | |
| 453 for i in range(20): | |
| 454 b = authority.getSerial(f) | |
| 455 self.failUnless(a < b) | |
| 456 a = b | |
| 457 | |
| 458 | |
| 459 class AXFRTest(unittest.TestCase): | |
| 460 def setUp(self): | |
| 461 self.results = None | |
| 462 self.d = defer.Deferred() | |
| 463 self.d.addCallback(self._gotResults) | |
| 464 self.controller = client.AXFRController('fooby.com', self.d) | |
| 465 | |
| 466 self.soa = dns.RRHeader(name='fooby.com', type=dns.SOA, cls=dns.IN, ttl=
86400, auth=False, | |
| 467 payload=dns.Record_SOA(mname='fooby.com', | |
| 468 rname='hooj.fooby.com', | |
| 469 serial=100, | |
| 470 refresh=200, | |
| 471 retry=300, | |
| 472 expire=400, | |
| 473 minimum=500, | |
| 474 ttl=600)) | |
| 475 | |
| 476 self.records = [ | |
| 477 self.soa, | |
| 478 dns.RRHeader(name='fooby.com', type=dns.NS, cls=dns.IN, ttl=700, aut
h=False, | |
| 479 payload=dns.Record_NS(name='ns.twistedmatrix.com', ttl=
700)), | |
| 480 | |
| 481 dns.RRHeader(name='fooby.com', type=dns.MX, cls=dns.IN, ttl=700, aut
h=False, | |
| 482 payload=dns.Record_MX(preference=10, exchange='mail.mv3
d.com', ttl=700)), | |
| 483 | |
| 484 dns.RRHeader(name='fooby.com', type=dns.A, cls=dns.IN, ttl=700, auth
=False, | |
| 485 payload=dns.Record_A(address='64.123.27.105', ttl=700))
, | |
| 486 self.soa | |
| 487 ] | |
| 488 | |
| 489 def _makeMessage(self): | |
| 490 # hooray they all have the same message format | |
| 491 return dns.Message(id=999, answer=1, opCode=0, recDes=0, recAv=1, auth=1
, rCode=0, trunc=0, maxSize=0) | |
| 492 | |
| 493 def testBindAndTNamesStyle(self): | |
| 494 # Bind style = One big single message | |
| 495 m = self._makeMessage() | |
| 496 m.queries = [dns.Query('fooby.com', dns.AXFR, dns.IN)] | |
| 497 m.answers = self.records | |
| 498 self.controller.messageReceived(m, None) | |
| 499 self.assertEquals(self.results, self.records) | |
| 500 | |
| 501 def _gotResults(self, result): | |
| 502 self.results = result | |
| 503 | |
| 504 def testDJBStyle(self): | |
| 505 # DJB style = message per record | |
| 506 records = self.records[:] | |
| 507 while records: | |
| 508 m = self._makeMessage() | |
| 509 m.queries = [] # DJB *doesn't* specify any queries.. hmm.. | |
| 510 m.answers = [records.pop(0)] | |
| 511 self.controller.messageReceived(m, None) | |
| 512 self.assertEquals(self.results, self.records) | |
| 513 | |
| 514 class HostsTestCase(unittest.TestCase): | |
| 515 def setUp(self): | |
| 516 f = open('EtcHosts', 'w') | |
| 517 f.write(''' | |
| 518 1.1.1.1 EXAMPLE EXAMPLE.EXAMPLETHING | |
| 519 1.1.1.2 HOOJY | |
| 520 ::1 ip6thingy | |
| 521 ''') | |
| 522 f.close() | |
| 523 self.resolver = hosts.Resolver('EtcHosts') | |
| 524 | |
| 525 def testGetHostByName(self): | |
| 526 data = [('EXAMPLE', '1.1.1.1'), | |
| 527 ('EXAMPLE.EXAMPLETHING', '1.1.1.1'), | |
| 528 ('HOOJY', '1.1.1.2'), | |
| 529 ] | |
| 530 ds = [self.resolver.getHostByName(n).addCallback(self.assertEqual, ip) | |
| 531 for n, ip in data] | |
| 532 return defer.gatherResults(ds) | |
| 533 | |
| 534 def testLookupAddress(self): | |
| 535 d = self.resolver.lookupAddress('HOOJY') | |
| 536 d.addCallback(lambda x: self.assertEqual(x[0][0].payload.dottedQuad(), | |
| 537 '1.1.1.2')) | |
| 538 return d | |
| 539 | |
| 540 def testIPv6(self): | |
| 541 d = self.resolver.lookupIPV6Address('ip6thingy') | |
| 542 d.addCallback(self.assertEqual, '::1') | |
| 543 return d | |
| 544 | |
| 545 testIPv6.skip = 'IPv6 support is not in our hosts resolver yet' | |
| 546 | |
| 547 def testNotImplemented(self): | |
| 548 return self.assertFailure(self.resolver.lookupMailExchange('EXAMPLE'), | |
| 549 NotImplementedError) | |
| 550 | |
| 551 def testQuery(self): | |
| 552 d = self.resolver.query(dns.Query('EXAMPLE')) | |
| 553 d.addCallback(lambda x: self.assertEqual(x[0][0].payload.dottedQuad(), | |
| 554 '1.1.1.1')) | |
| 555 return d | |
| 556 | |
| 557 def testNotFound(self): | |
| 558 return self.assertFailure(self.resolver.lookupAddress('foueoa'), | |
| 559 dns.DomainError) | |
| 560 | |
| 561 | |
| 562 class FakeDNSDatagramProtocol(object): | |
| 563 transport = object() | |
| 564 | |
| 565 def __init__(self): | |
| 566 self.queries = [] | |
| 567 | |
| 568 def query(self, address, queries, timeout=10, id=None): | |
| 569 self.queries.append((address, queries, timeout, id)) | |
| 570 return defer.fail(dns.DNSQueryTimeoutError(queries)) | |
| 571 | |
| 572 def removeResend(self, id): | |
| 573 # Ignore this for the time being. | |
| 574 pass | |
| 575 | |
| 576 class RetryLogic(unittest.TestCase): | |
| 577 testServers = [ | |
| 578 '1.2.3.4', | |
| 579 '4.3.2.1', | |
| 580 'a.b.c.d', | |
| 581 'z.y.x.w'] | |
| 582 | |
| 583 def testRoundRobinBackoff(self): | |
| 584 addrs = [(x, 53) for x in self.testServers] | |
| 585 r = client.Resolver(resolv=None, servers=addrs) | |
| 586 r.protocol = proto = FakeDNSDatagramProtocol() | |
| 587 return r.lookupAddress("foo.example.com" | |
| 588 ).addCallback(self._cbRoundRobinBackoff | |
| 589 ).addErrback(self._ebRoundRobinBackoff, proto | |
| 590 ) | |
| 591 | |
| 592 def _cbRoundRobinBackoff(self, result): | |
| 593 raise unittest.FailTest("Lookup address succeeded, should have timed out
") | |
| 594 | |
| 595 def _ebRoundRobinBackoff(self, failure, fakeProto): | |
| 596 failure.trap(defer.TimeoutError) | |
| 597 | |
| 598 # Assert that each server is tried with a particular timeout | |
| 599 # before the timeout is increased and the attempts are repeated. | |
| 600 | |
| 601 for t in (1, 3, 11, 45): | |
| 602 tries = fakeProto.queries[:len(self.testServers)] | |
| 603 del fakeProto.queries[:len(self.testServers)] | |
| 604 | |
| 605 tries.sort() | |
| 606 expected = list(self.testServers) | |
| 607 expected.sort() | |
| 608 | |
| 609 for ((addr, query, timeout, id), expectedAddr) in zip(tries, expecte
d): | |
| 610 self.assertEquals(addr, (expectedAddr, 53)) | |
| 611 self.assertEquals(timeout, t) | |
| 612 | |
| 613 self.failIf(fakeProto.queries) | |
| 614 | |
| 615 class ResolvConfHandling(unittest.TestCase): | |
| 616 def testMissing(self): | |
| 617 resolvConf = self.mktemp() | |
| 618 r = client.Resolver(resolv=resolvConf) | |
| 619 self.assertEquals(r.dynServers, [('127.0.0.1', 53)]) | |
| 620 r._parseCall.cancel() | |
| 621 | |
| 622 def testEmpty(self): | |
| 623 resolvConf = self.mktemp() | |
| 624 fObj = file(resolvConf, 'w') | |
| 625 fObj.close() | |
| 626 r = client.Resolver(resolv=resolvConf) | |
| 627 self.assertEquals(r.dynServers, [('127.0.0.1', 53)]) | |
| 628 r._parseCall.cancel() | |
| 629 | |
| 630 | |
| 631 | |
| 632 class FilterAnswersTests(unittest.TestCase): | |
| 633 """ | |
| 634 Test L{twisted.names.client.Resolver.filterAnswers}'s handling of various | |
| 635 error conditions it might encounter. | |
| 636 """ | |
| 637 def setUp(self): | |
| 638 # Create a resolver pointed at an invalid server - we won't be hitting | |
| 639 # the network in any of these tests. | |
| 640 self.resolver = Resolver(servers=[('0.0.0.0', 0)]) | |
| 641 | |
| 642 | |
| 643 def test_truncatedMessage(self): | |
| 644 """ | |
| 645 Test that a truncated message results in an equivalent request made via | |
| 646 TCP. | |
| 647 """ | |
| 648 m = Message(trunc=True) | |
| 649 m.addQuery('example.com') | |
| 650 | |
| 651 def queryTCP(queries): | |
| 652 self.assertEqual(queries, m.queries) | |
| 653 response = Message() | |
| 654 response.answers = ['answer'] | |
| 655 response.authority = ['authority'] | |
| 656 response.additional = ['additional'] | |
| 657 return succeed(response) | |
| 658 self.resolver.queryTCP = queryTCP | |
| 659 d = self.resolver.filterAnswers(m) | |
| 660 d.addCallback( | |
| 661 self.assertEqual, (['answer'], ['authority'], ['additional'])) | |
| 662 return d | |
| 663 | |
| 664 | |
| 665 def _rcodeTest(self, rcode, exc): | |
| 666 m = Message(rCode=rcode) | |
| 667 err = self.resolver.filterAnswers(m) | |
| 668 err.trap(exc) | |
| 669 | |
| 670 | |
| 671 def test_formatError(self): | |
| 672 """ | |
| 673 Test that a message with a result code of C{EFORMAT} results in a | |
| 674 failure wrapped around L{DNSFormatError}. | |
| 675 """ | |
| 676 return self._rcodeTest(EFORMAT, DNSFormatError) | |
| 677 | |
| 678 | |
| 679 def test_serverError(self): | |
| 680 """ | |
| 681 Like L{test_formatError} but for C{ESERVER}/L{DNSServerError}. | |
| 682 """ | |
| 683 return self._rcodeTest(ESERVER, DNSServerError) | |
| 684 | |
| 685 | |
| 686 def test_nameError(self): | |
| 687 """ | |
| 688 Like L{test_formatError} but for C{ENAME}/L{DNSNameError}. | |
| 689 """ | |
| 690 return self._rcodeTest(ENAME, DNSNameError) | |
| 691 | |
| 692 | |
| 693 def test_notImplementedError(self): | |
| 694 """ | |
| 695 Like L{test_formatError} but for C{ENOTIMP}/L{DNSNotImplementedError}. | |
| 696 """ | |
| 697 return self._rcodeTest(ENOTIMP, DNSNotImplementedError) | |
| 698 | |
| 699 | |
| 700 def test_refusedError(self): | |
| 701 """ | |
| 702 Like L{test_formatError} but for C{EREFUSED}/L{DNSQueryRefusedError}. | |
| 703 """ | |
| 704 return self._rcodeTest(EREFUSED, DNSQueryRefusedError) | |
| 705 | |
| 706 | |
| 707 def test_refusedErrorUnknown(self): | |
| 708 """ | |
| 709 Like L{test_formatError} but for an unrecognized error code and | |
| 710 L{DNSUnknownError}. | |
| 711 """ | |
| 712 return self._rcodeTest(EREFUSED + 1, DNSUnknownError) | |
| OLD | NEW |