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