diff --git a/plugins/UiConfig/media/js/ConfigStorage.coffee b/plugins/UiConfig/media/js/ConfigStorage.coffee index cef36625..c75fa748 100644 --- a/plugins/UiConfig/media/js/ConfigStorage.coffee +++ b/plugins/UiConfig/media/js/ConfigStorage.coffee @@ -51,6 +51,7 @@ class ConfigStorage extends Class options: [ {title: "IPv4", value: "ipv4"} {title: "IPv6", value: "ipv6"} + {title: "Dual (IPv4 & IPv6)", value: "dual"} ] description: "Accept incoming peers using IPv4 or IPv6 address. (default: IPv4)" diff --git a/plugins/UiConfig/media/js/all.js b/plugins/UiConfig/media/js/all.js index 53f4435a..b3dbe875 100644 --- a/plugins/UiConfig/media/js/all.js +++ b/plugins/UiConfig/media/js/all.js @@ -1367,7 +1367,7 @@ value: "ipv6" } ], - description: "Accept other peers using IPv4 or IPv6 address. (default: IPv4)" + description: "Accept incoming peers using IPv4 or IPv6 address. (default: IPv4)" }); section.items.push({ key: "fileserver_port", diff --git a/src/Config.py b/src/Config.py index ce53c664..e4dfc548 100644 --- a/src/Config.py +++ b/src/Config.py @@ -237,7 +237,7 @@ class Config(object): self.parser.add_argument('--fileserver_ip', help='FileServer bind address', default="*", metavar='ip') self.parser.add_argument('--fileserver_port', help='FileServer bind port (0: randomize)', default=0, type=int, metavar='port') self.parser.add_argument('--fileserver_port_range', help='FileServer randomization range', default="10000-40000", metavar='port') - self.parser.add_argument('--fileserver_ip_type', help='FileServer ip type', default="ipv4", choices=["ipv4", "ipv6"]) + self.parser.add_argument('--fileserver_ip_type', help='FileServer ip type', default="dual", choices=["ipv4", "ipv6", "dual"]) self.parser.add_argument('--ip_local', help='My local ips', default=ip_local, type=int, metavar='ip', nargs='*') self.parser.add_argument('--disable_udp', help='Disable UDP connections', action='store_true') diff --git a/src/Connection/ConnectionServer.py b/src/Connection/ConnectionServer.py index 60752029..c7686294 100644 --- a/src/Connection/ConnectionServer.py +++ b/src/Connection/ConnectionServer.py @@ -32,7 +32,7 @@ class ConnectionServer(object): self.port = port self.last_connection_id = 1 # Connection id incrementer self.log = logging.getLogger("ConnServer") - self.port_opened = None + self.port_opened = {} self.peer_blacklist = SiteManager.peer_blacklist self.tor_manager = TorManager(self.ip, self.port) @@ -42,11 +42,9 @@ class ConnectionServer(object): self.broken_ssl_ips = {} # Peerids of broken ssl connections self.ips = {} # Connection by ip self.has_internet = True # Internet outage detection - self.supported_ip_types = ["ipv4"] # Outgoing ip_type support - if self.isIpv6Supported(): - self.supported_ip_types.append("ipv6") self.stream_server = None + self.stream_server_proxy = None self.running = False self.stat_recv = defaultdict(lambda: defaultdict(int)) @@ -61,6 +59,7 @@ class ConnectionServer(object): self.had_external_incoming = False self.timecorrection = 0.0 + self.pool = Pool(500) # do not accept more than 500 connections # Bittorrent style peerid self.peer_id = "-UT3530-%s" % CryptHash.random(12, "base64") @@ -92,36 +91,15 @@ class ConnectionServer(object): CryptConnection.manager.crypt_supported, self.supported_ip_types )) try: - self.pool = Pool(500) # do not accept more than 500 connections - if helper.getIpType(self.ip) == "ipv6": - sock_address = (self.ip, self.port, 0, 0) - else: - sock_address = (self.ip, self.port) - self.stream_server = StreamServer( - sock_address, self.handleIncomingConnection, spawn=self.pool, backlog=100 + (self.ip, self.port), self.handleIncomingConnection, spawn=self.pool, backlog=100 ) except Exception, err: - self.log.info("StreamServer bind error: %s" % err) - - def isIpv6Supported(self): - if helper.getIpType(self.ip) == "ipv6": - return True - - # Test if we can connect to ipv6 address - ipv6_testip = "2001:19f0:6c01:e76:5400:1ff:fed6:3eca" - try: - sock = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) - sock.connect((ipv6_testip, 80)) - local_ipv6 = sock.getsockname()[0] - if local_ipv6 == "::1": - return False - else: - return True - except Exception as err: - return False + self.log.info("StreamServer create error: %s" % Debug.formatException(err)) def listen(self): + if self.stream_server_proxy: + gevent.spawn(self.listenProxy) try: self.stream_server.serve_forever() except Exception, err: diff --git a/src/File/FileServer.py b/src/File/FileServer.py index 264a6a67..8de4a298 100644 --- a/src/File/FileServer.py +++ b/src/File/FileServer.py @@ -1,9 +1,11 @@ import logging import time import random +import socket import gevent import gevent.pool +from gevent.server import StreamServer import util from util import helper @@ -13,6 +15,7 @@ from Peer import PeerPortchecker from Site import SiteManager from Connection import ConnectionServer from Plugin import PluginManager +from Debug import Debug @PluginManager.acceptPlugins @@ -23,7 +26,12 @@ class FileServer(ConnectionServer): self.portchecker = PeerPortchecker.PeerPortchecker(self) self.log = logging.getLogger("FileServer") self.ip_type = ip_type - if ip_type == "ipv6": + + self.supported_ip_types = ["ipv4"] # Outgoing ip_type support + if helper.getIpType(ip) == "ipv6" or self.isIpv6Supported(): + self.supported_ip_types.append("ipv6") + + if ip_type == "ipv6" or (ip_type == "dual" and "ipv6" in self.supported_ip_types): ip = ip.replace("*", "::") else: ip = ip.replace("*", "0.0.0.0") @@ -42,14 +50,22 @@ class FileServer(ConnectionServer): ConnectionServer.__init__(self, ip, port, self.handleRequest) + if ip_type == "dual" and ip == "::": + # Also bind to ipv4 addres in dual mode + try: + self.log.debug("Binding proxy to %s:%s" % ("::", self.port)) + self.stream_server_proxy = StreamServer( + ("0.0.0.0", self.port), self.handleIncomingConnection, spawn=self.pool, backlog=100 + ) + except Exception, err: + self.log.info("StreamServer proxy create error: %s" % Debug.formatException(err)) self.port_opened = {} if config.ip_external: # Ip external defined in arguments self.port_opened[helper.getIpType(config.ip_external)] = True SiteManager.peer_blacklist.append((config.ip_external, self.port)) # Add myself to peer blacklist - else: - self.port_opened = None # Check it later + self.sites = {} self.last_request = time.time() self.files_parsing = {} @@ -78,6 +94,35 @@ class FileServer(ConnectionServer): time.sleep(0.1) return False + def isIpv6Supported(self): + # Test if we can connect to ipv6 address + ipv6_testip = "2001:19f0:6c01:e76:5400:1ff:fed6:3eca" + try: + sock = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) + sock.connect((ipv6_testip, 80)) + local_ipv6 = sock.getsockname()[0] + if local_ipv6 == "::1": + self.log.debug("IPv6 not supported, no local IPv6 address") + return False + else: + self.log.debug("IPv6 supported on IP %s" % local_ipv6) + return True + except socket.error as err: + self.log.error("IPv6 not supported: %s" % err) + return False + except Exception as err: + self.log.error("IPv6 check error: %s" % err) + return False + + def listenProxy(self): + try: + self.stream_server_proxy.serve_forever() + except Exception, err: + if err.errno == 98: # Address already in use error + self.log.debug("StreamServer proxy listen error: %s" % err) + else: + self.log.info("StreamServer proxy listen error: %s" % err) + # Handle request to fileserver def handleRequest(self, connection, message): if config.verbose: