1# coding: utf-8 2"""Tests for the eventlet.support.greendns module""" 3 4import os 5import socket 6import tempfile 7import time 8from dns.resolver import NoAnswer, Answer, Resolver 9 10from eventlet.support import greendns 11from eventlet.support.greendns import dns 12import tests 13import tests.mock 14 15 16def _make_host_resolver(): 17 """Returns a HostResolver instance 18 19 The hosts file will be empty but accessible as a py.path.local 20 instance using the ``hosts`` attribute. 21 """ 22 hosts = tempfile.NamedTemporaryFile() 23 hr = greendns.HostsResolver(fname=hosts.name) 24 hr.hosts = hosts 25 hr._last_stat = 0 26 return hr 27 28 29class TestHostsResolver(tests.LimitedTestCase): 30 31 def test_default_fname(self): 32 hr = greendns.HostsResolver() 33 assert os.path.exists(hr.fname) 34 35 def test_readlines_lines(self): 36 hr = _make_host_resolver() 37 hr.hosts.write(b'line0\n') 38 hr.hosts.flush() 39 assert list(hr._readlines()) == ['line0'] 40 hr._last_stat = 0 41 hr.hosts.write(b'line1\n') 42 hr.hosts.flush() 43 assert list(hr._readlines()) == ['line0', 'line1'] 44 # Test reading of varied newline styles 45 hr._last_stat = 0 46 hr.hosts.seek(0) 47 hr.hosts.truncate() 48 hr.hosts.write(b'\naa\r\nbb\r cc \n\n\tdd ee') 49 hr.hosts.flush() 50 assert list(hr._readlines()) == ['aa', 'bb', 'cc', 'dd ee'] 51 # Test comments, including inline comments 52 hr._last_stat = 0 53 hr.hosts.seek(0) 54 hr.hosts.truncate() 55 hr.hosts.write(b'''\ 56# First couple lines 57# are comments. 58line1 59#comment 60line2 # inline comment 61''') 62 hr.hosts.flush() 63 assert list(hr._readlines()) == ['line1', 'line2'] 64 65 def test_readlines_missing_file(self): 66 hr = _make_host_resolver() 67 hr.hosts.close() 68 hr._last_stat = 0 69 assert list(hr._readlines()) == [] 70 71 def test_load_no_contents(self): 72 hr = _make_host_resolver() 73 hr._load() 74 assert not hr._v4 75 assert not hr._v6 76 assert not hr._aliases 77 78 def test_load_v4_v6_cname_aliases(self): 79 hr = _make_host_resolver() 80 hr.hosts.write(b'1.2.3.4 v4.example.com v4\n' 81 b'dead:beef::1 v6.example.com v6\n') 82 hr.hosts.flush() 83 hr._load() 84 assert hr._v4 == {'v4.example.com': '1.2.3.4', 'v4': '1.2.3.4'} 85 assert hr._v6 == {'v6.example.com': 'dead:beef::1', 86 'v6': 'dead:beef::1'} 87 assert hr._aliases == {'v4': 'v4.example.com', 88 'v6': 'v6.example.com'} 89 90 def test_load_v6_link_local(self): 91 hr = _make_host_resolver() 92 hr.hosts.write(b'fe80:: foo\n' 93 b'fe80:dead:beef::1 bar\n') 94 hr.hosts.flush() 95 hr._load() 96 assert not hr._v4 97 assert not hr._v6 98 99 def test_query_A(self): 100 hr = _make_host_resolver() 101 hr._v4 = {'v4.example.com': '1.2.3.4'} 102 ans = hr.query('v4.example.com') 103 assert ans[0].address == '1.2.3.4' 104 105 def test_query_ans_types(self): 106 # This assumes test_query_A above succeeds 107 hr = _make_host_resolver() 108 hr._v4 = {'v4.example.com': '1.2.3.4'} 109 hr._last_stat = time.time() 110 ans = hr.query('v4.example.com') 111 assert isinstance(ans, greendns.dns.resolver.Answer) 112 assert ans.response is None 113 assert ans.qname == dns.name.from_text('v4.example.com') 114 assert ans.rdtype == dns.rdatatype.A 115 assert ans.rdclass == dns.rdataclass.IN 116 assert ans.canonical_name == dns.name.from_text('v4.example.com') 117 assert ans.expiration 118 assert isinstance(ans.rrset, dns.rrset.RRset) 119 assert ans.rrset.rdtype == dns.rdatatype.A 120 assert ans.rrset.rdclass == dns.rdataclass.IN 121 ttl = greendns.HOSTS_TTL 122 assert ttl - 1 <= ans.rrset.ttl <= ttl + 1 123 rr = ans.rrset[0] 124 assert isinstance(rr, greendns.dns.rdtypes.IN.A.A) 125 assert rr.rdtype == dns.rdatatype.A 126 assert rr.rdclass == dns.rdataclass.IN 127 assert rr.address == '1.2.3.4' 128 129 def test_query_AAAA(self): 130 hr = _make_host_resolver() 131 hr._v6 = {'v6.example.com': 'dead:beef::1'} 132 ans = hr.query('v6.example.com', dns.rdatatype.AAAA) 133 assert ans[0].address == 'dead:beef::1' 134 135 def test_query_unknown_raises(self): 136 hr = _make_host_resolver() 137 with tests.assert_raises(greendns.dns.resolver.NoAnswer): 138 hr.query('example.com') 139 140 def test_query_unknown_no_raise(self): 141 hr = _make_host_resolver() 142 ans = hr.query('example.com', raise_on_no_answer=False) 143 assert isinstance(ans, greendns.dns.resolver.Answer) 144 assert ans.response is None 145 assert ans.qname == dns.name.from_text('example.com') 146 assert ans.rdtype == dns.rdatatype.A 147 assert ans.rdclass == dns.rdataclass.IN 148 assert ans.canonical_name == dns.name.from_text('example.com') 149 assert ans.expiration 150 assert isinstance(ans.rrset, greendns.dns.rrset.RRset) 151 assert ans.rrset.rdtype == dns.rdatatype.A 152 assert ans.rrset.rdclass == dns.rdataclass.IN 153 assert len(ans.rrset) == 0 154 155 def test_query_CNAME(self): 156 hr = _make_host_resolver() 157 hr._aliases = {'host': 'host.example.com'} 158 ans = hr.query('host', dns.rdatatype.CNAME) 159 assert ans[0].target == dns.name.from_text('host.example.com') 160 assert str(ans[0].target) == 'host.example.com.' 161 162 def test_query_unknown_type(self): 163 hr = _make_host_resolver() 164 with tests.assert_raises(greendns.dns.resolver.NoAnswer): 165 hr.query('example.com', dns.rdatatype.MX) 166 167 def test_getaliases(self): 168 hr = _make_host_resolver() 169 hr._aliases = {'host': 'host.example.com', 170 'localhost': 'host.example.com'} 171 res = set(hr.getaliases('host')) 172 assert res == set(['host.example.com', 'localhost']) 173 174 def test_getaliases_unknown(self): 175 hr = _make_host_resolver() 176 assert hr.getaliases('host.example.com') == [] 177 178 def test_getaliases_fqdn(self): 179 hr = _make_host_resolver() 180 hr._aliases = {'host': 'host.example.com'} 181 res = set(hr.getaliases('host.example.com')) 182 assert res == set(['host']) 183 184 def test_hosts_case_insensitive(self): 185 name = 'example.com' 186 hr = _make_host_resolver() 187 hr.hosts.write(b'1.2.3.4 ExAmPlE.CoM\n') 188 hr.hosts.flush() 189 hr._load() 190 191 ans = hr.query(name) 192 rr = ans.rrset[0] 193 assert isinstance(rr, greendns.dns.rdtypes.IN.A.A) 194 assert rr.rdtype == dns.rdatatype.A 195 assert rr.rdclass == dns.rdataclass.IN 196 assert rr.address == '1.2.3.4' 197 198 199def _make_mock_base_resolver(): 200 """A mocked base resolver class""" 201 class RR(object): 202 pass 203 204 class Resolver(object): 205 aliases = ['cname.example.com'] 206 raises = None 207 rr = RR() 208 rr6 = RR() 209 210 def query(self, *args, **kwargs): 211 self.args = args 212 self.kwargs = kwargs 213 if self.raises: 214 raise self.raises() 215 if hasattr(self, 'rrset'): 216 rrset = self.rrset 217 else: 218 if self.rr6 and self.args[1] == dns.rdatatype.AAAA: 219 rrset = [self.rr6] 220 else: 221 rrset = [self.rr] 222 return greendns.HostsAnswer('foo', 1, 1, rrset, False) 223 224 def getaliases(self, *args, **kwargs): 225 return self.aliases 226 227 return Resolver 228 229 230class TestUdp(tests.LimitedTestCase): 231 232 def setUp(self): 233 # Store this so we can reuse it for each test 234 self.query = greendns.dns.message.Message() 235 self.query.flags = greendns.dns.flags.QR 236 self.query_wire = self.query.to_wire() 237 super(TestUdp, self).setUp() 238 239 def test_udp_ipv4(self): 240 with tests.mock.patch('eventlet.support.greendns.socket.socket.recvfrom', 241 return_value=(self.query_wire, 242 ('127.0.0.1', 53))): 243 greendns.udp(self.query, '127.0.0.1') 244 245 def test_udp_ipv4_timeout(self): 246 with tests.mock.patch('eventlet.support.greendns.socket.socket.recvfrom', 247 side_effect=socket.timeout): 248 with tests.assert_raises(dns.exception.Timeout): 249 greendns.udp(self.query, '127.0.0.1', timeout=0.1) 250 251 def test_udp_ipv4_wrong_addr_ignore(self): 252 with tests.mock.patch('eventlet.support.greendns.socket.socket.recvfrom', 253 side_effect=socket.timeout): 254 with tests.assert_raises(dns.exception.Timeout): 255 greendns.udp(self.query, '127.0.0.1', timeout=0.1, ignore_unexpected=True) 256 257 def test_udp_ipv4_wrong_addr(self): 258 with tests.mock.patch('eventlet.support.greendns.socket.socket.recvfrom', 259 return_value=(self.query_wire, 260 ('127.0.0.2', 53))): 261 with tests.assert_raises(dns.query.UnexpectedSource): 262 greendns.udp(self.query, '127.0.0.1') 263 264 def test_udp_ipv6(self): 265 with tests.mock.patch('eventlet.support.greendns.socket.socket.recvfrom', 266 return_value=(self.query_wire, 267 ('::1', 53, 0, 0))): 268 greendns.udp(self.query, '::1') 269 270 def test_udp_ipv6_timeout(self): 271 with tests.mock.patch('eventlet.support.greendns.socket.socket.recvfrom', 272 side_effect=socket.timeout): 273 with tests.assert_raises(dns.exception.Timeout): 274 greendns.udp(self.query, '::1', timeout=0.1) 275 276 def test_udp_ipv6_addr_zeroes(self): 277 with tests.mock.patch('eventlet.support.greendns.socket.socket.recvfrom', 278 return_value=(self.query_wire, 279 ('0:00:0000::1', 53, 0, 0))): 280 greendns.udp(self.query, '::1') 281 282 def test_udp_ipv6_wrong_addr_ignore(self): 283 with tests.mock.patch('eventlet.support.greendns.socket.socket.recvfrom', 284 side_effect=socket.timeout): 285 with tests.assert_raises(dns.exception.Timeout): 286 greendns.udp(self.query, '::1', timeout=0.1, ignore_unexpected=True) 287 288 def test_udp_ipv6_wrong_addr(self): 289 with tests.mock.patch('eventlet.support.greendns.socket.socket.recvfrom', 290 return_value=(self.query_wire, 291 ('ffff:0000::1', 53, 0, 0))): 292 with tests.assert_raises(dns.query.UnexpectedSource): 293 greendns.udp(self.query, '::1') 294 295 296class TestProxyResolver(tests.LimitedTestCase): 297 298 def test_clear(self): 299 rp = greendns.ResolverProxy() 300 assert rp._cached_resolver is None 301 resolver = rp._resolver 302 assert resolver is not None 303 rp.clear() 304 assert rp._resolver is not None 305 assert rp._resolver != resolver 306 307 def _make_mock_hostsresolver(self): 308 """A mocked HostsResolver""" 309 base_resolver = _make_mock_base_resolver() 310 base_resolver.rr.address = '1.2.3.4' 311 return base_resolver() 312 313 def _make_mock_resolver(self): 314 """A mocked Resolver""" 315 base_resolver = _make_mock_base_resolver() 316 base_resolver.rr.address = '5.6.7.8' 317 return base_resolver() 318 319 def test_hosts(self): 320 hostsres = self._make_mock_hostsresolver() 321 rp = greendns.ResolverProxy(hostsres) 322 ans = rp.query('host.example.com') 323 assert ans[0].address == '1.2.3.4' 324 325 def test_hosts_noanswer(self): 326 hostsres = self._make_mock_hostsresolver() 327 res = self._make_mock_resolver() 328 rp = greendns.ResolverProxy(hostsres) 329 rp._resolver = res 330 hostsres.raises = greendns.dns.resolver.NoAnswer 331 ans = rp.query('host.example.com') 332 assert ans[0].address == '5.6.7.8' 333 334 def test_resolver(self): 335 res = self._make_mock_resolver() 336 rp = greendns.ResolverProxy() 337 rp._resolver = res 338 ans = rp.query('host.example.com') 339 assert ans[0].address == '5.6.7.8' 340 341 def test_noanswer(self): 342 res = self._make_mock_resolver() 343 rp = greendns.ResolverProxy() 344 rp._resolver = res 345 res.raises = greendns.dns.resolver.NoAnswer 346 with tests.assert_raises(greendns.dns.resolver.NoAnswer): 347 rp.query('host.example.com') 348 349 def test_nxdomain(self): 350 res = self._make_mock_resolver() 351 rp = greendns.ResolverProxy() 352 rp._resolver = res 353 res.raises = greendns.dns.resolver.NXDOMAIN 354 with tests.assert_raises(greendns.dns.resolver.NXDOMAIN): 355 rp.query('host.example.com') 356 357 def test_noanswer_hosts(self): 358 hostsres = self._make_mock_hostsresolver() 359 res = self._make_mock_resolver() 360 rp = greendns.ResolverProxy(hostsres) 361 rp._resolver = res 362 hostsres.raises = greendns.dns.resolver.NoAnswer 363 res.raises = greendns.dns.resolver.NoAnswer 364 with tests.assert_raises(greendns.dns.resolver.NoAnswer): 365 rp.query('host.example.com') 366 367 def _make_mock_resolver_aliases(self): 368 369 class RR(object): 370 target = 'host.example.com' 371 372 class Resolver(object): 373 call_count = 0 374 exc_type = greendns.dns.resolver.NoAnswer 375 376 def query(self, *args, **kwargs): 377 self.args = args 378 self.kwargs = kwargs 379 self.call_count += 1 380 if self.call_count < 2: 381 return greendns.HostsAnswer(args[0], 1, 5, [RR()], False) 382 else: 383 raise self.exc_type() 384 385 return Resolver() 386 387 def test_getaliases(self): 388 aliases_res = self._make_mock_resolver_aliases() 389 rp = greendns.ResolverProxy() 390 rp._resolver = aliases_res 391 aliases = set(rp.getaliases('alias.example.com')) 392 assert aliases == set(['host.example.com']) 393 394 def test_getaliases_fqdn(self): 395 aliases_res = self._make_mock_resolver_aliases() 396 rp = greendns.ResolverProxy() 397 rp._resolver = aliases_res 398 rp._resolver.call_count = 1 399 assert rp.getaliases('host.example.com') == [] 400 401 def test_getaliases_nxdomain(self): 402 aliases_res = self._make_mock_resolver_aliases() 403 rp = greendns.ResolverProxy() 404 rp._resolver = aliases_res 405 rp._resolver.call_count = 1 406 rp._resolver.exc_type = greendns.dns.resolver.NXDOMAIN 407 assert rp.getaliases('host.example.com') == [] 408 409 410class TestResolve(tests.LimitedTestCase): 411 412 def setUp(self): 413 base_resolver = _make_mock_base_resolver() 414 base_resolver.rr.address = '1.2.3.4' 415 self._old_resolver = greendns.resolver 416 greendns.resolver = base_resolver() 417 418 def tearDown(self): 419 greendns.resolver = self._old_resolver 420 421 def test_A(self): 422 ans = greendns.resolve('host.example.com', socket.AF_INET) 423 assert ans[0].address == '1.2.3.4' 424 assert greendns.resolver.args == ('host.example.com', dns.rdatatype.A) 425 426 def test_AAAA(self): 427 greendns.resolver.rr6.address = 'dead:beef::1' 428 ans = greendns.resolve('host.example.com', socket.AF_INET6) 429 assert ans[0].address == 'dead:beef::1' 430 assert greendns.resolver.args == ('host.example.com', dns.rdatatype.AAAA) 431 432 def test_unknown_rdtype(self): 433 with tests.assert_raises(socket.gaierror): 434 greendns.resolve('host.example.com', socket.AF_INET6 + 1) 435 436 def test_timeout(self): 437 greendns.resolver.raises = greendns.dns.exception.Timeout 438 with tests.assert_raises(socket.gaierror): 439 greendns.resolve('host.example.com') 440 441 def test_exc(self): 442 greendns.resolver.raises = greendns.dns.exception.DNSException 443 with tests.assert_raises(socket.gaierror): 444 greendns.resolve('host.example.com') 445 446 def test_noraise_noanswer(self): 447 greendns.resolver.rrset = None 448 ans = greendns.resolve('example.com', raises=False) 449 assert not ans.rrset 450 451 def test_noraise_nxdomain(self): 452 greendns.resolver.raises = greendns.dns.resolver.NXDOMAIN 453 ans = greendns.resolve('example.com', raises=False) 454 assert not ans.rrset 455 456 457class TestResolveCname(tests.LimitedTestCase): 458 459 def setUp(self): 460 base_resolver = _make_mock_base_resolver() 461 base_resolver.rr.target = 'cname.example.com' 462 self._old_resolver = greendns.resolver 463 greendns.resolver = base_resolver() 464 465 def tearDown(self): 466 greendns.resolver = self._old_resolver 467 468 def test_success(self): 469 cname = greendns.resolve_cname('alias.example.com') 470 assert cname == 'cname.example.com' 471 472 def test_timeout(self): 473 greendns.resolver.raises = greendns.dns.exception.Timeout 474 with tests.assert_raises(socket.gaierror): 475 greendns.resolve_cname('alias.example.com') 476 477 def test_nodata(self): 478 greendns.resolver.raises = greendns.dns.exception.DNSException 479 with tests.assert_raises(socket.gaierror): 480 greendns.resolve_cname('alias.example.com') 481 482 def test_no_answer(self): 483 greendns.resolver.raises = greendns.dns.resolver.NoAnswer 484 assert greendns.resolve_cname('host.example.com') == 'host.example.com' 485 486 487def _make_mock_resolve(): 488 """A stubbed out resolve function 489 490 This monkeypatches the greendns.resolve() function with a mock. 491 You must give it answers by calling .add(). 492 """ 493 494 class MockAnswer(list): 495 pass 496 497 class MockResolve(object): 498 499 def __init__(self): 500 self.answers = {} 501 502 def __call__(self, name, family=socket.AF_INET, raises=True, 503 _proxy=None, use_network=True): 504 qname = dns.name.from_text(name) 505 try: 506 rrset = self.answers[name][family] 507 except KeyError: 508 if raises: 509 raise greendns.dns.resolver.NoAnswer() 510 rrset = dns.rrset.RRset(qname, 1, 1) 511 ans = MockAnswer() 512 ans.qname = qname 513 ans.rrset = rrset 514 ans.extend(rrset.items) 515 return ans 516 517 def add(self, name, addr): 518 """Add an address to a name and family""" 519 try: 520 rdata = dns.rdtypes.IN.A.A(dns.rdataclass.IN, 521 dns.rdatatype.A, addr) 522 family = socket.AF_INET 523 except (socket.error, dns.exception.SyntaxError): 524 rdata = dns.rdtypes.IN.AAAA.AAAA(dns.rdataclass.IN, 525 dns.rdatatype.AAAA, addr) 526 family = socket.AF_INET6 527 family_dict = self.answers.setdefault(name, {}) 528 rrset = family_dict.get(family) 529 if not rrset: 530 family_dict[family] = rrset = dns.rrset.RRset( 531 dns.name.from_text(name), rdata.rdclass, rdata.rdtype) 532 rrset.add(rdata) 533 534 resolve = MockResolve() 535 return resolve 536 537 538class TestGetaddrinfo(tests.LimitedTestCase): 539 540 def _make_mock_resolve_cname(self): 541 """A stubbed out cname function""" 542 543 class ResolveCname(object): 544 qname = None 545 cname = 'cname.example.com' 546 547 def __call__(self, host): 548 self.qname = host 549 return self.cname 550 551 resolve_cname = ResolveCname() 552 return resolve_cname 553 554 def setUp(self): 555 self._old_resolve = greendns.resolve 556 self._old_resolve_cname = greendns.resolve_cname 557 self._old_orig_getaddrinfo = greendns.socket.getaddrinfo 558 559 def tearDown(self): 560 greendns.resolve = self._old_resolve 561 greendns.resolve_cname = self._old_resolve_cname 562 greendns.socket.getaddrinfo = self._old_orig_getaddrinfo 563 564 def test_getaddrinfo(self): 565 greendns.resolve = _make_mock_resolve() 566 greendns.resolve.add('example.com', '127.0.0.2') 567 greendns.resolve.add('example.com', '::1') 568 res = greendns.getaddrinfo('example.com', 'domain') 569 addr = ('127.0.0.2', 53) 570 tcp = (socket.AF_INET, socket.SOCK_STREAM, socket.IPPROTO_TCP, addr) 571 udp = (socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP, addr) 572 addr = ('::1', 53, 0, 0) 573 tcp6 = (socket.AF_INET6, socket.SOCK_STREAM, socket.IPPROTO_TCP, addr) 574 udp6 = (socket.AF_INET6, socket.SOCK_DGRAM, socket.IPPROTO_UDP, addr) 575 filt_res = [ai[:3] + (ai[4],) for ai in res] 576 assert tcp in filt_res 577 assert udp in filt_res 578 assert tcp6 in filt_res 579 assert udp6 in filt_res 580 581 def test_getaddrinfo_idn(self): 582 greendns.resolve = _make_mock_resolve() 583 idn_name = u'евентлет.com' 584 greendns.resolve.add(idn_name.encode('idna').decode('ascii'), '127.0.0.2') 585 res = greendns.getaddrinfo(idn_name, 'domain') 586 addr = ('127.0.0.2', 53) 587 tcp = (socket.AF_INET, socket.SOCK_STREAM, socket.IPPROTO_TCP, addr) 588 udp = (socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP, addr) 589 filt_res = [ai[:3] + (ai[4],) for ai in res] 590 assert tcp in filt_res 591 assert udp in filt_res 592 593 def test_getaddrinfo_inet(self): 594 greendns.resolve = _make_mock_resolve() 595 greendns.resolve.add('example.com', '127.0.0.2') 596 res = greendns.getaddrinfo('example.com', 'domain', socket.AF_INET) 597 addr = ('127.0.0.2', 53) 598 tcp = (socket.AF_INET, socket.SOCK_STREAM, socket.IPPROTO_TCP, addr) 599 udp = (socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP, addr) 600 assert tcp in [ai[:3] + (ai[4],) for ai in res] 601 assert udp in [ai[:3] + (ai[4],) for ai in res] 602 603 def test_getaddrinfo_inet6(self): 604 greendns.resolve = _make_mock_resolve() 605 greendns.resolve.add('example.com', '::1') 606 res = greendns.getaddrinfo('example.com', 'domain', socket.AF_INET6) 607 addr = ('::1', 53, 0, 0) 608 tcp = (socket.AF_INET6, socket.SOCK_STREAM, socket.IPPROTO_TCP, addr) 609 udp = (socket.AF_INET6, socket.SOCK_DGRAM, socket.IPPROTO_UDP, addr) 610 assert tcp in [ai[:3] + (ai[4],) for ai in res] 611 assert udp in [ai[:3] + (ai[4],) for ai in res] 612 613 def test_getaddrinfo_only_a_ans(self): 614 greendns.resolve = _make_mock_resolve() 615 greendns.resolve.add('example.com', '1.2.3.4') 616 res = greendns.getaddrinfo('example.com', 0) 617 addr = [('1.2.3.4', 0)] * len(res) 618 assert addr == [ai[-1] for ai in res] 619 620 def test_getaddrinfo_only_aaaa_ans(self): 621 greendns.resolve = _make_mock_resolve() 622 greendns.resolve.add('example.com', 'dead:beef::1') 623 res = greendns.getaddrinfo('example.com', 0) 624 addr = [('dead:beef::1', 0, 0, 0)] * len(res) 625 assert addr == [ai[-1] for ai in res] 626 627 def test_getaddrinfo_hosts_only_ans_with_timeout(self): 628 def clear_raises(res_self): 629 res_self.raises = None 630 return greendns.dns.resolver.NoAnswer() 631 632 hostsres = _make_mock_base_resolver() 633 hostsres.raises = clear_raises 634 hostsres.rr.address = '1.2.3.4' 635 greendns.resolver = greendns.ResolverProxy(hostsres()) 636 res = _make_mock_base_resolver() 637 res.raises = greendns.dns.exception.Timeout 638 greendns.resolver._resolver = res() 639 640 result = greendns.getaddrinfo('example.com', 0, 0) 641 addr = [('1.2.3.4', 0)] * len(result) 642 assert addr == [ai[-1] for ai in result] 643 644 def test_getaddrinfo_hosts_only_ans_with_error(self): 645 def clear_raises(res_self): 646 res_self.raises = None 647 return greendns.dns.resolver.NoAnswer() 648 649 hostsres = _make_mock_base_resolver() 650 hostsres.raises = clear_raises 651 hostsres.rr.address = '1.2.3.4' 652 greendns.resolver = greendns.ResolverProxy(hostsres()) 653 res = _make_mock_base_resolver() 654 res.raises = greendns.dns.exception.DNSException 655 greendns.resolver._resolver = res() 656 657 result = greendns.getaddrinfo('example.com', 0, 0) 658 addr = [('1.2.3.4', 0)] * len(result) 659 assert addr == [ai[-1] for ai in result] 660 661 def test_getaddrinfo_hosts_only_timeout(self): 662 hostsres = _make_mock_base_resolver() 663 hostsres.raises = greendns.dns.resolver.NoAnswer 664 greendns.resolver = greendns.ResolverProxy(hostsres()) 665 res = _make_mock_base_resolver() 666 res.raises = greendns.dns.exception.Timeout 667 greendns.resolver._resolver = res() 668 669 with tests.assert_raises(socket.gaierror): 670 greendns.getaddrinfo('example.com', 0, 0) 671 672 def test_getaddrinfo_hosts_only_dns_error(self): 673 hostsres = _make_mock_base_resolver() 674 hostsres.raises = greendns.dns.resolver.NoAnswer 675 greendns.resolver = greendns.ResolverProxy(hostsres()) 676 res = _make_mock_base_resolver() 677 res.raises = greendns.dns.exception.DNSException 678 greendns.resolver._resolver = res() 679 680 with tests.assert_raises(socket.gaierror): 681 greendns.getaddrinfo('example.com', 0, 0) 682 683 def test_canonname(self): 684 greendns.resolve = _make_mock_resolve() 685 greendns.resolve.add('host.example.com', '1.2.3.4') 686 greendns.resolve_cname = self._make_mock_resolve_cname() 687 res = greendns.getaddrinfo('host.example.com', 0, 688 0, 0, 0, socket.AI_CANONNAME) 689 assert res[0][3] == 'cname.example.com' 690 691 def test_host_none(self): 692 res = greendns.getaddrinfo(None, 80) 693 for addr in set(ai[-1] for ai in res): 694 assert addr in [('127.0.0.1', 80), ('::1', 80, 0, 0)] 695 696 def test_host_none_passive(self): 697 res = greendns.getaddrinfo(None, 80, 0, 0, 0, socket.AI_PASSIVE) 698 for addr in set(ai[-1] for ai in res): 699 assert addr in [('0.0.0.0', 80), ('::', 80, 0, 0)] 700 701 def test_v4mapped(self): 702 greendns.resolve = _make_mock_resolve() 703 greendns.resolve.add('example.com', '1.2.3.4') 704 res = greendns.getaddrinfo('example.com', 80, 705 socket.AF_INET6, 0, 0, socket.AI_V4MAPPED) 706 addrs = set(ai[-1] for ai in res) 707 assert addrs == set([('::ffff:1.2.3.4', 80, 0, 0)]) 708 709 def test_v4mapped_all(self): 710 greendns.resolve = _make_mock_resolve() 711 greendns.resolve.add('example.com', '1.2.3.4') 712 greendns.resolve.add('example.com', 'dead:beef::1') 713 res = greendns.getaddrinfo('example.com', 80, socket.AF_INET6, 0, 0, 714 socket.AI_V4MAPPED | socket.AI_ALL) 715 addrs = set(ai[-1] for ai in res) 716 for addr in addrs: 717 assert addr in [('::ffff:1.2.3.4', 80, 0, 0), 718 ('dead:beef::1', 80, 0, 0)] 719 720 def test_numericserv(self): 721 greendns.resolve = _make_mock_resolve() 722 greendns.resolve.add('example.com', '1.2.3.4') 723 with tests.assert_raises(socket.gaierror): 724 greendns.getaddrinfo('example.com', 'www', 0, 0, 0, socket.AI_NUMERICSERV) 725 726 def test_numerichost(self): 727 greendns.resolve = _make_mock_resolve() 728 greendns.resolve.add('example.com', '1.2.3.4') 729 with tests.assert_raises(socket.gaierror): 730 greendns.getaddrinfo('example.com', 80, 0, 0, 0, socket.AI_NUMERICHOST) 731 732 def test_noport(self): 733 greendns.resolve = _make_mock_resolve() 734 greendns.resolve.add('example.com', '1.2.3.4') 735 ai = greendns.getaddrinfo('example.com', None) 736 assert ai[0][-1][1] == 0 737 738 def test_AI_ADDRCONFIG(self): 739 # When the users sets AI_ADDRCONFIG but only has an IPv4 740 # address configured we will iterate over the results, but the 741 # call for the IPv6 address will fail rather then return an 742 # empty list. In that case we should catch the exception and 743 # only return the ones which worked. 744 def getaddrinfo(addr, port, family, socktype, proto, aiflags): 745 if addr == '127.0.0.1': 746 return [(socket.AF_INET, 1, 0, '', ('127.0.0.1', 0))] 747 elif addr == '::1' and aiflags & socket.AI_ADDRCONFIG: 748 raise socket.error(socket.EAI_ADDRFAMILY, 749 'Address family for hostname not supported') 750 elif addr == '::1' and not aiflags & socket.AI_ADDRCONFIG: 751 return [(socket.AF_INET6, 1, 0, '', ('::1', 0, 0, 0))] 752 greendns.socket.getaddrinfo = getaddrinfo 753 greendns.resolve = _make_mock_resolve() 754 greendns.resolve.add('localhost', '127.0.0.1') 755 greendns.resolve.add('localhost', '::1') 756 res = greendns.getaddrinfo('localhost', None, 757 0, 0, 0, socket.AI_ADDRCONFIG) 758 assert res == [(socket.AF_INET, 1, 0, '', ('127.0.0.1', 0))] 759 760 def test_AI_ADDRCONFIG_noaddr(self): 761 # If AI_ADDRCONFIG is used but there is no address we need to 762 # get an exception, not an empty list. 763 def getaddrinfo(addr, port, family, socktype, proto, aiflags): 764 raise socket.error(socket.EAI_ADDRFAMILY, 765 'Address family for hostname not supported') 766 greendns.socket.getaddrinfo = getaddrinfo 767 greendns.resolve = _make_mock_resolve() 768 try: 769 greendns.getaddrinfo('::1', None, 0, 0, 0, socket.AI_ADDRCONFIG) 770 except socket.error as e: 771 assert e.errno == socket.EAI_ADDRFAMILY 772 773 774class TestIsIpAddr(tests.LimitedTestCase): 775 776 def test_isv4(self): 777 assert greendns.is_ipv4_addr('1.2.3.4') 778 779 def test_isv4_false(self): 780 assert not greendns.is_ipv4_addr('260.0.0.0') 781 782 def test_isv6(self): 783 assert greendns.is_ipv6_addr('dead:beef::1') 784 785 def test_isv6_invalid(self): 786 assert not greendns.is_ipv6_addr('foobar::1') 787 788 def test_v4(self): 789 assert greendns.is_ip_addr('1.2.3.4') 790 791 def test_v4_illegal(self): 792 assert not greendns.is_ip_addr('300.0.0.1') 793 794 def test_v6_addr(self): 795 assert greendns.is_ip_addr('::1') 796 797 def test_isv4_none(self): 798 assert not greendns.is_ipv4_addr(None) 799 800 def test_isv6_none(self): 801 assert not greendns.is_ipv6_addr(None) 802 803 def test_none(self): 804 assert not greendns.is_ip_addr(None) 805 806 807class TestGethostbyname(tests.LimitedTestCase): 808 809 def setUp(self): 810 self._old_resolve = greendns.resolve 811 greendns.resolve = _make_mock_resolve() 812 813 def tearDown(self): 814 greendns.resolve = self._old_resolve 815 816 def test_ipaddr(self): 817 assert greendns.gethostbyname('1.2.3.4') == '1.2.3.4' 818 819 def test_name(self): 820 greendns.resolve.add('host.example.com', '1.2.3.4') 821 assert greendns.gethostbyname('host.example.com') == '1.2.3.4' 822 823 824class TestGetaliases(tests.LimitedTestCase): 825 826 def _make_mock_resolver(self): 827 base_resolver = _make_mock_base_resolver() 828 resolver = base_resolver() 829 resolver.aliases = ['cname.example.com'] 830 return resolver 831 832 def setUp(self): 833 self._old_resolver = greendns.resolver 834 greendns.resolver = self._make_mock_resolver() 835 836 def tearDown(self): 837 greendns.resolver = self._old_resolver 838 839 def test_getaliases(self): 840 assert greendns.getaliases('host.example.com') == ['cname.example.com'] 841 842 843class TestGethostbyname_ex(tests.LimitedTestCase): 844 845 def _make_mock_getaliases(self): 846 847 class GetAliases(object): 848 aliases = ['cname.example.com'] 849 850 def __call__(self, *args, **kwargs): 851 return self.aliases 852 853 getaliases = GetAliases() 854 return getaliases 855 856 def setUp(self): 857 self._old_resolve = greendns.resolve 858 greendns.resolve = _make_mock_resolve() 859 self._old_getaliases = greendns.getaliases 860 861 def tearDown(self): 862 greendns.resolve = self._old_resolve 863 greendns.getaliases = self._old_getaliases 864 865 def test_ipaddr(self): 866 res = greendns.gethostbyname_ex('1.2.3.4') 867 assert res == ('1.2.3.4', [], ['1.2.3.4']) 868 869 def test_name(self): 870 greendns.resolve.add('host.example.com', '1.2.3.4') 871 greendns.getaliases = self._make_mock_getaliases() 872 greendns.getaliases.aliases = [] 873 res = greendns.gethostbyname_ex('host.example.com') 874 assert res == ('host.example.com', [], ['1.2.3.4']) 875 876 def test_multiple_addrs(self): 877 greendns.resolve.add('host.example.com', '1.2.3.4') 878 greendns.resolve.add('host.example.com', '1.2.3.5') 879 greendns.getaliases = self._make_mock_getaliases() 880 greendns.getaliases.aliases = [] 881 res = greendns.gethostbyname_ex('host.example.com') 882 assert res == ('host.example.com', [], ['1.2.3.4', '1.2.3.5']) 883 884 885class TinyDNSTests(tests.LimitedTestCase): 886 887 def test_raise_dns_tcp(self): 888 # https://github.com/eventlet/eventlet/issues/499 889 # None means we don't want the server to find the IP 890 with tests.dns_tcp_server(None) as dnsaddr: 891 resolver = Resolver() 892 resolver.nameservers = [dnsaddr[0]] 893 resolver.nameserver_ports[dnsaddr[0]] = dnsaddr[1] 894 895 with self.assertRaises(NoAnswer): 896 resolver.query('host.example.com', 'a', tcp=True) 897 898 def test_noraise_dns_tcp(self): 899 # https://github.com/eventlet/eventlet/issues/499 900 expected_ip = "192.168.1.1" 901 with tests.dns_tcp_server(expected_ip) as dnsaddr: 902 resolver = Resolver() 903 resolver.nameservers = [dnsaddr[0]] 904 resolver.nameserver_ports[dnsaddr[0]] = dnsaddr[1] 905 response = resolver.query('host.example.com', 'a', tcp=True) 906 self.assertIsInstance(response, Answer) 907 self.assertEqual(list(response.rrset.items)[0].address, expected_ip) 908 909 910def test_reverse_name(): 911 tests.run_isolated('greendns_from_address_203.py') 912 913 914def test_proxy_resolve_unqualified(): 915 # https://github.com/eventlet/eventlet/issues/363 916 rp = greendns.ResolverProxy(filename=None) 917 rp._resolver.search.append(dns.name.from_text('example.com')) 918 with tests.mock.patch('dns.resolver.Resolver.query', side_effect=dns.resolver.NoAnswer) as m: 919 try: 920 rp.query('machine') 921 assert False, 'Expected NoAnswer exception' 922 except dns.resolver.NoAnswer: 923 pass 924 assert any(call[0][0] == dns.name.from_text('machine') for call in m.call_args_list) 925 assert any(call[0][0] == dns.name.from_text('machine.') for call in m.call_args_list) 926 927 928def test_hosts_priority(): 929 name = 'example.com' 930 addr_from_ns = '1.0.2.0' 931 932 hr = _make_host_resolver() 933 rp = greendns.ResolverProxy(hosts_resolver=hr, filename=None) 934 base = _make_mock_base_resolver() 935 base.rr.address = addr_from_ns 936 rp._resolver = base() 937 938 # Default behavior 939 rrns = greendns.resolve(name, _proxy=rp).rrset[0] 940 assert rrns.address == addr_from_ns 941 942 # Hosts result must shadow that from nameservers 943 hr.hosts.write(b'1.2.3.4 example.com\ndead:beef::1 example.com\n') 944 hr.hosts.flush() 945 hr._load() 946 rrs4 = greendns.resolve(name, family=socket.AF_INET, _proxy=rp).rrset 947 assert len(rrs4) == 1 948 assert rrs4[0].address == '1.2.3.4', rrs4[0].address 949 rrs6 = greendns.resolve(name, family=socket.AF_INET6, _proxy=rp).rrset 950 assert len(rrs6) == 1 951 assert rrs6[0].address == 'dead:beef::1', rrs6[0].address 952 953 954def test_hosts_no_network(): 955 956 name = 'example.com' 957 addr_from_ns = '1.0.2.0' 958 addr6_from_ns = 'dead:beef::1' 959 960 hr = _make_host_resolver() 961 rp = greendns.ResolverProxy(hosts_resolver=hr, filename=None) 962 base = _make_mock_base_resolver() 963 base.rr.address = addr_from_ns 964 base.rr6.address = addr6_from_ns 965 rp._resolver = base() 966 967 with tests.mock.patch.object(greendns, 'resolver', 968 new_callable=tests.mock.PropertyMock(return_value=rp)): 969 res = greendns.getaddrinfo('example.com', 'domain', socket.AF_UNSPEC) 970 # Default behavior 971 addr = (addr_from_ns, 53) 972 tcp = (socket.AF_INET, socket.SOCK_STREAM, socket.IPPROTO_TCP, addr) 973 udp = (socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP, addr) 974 addr = (addr6_from_ns, 53, 0, 0) 975 tcp6 = (socket.AF_INET6, socket.SOCK_STREAM, socket.IPPROTO_TCP, addr) 976 udp6 = (socket.AF_INET6, socket.SOCK_DGRAM, socket.IPPROTO_UDP, addr) 977 filt_res = [ai[:3] + (ai[4],) for ai in res] 978 assert tcp in filt_res 979 assert udp in filt_res 980 assert tcp6 in filt_res 981 assert udp6 in filt_res 982 983 # Hosts result must shadow that from nameservers 984 hr = _make_host_resolver() 985 hr.hosts.write(b'1.2.3.4 example.com') 986 hr.hosts.flush() 987 hr._load() 988 greendns.resolver._hosts = hr 989 990 res = greendns.getaddrinfo('example.com', 'domain', socket.AF_UNSPEC) 991 filt_res = [ai[:3] + (ai[4],) for ai in res] 992 993 # Make sure that only IPv4 entry from hosts is present. 994 assert tcp not in filt_res 995 assert udp not in filt_res 996 assert tcp6 not in filt_res 997 assert udp6 not in filt_res 998 999 addr = ('1.2.3.4', 53) 1000 tcp = (socket.AF_INET, socket.SOCK_STREAM, socket.IPPROTO_TCP, addr) 1001 udp = (socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP, addr) 1002 assert tcp in filt_res 1003 assert udp in filt_res 1004 1005 1006def test_import_rdtypes_then_eventlet(): 1007 # https://github.com/eventlet/eventlet/issues/479 1008 tests.run_isolated('greendns_import_rdtypes_then_eventlet.py') 1009