1# -*- coding: utf-8 -*-
2
3from __future__ import absolute_import
4from __future__ import print_function
5from __future__ import unicode_literals
6from __future__ import with_statement
7
8from zope.interface import implementer
9from zope.interface import Interface, Attribute
10
11
12class ITor(Interface):
13    """
14    Represents a tor instance. This high-level API should provide all
15    objects you need to interact with tor.
16    """
17
18    process = Attribute("TorProcessProtocol instance if we launched this Tor")
19    protocol = Attribute("A TorControlProtocol connected to our Tor")
20    version = Attribute("The version of the Tor we're connected to")
21
22    def quit(self):
23        """
24        Closes the control connection, and if we launched this Tor
25        instance we'll send it a TERM and wait until it exits.
26
27        :return: a Deferred that fires when we've quit
28        """
29
30    def get_config(self):
31        """
32        :return: a Deferred that fires with a TorConfig instance. This
33            instance represents up-to-date configuration of the tor
34            instance (even if another controller is connected). If you
35            call this more than once you'll get the same TorConfig back.
36        """
37
38    def create_state(self):
39        """
40        returns a Deferred that fires with a ready-to-go
41        :class:`txtorcon.TorState` instance.
42        """
43
44    def web_agent(self, pool=None, _socks_endpoint=None):
45        """
46        :param _socks_endpoint: If ``None`` (the default), a suitable
47            SOCKS port is chosen from our config (or added). If supplied,
48            should be a Deferred which fires an IStreamClientEndpoint
49            (e.g. the return-value from
50            :meth:`txtorcon.TorConfig.socks_endpoint`) or an immediate
51            IStreamClientEndpoint You probably don't need to mess with
52            this.
53
54        :param pool: passed on to the Agent (as ``pool=``)
55        """
56
57    def dns_resolve(self, hostname):
58        """
59        :param hostname: a string
60
61        :returns: a Deferred that calbacks with the hostname as looked-up
62            via Tor (or errback).  This uses Tor's custom extension to the
63            SOCKS5 protocol.
64        """
65
66    def dns_resolve_ptr(self, ip):
67        """
68        :param ip: a string, like "127.0.0.1"
69
70        :returns: a Deferred that calbacks with the IP address as
71            looked-up via Tor (or errback).  This uses Tor's custom
72            extension to the SOCKS5 protocol.
73        """
74
75    def stream_via(self, host, port, tls=False, _socks_endpoint=None):
76        """
77        This returns an IStreamClientEndpoint instance that will use this
78        Tor (via SOCKS) to visit the ``(host, port)`` indicated.
79
80        :param host: The host to connect to. You MUST pass host-names
81            to this. If you absolutely know that you've not leaked DNS
82            (e.g. you save IPs in your app's configuration or similar)
83            then you can pass an IP.
84
85        :param port: Port to connect to.
86
87        :param tls: If True, it will wrap the return endpoint in one
88            that does TLS (default: False).
89
90        :param _socks_endpoint: Normally not needed (default: None)
91            but you can pass an IStreamClientEndpoint_ directed at one
92            of the local Tor's SOCKS5 ports (e.g. created with
93            :meth:`txtorcon.TorConfig.create_socks_endpoint`). Can be
94            a Deferred.
95        """
96
97
98class IStreamListener(Interface):
99    """
100    Notifications about changes to a :class:`txtorcon.Stream`.
101
102    If you wish for your listener to be added to *all* new streams,
103    see :meth:`txtorcon.TorState.add_stream_listener`.
104    """
105
106    def stream_new(stream):
107        "a new stream has been created"
108
109    def stream_succeeded(stream):
110        "stream has succeeded"
111
112    def stream_attach(stream, circuit):
113        "the stream has been attached to a circuit"
114
115    def stream_detach(stream, **kw):
116        """
117        the stream has been detached from its circuit
118
119        :param kw:
120            provides any flags for this event, which will include at
121            least REASON (but may include anything). See control-spec.
122        """
123
124    def stream_closed(stream, **kw):
125        """
126        stream has been closed (won't be in controller's list anymore).
127
128        :param kw:
129            provides any flags for this event, which will include at
130            least REASON (but may include anything). See control-spec.
131        """
132
133    def stream_failed(stream, **kw):
134        """
135        stream failed for some reason (won't be in controller's list anymore).
136
137        :param kw:
138            a dict of all the flags for the stream failure; see
139            control-spec but these will include REASON and sometimes
140            REMOTE_REASON (if the remote Tor closed the
141            connection). Both an all-uppercase and all-lowercase
142            version of each keyword is supplied (by the library; Tor
143            provides all-uppercase only). Others may include
144            BUILD_FLAGS, PURPOSE, HS_STATE, REND_QUERY, TIME_CREATED
145            (or anything else).
146        """
147
148
149@implementer(IStreamListener)
150class StreamListenerMixin(object):
151    """
152    Implements all of :class:`txtorcon.IStreamListener` with no-op
153    methods. You may subclass from this if you don't care about most
154    of the notifications.
155    """
156
157    def stream_new(self, stream):
158        pass
159
160    def stream_succeeded(self, stream):
161        pass
162
163    def stream_attach(self, stream, circuit):
164        pass
165
166    def stream_detach(self, stream, **kw):
167        pass
168
169    def stream_closed(self, stream, **kw):
170        pass
171
172    def stream_failed(self, stream, **kw):
173        pass
174
175
176class IStreamAttacher(Interface):
177    """
178    Used by :class:`txtorcon.TorState` to map streams to circuits (see
179    :meth:`txtorcon.TorState.set_attacher`).
180
181    Each time a new :class:`txtorcon.Stream` is created, this
182    interface will be queried to find out which
183    :class:`txtorcon.Circuit` it should be attached to.
184
185    Only advanced use-cases should need to use this directly; for most
186    users, using the :func:`txtorcon.Circuit.stream_via` interface
187    should be preferred.
188    """
189
190    def attach_stream_failure(stream, fail):
191        """
192        :param stream:
193            The stream we were trying to attach.
194
195        :param fail:
196            A Failure instance.
197
198        A failure has occurred while trying to attach the stream.
199        """
200
201    def attach_stream(stream, circuits):
202        """
203        :param stream:
204            The stream to attach, which will be in NEW or NEWRESOLVE
205            state.
206
207        :param circuits:
208            all currently available :class:`txtorcon.Circuit` objects
209            in the :class:`txtorcon.TorState` in a dict indexed by id.
210            Note they are *not* limited to BUILT circuits.
211
212        You should return a :class:`txtorcon.Circuit` instance which
213        should be at state BUILT in the currently running Tor. You may
214        also return a Deferred which will callback with the desired
215        circuit. In this case, you will probably need to be aware that
216        the callback from :meth:`txtorcon.TorState.build_circuit` does
217        not wait for the circuit to be in BUILT state.
218
219        Alternatively, you may return None in which case the Tor
220        controller will be told to choose a circuit itself.
221
222        Note that Tor will refuse to attach to any circuit not in
223        BUILT state; see ATTACHSTREAM in control-spec.txt
224
225        Note also that although you get a request to attach a stream
226        that ends in .onion Tor doesn't currently let you specify how
227        to attach .onion addresses and will always give a 551 error.
228        """
229
230
231class ICircuitContainer(Interface):
232    """
233    An interface that contains a bunch of Circuit objects and can look
234    them up by id.
235    """
236
237    def find_circuit(circ_id):
238        ":return: a circuit for the cird_id, or exception."
239
240    def close_circuit(circuit, **kwargs):
241        """
242        Close a circuit.
243        :return: a Deferred which callbacks when the closing process
244        is started (not necessarily finished inside Tor).
245        """
246
247    # FIXME do we need an IStreamContainer that Stream instances get?
248    # (Currently, they get an ICircuitContainer...)
249    def close_stream(stream, **kwargs):
250        """
251        Close a stream.
252        :return: a Deferred which callbacks when the closing process
253        is started (not necessarily finished inside Tor).
254        """
255
256
257class ICircuitListener(Interface):
258    """
259    An interface to listen for updates to Circuits.
260    """
261
262    def circuit_new(circuit):
263        """A new circuit has been created.  You'll always get one of
264        these for every Circuit even if it doesn't go through the "launched"
265        state."""
266
267    def circuit_launched(circuit):
268        "A new circuit has been started."
269
270    def circuit_extend(circuit, router):
271        "A circuit has been extended to include a new router hop."
272
273    def circuit_built(circuit):
274        """
275        A circuit has been extended to all hops (usually 3 for user
276        circuits).
277        """
278
279    def circuit_closed(circuit, **kw):
280        """
281        A circuit has been closed cleanly (won't be in controller's list
282        any more).
283
284        :param kw:
285            A dict of additional args. REASON is alsways included, and
286            often REMOTE_REASON also. See the control-spec
287            documentation.  As of this writing, REASON is one of the
288            following strings: MISC, RESOLVEFAILED, CONNECTREFUSED,
289            EXITPOLICY, DESTROY, DONE, TIMEOUT, NOROUTE, HIBERNATING,
290            INTERNAL,RESOURCELIMIT, CONNRESET, TORPROTOCOL,
291            NOTDIRECTORY, END, PRIVATE_ADDR. However, don't depend on
292            that: it could be anything.
293
294            To facilitate declaring args you want in the method
295            (e.g. ``circuit_failed(self, circuit, reason=None,
296            remote_reason=None, **kw)``) lower-case versions of all the
297            keys are also provided (pointing to the same -- usually
298            UPPERCASE -- strings as the upper-case keys).
299
300        """
301
302    def circuit_failed(circuit, **kw):
303        """
304        A circuit has been closed because something went wrong.
305
306        The circuit won't be in the TorState's list anymore.
307
308        :param kw:
309            A dict of additional args. REASON is alsways included, and
310            often REMOTE_REASON also. See the control-spec
311            documentation.  As of this writing, REASON is one of the
312            following strings: MISC, RESOLVEFAILED, CONNECTREFUSED,
313            EXITPOLICY, DESTROY, DONE, TIMEOUT, NOROUTE, HIBERNATING,
314            INTERNAL,RESOURCELIMIT, CONNRESET, TORPROTOCOL,
315            NOTDIRECTORY, END, PRIVATE_ADDR. However, don't depend on
316            that: it could be anything.
317
318            To facilitate declaring args you want in the method
319            (e.g. ``circuit_failed(self, circuit, reason=None,
320            remote_reason=None, **kw)``) lower-case versions of all the
321            keys are also provided (pointing to the same -- usually
322            UPPERCASE -- strings as the upper-case keys).
323        """
324
325
326@implementer(ICircuitListener)
327class CircuitListenerMixin(object):
328    """
329    Implements all of ICircuitListener with no-op methods. Subclass
330    from this if you don't care about most of the notifications.
331    """
332
333    def circuit_new(self, circuit):
334        pass
335
336    def circuit_launched(self, circuit):
337        pass
338
339    def circuit_extend(self, circuit, router):
340        pass
341
342    def circuit_built(self, circuit):
343        pass
344
345    def circuit_closed(self, circuit, **kw):
346        pass
347
348    def circuit_failed(self, circuit, **kw):
349        pass
350
351
352class ITorControlProtocol(Interface):
353    """
354    This defines the API to the TorController object.
355
356    This is the usual entry-point to this library, and you shouldn't
357    need to call methods outside this interface.
358    """
359
360    def get_info(info):
361        """
362        :return: a Deferred which will callback with the info keys you
363           asked for. For values ones, see control-spec.
364        """
365
366    def get_conf(*args):
367        """
368        Returns one or many configuration values via Deferred. See
369        control-spec for valid keys. The value will be a dictionary.
370        """
371
372    def signal(signal_name):
373        """
374        Issues a signal to Tor. See control-spec or .valid_signals for
375        which ones are available and their return values.
376        """
377
378    def build_circuit(routers):
379        """
380        Builds a circuit consisting of exactly the routers specified,
381        in order.  This issues a series of EXTENDCIRCUIT calls to Tor;
382        the deferred returned from this is for the final
383        EXTEND. FIXME: should return the Circuit instance, but
384        currently returns final extend message 'EXTEND 1234' for
385        example.
386        """
387
388    def close_circuit(circuit):
389        """
390        Asks Tor to close the circuit. Note that the Circuit instance
391        is only removed as a result of the next CIRC CLOSED event. The
392        Deferred returned from this method callbacks when the
393        CLOSECIRCUIT command has successfully executed, not when the
394        circuit is actually gone.
395
396        If you wish to know when this circuit is actually gone, add an
397        ICircuitListener and wait for circuit_closed()
398        """
399
400    def add_event_listener(evt, callback):
401        """
402        Add a listener to an Event object. This may be called multiple
403        times for the same event. Every time the event happens, the
404        callback method will be called. The callback has one argument
405        (a string, the contents of the event, minus the '650' and the
406        name of the event)
407
408        FIXME: should have an interface for the callback.
409        """
410
411
412class IRouterContainer(Interface):
413
414    unique_routers = Attribute("contains a list of all the Router instances")
415
416    def router_from_id(routerid):
417        """
418        Note that this method MUST always return a Router instance --
419        if you ask for a router ID that didn't yet exist, it is
420        created (although without IP addresses and such because it
421        wasn't in the consensus). You may find out if a Router came
422        from the 'GETINFO ns/all' list by checking the from_consensus
423        attribute. This is to simplify code like in Circuit.update()
424        that needs to handle the case where an EXTENDED circuit event
425        is the only time we've seen a Router -- it's possible for Tor
426        to do things with routers not in the consensus (like extend
427        circuits to them).
428
429        :return: a router by its ID.
430        """
431
432
433class IAddrListener(Interface):
434    def addrmap_added(addr):
435        """
436        A new address was added to the address map.
437        """
438
439    def addrmap_expired(name):
440        """
441        An address has expired from the address map.
442        """
443