Ticket #4362: getaddrinfo-4362-3-1.patch
| File getaddrinfo-4362-3-1.patch, 14.4 KB (added by rwall, 3 months ago) |
|---|
-
twisted/internet/base.py
=== modified file 'twisted/internet/base.py'
17 17 18 18 import traceback 19 19 20 from twisted.python.util import FancyEqMixin 20 21 from twisted.python.compat import set 21 22 from twisted.internet.interfaces import IReactorCore, IReactorTime, IReactorThreads 22 23 from twisted.internet.interfaces import IResolverSimple, IReactorPluggableResolver 23 24 from twisted.internet.interfaces import IConnector, IDelayedCall 25 from twisted.internet.interfaces import INameResolver 24 26 from twisted.internet import fdesc, main, error, abstract, defer, threads 25 27 from twisted.python import log, failure, _reflectpy3 as reflect 26 28 from twisted.python.runtime import seconds as runtimeSeconds, platform … … 209 211 return "".join(L) 210 212 211 213 214 @implementer(INameResolver) 215 class _ResolverComplexifier(object): 216 def __init__(self, resolver): 217 self._resolver = resolver 218 219 220 def getAddressInformation(self, name, service, *args): 221 d = self._resolver.getHostByName(name) 222 def cbResolved(address): 223 family = socket.getaddrinfo(address, 0)[0][0] 224 return [ 225 AddressInformation( 226 family, 227 socket.SOCK_STREAM, 228 socket.IPPROTO_TCP, 229 "", 230 (address, service))] 231 d.addCallback(cbResolved) 232 return d 233 234 212 235 213 236 @implementer(IResolverSimple) 214 237 class ThreadedResolver(object): … … 278 301 279 302 280 303 304 class AddressInformation(object, FancyEqMixin): 305 compareAttributes = ('family', 'type', 'protocol', 'canonicalName', 'address') 306 307 def __init__(self, family, type, protocol, canonicalName, address): 308 self.family = family 309 self.type = type 310 self.protocol = protocol 311 self.canonicalName = canonicalName 312 self.address = address 313 314 315 281 316 @implementer(IResolverSimple) 282 317 class BlockingResolver: 283 318 … … 507 542 reflect.qual(self.__class__) + " did not implement installWaker") 508 543 509 544 def installResolver(self, resolver): 510 assert IResolverSimple.providedBy(resolver)545 resolver = INameResolver(resolver) 511 546 oldResolver = self.resolver 512 547 self.resolver = resolver 513 548 return oldResolver … … 567 602 return defer.succeed('0.0.0.0') 568 603 if abstract.isIPAddress(name): 569 604 return defer.succeed(name) 570 return self.resolver.getHostByName(name, timeout) 605 d = self.resolver.getAddressInformation(name, 0) 606 def cbGotInfo(addresses): 607 for info in addresses: 608 if info.family == socket.AF_INET: 609 return info.address[0] 610 # XXX Test me 611 d.addCallback(cbGotInfo) 612 return d 613 571 614 572 615 # Installation. 573 616 -
twisted/internet/interfaces.py
=== modified file 'twisted/internet/interfaces.py'
54 54 """ 55 55 56 56 57 58 class INameResolver(Interface): 59 """ 60 XXX Write me. 61 62 RFC 3484. 63 """ 64 def getAddressInformation(name, service, family=None, socktype=None, 65 proto=None, flags=None): 66 """ 67 Get the address information associated with the given name. 68 69 @param name: A hostname to resolve. 70 @type name: C{str} 71 72 @param service: A port number or the name of the service for which to find address 73 information. For example, C{22} or C{"ssh"}. 74 @type service: C{int} or C{str} 75 76 @param family: If specified, limit results to addresses from this family. Must be one 77 of the address family constants from the socket module. For example, 78 L{socket.AF_INET}. 79 80 @param socktype: If specified, limit results to addresses for this socket type. Must be 81 one of the socket type constants from the socket module. For example, 82 L{socket.SOCK_STREAM}. 83 84 @param proto: If specified, limit results to addresses for this socket protocol. Must 85 be one of the protocol constants from the socket module. For example, 86 L{socket.IPPROTO_TCP}. 87 88 @param flags: A bitvector specifying zero or more of the following:: 89 - Yea right. Go read the `getaddrinfo(3)` man page. 90 91 @raise ValueError: If one of the specified flags is not supported by the 92 implementation. All flags are optional. 93 94 @return: A L{Deferred} which will fire when the resolution completes. If resolution is 95 successful, the result will be a list of objects with the following attributes: 96 97 - C{family}: the family of this address 98 - C{type}: the type of this address 99 - C{protocol}: the protocol of this address 100 - C{canonicalName}: the canonical name associated with this address, or an empty 101 string. 102 - C{address}: The actual address itself, including a port number. This is suitable 103 to be passed directly to L{socket.socket.connect}. 104 """ 105 106 57 107 class IResolverSimple(Interface): 58 108 59 109 def getHostByName(name, timeout = (1, 3, 11, 45)): -
twisted/internet/test/test_base.py
=== modified file 'twisted/internet/test/test_base.py'
12 12 from queue import Queue 13 13 14 14 from zope.interface import implementer 15 from zope.interface.verify import verifyClass 15 16 16 17 from twisted.python.threadpool import ThreadPool 17 18 from twisted.internet.interfaces import IReactorTime, IReactorThreads 19 from twisted.internet.interfaces import INameResolver 18 20 from twisted.internet.error import DNSLookupError 19 from twisted.internet.base import ThreadedResolver, DelayedCall 21 from twisted.internet.base import ( 22 ThreadedNameResolver, ThreadedResolver, AddressInformation, 23 _ResolverComplexifier, DelayedCall) 20 24 from twisted.internet.task import Clock 21 25 from twisted.trial.unittest import TestCase 22 26 27 from twisted.internet.test.test_tcp import FakeResolver 28 23 29 24 30 @implementer(IReactorTime, IReactorThreads) 25 31 class FakeReactor(object): … … 53 59 54 60 55 61 62 class NameResolverAdapterTests(TestCase): 63 """ 64 L{_ResolverComplexifier} adapters an L{IResolverSimple} provider 65 to L{INameResolver}. 66 """ 67 def test_interface(self): 68 """ 69 L{_ResolverComplexifier} implements L{INameResolver}. 70 """ 71 self.assertTrue(verifyClass(INameResolver, _ResolverComplexifier)) 72 73 74 def _successTest(self, address, family): 75 simple = FakeResolver({'example.com': address}) 76 resolver = _ResolverComplexifier(simple) 77 d = resolver.getAddressInformation('example.com', 1234) 78 d.addCallback( 79 self.assertEquals, [ 80 AddressInformation( 81 family, 82 socket.SOCK_STREAM, 83 socket.IPPROTO_TCP, 84 "", 85 (address, 1234))]) 86 return d 87 88 89 def test_ipv4Success(self): 90 """ 91 L{_ResolverComplexifier} calls the wrapped object's 92 C{getHostByName} method and returns a L{Deferred} which fires 93 with a list of one element containing an AF_INET element with 94 the IPv4 address which C{getHostByName}'s L{Deferred} fired 95 with. 96 """ 97 return self._successTest('192.168.1.12', socket.AF_INET) 98 99 100 def test_ipv6Success(self): 101 """ 102 L{_ResolverComplexifier} calls the wrapped object's 103 C{getHostByName} method and returns a L{Deferred} which fires 104 with a list of one element containing an AF_INET6 element with 105 the IPv6 address which C{getHostByName}'s L{Deferred} fired 106 with. 107 """ 108 return self._successTest('::1', socket.AF_INET6) 109 110 111 def test_failure(self): 112 """ 113 The L{Deferred} L{_ResolverComplexifier.getAddressInformation} 114 returns fails if the wrapped resolver's C{getHostByName} 115 L{Deferred} fails. 116 """ 117 error = DNSLookupError("Problems abound") 118 simple = FakeResolver({'example.com': error}) 119 resolver = _ResolverComplexifier(simple) 120 d = resolver.getAddressInformation('example.com', 1234) 121 return self.assertFailure(d, DNSLookupError) 122 123 124 125 class ThreadedNameResolverTests(TestCase): 126 """ 127 Tests for L{ThreadedNameResolver}. 128 """ 129 def test_interface(self): 130 """ 131 L{ThreadedNameResolver} implements L{INameResolver}. 132 """ 133 self.assertTrue(verifyClass(INameResolver, ThreadedNameResolver)) 134 135 136 def test_success(self): 137 """ 138 If the underlying C{getaddrinfo} library call completes 139 successfully and returns results, the L{Deferred} returned by 140 L{ThreadedNameResolver.getAddressInformation} fires with a 141 list of L{AddressInformation} instances representing those 142 results. 143 """ 144 145 56 146 class ThreadedResolverTests(TestCase): 57 147 """ 58 148 Tests for L{ThreadedResolver}. -
twisted/internet/test/test_core.py
=== modified file 'twisted/internet/test/test_core.py'
12 12 import signal 13 13 import time 14 14 import inspect 15 15 import socket 16 17 from zope.interface import implementer 18 19 from twisted.internet.interfaces import INameResolver 16 20 from twisted.internet.abstract import FileDescriptor 17 21 from twisted.internet.error import ReactorAlreadyRunning, ReactorNotRestartable 18 from twisted.internet.defer import Deferred 22 from twisted.internet.defer import Deferred, succeed 23 from twisted.internet.base import AddressInformation 19 24 from twisted.internet.test.reactormixins import ReactorBuilder 20 25 21 26 27 22 28 class ObjectModelIntegrationMixin(object): 23 29 """ 24 30 Helpers for tests about the object model of reactor-related objects. … … 41 47 42 48 43 49 50 @implementer(INameResolver) 51 class MemoryNameResolver(object): 52 def __init__(self, names): 53 self._names = names 54 55 def getAddressInformation(self, name, service, family=None, type=None, 56 protocol=None, flags=None): 57 return succeed([ 58 address 59 for address 60 in self._names[name, service] 61 if family is None or family == address.family 62 and type is None or type == address.type 63 and protocol is None or protocol == address.protocol]) 64 65 66 44 67 class ObjectModelIntegrationTest(ReactorBuilder, ObjectModelIntegrationMixin): 45 68 """ 46 69 Test details of object model integration against all reactors. … … 328 351 self.assertEqual(events, ['tested']) 329 352 330 353 354 def test_resolve(self): 355 """ 356 C{reactor.resolve(name)} calls the C{getAddressInformation} 357 method of the installed resolver and returns a L{Deferred} 358 which fires with the first C{AF_INET} family element from the 359 result of C{getAddressInformation}. 360 """ 361 resolver = MemoryNameResolver({ 362 ('example.com', 0): [ 363 AddressInformation( 364 socket.AF_INET6, 365 socket.SOCK_STREAM, 366 socket.IPPROTO_TCP, 367 "", 368 ("::1", 0)), 369 AddressInformation( 370 socket.AF_INET, 371 socket.SOCK_STREAM, 372 socket.IPPROTO_TCP, 373 "", 374 ("127.0.0.1", 22))]}) 375 376 reactor = self.buildReactor() 377 reactor.installResolver(resolver) 378 d = reactor.resolve("example.com") 379 d.addCallback(self.assertEquals, "127.0.0.1") 380 return d 381 331 382 332 383 globals().update(SystemEventTestsBuilder.makeTestCaseClasses()) 333 384 globals().update(ObjectModelIntegrationTest.makeTestCaseClasses()) -
twisted/internet/test/test_tcp.py
=== modified file 'twisted/internet/test/test_tcp.py'
25 25 ConnectionLost, UserError, ConnectionRefusedError, ConnectionDone, 26 26 ConnectionAborted) 27 27 from twisted.internet.interfaces import ( 28 ILoggingContext, IConnector, IReactorFDSet, IReactorSocket, IReactorTCP )28 ILoggingContext, IConnector, IReactorFDSet, IReactorSocket, IReactorTCP, IResolverSimple) 29 29 from twisted.internet.address import IPv4Address, IPv6Address 30 30 from twisted.internet.defer import ( 31 Deferred, DeferredList, maybeDeferred, gatherResults )31 Deferred, DeferredList, maybeDeferred, gatherResults, succeed, fail) 32 32 from twisted.internet.endpoints import TCP4ServerEndpoint, TCP4ClientEndpoint 33 from twisted.internet.error import DNSLookupError 33 34 from twisted.internet.protocol import ServerFactory, ClientFactory, Protocol 34 35 from twisted.internet.interfaces import ( 35 36 IPushProducer, IPullProducer, IHalfCloseableProtocol) … … 108 109 client.connect(address) 109 110 110 111 112 class Stop(ClientFactory): 113 """ 114 A client factory which stops a reactor when a connection attempt fails. 115 """ 116 def __init__(self, reactor): 117 self.reactor = reactor 118 119 120 def clientConnectionFailed(self, connector, reason): 121 self.reactor.stop() 122 123 124 @implementer(IResolverSimple) 125 class FakeResolver: 126 """ 127 A resolver implementation based on a C{dict} mapping names to addresses. 128 """ 129 130 def __init__(self, names): 131 self.names = names 132 133 134 def getHostByName(self, name, timeout=None): 135 try: 136 return succeed(self.names[name]) 137 except KeyError: 138 return fail(DNSLookupError("FakeResolver couldn't find " + name)) 139 140 141 142 class TCPClientTestsBuilder(ReactorBuilder): 143 """ 144 Builder defining tests relating to L{IReactorTCP.connectTCP}. 145 """ 146 def _freePort(self, interface='127.0.0.1'): 147 probe = socket.socket() 148 try: 149 probe.bind((interface, 0)) 150 return probe.getsockname() 151 finally: 152 probe.close() 153 154 def test_clientConnectionFailedStopsReactor(self): 155 """ 156 The reactor can be stopped by a client factory's 157 C{clientConnectionFailed} method. 158 """ 159 host, port = self._freePort() 160 reactor = self.buildReactor() 161 reactor.connectTCP(host, port, Stop(reactor)) 162 111 163 112 164 class FakeSocket(object): 113 165 """
