class TorGuard: def __init__(self, router, purpose=None, consensus=None, auth_data=None): self._router = router self._purpose = purpose self._consensus = consensus self._auth_data = auth_data self._state = GuardState.Connecting logger.info('Connecting to guard node %s... (%s)', self._router, self._purpose) self.__tor_socket = TorCellSocket(self._router) self.__tor_socket.connect() self._sender = TorSender(self.__tor_socket) self._circuits = CircuitsList(self) self._handler_mgr = CellHandlerManager() self._handler_mgr.subscribe_for(CellDestroy, self._on_destroy) self._handler_mgr.subscribe_for([CellCreatedFast, CellCreated2], self._on_cell) self._handler_mgr.subscribe_for(CellRelay, self._on_relay) self._receiver = TorReceiver(self.__tor_socket, self._handler_mgr) self._receiver.start() self._state = GuardState.Connected @property def consensus(self): return self._consensus @property def router(self): return self._router @property def auth_data(self): return self._auth_data def __enter__(self): """Return Guard object.""" return self def __exit__(self, exc_type, exc_val, exc_tb): """Disconnect from Guard node.""" self.close() def close(self): logger.info('Closing guard connections (%s)...', self._purpose) self._state = GuardState.Disconnecting self._destroy_all_circuits() self._receiver.stop() self.__tor_socket.close() self._state = GuardState.Disconnected def _destroy_all_circuits(self): logger.debug('Destroying all circuits...') if self._circuits: for circuit in list(self._circuits.values()): self.destroy_circuit(circuit) @cell_to_circuit def _on_destroy(self, cell, circuit): logger.info('On destroy: circuit #%x', cell.circuit_id) send_destroy = isinstance(cell, CellRelayTruncated) self.destroy_circuit(circuit, send_destroy=send_destroy) @cell_to_circuit def _on_cell(self, cell, circuit): circuit.handle_cell(cell) @cell_to_circuit def _on_relay(self, cell: CellRelay, circuit): circuit.handle_relay(cell) def send_cell(self, cell): return self._sender.send(cell) @retry(3, (CircuitExtendError, CellTimeoutError), log_func=functools.partial(log_retry, msg='Retry circuit creation')) def create_circuit(self, hops_count, extend_routers=None): if self._state != GuardState.Connected: raise Exception('You must connect to guard node first') circuit = self._circuits.create_new() try: circuit.create() circuit.build_hops(hops_count) if extend_routers: for router in extend_routers: circuit.extend(router) except Exception: # We must close here because we didn't enter to circuit yet to guard by context manager circuit.close() raise return circuit def destroy_circuit(self, circuit, send_destroy=True): logger.info('Destroy circuit #%x', circuit.id) circuit.destroy(send_destroy=send_destroy) self._circuits.remove(circuit.id) def register(self, sock_or_stream, events, callback): return self._receiver.register(sock_or_stream, events, callback) def unregister(self, sock_or_stream): self._receiver.unregister(sock_or_stream)
class TorGuard: def __init__(self, router, consensus, auth_data): self._router = router self._consensus = consensus self._auth_data = auth_data self.__tor_socket = None self._circuits_manager = None self._handler_mgr = None self._sender = None self._receiver = None self._state = GuardState.Disconnected def __enter__(self): """Return Guard object.""" return self def __exit__(self, exc_type, exc_val, exc_tb): """Disconnect from Guard node.""" self.close() def connect(self): logger.info('Connecting to guard node %s...', self._router) self._state = GuardState.Connecting self.__tor_socket = TorCellSocket(self._router) self.__tor_socket.connect() self._sender = TorSender(self.__tor_socket) self._circuits_manager = CircuitsManager(self._router, self._sender, self._consensus, self._auth_data) self._handler_mgr = CellHandlerManager() self._handler_mgr.subscribe_for(CellDestroy, self._on_destroy) self._handler_mgr.subscribe_for(CellCreated2, self._on_cell) self._handler_mgr.subscribe_for(CellRelay, self._on_relay) self._receiver = TorReceiver(self.__tor_socket, self._handler_mgr) self._receiver.start() self._state = GuardState.Connected def close(self): logger.info('Closing guard connections...') self._state = GuardState.Disconnecting self._destroy_all_circuits() self._receiver.stop() self.__tor_socket.close() self._state = GuardState.Disconnected def _destroy_all_circuits(self): logger.debug('Destroying all circuits...') for circuit in list(self._circuits_manager.circuits()): self.destroy_circuit(circuit) def _get_circuit(self, circuit_id): circuit = self._circuits_manager.get_by_id(circuit_id) if not circuit: if self._state != GuardState.Connected: return raise Exception('Circuit #{:x} not found'.format(circuit_id)) return circuit @cell_to_circuit def _on_destroy(self, cell, circuit): logger.info('On destroy: circuit #%x', cell.circuit_id) send_destroy = isinstance(cell, CellRelayTruncated) self.destroy_circuit(circuit, send_destroy=send_destroy) @cell_to_circuit def _on_cell(self, cell, circuit): circuit.handle_cell(cell) @cell_to_circuit def _on_relay(self, cell: CellRelay, circuit): circuit.handle_relay(cell) @retry(3, (CircuitExtendError, CellTimeoutError,), log_func=functools.partial(log_retry, msg='Retry circuit creation')) def create_circuit_(self, hops_count): if self._state != GuardState.Connected: raise Exception('You must connect to guard node first') circuit = self._circuits_manager.create_new() try: circuit.create(self) circuit.build_hops(hops_count) except CellTimeoutError: self.destroy_circuit(circuit) raise return circuit @contextmanager def create_circuit(self, hops_count): circuit = self.create_circuit_(hops_count) try: yield circuit finally: self.destroy_circuit(circuit) def destroy_circuit(self, circuit, send_destroy=True): logger.info('Destroy circuit #%x', circuit.id) circuit.destroy(send_destroy=send_destroy) self._circuits_manager.remove(circuit.id)