Ticket #3014: twisted-ipv6.patch
| File twisted-ipv6.patch, 15.7 KB (added by philmayers, 3 years ago) |
|---|
-
twisted/internet/abstract.py
diff --git a/twisted/internet/abstract.py b/twisted/internet/abstract.py index 64f77b0..c15158f 100644
a b 16 16 from twisted.persisted import styles 17 17 from twisted.internet import interfaces, main 18 18 19 import socket 19 20 20 21 class FileDescriptor(object): 21 22 """An object which can be operated on by select(). … … 380 381 return True 381 382 return False 382 383 384 def isIPv6Address(addr): 385 if '%' in addr: 386 addr, scope = addr.split('%', 1) 387 try: 388 s = socket.inet_pton(socket.AF_INET6, addr) 389 except Exception, ex: 390 print ex 391 return False 392 else: 393 return True 394 383 395 384 396 __all__ = ["FileDescriptor"] -
twisted/internet/tcp.py
diff --git a/twisted/internet/tcp.py b/twisted/internet/tcp.py index 23d56b8..e141c3d 100644
a b 602 602 fdesc._setCloseOnExec(s.fileno()) 603 603 return s 604 604 605 def resolveAddress(self):606 if abstract.isIPAddress(self.addr[0]):607 self._setRealAddress(self.addr[0])608 else:609 d = self.reactor.resolve(self.addr[0])610 d.addCallbacks(self._setRealAddress, self.failIfNotConnected)611 605 612 def _setRealAddress(self, address):613 self.realAddress = (address, self.addr[1])614 self.doConnect()615 606 616 607 def doConnect(self): 617 608 """I connect the socket. … … 684 675 def __init__(self, host, port, bindAddress, connector, reactor=None): 685 676 # BaseClient.__init__ is invoked later 686 677 self.connector = connector 678 self.bindAddress = bindAddress 679 self.reactor = reactor 687 680 self.addr = (host, port) 688 681 689 whenDone = self.resolveAddress690 682 err = None 691 683 skt = None 692 684 685 self._start(host, port, True) 686 687 def _start(self, host, port, resolve=False): 688 689 if abstract.isIPAddress(host): 690 self.addressFamily = socket.AF_INET 691 self.realAddress = (host, port) 692 693 elif abstract.isIPv6Address(host): 694 # we need to pass this through getaddrinfo to handle 695 # scope ID, but we don't want it to block, hence 696 # the flags.. 697 self.addressFamily = socket.AF_INET6 698 replies = socket.getaddrinfo(host, port, 699 self.addressFamily, 700 self.socketType, 701 0, 702 socket.AI_NUMERICHOST # numeric hosts - no DNS! 703 ) 704 if not replies: 705 err = error.DNSResolutionError('couldnt resolve v6 scope '+host) 706 self.failIfNotConnected(err) 707 return 708 # FIXME: what to do about multiple replies? 709 self.realAddress = replies[0][4] 710 711 elif resolve: 712 # resolve is only set on 1st call from "init"; 2nd 713 # call is the answer, we set ourselves as callback 714 # FIXME: currently this only does IPv4 lookups 715 d = self.reactor.resolve(host) 716 d.addCallback(self._start, port).addErrback(self.failIfNotConnected) 717 return 718 719 else: 720 # wtf? 721 err = error.DNSResolutionError('couldnt resolve '+self.addr[0]) 722 self.failIfNotConnected(err) 723 return 724 725 # create our socket 693 726 try: 694 727 skt = self.createInternetSocket() 695 728 except socket.error, se: 696 729 err = error.ConnectBindError(se[0], se[1]) 697 whenDone = None 698 if whenDone and bindAddress is not None: 730 self._finishInit(None, skt, err, self.reactor) 731 return 732 733 if self.bindAddress is not None: 699 734 try: 700 skt.bind( bindAddress)735 skt.bind(self.bindAddress) 701 736 except socket.error, se: 702 737 err = error.ConnectBindError(se[0], se[1]) 703 whenDone = None 704 self._finishInit(whenDone, skt, err, reactor) 738 self._finishInit(None, skt, err, self.reactor) 739 return 740 741 self._finishInit(self.doConnect, skt, None, self.reactor) 705 742 706 743 def getHost(self): 707 744 """Returns an IPv4Address. 708 745 709 746 This indicates the address from which I am connecting. 710 747 """ 711 return address.IPv4Address('TCP', *(self.socket.getsockname() + ('INET',))) 748 addr = self.socket.getsockname() 749 return address.IPv4Address('TCP', addr[0], addr[1], 'INET') 712 750 713 751 def getPeer(self): 714 752 """Returns an IPv4Address. 715 753 716 754 This indicates the address that I am connected to. 717 755 """ 718 return address.IPv4Address('TCP', *(self.realAddress + ('INET',)))756 return address.IPv4Address('TCP', self.realAddress[0], self.realAddress[1], 'INET') 719 757 720 758 def __repr__(self): 721 759 s = '<%s to %s at %x>' % (self.__class__, self.addr, unsignedID(self)) … … 770 808 771 809 This indicates the server's address. 772 810 """ 773 return address.IPv4Address('TCP', *(self.socket.getsockname() + ('INET',))) 811 addr = self.socket.getsockname() 812 return address.IPv4Address('TCP', addr[0], addr[1], 'INET') 774 813 775 814 def getPeer(self): 776 815 """Returns an IPv4Address. 777 816 778 817 This indicates the client's address. 779 818 """ 780 return address.IPv4Address('TCP', *(self.client + ('INET',)))819 return address.IPv4Address('TCP', self.client[0], self.client[1], 'INET') 781 820 782 821 class Port(base.BasePort, _SocketCloser): 783 822 """ … … 827 866 self.factory = factory 828 867 self.backlog = backlog 829 868 self.interface = interface 869 if interface and abstract.isIPv6Address(interface): 870 self.addressFamily = socket.AF_INET6 830 871 831 872 def __repr__(self): 832 873 if self._realPortNumber is not None: … … 872 913 self.startReading() 873 914 874 915 875 def _buildAddr(self, (host, port)):876 return address._ServerFactoryIPv4Address('TCP', host, port)916 def _buildAddr(self, addr): 917 return address._ServerFactoryIPv4Address('TCP', addr[0], addr[1]) 877 918 878 919 879 920 def doRead(self): … … 1005 1046 1006 1047 This indicates the server's address. 1007 1048 """ 1008 return address.IPv4Address('TCP', *(self.socket.getsockname() + ('INET',))) 1049 addr = self.socket.getsockname() 1050 return address.IPv4Address('TCP', addr[0], addr[1], 'INET') 1009 1051 1010 1052 class Connector(base.BaseConnector): 1011 1053 def __init__(self, host, port, factory, timeout, bindAddress, reactor=None): -
(a) /dev/null vs. (b) b/twisted/internet/test/test_ipv6.py
diff --git a/twisted/internet/test/test_ipv6.py b/twisted/internet/test/test_ipv6.py new file mode 100644 index 0000000..b1aa1d5
a b 1 # Copyright (c) 2008-2010 Twisted Matrix Laboratories. 2 # See LICENSE for details. 3 4 """ 5 Tests for implementations of IPv6 6 """ 7 8 __metaclass__ = type 9 10 import socket 11 12 from zope.interface import implements 13 from zope.interface.verify import verifyObject 14 15 from twisted.internet.test.reactormixins import ReactorBuilder 16 from twisted.internet.error import DNSLookupError 17 from twisted.internet.interfaces import IResolverSimple, IConnector, IListeningPort 18 from twisted.internet.address import IPv4Address 19 from twisted.internet.defer import succeed, fail, maybeDeferred 20 from twisted.internet.protocol import ServerFactory, ClientFactory, Protocol, DatagramProtocol 21 from twisted.python import log 22 23 class Stop(ClientFactory): 24 """ 25 A client factory which stops a reactor when a connection attempt fails. 26 """ 27 def __init__(self, reactor): 28 self.reactor = reactor 29 30 31 def clientConnectionFailed(self, connector, reason): 32 self.reactor.stop() 33 34 class v6TestsBuilder(ReactorBuilder): 35 def _freePort(self, interface='::1'): 36 probe = socket.socket(socket.AF_INET6) 37 try: 38 probe.bind((interface, 0)) 39 ip, port, flow, scope = probe.getsockname() 40 return (ip, port) 41 finally: 42 probe.close() 43 44 45 def test_clientConnectionFailedStopsReactor(self): 46 host, port = self._freePort() 47 48 reactor = self.buildReactor() 49 50 reactor.connectTCP('::1', port, Stop(reactor)) 51 reactor.run() 52 53 54 def test_addresses(self): 55 """ 56 A client's transport's C{getHost} and C{getPeer} return L{IPv4Address} 57 instances which give the dotted-quad string form of the local and 58 remote endpoints of the connection respectively. 59 """ 60 host, port = self._freePort() 61 reactor = self.buildReactor() 62 63 serverFactory = ServerFactory() 64 serverFactory.protocol = Protocol 65 server = reactor.listenTCP(0, serverFactory, interface='::1') 66 serverAddress = server.getHost() 67 68 addresses = {'host': None, 'peer': None} 69 class CheckAddress(Protocol): 70 def makeConnection(self, transport): 71 addresses['host'] = transport.getHost() 72 addresses['peer'] = transport.getPeer() 73 reactor.stop() 74 75 clientFactory = Stop(reactor) 76 clientFactory.protocol = CheckAddress 77 reactor.connectTCP('::1', server.getHost().port, clientFactory, bindAddress=('::1', port)) 78 79 reactor.run() # self.runReactor(reactor) 80 81 self.assertEqual( 82 addresses['host'], 83 IPv4Address('TCP', '::1', port)) 84 self.assertEqual( 85 addresses['peer'], 86 IPv4Address('TCP', '::1', serverAddress.port)) 87 88 def test_mixed4to6(self): 89 reactor = self.buildReactor() 90 91 addresses = {'server': None, 'client': None, 'cserver': None} 92 93 class SvProto(Protocol): 94 def connectionMade(self): 95 addresses['client'] = self.transport.getPeer() 96 addresses['server'] = self.transport.getHost() 97 98 serverFactory = ServerFactory() 99 serverFactory.protocol = SvProto 100 host, sport = self._freePort() 101 server = reactor.listenTCP(sport, serverFactory, interface='::') 102 103 class ClProto(Protocol): 104 def makeConnection(self, transport): 105 addresses['cserver'] = transport.getPeer() 106 reactor.stop() 107 108 clientFactory = Stop(reactor) 109 clientFactory.protocol = ClProto 110 111 host, cport = self._freePort() 112 server = reactor.connectTCP('127.0.0.1', sport, clientFactory, bindAddress=('', cport)) 113 114 reactor.run() 115 116 self.assertEqual( 117 addresses['client'], 118 IPv4Address('TCP', '::ffff:127.0.0.1', cport)) 119 self.assertEqual( 120 addresses['cserver'], 121 IPv4Address('TCP', '127.0.0.1', sport)) 122 123 124 125 class UDP6ServerTestsBuilder(ReactorBuilder): 126 def _freePort(self, interface='::1'): 127 probe = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) 128 try: 129 probe.bind((interface, 0)) 130 ip, port, flow, scope = probe.getsockname() 131 return (ip, port) 132 finally: 133 probe.close() 134 135 def test_interface(self): 136 reactor = self.buildReactor() 137 port = reactor.listenUDP(0, DatagramProtocol(), interface='::') 138 self.assertTrue(verifyObject(IListeningPort, port)) 139 140 def test_traffic(self): 141 reactor = self.buildReactor() 142 143 dgs = [] 144 class Accum(DatagramProtocol): 145 def datagramReceived(self, data, addr): 146 dgs.append((addr, data)) 147 self.transport.write('r'+data, addr) 148 149 class Xmit(DatagramProtocol): 150 def perform(self, addr, port): 151 self.transport.write('msg', (addr, port)) 152 def datagramReceived(self, data, addr): 153 dgs.append((addr, data)) 154 reactor.stop() 155 156 ip, lport = self._freePort() 157 p = reactor.listenUDP(lport, Accum(), interface='::1') 158 159 ip, xport = self._freePort() 160 xmit = Xmit() 161 p = reactor.listenUDP(xport, xmit, interface='::1') 162 163 reactor.callLater(0, xmit.perform, '::1', lport) 164 reactor.callLater(10, reactor.stop) 165 reactor.run() 166 167 self.assertEqual( 168 dgs, 169 [(('::1', xport), 'msg'), (('::1', lport), 'rmsg')] 170 ) 171 172 def test_mixed4to6(self): 173 reactor = self.buildReactor() 174 175 dgs = [] 176 class Accum(DatagramProtocol): 177 def datagramReceived(self, data, addr): 178 dgs.append((addr, data)) 179 self.transport.write('r'+data, addr) 180 181 class Xmit(DatagramProtocol): 182 def perform(self, addr, port): 183 self.transport.write('msg', (addr, port)) 184 def datagramReceived(self, data, addr): 185 dgs.append((addr, data)) 186 reactor.stop() 187 188 ip, lport = self._freePort() 189 p = reactor.listenUDP(lport, Accum(), interface='::') 190 191 ip, xport = self._freePort() 192 xmit = Xmit() 193 p = reactor.listenUDP(xport, xmit) 194 195 reactor.callLater(0, xmit.perform, '127.0.0.1', lport) 196 reactor.callLater(10, reactor.stop) 197 reactor.run() 198 199 self.assertEqual( 200 dgs, 201 [(('::ffff:127.0.0.1', xport), 'msg'), (('127.0.0.1', lport), 'rmsg')] 202 ) 203 204 globals().update(v6TestsBuilder.makeTestCaseClasses()) 205 globals().update(UDP6ServerTestsBuilder.makeTestCaseClasses()) -
twisted/internet/udp.py
diff --git a/twisted/internet/udp.py b/twisted/internet/udp.py index 3a21453..c5672c0 100644
a b 62 62 self.interface = interface 63 63 self.setLogStr() 64 64 self._connectedAddr = None 65 if interface and abstract.isIPv6Address(interface): 66 self.addressFamily = socket.AF_INET6 65 67 66 68 def __repr__(self): 67 69 if self._realPortNumber is not None: … … 127 129 else: 128 130 read += len(data) 129 131 try: 132 addr = (addr[0], addr[1]) 130 133 self.protocol.datagramReceived(data, addr) 131 134 except: 132 135 log.err() … … 160 163 raise 161 164 else: 162 165 assert addr != None 163 if not addr[0].replace(".", "").isdigit() and addr[0] != "<broadcast>": 166 if abstract.isIPAddress(addr[0]) or abstract.isIPv6Address(addr[0]): 167 pass 168 elif addr[0] != "<broadcast>": 164 169 warnings.warn("Please only pass IPs to write(), not hostnames", 165 170 DeprecationWarning, stacklevel=2) 166 171 try: … … 188 193 """ 189 194 if self._connectedAddr: 190 195 raise RuntimeError, "already connected, reconnecting is not currently supported (talk to itamar if you want this)" 191 if not abstract.isIPAddress(host) :196 if not abstract.isIPAddress(host) and not abstract.isIPv6Address(host): 192 197 raise ValueError, "please pass only IP addresses, not domain names" 193 198 self._connectedAddr = (host, port) 194 199 self.socket.connect((host, port)) … … 242 247 243 248 This indicates the address from which I am connecting. 244 249 """ 245 return address.IPv4Address('UDP', *(self.socket.getsockname() + ('INET_UDP',)))246 250 addr = self.socket.getsockname() 251 return address.IPv4Address('UDP', addr[0], addr[1], 'INET_UDP') 247 252 248 253 254 # FIXME: implement ipv6 multicast... 249 255 class MulticastMixin: 250 256 """ 251 257 Implement multicast functionality.
