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 = "Connected (via Stem)" except Exception as err: print("\n") traceback.print_exc() print("\n") self.controller = None self.status = "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 as err: self.status = "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 as err: self.status = "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 as err: self.status = "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 ""