Exemplo n.º 1
0
    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()
Exemplo n.º 2
0
    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())
Exemplo n.º 3
0
    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()
Exemplo n.º 4
0
    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()
Exemplo n.º 5
0
    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())
Exemplo n.º 6
0
    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)