Example #1
0
 def _create_local_discovery(self):
     assert self.use_local_discovery
     self._creating_local_discovery = True
     try:
         self.local_discovery = LocalDiscovery(self.rawserver, self.port,
                                               self._start_connection)
         self._creating_local_discovery = False
     except:
         self.rawserver.add_task(5, self._create_local_discovery)
 def _create_local_discovery(self):
     self._creating_local_discorvery = True
     try:
         self.local_discovery = LocalDiscovery(self.rawserver, self.port,
                                               self._start_connection)
         self._creating_local_discorvery = False
     except:
         self.rawserver.add_task(5, self._create_local_discovery)
Example #3
0
class SingleportListener(Handler):
    """Manages a server socket common to all torrents.  When a remote
       peer opens a connection to the local peer, the SingleportListener
       maps that peer on to the appropriate torrent's connection manager
       (see SingleportListener.select_torrent).

       See Connector which upcalls to select_torrent after the infohash is 
       received in the opening handshake."""
    def __init__(self, rawserver, nattraverser, log_prefix,
                 use_local_discovery):
        self.rawserver = rawserver
        self.nattraverser = nattraverser
        self.port = 0
        self.ports = {}
        self.port_change_notification = None
        self.torrents = {}
        self.connectors = set()
        self.infohash = None
        self.obfuscated_torrents = {}
        self.local_discovery = None
        self.ld_services = {}
        self.use_local_discovery = use_local_discovery
        self._creating_local_discovery = False
        self.log_prefix = log_prefix
        self.logger = logging.getLogger(self.log_prefix)

    def _close(self, port):
        serversocket = self.ports[port][0]
        if self.nattraverser:
            try:
                self.nattraverser.unregister_port(port, "TCP")
            except:
                # blanket, just incase - we don't want to interrupt things
                self.logger.warning("UPnP deregistration error",
                                    exc_info=sys.exc_info())
        self.rawserver.stop_listening(serversocket)
        serversocket.close()
        if self.local_discovery:
            self.local_discovery.stop()
            self.local_discovery = None

    def _check_close(self, port):
        if not port or self.port == port or len(self.ports[port][1]) > 0:
            return
        self._close(port)
        del self.ports[port]

    def open_port(self, port, config):
        """Starts BitTorrent running as a server on the specified port."""
        if port in self.ports:
            self.port = port
            return
        s = self.rawserver.create_serversocket(port, config['bind'])
        if self.nattraverser:
            try:
                d = self.nattraverser.register_port(port, port, "TCP",
                                                    config['bind'], app_name)

                def change(*a):
                    self.rawserver.external_add_task(0, self._change_port, *a)

                d.addCallback(change)

                def silent(*e):
                    pass

                d.addErrback(silent)
            except:
                # blanket, just incase - we don't want to interrupt things
                self.logger.warning("UPnP registration error",
                                    exc_info=sys.exc_info())
        self.rawserver.start_listening(s, self)
        oldport = self.port
        self.port = port
        self.ports[port] = [s, {}]
        self._check_close(oldport)

        if self.local_discovery:
            self.local_discovery.stop()
        if self.use_local_discovery:
            self._create_local_discovery()

    def _create_local_discovery(self):
        assert self.use_local_discovery
        self._creating_local_discovery = True
        try:
            self.local_discovery = LocalDiscovery(self.rawserver, self.port,
                                                  self._start_connection)
            self._creating_local_discovery = False
        except:
            self.rawserver.add_task(5, self._create_local_discovery)

    def _start_connection(self, addr, infohash):
        infohash = infohash.decode('hex')
        if infohash not in self.torrents:
            return
        connection_manager = self.torrents[infohash]
        # TODO: peer id?
        connection_manager.start_connection(addr, None)

    def _change_port(self, port):
        if self.port == port:
            return
        [serversocket, callbacks] = self.ports[self.port]
        self.ports[port] = [serversocket, callbacks]
        del self.ports[self.port]
        self.port = port
        for callback in callbacks:
            if callback:
                callback(port)

    def get_port(self, callback=None):
        if self.port:
            callbacks = self.ports[self.port][1]
            callbacks.setdefault(callback, 0)
            callbacks[callback] += 1
        return self.port

    def release_port(self, port, callback=None):
        callbacks = self.ports[port][1]
        callbacks[callback] -= 1
        if callbacks[callback] == 0:
            del callbacks[callback]
        self._check_close(port)

    def close_sockets(self):
        for port in self.ports.iterkeys():
            self._close(port)

    def add_torrent(self, infohash, connection_manager):
        if infohash in self.torrents:
            raise BTFailure(
                _("Can't start two separate instances of the same "
                  "torrent"))
        self.torrents[infohash] = connection_manager
        key = sha1('req2' + infohash).digest()
        self.obfuscated_torrents[key] = connection_manager
        if self.local_discovery:
            service = self.local_discovery.announce(
                infohash.encode('hex'), connection_manager.my_id.encode('hex'))
            self.ld_services[infohash] = service

    def remove_torrent(self, infohash):
        del self.torrents[infohash]
        del self.obfuscated_torrents[sha1('req2' + infohash).digest()]
        if infohash in self.ld_services:
            service = self.ld_services.pop(infohash)
            if self.local_discovery:
                self.local_discovery.unannounce(service)

    def connection_made(self, connection):
        """Called when TCP connection has finished opening, but before
           BitTorrent protocol has begun."""
        if ONLY_LOCAL and connection.ip != '127.0.0.1' and not connection.ip.startswith(
                "192.168"):
            return
        if GLOBAL_FILTER and not GLOBAL_FILTER(connection.ip, connection.port,
                                               "in"):
            return
        connector = Connector(self,
                              connection,
                              None,
                              False,
                              log_prefix=self.log_prefix)
        self.connectors.add(connector)

    def select_torrent(self, connector, infohash):
        """Called when infohash has been received allowing us to map
           the connection on to a given Torrent's ConnectionManager."""
        # call-up from Connector.
        if infohash in self.torrents:
            accepted = self.torrents[infohash].singleport_connection(connector)
            if not accepted:
                # the connection manager may refuse the connection, in which
                # case keep the connection in our list until it is dropped
                connector.close()
            else:
                # otherwise remove it
                self.connectors.remove(connector)

    def select_torrent_obfuscated(self, connector, streamid):
        if ONLY_LOCAL and connector.connection.ip != '127.0.0.1':
            return
        if streamid not in self.obfuscated_torrents:
            return
        self.obfuscated_torrents[streamid].singleport_connection(connector)

    def connection_lost(self, connector):
        assert isinstance(connector, Connector)
        self.connectors.remove(connector)

    def remove_addr_from_cache(self, addr):
        # since this was incoming, we don't cache the peer anyway
        pass
Example #4
0
class SingleportListener(Handler):
    """Manages a server socket common to all torrents.  When a remote
       peer opens a connection to the local peer, the SingleportListener
       maps that peer on to the appropriate torrent's connection manager
       (see SingleportListener.select_torrent).

       See Connector which upcalls to select_torrent after the infohash is 
       received in the opening handshake."""
    def __init__(self, rawserver, nattraverser, log_prefix, 
                 use_local_discovery):
        self.rawserver = rawserver
        self.nattraverser = nattraverser
        self.port = 0
        self.ports = {}
        self.port_change_notification = None
        self.torrents = {}
        self.connectors = set()
        self.infohash = None
        self.obfuscated_torrents = {}
        self.local_discovery = None
        self.ld_services = {}
        self.use_local_discovery = use_local_discovery
        self._creating_local_discovery = False
        self.log_prefix = log_prefix
        self.logger = logging.getLogger(self.log_prefix)

    def _close(self, port):        
        serversocket = self.ports[port][0]
        if self.nattraverser:
            try:
                self.nattraverser.unregister_port(port, "TCP")
            except:
                # blanket, just incase - we don't want to interrupt things
                self.logger.warning("UPnP deregistration error",
                                    exc_info=sys.exc_info())
        self.rawserver.stop_listening(serversocket)
        serversocket.close()
        if self.local_discovery:
            self.local_discovery.stop()
            self.local_discovery = None

    def _check_close(self, port):
        if not port or self.port == port or len(self.ports[port][1]) > 0:
            return
        self._close(port)
        del self.ports[port]

    def open_port(self, port, config):
        """Starts BitTorrent running as a server on the specified port."""
        if port in self.ports:
            self.port = port
            return
        s = self.rawserver.create_serversocket(port, config['bind'])
        if self.nattraverser:
            try:
                d = self.nattraverser.register_port(port, port, "TCP", 
                                                    config['bind'],
                                                    app_name)
                def change(*a):
                    self.rawserver.external_add_task(0, self._change_port, *a)
                d.addCallback(change)
                def silent(*e):
                    pass
                d.addErrback(silent)
            except:
                # blanket, just incase - we don't want to interrupt things
                self.logger.warning("UPnP registration error",
                                    exc_info=sys.exc_info())
        self.rawserver.start_listening(s, self)
        oldport = self.port
        self.port = port
        self.ports[port] = [s, {}]        
        self._check_close(oldport)

        if self.local_discovery:
            self.local_discovery.stop()
        if self.use_local_discovery:
            self._create_local_discovery()

    def _create_local_discovery(self):
        assert self.use_local_discovery
        self._creating_local_discovery = True
        try:
            self.local_discovery = LocalDiscovery(self.rawserver, self.port,
                                                  self._start_connection)
            self._creating_local_discovery = False
        except:
            self.rawserver.add_task(5, self._create_local_discovery)

    def _start_connection(self, addr, infohash):
        infohash = infohash.decode('hex')
        if infohash not in self.torrents:
            return
        connection_manager = self.torrents[infohash]
        # TODO: peer id?
        connection_manager.start_connection(addr, None)
        
    def _change_port(self, port):
        if self.port == port:
            return
        [serversocket, callbacks] = self.ports[self.port]
        self.ports[port] = [serversocket, callbacks]
        del self.ports[self.port]
        self.port = port
        for callback in callbacks:
            if callback:
                callback(port)

    def get_port(self, callback = None):
        if self.port:
            callbacks = self.ports[self.port][1]
            callbacks.setdefault(callback, 0)
            callbacks[callback] += 1
        return self.port

    def release_port(self, port, callback = None):
        callbacks = self.ports[port][1]
        callbacks[callback] -= 1
        if callbacks[callback] == 0:
            del callbacks[callback]
        self._check_close(port)

    def close_sockets(self):
        for port in self.ports.iterkeys():
            self._close(port)

    def add_torrent(self, infohash, connection_manager):
        if infohash in self.torrents:
            raise BTFailure(_("Can't start two separate instances of the same "
                              "torrent"))
        self.torrents[infohash] = connection_manager
        key = sha('req2' + infohash).digest()
        self.obfuscated_torrents[key] = connection_manager
        if self.local_discovery:
            service = self.local_discovery.announce(infohash.encode('hex'),
                                                    connection_manager.my_id.encode('hex'))
            self.ld_services[infohash] = service

    def remove_torrent(self, infohash):
        del self.torrents[infohash]
        del self.obfuscated_torrents[sha('req2' + infohash).digest()]
        if infohash in self.ld_services:
            service = self.ld_services.pop(infohash)
            if self.local_discovery:
                self.local_discovery.unannounce(service)

    def connection_made(self, connection):
        """Called when TCP connection has finished opening, but before
           BitTorrent protocol has begun."""
        if ONLY_LOCAL and connection.ip != '127.0.0.1' and not connection.ip.startswith("192.168") :
            return
        if GLOBAL_FILTER and not GLOBAL_FILTER(connection.ip, connection.port, "in"):
            return
        connector = Connector(self, connection, None, False,
                              log_prefix=self.log_prefix)
        self.connectors.add(connector)

    def select_torrent(self, connector, infohash):
        """Called when infohash has been received allowing us to map
           the connection on to a given Torrent's ConnectionManager."""
        # call-up from Connector.
        if infohash in self.torrents:
            accepted = self.torrents[infohash].singleport_connection(connector)
            if not accepted:
                # the connection manager may refuse the connection, in which
                # case keep the connection in our list until it is dropped
                connector.close()
            else:
                # otherwise remove it
                self.connectors.remove(connector)

    def select_torrent_obfuscated(self, connector, streamid):
        if ONLY_LOCAL and connector.connection.ip != '127.0.0.1':
            return
        if streamid not in self.obfuscated_torrents:
            return
        self.obfuscated_torrents[streamid].singleport_connection(connector)

    def connection_lost(self, connector):
        if (ONLY_LOCAL or GLOBAL_FILTER) and connector not in self.connectors:
            return
        assert isinstance(connector, Connector)
        self.connectors.remove(connector)

    def remove_addr_from_cache(self, addr):
        # since this was incoming, we don't cache the peer anyway
        pass