135 lines
4.3 KiB
Python
135 lines
4.3 KiB
Python
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 ""
|