def __init__(self, protocol, bootstrap=True, write_state_diagram=False): self.protocol = ITorControlProtocol(protocol) ## fixme could use protocol.on_disconnect to re-connect; see issue #3 ## could override these to get your own Circuit/Stream subclasses ## to track these things self.circuit_factory = Circuit self.stream_factory = Stream self.attacher = None """If set, provides :class:`txtorcon.interface.IStreamAttacher` to attach new streams we hear about.""" self.tor_binary = 'tor' self.circuit_listeners = [] self.stream_listeners = [] self.addrmap = AddrMap() self.circuits = {} # keys on id (integer) self.streams = {} # keys on id (integer) self.routers = {} # keys by hexid (string) and by unique names self.routers_by_name = {} # keys on name, value always list (many duplicate "Unnamed" routers, for example) self.guards = {} # potentially-usable as entry guards, I think? (any router with 'Guard' flag) self.entry_guards = {} # from GETINFO entry-guards, our current entry guards self.unusable_entry_guards = [] # list of entry guards we didn't parse out self.authorities = {} # keys by name self.cleanup = None # see set_attacher class die(object): __name__ = 'die' # FIXME? just to ease spagetti.py:82's pain def __init__(self, msg): self.msg = msg def __call__(self, *args): raise RuntimeError(self.msg % tuple(args)) def nothing(*args): pass waiting_r = State("waiting_r") waiting_w = State("waiting_w") waiting_p = State("waiting_p") waiting_s = State("waiting_s") def ignorable_line(x): return x.strip() == '.' or x.strip() == 'OK' or x[:3] == 'ns/' or x.strip() == '' waiting_r.add_transition(Transition(waiting_r, ignorable_line, nothing)) waiting_r.add_transition(Transition(waiting_s, lambda x: x[:2] == 'r ', self._router_begin)) ## FIXME use better method/func than die!! waiting_r.add_transition(Transition(waiting_r, lambda x: x[:2] != 'r ', die('Expected "r " while parsing routers not "%s"'))) waiting_s.add_transition(Transition(waiting_w, lambda x: x[:2] == 's ', self._router_flags)) waiting_s.add_transition(Transition(waiting_s, lambda x: x[:2] == 'a ', self._router_address)) waiting_s.add_transition(Transition(waiting_r, ignorable_line, nothing)) waiting_s.add_transition(Transition(waiting_r, lambda x: x[:2] != 's ' and x[:2] != 'a ', die('Expected "s " while parsing routers not "%s"'))) waiting_s.add_transition(Transition(waiting_r, lambda x: x.strip() == '.', nothing)) waiting_w.add_transition(Transition(waiting_p, lambda x: x[:2] == 'w ', self._router_bandwidth)) waiting_w.add_transition(Transition(waiting_r, ignorable_line, nothing)) waiting_w.add_transition(Transition(waiting_s, lambda x: x[:2] == 'r ', self._router_begin)) # "w" lines are optional waiting_w.add_transition(Transition(waiting_r, lambda x: x[:2] != 'w ', die('Expected "w " while parsing routers not "%s"'))) waiting_w.add_transition(Transition(waiting_r, lambda x: x.strip() == '.', nothing)) waiting_p.add_transition(Transition(waiting_r, lambda x: x[:2] == 'p ', self._router_policy)) waiting_p.add_transition(Transition(waiting_r, ignorable_line, nothing)) waiting_p.add_transition(Transition(waiting_s, lambda x: x[:2] == 'r ', self._router_begin)) # "p" lines are optional waiting_p.add_transition(Transition(waiting_r, lambda x: x[:2] != 'p ', die('Expected "p " while parsing routers not "%s"'))) waiting_p.add_transition(Transition(waiting_r, lambda x: x.strip() == '.', nothing)) self._network_status_parser = FSM([waiting_r, waiting_s, waiting_w, waiting_p]) if write_state_diagram: with open('routerfsm.dot', 'w') as fsmfile: fsmfile.write(self._network_status_parser.dotty()) self.post_bootstrap = defer.Deferred() if bootstrap: if self.protocol.post_bootstrap: self.protocol.post_bootstrap.addCallback(self._bootstrap).addErrback(self.post_bootstrap.errback) else: self._bootstrap()
def __init__(self, password=None): """ password is only used if the Tor doesn't have COOKIE authentication turned on. Tor's default is COOKIE. """ self.password = password """If set, a password to use for authentication to Tor (default is to use COOKIE, however).""" self.version = None """Version of Tor we've connected to.""" self.is_owned = None """If not None, this is the PID of the Tor process we own (TAKEOWNERSHIP, etc).""" self.events = {} """events we've subscribed to (keyed by name like "GUARD", "STREAM")""" self.valid_events = {} """all valid events (name -> Event instance)""" self.valid_signals = [] """A list of all valid signals we accept from Tor""" self.post_bootstrap = defer.Deferred() """ This Deferred is triggered when we're done setting up (authentication, getting information from Tor). You will want to use this to do things with the :class:`TorControlProtocol` class when it's set up, like:: def setup_complete(proto): print "Setup complete, attached to Tor version",proto.version def setup(proto): proto.post_bootstrap.addCallback(setup_complete) TCP4ClientEndpoint(reactor, "localhost", 9051).connect(TorProtocolFactory()) d.addCallback(setup) See the helper method :func:`txtorcon.build_tor_connection`. """ ## variables related to the state machine self.defer = None # Deferred we returned for the current command self.response = '' self.code = None self.command = None # currently processing this command self.commands = [] # queued commands ## Here we build up the state machine. Mostly it's pretty ## simply, confounded by the fact that 600's (notify) can come ## at any time AND can be multi-line itself. Luckily, these ## can't be nested, nor can the responses be interleaved. idle = State("IDLE") recv = State("RECV") recvmulti = State("RECV_PLUS") recvnotify = State("NOTIFY_MULTILINE") idle.add_transition(Transition(idle, self._is_single_line_response, self._broadcast_response)) idle.add_transition(Transition(recvmulti, self._is_multi_line, self._start_command)) idle.add_transition(Transition(recv, self._is_continuation_line, self._start_command)) recv.add_transition(Transition(recv, self._is_continuation_line, self._accumulate_response)) recv.add_transition(Transition(idle, self._is_finish_line, self._broadcast_response)) recvmulti.add_transition(Transition(recv, self._is_end_line, lambda x: None)) recvmulti.add_transition(Transition(recvmulti, self._is_not_end_line, self._accumulate_multi_response)) self.fsm = FSM([recvnotify, idle, recvmulti, recv]) self.state_idle = idle ## hand-set initial state default start state is first in the ## list; the above looks nice in dotty though self.fsm.state = idle if DEBUG: self.debuglog = open('txtorcon-debug.log', 'w') with open('fsm.dot', 'w') as fsmfile: fsmfile.write(self.fsm.dotty())
def __init__(self, protocol, bootstrap=True, write_state_diagram=False): self.protocol = ITorControlProtocol(protocol) ## fixme could use protocol.on_disconnect to re-connect; see issue #3 ## could override these to get your own Circuit/Stream subclasses ## to track these things self.circuit_factory = Circuit self.stream_factory = Stream self.attacher = None """If set, provides :class:`txtorcon.interface.IStreamAttacher` to attach new streams we hear about.""" self.tor_binary = 'tor' self.circuit_listeners = [] self.stream_listeners = [] self.addrmap = AddrMap() self.circuits = {} # keys on id (integer) self.streams = {} # keys on id (integer) self.routers = {} # keys by hexid (string) and by unique names self.routers_by_name = { } # keys on name, value always list (many duplicate "Unnamed" routers, for example) self.guards = { } # potentially-usable as entry guards, I think? (any router with 'Guard' flag) self.entry_guards = { } # from GETINFO entry-guards, our current entry guards self.unusable_entry_guards = [ ] # list of entry guards we didn't parse out self.authorities = {} # keys by name self.cleanup = None # see set_attacher class die(object): __name__ = 'die' # FIXME? just to ease spagetti.py:82's pain def __init__(self, msg): self.msg = msg def __call__(self, *args): raise RuntimeError(self.msg % tuple(args)) def nothing(*args): pass waiting_r = State("waiting_r") waiting_w = State("waiting_w") waiting_p = State("waiting_p") waiting_s = State("waiting_s") def ignorable_line(x): return x.strip() == '.' or x.strip( ) == 'OK' or x[:3] == 'ns/' or x.strip() == '' waiting_r.add_transition(Transition(waiting_r, ignorable_line, nothing)) waiting_r.add_transition( Transition(waiting_s, lambda x: x[:2] == 'r ', self._router_begin)) ## FIXME use better method/func than die!! waiting_r.add_transition( Transition(waiting_r, lambda x: x[:2] != 'r ', die('Expected "r " while parsing routers not "%s"'))) waiting_s.add_transition( Transition(waiting_w, lambda x: x[:2] == 's ', self._router_flags)) waiting_s.add_transition( Transition(waiting_s, lambda x: x[:2] == 'a ', self._router_address)) waiting_s.add_transition(Transition(waiting_r, ignorable_line, nothing)) waiting_s.add_transition( Transition(waiting_r, lambda x: x[:2] != 's ' and x[:2] != 'a ', die('Expected "s " while parsing routers not "%s"'))) waiting_s.add_transition( Transition(waiting_r, lambda x: x.strip() == '.', nothing)) waiting_w.add_transition( Transition(waiting_p, lambda x: x[:2] == 'w ', self._router_bandwidth)) waiting_w.add_transition(Transition(waiting_r, ignorable_line, nothing)) waiting_w.add_transition( Transition(waiting_s, lambda x: x[:2] == 'r ', self._router_begin)) # "w" lines are optional waiting_w.add_transition( Transition(waiting_r, lambda x: x[:2] != 'w ', die('Expected "w " while parsing routers not "%s"'))) waiting_w.add_transition( Transition(waiting_r, lambda x: x.strip() == '.', nothing)) waiting_p.add_transition( Transition(waiting_r, lambda x: x[:2] == 'p ', self._router_policy)) waiting_p.add_transition(Transition(waiting_r, ignorable_line, nothing)) waiting_p.add_transition( Transition(waiting_s, lambda x: x[:2] == 'r ', self._router_begin)) # "p" lines are optional waiting_p.add_transition( Transition(waiting_r, lambda x: x[:2] != 'p ', die('Expected "p " while parsing routers not "%s"'))) waiting_p.add_transition( Transition(waiting_r, lambda x: x.strip() == '.', nothing)) self._network_status_parser = FSM( [waiting_r, waiting_s, waiting_w, waiting_p]) if write_state_diagram: with open('routerfsm.dot', 'w') as fsmfile: fsmfile.write(self._network_status_parser.dotty()) self.post_bootstrap = defer.Deferred() if bootstrap: if self.protocol.post_bootstrap: self.protocol.post_bootstrap.addCallback( self._bootstrap).addErrback(self.post_bootstrap.errback) else: self._bootstrap()
def __init__(self, password_function=None): """ :param password_function: A zero-argument callable which returns a password (or Deferred). It is only called if the Tor doesn't have COOKIE authentication turned on. Tor's default is COOKIE. """ self.password_function = password_function """If set, a callable to query for a password to use for authentication to Tor (default is to use COOKIE, however). May return Deferred.""" self.version = None """Version of Tor we've connected to.""" self.is_owned = None """If not None, this is the PID of the Tor process we own (TAKEOWNERSHIP, etc).""" self.events = {} """events we've subscribed to (keyed by name like "GUARD", "STREAM")""" self.valid_events = {} """all valid events (name -> Event instance)""" self.valid_signals = [] """A list of all valid signals we accept from Tor""" self.on_disconnect = defer.Deferred() """ This Deferred is triggered when the connection is closed. If there was an error, the errback is called instead. """ self.post_bootstrap = defer.Deferred() """ This Deferred is triggered when we're done setting up (authentication, getting information from Tor). You will want to use this to do things with the :class:`TorControlProtocol` class when it's set up, like:: def setup_complete(proto): print "Setup complete, attached to Tor version",proto.version def setup(proto): proto.post_bootstrap.addCallback(setup_complete) TCP4ClientEndpoint(reactor, "localhost", 9051).connect(TorProtocolFactory()) d.addCallback(setup) See the helper method :func:`txtorcon.build_tor_connection`. """ ## variables related to the state machine self.defer = None # Deferred we returned for the current command self.response = '' self.code = None self.command = None # currently processing this command self.commands = [] # queued commands ## Here we build up the state machine. Mostly it's pretty ## simply, confounded by the fact that 600's (notify) can come ## at any time AND can be multi-line itself. Luckily, these ## can't be nested, nor can the responses be interleaved. idle = State("IDLE") recv = State("RECV") recvmulti = State("RECV_PLUS") recvnotify = State("NOTIFY_MULTILINE") idle.add_transition( Transition(idle, self._is_single_line_response, self._broadcast_response)) idle.add_transition( Transition(recvmulti, self._is_multi_line, self._start_command)) idle.add_transition( Transition(recv, self._is_continuation_line, self._start_command)) recv.add_transition( Transition(recvmulti, self._is_multi_line, self._accumulate_response)) recv.add_transition( Transition(recv, self._is_continuation_line, self._accumulate_response)) recv.add_transition( Transition(idle, self._is_finish_line, self._broadcast_response)) recvmulti.add_transition( Transition(recv, self._is_end_line, lambda x: None)) recvmulti.add_transition( Transition(recvmulti, self._is_not_end_line, self._accumulate_multi_response)) self.fsm = FSM([recvnotify, idle, recvmulti, recv]) self.state_idle = idle ## hand-set initial state default start state is first in the ## list; the above looks nice in dotty though self.fsm.state = idle self.stop_debug()
def __init__(self, password_function=None, api_version=1): """ :param password_function: A zero-argument callable which returns a password (or Deferred). It is only called if the Tor doesn't have COOKIE authentication turned on. Tor's default is COOKIE. :param api_version: Specifies which version of the API you wish to use. This is to ease transitions to new API arguments or meanings in the future -- current users can safely set this to its current default value (1) to be sure their usage of public-facing methods in this object won't change. Introduced in 0.9.2. The following API versions are known: * ``api_version=1``: 0.9.2 and later; the current API version. """ self.password_function = password_function """If set, a callable to query for a password to use for authentication to Tor (default is to use COOKIE, however). May return Deferred.""" self.version = None """Version of Tor we've connected to.""" self.is_owned = None """If not None, this is the PID of the Tor process we own (TAKEOWNERSHIP, etc).""" self.events = {} """events we've subscribed to (keyed by name like "GUARD", "STREAM")""" self.valid_events = {} """all valid events (name -> Event instance)""" self.valid_signals = [] """A list of all valid signals we accept from Tor""" if api_version == 0: api_version = 1 self.api_version = api_version self.on_disconnect = defer.Deferred() """ This Deferred is triggered when the connection is closed. If there was an error, the errback is called instead. """ self.post_bootstrap = defer.Deferred() """ This Deferred is triggered when we're done setting up (authentication, getting information from Tor). You will want to use this to do things with the :class:`TorControlProtocol` class when it's set up, like:: def setup_complete(proto): print "Setup complete, attached to Tor version",proto.version def setup(proto): proto.post_bootstrap.addCallback(setup_complete) TCP4ClientEndpoint(reactor, "localhost", 9051).connect(TorProtocolFactory()) d.addCallback(setup) See the helper method :func:`txtorcon.build_tor_connection`. """ ## variables related to the state machine self.defer = None # Deferred we returned for the current command self.response = '' self.code = None self.command = None # currently processing this command self.commands = [] # queued commands ## Here we build up the state machine. Mostly it's pretty ## simply, confounded by the fact that 600's (notify) can come ## at any time AND can be multi-line itself. Luckily, these ## can't be nested, nor can the responses be interleaved. idle = State("IDLE") recv = State("RECV") recvmulti = State("RECV_PLUS") recvnotify = State("NOTIFY_MULTILINE") idle.add_transition(Transition(idle, self._is_single_line_response, self._broadcast_response)) idle.add_transition(Transition(recvmulti, self._is_multi_line, self._start_command)) idle.add_transition(Transition(recv, self._is_continuation_line, self._start_command)) recv.add_transition(Transition(recvmulti, self._is_multi_line, self._accumulate_response)) recv.add_transition(Transition(recv, self._is_continuation_line, self._accumulate_response)) recv.add_transition(Transition(idle, self._is_finish_line, self._broadcast_response)) recvmulti.add_transition(Transition(recv, self._is_end_line, lambda x: None)) recvmulti.add_transition(Transition(recvmulti, self._is_not_end_line, self._accumulate_multi_response)) self.fsm = FSM([recvnotify, idle, recvmulti, recv]) self.state_idle = idle ## hand-set initial state default start state is first in the ## list; the above looks nice in dotty though self.fsm.state = idle if DEBUG: self.debuglog = open('txtorcon-debug.log', 'w') with open('fsm.dot', 'w') as fsmfile: fsmfile.write(self.fsm.dotty())
def __init__(self, protocol, bootstrap=True): self.protocol = ITorControlProtocol(protocol) # fixme could use protocol.on_disconnect to re-connect; see issue #3 # could override these to get your own Circuit/Stream subclasses # to track these things self.circuit_factory = Circuit self.stream_factory = Stream self.attacher = None """If set, provides :class:`txtorcon.interface.IStreamAttacher` to attach new streams we hear about.""" self.tor_binary = "tor" self.circuit_listeners = [] self.stream_listeners = [] self.addrmap = AddrMap() self.circuits = {} # keys on id (integer) self.streams = {} # keys on id (integer) self.all_routers = set() # list of unique routers self.routers = {} # keys by hexid (string) and by unique names self.routers_by_name = {} # keys on name, value always list (many duplicate "Unnamed" routers, for example) self.routers_by_hash = {} # keys by hexid (string) self.guards = {} # potentially-usable as entry guards, I think? (any router with 'Guard' flag) self.entry_guards = {} # from GETINFO entry-guards, our current entry guards self.unusable_entry_guards = [] # list of entry guards we didn't parse out self.authorities = {} # keys by name self.cleanup = None # see set_attacher class die(object): __name__ = "die" # FIXME? just to ease spagetti.py:82's pain def __init__(self, msg): self.msg = msg def __call__(self, *args): raise RuntimeError(self.msg % tuple(args)) waiting_r = State("waiting_r") waiting_w = State("waiting_w") waiting_p = State("waiting_p") waiting_s = State("waiting_s") def ignorable_line(x): x = x.strip() return x in [".", "OK", ""] or x.startswith("ns/") waiting_r.add_transition(Transition(waiting_r, ignorable_line, None)) waiting_r.add_transition(Transition(waiting_s, lambda x: x.startswith("r "), self._router_begin)) # FIXME use better method/func than die!! waiting_r.add_transition( Transition(waiting_r, lambda x: not x.startswith("r "), die('Expected "r " while parsing routers not "%s"')) ) waiting_s.add_transition(Transition(waiting_w, lambda x: x.startswith("s "), self._router_flags)) waiting_s.add_transition(Transition(waiting_s, lambda x: x.startswith("a "), self._router_address)) waiting_s.add_transition(Transition(waiting_r, ignorable_line, None)) waiting_s.add_transition( Transition( waiting_r, lambda x: not x.startswith("s ") and not x.startswith("a "), die('Expected "s " while parsing routers not "%s"'), ) ) waiting_s.add_transition(Transition(waiting_r, lambda x: x.strip() == ".", None)) waiting_w.add_transition(Transition(waiting_p, lambda x: x.startswith("w "), self._router_bandwidth)) waiting_w.add_transition(Transition(waiting_r, ignorable_line, None)) waiting_w.add_transition( Transition(waiting_s, lambda x: x.startswith("r "), self._router_begin) ) # "w" lines are optional waiting_w.add_transition( Transition(waiting_r, lambda x: not x.startswith("w "), die('Expected "w " while parsing routers not "%s"')) ) waiting_w.add_transition(Transition(waiting_r, lambda x: x.strip() == ".", None)) waiting_p.add_transition(Transition(waiting_r, lambda x: x.startswith("p "), self._router_policy)) waiting_p.add_transition(Transition(waiting_r, ignorable_line, None)) waiting_p.add_transition( Transition(waiting_s, lambda x: x.startswith("r "), self._router_begin) ) # "p" lines are optional waiting_p.add_transition( Transition(waiting_r, lambda x: x[:2] != "p ", die('Expected "p " while parsing routers not "%s"')) ) waiting_p.add_transition(Transition(waiting_r, lambda x: x.strip() == ".", None)) self._network_status_parser = FSM([waiting_r, waiting_s, waiting_w, waiting_p]) self.post_bootstrap = defer.Deferred() if bootstrap: self.protocol.post_bootstrap.addCallback(self._bootstrap) self.protocol.post_bootstrap.addErrback(self.post_bootstrap.errback)