From 0edebda1c7d87ea7ed68c904a92b12c9322f1ece Mon Sep 17 00:00:00 2001 From: bendikro Date: Fri, 29 Apr 2016 22:42:34 +0200 Subject: [PATCH] [WebUI] Log correct http address if listening on IPv6 --- DEPENDS | 2 + deluge/common.py | 90 ++++++++++++++++++++++++++----------- deluge/tests/test_common.py | 14 +++++- deluge/ui/web/server.py | 11 +++-- tox.ini | 1 + 5 files changed, 88 insertions(+), 30 deletions(-) diff --git a/DEPENDS b/DEPENDS index b467900d7..553109564 100644 --- a/DEPENDS +++ b/DEPENDS @@ -11,8 +11,10 @@ * geoip-database (optional) * setproctitle (optional) * pillow (optional) + * py2-ipaddress (optional, required for Windows IPv6) * rencode >= 1.0.2 (optional), python port bundled. + === Gtk UI === * pygtk >= 2.16 * librsvg diff --git a/deluge/common.py b/deluge/common.py index c27fc1dfb..ccb8945b9 100644 --- a/deluge/common.py +++ b/deluge/common.py @@ -721,44 +721,84 @@ def free_space(path): def is_ip(ip): - """ - A simple test to see if 'ip' is valid + """A test to see if 'ip' is a valid IPv4 or IPv6 address. - :param ip: the ip to check - :type ip: string - :returns: True or False - :rtype: bool + Args: + ip (str): The IP to test. - :Example: + Returns: + bool: Whether IP is valid is not. - >>> is_ip('127.0.0.1') - True + Examples: + >>> is_ip("192.0.2.0") + True + >>> is_ip("2001:db8::") + True """ + + return is_ipv4(ip) or is_ipv6(ip) + + +def is_ipv4(ip): + """A test to see if 'ip' is a valid IPv4 address. + + Args: + ip (str): The IP to test. + + Returns: + bool: Whether IP is valid is not. + + Examples: + >>> is_ipv4("192.0.2.0") + True + + """ + import socket - # first we test ipv4 try: if windows_check(): - if socket.inet_aton(ip): - return True + return socket.inet_aton(ip) else: - if socket.inet_pton(socket.AF_INET, ip): - return True - except socket.error: - if not socket.has_ipv6: - return False - # now test ipv6 - try: - if windows_check(): - log.warning('ipv6 check unavailable on windows') - return True - else: - if socket.inet_pton(socket.AF_INET6, ip): - return True + return socket.inet_pton(socket.AF_INET, ip) except socket.error: return False +def is_ipv6(ip): + """A test to see if 'ip' is a valid IPv6 address. + + Args: + ip (str): The IP to test. + + Returns: + bool: Whether IP is valid is not. + + Examples: + >>> is_ipv6("2001:db8::") + True + + """ + + try: + import ipaddress + except ImportError: + import socket + try: + return socket.inet_pton(socket.AF_INET6, ip) + except (socket.error, AttributeError): + if windows_check(): + log.warning('Unable to verify IPv6 Address on Windows.') + return True + else: + try: + return ipaddress.IPv6Address(ip) + except ipaddress.AddressValueError: + pass + + return False + + def decode_string(s, encoding='utf8'): """ Decodes a string and return unicode. If it cannot decode using diff --git a/deluge/tests/test_common.py b/deluge/tests/test_common.py index be0a15ab4..fc6bee8e2 100644 --- a/deluge/tests/test_common.py +++ b/deluge/tests/test_common.py @@ -68,8 +68,18 @@ class CommonTestCase(unittest.TestCase): self.failUnless(get_path_size('non-existant.file') == -1) def test_is_ip(self): - self.failUnless(is_ip('127.0.0.1')) - self.failIf(is_ip('127..0.0')) + self.failUnless(is_ip('192.0.2.0')) + self.failIf(is_ip('192..0.0')) + self.failUnless(is_ip('2001:db8::')) + self.failIf(is_ip('2001:db8:')) + + def test_is_ipv4(self): + self.failUnless(is_ip('192.0.2.0')) + self.failIf(is_ip('192..0.0')) + + def test_is_ipv6(self): + self.failUnless(is_ip('2001:db8::')) + self.failIf(is_ip('2001:db8:')) def test_version_split(self): self.failUnless(VersionSplit('1.2.2') == VersionSplit('1.2.2')) diff --git a/deluge/ui/web/server.py b/deluge/ui/web/server.py index 3f206215d..00c72ebef 100644 --- a/deluge/ui/web/server.py +++ b/deluge/ui/web/server.py @@ -21,6 +21,7 @@ from twisted.internet.ssl import SSL, Certificate, CertificateOptions, KeyPair from twisted.web import http, resource, server, static from deluge import common, component, configmanager +from deluge.common import is_ipv6 from deluge.core.rpcserver import check_ssl_keys from deluge.ui.tracker_icons import TrackerIcons from deluge.ui.util import lang @@ -568,7 +569,7 @@ class DelugeWeb(component.Component): self.base = self.config['base'] if options: - self.interface = options.interface if options.interface else self.interface + self.interface = options.interface if options.interface is not None else self.interface self.port = options.port if options.port else self.port self.base = options.base if options.base else self.base if options.ssl: @@ -635,7 +636,9 @@ class DelugeWeb(component.Component): def start_normal(self): self.socket = reactor.listenTCP(self.port, self.site, interface=self.interface) - log.info('Serving at http://%s:%s%s', self.interface, self.port, self.base) + ip = self.socket.getHost().host + ip = '[%s]' % ip if is_ipv6(ip) else ip + log.info('Serving at http://%s:%s%s', ip, self.port, self.base) def start_ssl(self): check_ssl_keys() @@ -649,7 +652,9 @@ class DelugeWeb(component.Component): options.getContext().set_options(SSL.OP_NO_SSLv2 | SSL.OP_NO_SSLv3) self.socket = reactor.listenSSL(self.port, self.site, options, interface=self.interface) - log.info('Serving at https://%s:%s%s', self.interface, self.port, self.base) + ip = self.socket.getHost().host + ip = '[%s]' % ip if is_ipv6(ip) else ip + log.info('Serving at https://%s:%s%s', ip, self.port, self.base) def stop(self): log.info('Shutting down webserver') diff --git a/tox.ini b/tox.ini index 96517a4f1..8492a4962 100644 --- a/tox.ini +++ b/tox.ini @@ -31,6 +31,7 @@ deps = mock slimit pillow + py2-ipaddress whitelist_externals = py.test commands = {envpython} setup.py test