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)
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
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