r46926 - Merge ipv6-client-port-overflow-7798.
adiroiban-TA+aISz0psMTMxyoc4vAAJOcrHinNvQL0E9HWUfgJXw@public.gmane.org
| Newsgroups | gmane.comp.python.twisted.commits |
|---|---|
| Message-ID | <[email protected]> |
Author: adiroiban
Date: Sat Mar 5 17:00:14 2016
New Revision: 46926
Added:
branches/ipv6-client-port-overflow-7798-2/twisted/topfiles/7798.bugfix
Modified:
branches/ipv6-client-port-overflow-7798-2/twisted/internet/tcp.py
branches/ipv6-client-port-overflow-7798-2/twisted/internet/test/test_tcp.py
Log:
Merge ipv6-client-port-overflow-7798.
Modified: branches/ipv6-client-port-overflow-7798-2/twisted/internet/tcp.py
==============================================================================
--- branches/ipv6-client-port-overflow-7798-2/twisted/internet/tcp.py (original)
+++ branches/ipv6-client-port-overflow-7798-2/twisted/internet/tcp.py Sat Mar 5 17:00:14 2016
@@ -82,7 +82,7 @@
from errno import errorcode
# Twisted Imports
-from twisted.internet import base, address, fdesc
+from twisted.internet import address, base, defer, fdesc
from twisted.internet.task import deferLater
from twisted.python import log, failure, reflect
from twisted.python.util import untilConcludes
@@ -414,9 +414,11 @@
if self._requiresResolution:
d = self.reactor.resolve(self.addr[0])
d.addCallback(lambda n: (n,) + self.addr[1:])
- d.addCallbacks(self._setRealAddress, self.failIfNotConnected)
else:
- self._setRealAddress(self.addr)
+ d = defer.succeed(self.addr)
+
+ d.addCallback(self._setRealAddress)
+ d.addErrback(self.failIfNotConnected)
def _setRealAddress(self, address):
@@ -640,7 +642,19 @@
@raise socket.gaierror: if either the IP or port is not numeric as it
should be.
"""
- return socket.getaddrinfo(ip, port, 0, 0, 0, _NUMERIC_ONLY)[0][4]
+ usedPort = port
+ if isinstance(port, int):
+ # On OS X `getaddrinfo` will reject invalid port number while other
+ # systems accept it as long as it is an int.
+ # We pass a valid port which is later overwritten.
+ usedPort = 1
+
+ result = socket.getaddrinfo(ip, usedPort, 0, 0, 0, _NUMERIC_ONLY)[0][4]
+ # On non OS X `getaddrinfo` will also `resolve` invalid port numbers into a
+ # valid one. Example 123456 is resolved as 57920, but we want to preserve the
+ # initial port number.
+ result = (result[0], port, result[2], result[3])
+ return result
Modified: branches/ipv6-client-port-overflow-7798-2/twisted/internet/test/test_tcp.py
==============================================================================
--- branches/ipv6-client-port-overflow-7798-2/twisted/internet/test/test_tcp.py (original)
+++ branches/ipv6-client-port-overflow-7798-2/twisted/internet/test/test_tcp.py Sat Mar 5 17:00:14 2016
@@ -11,7 +11,9 @@
__metaclass__ = type
import errno
+import os
import socket
+import sys
from functools import wraps
@@ -26,7 +28,7 @@
from twisted.trial.unittest import SkipTest, TestCase
from twisted.internet.error import (
ConnectionLost, UserError, ConnectionRefusedError, ConnectionDone,
- ConnectionAborted, DNSLookupError, NoProtocol)
+ ConnectionAborted, DNSLookupError, NoProtocol, ServiceNameUnknownError)
from twisted.internet.test.connectionmixins import (
LogObserverMixin, ConnectionTestsMixin, StreamClientTestsMixin,
findFreePort, ConnectableProtocol, EndpointCreator,
@@ -40,7 +42,8 @@
from twisted.internet.defer import (
Deferred, DeferredList, maybeDeferred, gatherResults, succeed, fail)
from twisted.internet.endpoints import TCP4ServerEndpoint, TCP4ClientEndpoint
-from twisted.internet.protocol import ServerFactory, ClientFactory, Protocol
+from twisted.internet.protocol import (
+ ClientFactory, Factory, Protocol, ServerFactory)
from twisted.internet.interfaces import (
IPushProducer, IPullProducer, IHalfCloseableProtocol)
from twisted.internet.tcp import Connection, Server, _resolveIPv6
@@ -68,6 +71,12 @@
if platform.isWindows():
from twisted.internet.test import _win32ifaces
getLinkLocalIPv6Addresses = _win32ifaces.win32GetLinkLocalIPv6Addresses
+
+ try:
+ from twisted.internet.iocpreactor.reactor import IOCPReactor
+ except ImportError:
+ IOCPReactor = None
+
else:
try:
from twisted.internet.test import _posixifaces
@@ -76,6 +85,9 @@
else:
getLinkLocalIPv6Addresses = _posixifaces.posixGetLinkLocalIPv6Addresses
+ # Outside of Windows we don't have an IOCP reactor.
+ IOCPReactor = None
+
def getLinkLocalIPv6Address():
@@ -700,6 +712,136 @@
self.assertEqual(BrokenContextFactory.message, str(results[0]))
+ def test_connectPortUnderflow(self):
+ """
+ When trying to connect using a port outside of the 1-65535 range the
+ error is raised as an errback.
+ """
+ def test(message):
+ self.assertConnectPortError(-1, message)
+
+ if sys.version_info[:2] == (2, 6):
+ # On Python 2.6 we get this generic error.
+ test('Connection refused')
+ elif platform.isWindows():
+ if self.reactorFactory is IOCPReactor:
+ # Windows IOCP reactor.
+ test("can't convert negative value to unsigned short")
+ else:
+ # Windows select reactor.
+ test('getsockaddrarg: port must be 0-65535.')
+ else:
+ test('getsockaddrarg: port must be 0-65535.')
+
+
+ def test_connectPortOverflow(self):
+ """
+ When trying to connect using a port outside of the 1-65535 range the
+ error is raised as an errback.
+ """
+ def test(message):
+ self.assertConnectPortError(65536, message)
+
+ if sys.version_info[:2] == (2, 6):
+ # On Python 2.6 we get generic errors.
+ if platform.isMacOSX():
+ test("Can't assign requested address")
+ else:
+ test('Connection refused')
+ elif platform.isWindows():
+ if self.reactorFactory is IOCPReactor:
+ # Windows IOCP reactor.
+ test('value too large to convert to unsigned short')
+ else:
+ # Windows select reactor.
+ test('getsockaddrarg: port must be 0-65535.')
+ else:
+ test('getsockaddrarg: port must be 0-65535.')
+
+
+ def test_connectPortZero(self):
+ """
+ An error is raised as errback when trying to listed on the
+ reserved port C{0}.
+ """
+ def test(message):
+ self.assertConnectPortError(0, message)
+
+ if platform.isMacOSX():
+ test("Can't assign requested address")
+ elif platform.isWindows():
+ if self.reactorFactory is IOCPReactor:
+ # Windows IOCP reactor.
+ test('Unknown error')
+ else:
+ # Windows select reactor.
+ test('The requested address is not valid in its context.')
+ else:
+ if os.getenv('LANG', None) == 'fr_FR.UTF-8':
+ # Coverage builder is executed with French locale.
+ test('Connexion refus\xc3\xa9e')
+ else:
+ test('Connection refused')
+
+
+ def test_connectPortNotNumeric(self):
+ """
+ When trying to connect with an endpoint using a port which can not be
+ resolved to a service an errback is raised.
+ """
+ self.assertConnectPortError(
+ 'invalid', "service/proto not found ('invalid')")
+
+
+ # This is used to count the number of calls in a single test run.
+ # It is defined as a class member but used as an instance member.
+ assertConnectPortErrorCalls = 0
+
+
+ def assertConnectPortError(self, port, message):
+ """
+ Check that trying to connect to C{port} will raise an errback.
+
+ @param port: Port number for which to try a client connection.
+ @type port: C{int}
+
+ @param message: String representation of the expected error.
+ @type message: C{str}
+
+ @raise AssertionError: when called multiple time from the same test.
+ """
+ def resetCallCount():
+ self.assertConnectPortErrorCalls = 0
+
+ if self.assertConnectPortErrorCalls != 0:
+ raise AssertionError(
+ 'This assertion can only be called once per test.')
+ else:
+ self.assertConnectPortErrorCalls = 1
+ self.addCleanup(resetCallCount)
+
+ reactor = self.buildReactor()
+ results = []
+
+ # Connect with invalid port number.
+ fakeServerAddress = self.addressClass("TCP", self.interface, port)
+ endpoint = self.endpoints.client(reactor, fakeServerAddress)
+ connectDeferred = endpoint.connect(Factory.forProtocol(Protocol))
+
+ def whenRun():
+ """
+ Accumulate errors and stop reactor.
+ """
+ connectDeferred.addErrback(lambda failure: results.append(failure))
+ connectDeferred.addBoth(lambda ign: reactor.stop())
+ needsRunningReactor(reactor, whenRun)
+
+ self.runReactor(reactor)
+
+ errors = [failure.value.args[0] for failure in results]
+ self.assertEqual([message], errors)
+
+
class TCP4ClientTestsBuilder(TCPClientTestsBase):
"""
@@ -853,6 +995,25 @@
self.assertEqual(protocolMadeAndClosed, [(1, 1)])
+ def test_invalidServiceName(self):
+ """
+ When connection is done with an invalid service name as port
+ L{ServiceNameUnknownError} is raised.
+ """
+ reactor = self.buildReactor()
+
+ error = self.assertRaises(
+ ServiceNameUnknownError,
+ reactor.connectTCP,
+ self.interface, 'invalid-port', MyClientFactory(),
+ )
+
+ self.assertEqual(
+ "Service name given as port is unknown: "
+ "service/proto not found ('invalid-port').",
+ str(error))
+
+
class TCP4ConnectorTestsBuilder(TCPConnectorTestsBuilder):
interface = '127.0.0.1'
@@ -2519,3 +2680,12 @@
# but, luckily, IP presentation format and what it means to be a port
# number are a little better specified.
self.assertEqual(result[:2], ("::1", 2))
+
+
+ def test_resolveIPv6OverflowPort(self):
+ """
+ L{_resolveIPv6} preserve the requested port number, event when it is
+ not valid.
+ """
+ result = _resolveIPv6("::1", 123456)
+ self.assertEqual(result[:2], ("::1", 123456))