04. Events¶
Events are, besides of commands the other major way to get data from Tor. You can subscribe to events and get asynchronous notifications until you disable the event.
List of events¶
The following code displays the list of all events supported by your Tor:
1#!/usr/bin/env python
2
3import asyncio
4import os
5from aiostem import Controller
6
7async def main():
8 password = os.environ.get('AIOSTEM_PASS', 'password')
9 host = os.environ.get('AIOSTEM_HOST', 'localhost')
10 port = os.environ.get('AIOSTEM_PORT', 9051)
11
12 print(f'[>] Connecting to {host} on port {port}')
13 async with Controller.from_port(host, int(port)) as ctrl:
14 reply = await ctrl.authenticate(password)
15 reply.raise_for_status()
16
17 reply = await ctrl.get_info('events/names')
18 reply.raise_for_status()
19
20 names = sorted(reply['events/names'].split(' '))
21 print(f'Listing {len(names)} events:')
22 for name in names:
23 print(f'- {name}')
24
25if __name__ == '__main__':
26 asyncio.run(main())
All events handled by this library are described on EventWord.
Additionally, we also have support for events that are internal to this library, documented
on EventWordInternal. Currently only DISCONNECT is
supported, providing a way to be notified when the controller has been disconnected from the
remote Tor daemon (this may happen when Tor is shutting down for example).
Subscribe to events¶
Event subscription and unsubscription are handled by add_event_handler()
and del_event_handler(). These functions register a callback you provide,
and ask Tor for the associated events using set_events(). Do not call this
method by yourself, as this is supposed to be handled by our event manager.
The following example generates events on demand through resolve(), which
performs a DNS resolution for the provided domain(s). Its results cannot be provided back
immediately, so they are provided as an ADDRMAP, parsed through
EventAddrMap.
1#!/usr/bin/env python
2
3import asyncio
4import os
5import sys
6from aiostem import Controller
7from aiostem.event import EventAddrMap
8from functools import partial
9
10def on_addrmap(done, event):
11 if isinstance(event, EventAddrMap):
12 print(f'{event.original} is located at {event.replacement}')
13 done.set()
14
15async def main():
16 password = os.environ.get('AIOSTEM_PASS', 'password')
17 host = os.environ.get('AIOSTEM_HOST', 'localhost')
18 port = os.environ.get('AIOSTEM_PORT', 9051)
19
20 # Simple asyncio event to exit when the event has been received.
21 done = asyncio.Event()
22
23 print(f'[>] Connecting to {host} on port {port}')
24 async with Controller.from_port(host, int(port)) as ctrl:
25 reply = await ctrl.authenticate(password)
26 reply.raise_for_status()
27
28 await ctrl.add_event_handler('ADDRMAP', partial(on_addrmap, done))
29 reply = await ctrl.resolve(sys.argv[1:])
30 reply.raise_for_status()
31
32 # Wait until the address is resolved.
33 await done.wait()
34
35if __name__ == '__main__':
36 asyncio.run(main())
Its intended use is as follow:
$ python examples/events_resolve.py google.com
[>] Connecting to localhost on port 9051
google.com is located at 142.251.39.110
The callback method provided here can be either synchronous or asynchronous, but you need
to take extra care here since the callback methods run directly from the stream reader task.
If you need extra time, consider putting items in an asyncio.Queue and handle
events in a separate task.