diff --git a/plugins/disabled-StemPort/StemPortPlugin.py b/plugins/disabled-StemPort/StemPortPlugin.py new file mode 100644 index 00000000..3a3787c7 --- /dev/null +++ b/plugins/disabled-StemPort/StemPortPlugin.py @@ -0,0 +1,135 @@ +import logging +import traceback + +import socket +import stem +from stem import Signal +from stem.control import Controller +from stem.socket import ControlPort + +from Plugin import PluginManager +from Config import config +from Debug import Debug + +if config.tor != "disable": + from gevent import monkey + monkey.patch_time() + monkey.patch_socket(dns=False) + monkey.patch_thread() + print "Stem Port Plugin: modules are patched." +else: + print "Stem Port Plugin: Tor mode disabled. Module patching skipped." + + +class PatchedControlPort(ControlPort): + def _make_socket(self): + try: + if "socket_noproxy" in dir(socket): # Socket proxy-patched, use non-proxy one + control_socket = socket.socket_noproxy(socket.AF_INET, socket.SOCK_STREAM) + else: + control_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + + # TODO: repeated code - consider making a separate method + + control_socket.connect((self._control_addr, self._control_port)) + return control_socket + except socket.error as exc: + raise stem.SocketError(exc) + +def from_port(address = '127.0.0.1', port = 'default'): + import stem.connection + + if not stem.util.connection.is_valid_ipv4_address(address): + raise ValueError('Invalid IP address: %s' % address) + elif port != 'default' and not stem.util.connection.is_valid_port(port): + raise ValueError('Invalid port: %s' % port) + + if port == 'default': + raise ValueError('Must specify a port') + else: + control_port = PatchedControlPort(address, port) + + return Controller(control_port) + + +@PluginManager.registerTo("TorManager") +class TorManagerPlugin(object): + + def connectController(self): + self.log.info("Authenticate using Stem... %s:%s" % (self.ip, self.port)) + + try: + with self.lock: + if config.tor_password: + controller = from_port(port=self.port, password=config.tor_password) + else: + controller = from_port(port=self.port) + controller.authenticate() + self.controller = controller + self.status = u"Connected (via Stem)" + except Exception, err: + print("\n") + traceback.print_exc() + print("\n") + + self.controller = None + self.status = u"Error (%s)" % err + self.log.error("Tor stem connect error: %s" % Debug.formatException(err)) + + return self.controller + + + def disconnect(self): + self.controller.close() + self.controller = None + + + def resetCircuits(self): + try: + self.controller.signal(Signal.NEWNYM) + except Exception, err: + self.status = u"Stem reset circuits error (%s)" % err + self.log.error("Stem reset circuits error: %s" % err) + + + def makeOnionAndKey(self): + try: + service = self.controller.create_ephemeral_hidden_service( + {self.fileserver_port: self.fileserver_port}, + await_publication = False + ) + if service.private_key_type != "RSA1024": + raise Exception("ZeroNet doesn't support crypto " + service.private_key_type) + + self.log.debug("Stem created %s.onion (async descriptor publication)" % service.service_id) + + return (service.service_id, service.private_key) + + except Exception, err: + self.status = u"AddOnion error (Stem: %s)" % err + self.log.error("Failed to create hidden service with Stem: " + err) + return False + + + def delOnion(self, address): + try: + self.controller.remove_ephemeral_hidden_service(address) + return True + except Exception, err: + self.status = u"DelOnion error (Stem: %s)" % err + self.log.error("Stem failed to delete %s.onion: %s" % (address, err)) + self.disconnect() # Why? + return False + + + def request(self, cmd): + with self.lock: + if not self.enabled: + return False + else: + self.log.error("[WARNING] StemPort self.request should not be called") + return "" + + def send(self, cmd, conn=None): + self.log.error("[WARNING] StemPort self.send should not be called") + return "" diff --git a/plugins/disabled-StemPort/__init__.py b/plugins/disabled-StemPort/__init__.py new file mode 100644 index 00000000..1ce4d973 --- /dev/null +++ b/plugins/disabled-StemPort/__init__.py @@ -0,0 +1,10 @@ +try: + from stem.control import Controller + stem_found = True +except Exception, err: + print "STEM NOT FOUND! %s" % err + stem_found = False + +if stem_found: + print "Starting Stem plugin..." + import StemPortPlugin diff --git a/src/Tor/TorManager.py b/src/Tor/TorManager.py index b91cd937..9f2b1801 100644 --- a/src/Tor/TorManager.py +++ b/src/Tor/TorManager.py @@ -20,9 +20,11 @@ except: from gevent.lock import RLock from util import helper from Debug import Debug +from Plugin import PluginManager -class TorManager: +@PluginManager.acceptPlugins +class TorManager(object): def __init__(self, fileserver_ip=None, fileserver_port=None): self.privatekeys = {} # Onion: Privatekey self.site_onions = {} # Site address: Onion @@ -58,7 +60,7 @@ class TorManager: assert self.connect(), "No connection" self.log.debug("Tor proxy port %s check ok" % config.tor_proxy) except Exception, err: - self.log.debug("Tor proxy port %s check error: %s" % (config.tor_proxy, err)) + self.log.info("Starting self-bundled Tor, due to Tor proxy port %s check error: %s" % (config.tor_proxy, err)) self.enabled = False # Change to self-bundled Tor ports from lib.PySocks import socks @@ -148,12 +150,15 @@ class TorManager: self.site_onions = {} self.privatekeys = {} + return self.connectController() + + def connectController(self): if "socket_noproxy" in dir(socket): # Socket proxy-patched, use non-proxy one conn = socket.socket_noproxy(socket.AF_INET, socket.SOCK_STREAM) else: conn = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - self.log.debug("Connecting to %s:%s" % (self.ip, self.port)) + self.log.info("Connecting to Tor Controller %s:%s" % (self.ip, self.port)) try: with self.lock: conn.connect((self.ip, self.port)) @@ -203,19 +208,28 @@ class TorManager: self.log.error("Tor reset circuits error: %s" % res) def addOnion(self): + result = self.makeOnionAndKey() + if result: + onion_address, onion_privatekey = result + self.privatekeys[onion_address] = onion_privatekey + self.status = u"OK (%s onions running)" % len(self.privatekeys) + SiteManager.peer_blacklist.append((onion_address + ".onion", self.fileserver_port)) + return onion_address + else: + return False + + def makeOnionAndKey(self): res = self.request("ADD_ONION NEW:RSA1024 port=%s" % self.fileserver_port) match = re.search("ServiceID=([A-Za-z0-9]+).*PrivateKey=RSA1024:(.*?)[\r\n]", res, re.DOTALL) if match: onion_address, onion_privatekey = match.groups() - self.privatekeys[onion_address] = onion_privatekey - self.status = u"OK (%s onion running)" % len(self.privatekeys) - SiteManager.peer_blacklist.append((onion_address + ".onion", self.fileserver_port)) - return onion_address + return (onion_address, onion_privatekey) else: self.status = u"AddOnion error (%s)" % res self.log.error("Tor addOnion error: %s" % res) return False + def delOnion(self, address): res = self.request("DEL_ONION %s" % address) if "250 OK" in res: @@ -278,11 +292,13 @@ class TorManager: self.log.debug("Created new hidden service for %s: %s" % (site_address, onion)) return onion + # Creates and returns a + # socket that has connected to the Tor Network def createSocket(self, onion, port): if not self.enabled: return False - self.log.debug("Creating new socket to %s:%s" % (onion, port)) - if config.tor == "always": # Every socket is proxied by default + self.log.debug("Creating new Tor socket to %s:%s" % (onion, port)) + if config.tor == "always": # Every socket is proxied by default, in this mode sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect((onion, int(port))) else: