Vidalia 0.3.1
TorEvents.cpp
Go to the documentation of this file.
1/*
2** This file is part of Vidalia, and is subject to the license terms in the
3** LICENSE file, found in the top level directory of this distribution. If
4** you did not receive the LICENSE file with this file, you may obtain it
5** from the Vidalia source package distributed by the Vidalia Project at
6** http://www.torproject.org/projects/vidalia.html. No part of Vidalia,
7** including this file, may be copied, modified, propagated, or distributed
8** except according to the terms described in the LICENSE file.
9*/
10
11/*
12** \file TorEvents.cpp
13** \brief Parses and dispatches events from Tor
14*/
15
16
17#include "TorEvents.h"
18#include "ControlReply.h"
19#include "ReplyLine.h"
20#include "Circuit.h"
21#include "Stream.h"
22#include "BootstrapStatus.h"
23
24#include "stringutil.h"
25
26#include <QHostAddress>
27#include <QMetaType>
28
29/** Format of expiry times in address map events. */
30#define DATE_FMT "\"yyyy-MM-dd HH:mm:ss\""
31
32
33/** Default constructor */
34TorEvents::TorEvents(QObject *parent)
35 : QObject(parent)
36{
37 qRegisterMetaType<tc::Severity>();
38 qRegisterMetaType<tc::SocksError>();
39 qRegisterMetaType<tc::TorVersionStatus>();
40
41 qRegisterMetaType<BootstrapStatus>("BootstrapStatus");
42 qRegisterMetaType<Circuit>("Circuit");
43 qRegisterMetaType<Stream>("Stream");
44
45 qRegisterMetaType<QHostAddress>("QHostAddress");
46 qRegisterMetaType<QDateTime>("QDateTime");
47}
48
49/** Converts an event type to a string Tor understands */
50QString
52{
53 QString event;
54 switch (e) {
55 case Bandwidth: event = "BW"; break;
56 case LogDebug: event = "DEBUG"; break;
57 case LogInfo: event = "INFO"; break;
58 case LogNotice: event = "NOTICE"; break;
59 case LogWarn: event = "WARN"; break;
60 case LogError: event = "ERR"; break;
61 case CircuitStatus: event = "CIRC"; break;
62 case StreamStatus: event = "STREAM"; break;
63 case NewDescriptor: event = "NEWDESC"; break;
64 case AddressMap: event = "ADDRMAP"; break;
65 case GeneralStatus: event = "STATUS_GENERAL"; break;
66 case ClientStatus: event = "STATUS_CLIENT"; break;
67 case ServerStatus: event = "STATUS_SERVER"; break;
68 default: event = "UNKNOWN"; break;
69 }
70 return event;
71}
72
73/** Converts an event in the string form sent by Tor to its enum value */
75TorEvents::toTorEvent(const QString &event)
76{
77 Event e;
78 if (event == "BW") {
79 e = Bandwidth;
80 } else if (event == "CIRC") {
81 e = CircuitStatus;
82 } else if (event == "STREAM") {
83 e = StreamStatus;
84 } else if (event == "DEBUG") {
85 e = LogDebug;
86 } else if (event == "INFO") {
87 e = LogInfo;
88 } else if (event == "NOTICE") {
89 e = LogNotice;
90 } else if (event == "WARN") {
91 e = LogWarn;
92 } else if (event == "ERR") {
93 e = LogError;
94 } else if (event == "NEWDESC") {
95 e = NewDescriptor;
96 } else if (event == "ADDRMAP") {
97 e = AddressMap;
98 } else if (event == "STATUS_GENERAL") {
99 e = GeneralStatus;
100 } else if (event == "STATUS_CLIENT") {
101 e = ClientStatus;
102 } else if (event == "STATUS_SERVER") {
103 e = ServerStatus;
104 } else {
105 e = Unknown;
106 }
107 return e;
108}
109
110/** Parse the event type out of a message line and return the corresponding
111 * Event enum value */
114{
115 QString msg = line.getMessage();
116 int i = msg.indexOf(" ");
117 return toTorEvent(msg.mid(0, i));
118}
119
120/** Handles an event message from Tor. An event message can potentially have
121 * more than one line, so we will iterate through them all and dispatch the
122 * necessary events. */
123void
125{
126 foreach(ReplyLine line, reply.getLines()) {
127 switch (parseEventType(line)) {
128 case Bandwidth: handleBandwidthUpdate(line); break;
129 case CircuitStatus: handleCircuitStatus(line); break;
130 case StreamStatus: handleStreamStatus(line); break;
131 case NewDescriptor: handleNewDescriptor(line); break;
132 case AddressMap: handleAddressMap(line); break;
133
134 case GeneralStatus:
135 handleStatusEvent(GeneralStatus, line); break;
136 case ClientStatus:
137 handleStatusEvent(ClientStatus, line); break;
138 case ServerStatus:
139 handleStatusEvent(ServerStatus, line); break;
140
141 case LogDebug:
142 case LogInfo:
143 case LogNotice:
144 case LogWarn:
145 case LogError:
146 handleLogMessage(line); break;
147 default: break;
148 }
149 }
150}
151
152/** Handle a bandwidth update event, to inform the controller of the bandwidth
153 * used in the last second. The format of this message is:
154 *
155 * "650" SP "BW" SP BytesRead SP BytesWritten
156 * BytesRead = 1*DIGIT
157 * BytesWritten = 1*DIGIT
158 */
159void
161{
162 QStringList msg = line.getMessage().split(" ");
163 if (msg.size() >= 3) {
164 quint64 bytesIn = (quint64)msg.at(1).toULongLong();
165 quint64 bytesOut = (quint64)msg.at(2).toULongLong();
166
167 /* Post the event to each of the interested targets */
168 emit bandwidthUpdate(bytesIn, bytesOut);
169 }
170}
171
172/** Handle a circuit status event. The format of this message is:
173 *
174 * "650" SP "CIRC" SP CircuitID SP CircStatus SP Path
175 * CircStatus =
176 * "LAUNCHED" / ; circuit ID assigned to new circuit
177 * "BUILT" / ; all hops finished, can now accept streams
178 * "EXTENDED" / ; one more hop has been completed
179 * "FAILED" / ; circuit closed (was not built)
180 * "CLOSED" ; circuit closed (was built)
181 * Path = ServerID *("," ServerID)
182 */
183void
185{
186 QString msg = line.getMessage().trimmed();
187 int i = msg.indexOf(" ") + 1;
188 if (i > 0) {
189 /* Post the event to each of the interested targets */
190 Circuit circ(msg.mid(i));
191 if (circ.isValid())
192 emit circuitStatusChanged(circ);
193 }
194}
195
196/** Handle a stream status event. The format of this message is:
197 *
198 * "650" SP "STREAM" SP StreamID SP StreamStatus SP CircID SP Target SP
199 * StreamStatus =
200 * "NEW" / ; New request to connect
201 * "NEWRESOLVE" / ; New request to resolve an address
202 * "SENTCONNECT" / ; Sent a connect cell along a circuit
203 * "SENTRESOLVE" / ; Sent a resolve cell along a circuit
204 * "SUCCEEDED" / ; Received a reply; stream established
205 * "FAILED" / ; Stream failed and not retriable.
206 * "CLOSED" / ; Stream closed
207 * "DETACHED" ; Detached from circuit; still retriable.
208 * Target = Address ":" Port
209 *
210 * If the circuit ID is 0, then the stream is unattached.
211 */
212void
214{
215 QString msg = line.getMessage().trimmed();
216 int i = msg.indexOf(" ") + 1;
217 if (i > 0) {
218 Stream stream = Stream::fromString(msg.mid(i));
219 if (stream.isValid())
220 emit streamStatusChanged(stream);
221 }
222}
223
224/** Handle a log message event. The format of this message is:
225 * The syntax is:
226 *
227 * "650" SP Severity SP ReplyText
228 * or
229 * "650+" Severity CRLF Data
230 * Severity = "DEBUG" / "INFO" / "NOTICE" / "WARN"/ "ERR"
231 */
232void
234{
235 QString msg = line.getMessage();
236 int i = msg.indexOf(" ");
237 tc::Severity severity = tc::severityFromString(msg.mid(0, i));
238 QString logLine = (line.getData().size() > 0 ? line.getData().join("\n") :
239 msg.mid(i+1));
240
241 emit logMessage(severity, logLine);
242}
243
244/** Handles a new descriptor event. The format for event messages of this type
245 * is:
246 *
247 * "650" SP "NEWDESC" 1*(SP ServerID)
248 */
249void
251{
252 QString descs = line.getMessage();
253 QStringList descList = descs.mid(descs.indexOf(" ")+1).split(" ");
254 emit newDescriptors(descList);
255}
256
257/** Handles a new or updated address mapping event. The format for event
258 * messages of this type is:
259 *
260 * "650" SP "ADDRMAP" SP Address SP Address SP Expiry
261 * Expiry = DQUOTE ISOTime DQUOTE / "NEVER"
262 *
263 * Expiry is expressed as the local time (rather than GMT).
264 */
265void
267{
268 QStringList msg = line.getMessage().split(" ");
269 if (msg.size() >= 4) {
270 QDateTime expires;
271 if (msg.size() >= 5 && msg.at(3) != "NEVER")
272 expires = QDateTime::fromString(msg.at(3) + " " + msg.at(4), DATE_FMT);
273 emit addressMapped(msg.at(1), msg.at(2), expires);
274 }
275}
276
277/** Handles a Tor status event. The format for event messages of this type is:
278 *
279 * "650" SP StatusType SP StatusSeverity SP StatusAction
280 * [SP StatusArguments] CRLF
281 *
282 * StatusType = "STATUS_GENERAL" / "STATUS_CLIENT" / "STATUS_SERVER"
283 * StatusSeverity = "NOTICE" / "WARN" / "ERR"
284 * StatusAction = 1*ALPHA
285 * StatusArguments = StatusArgument *(SP StatusArgument)
286 * StatusArgument = StatusKeyword '=' StatusValue
287 * StatusKeyword = 1*(ALNUM / "_")
288 * StatusValue = 1*(ALNUM / '_') / QuotedString
289 */
290void
292{
293 QString status;
294 tc::Severity severity;
295 QHash<QString,QString> args;
296 QString msg = line.getMessage();
297
298 severity = tc::severityFromString(msg.section(' ', 1, 1));
299 status = msg.section(' ', 2, 2);
300 args = string_parse_keyvals(msg.section(' ', 3));
301 switch (e) {
302 case ClientStatus:
303 handleClientStatusEvent(severity, status, args);
304 break;
305
306 case ServerStatus:
307 handleServerStatusEvent(severity, status, args);
308 break;
309
310 case GeneralStatus:
311 handleGeneralStatusEvent(severity, status, args);
312 break;
313
314 default:
315 break;
316 }
317}
318
319/** Parses and emits a general Tor status event. */
320void
322 const QString &action,
323 const QHash<QString,QString> &args)
324{
325 if (! action.compare("DANGEROUS_TOR_VERSION", Qt::CaseInsensitive)) {
326 QString reason = args.value("REASON");
327 QString current = args.value("CURRENT");
328 QStringList recommended = args.value("RECOMMENDED")
329 .split(",", QString::SkipEmptyParts);
330 if (! reason.compare("NEW", Qt::CaseInsensitive))
331 emit dangerousTorVersion(tc::NewTorVersion, current, recommended);
332 else if (! reason.compare("UNRECOMMENDED", Qt::CaseInsensitive))
333 emit dangerousTorVersion(tc::UnrecommendedTorVersion, current, recommended);
334 else if (! reason.compare("OBSOLETE", Qt::CaseInsensitive)
335 || ! reason.compare("OLD", Qt::CaseInsensitive))
336 emit dangerousTorVersion(tc::ObsoleteTorVersion, current, recommended);
337 } else if (! action.compare("CLOCK_SKEW", Qt::CaseInsensitive)) {
338 int skew;
339 bool ok = false;
340 if (args.contains("SKEW"))
341 skew = args.value("SKEW").toInt(&ok);
342 else if (args.contains("MIN_SKEW"))
343 skew = args.value("MIN_SKEW").toInt(&ok);
344 if (ok)
345 emit clockSkewed(skew, args.value("SOURCE"));
346 } else if (! action.compare("BUG", Qt::CaseInsensitive)) {
347 emit bug(args.value("REASON"));
348 }
349}
350
351/** Parses and emits a Tor client status event. */
352void
354 const QString &action,
355 const QHash<QString,QString> &args)
356{
357 if (! action.compare("CIRCUIT_ESTABLISHED", Qt::CaseInsensitive)) {
358 emit circuitEstablished();
359 } else if (! action.compare("DANGEROUS_PORT", Qt::CaseInsensitive)) {
360 bool reject = ! args.value("RESULT").compare("REJECT", Qt::CaseInsensitive);
361 emit dangerousPort(args.value("PORT").toUInt(), reject);
362 } else if (! action.compare("DANGEROUS_SOCKS", Qt::CaseInsensitive)) {
363 emit socksError(tc::DangerousSocksTypeError, args.value("ADDRESS"));
364 } else if (! action.compare("SOCKS_UNKNOWN_PROTOCOL", Qt::CaseInsensitive)) {
366 } else if (! action.compare("SOCKS_BAD_HOSTNAME", Qt::CaseInsensitive)) {
367 emit socksError(tc::BadSocksHostnameError, args.value("HOSTNAME"));
368 } else if (! action.compare("BOOTSTRAP", Qt::CaseInsensitive)) {
369 BootstrapStatus status
370 = BootstrapStatus(severity,
371 BootstrapStatus::statusFromString(args.value("TAG")),
372 args.value("PROGRESS").toInt(),
373 args.value("SUMMARY"),
374 args.value("WARNING"),
375 tc::connectionStatusReasonFromString(args.value("REASON")),
377 args.value("RECOMMENDATION")));
378 emit bootstrapStatusChanged(status);
379 }
380}
381
382/** Parses and emits a Tor server status event. */
383void
385 const QString &action,
386 const QHash<QString,QString> &args)
387{
388 if (! action.compare("EXTERNAL_ADDRESS", Qt::CaseInsensitive)) {
389 emit externalAddressChanged(QHostAddress(args.value("ADDRESS")),
390 args.value("HOSTNAME"));
391 } else if (! action.compare("CHECKING_REACHABILITY", Qt::CaseInsensitive)) {
392 if (args.contains("ORADDRESS")) {
393 QPair<QHostAddress,quint16> pair = splitAddress(args.value("ORADDRESS"));
394 if (! pair.first.isNull())
395 emit checkingOrPortReachability(pair.first, pair.second);
396 } else if (args.contains("DIRADDRESS")) {
397 QPair<QHostAddress,quint16> pair = splitAddress(args.value("DIRADDRESS"));
398 if (! pair.first.isNull())
399 emit checkingDirPortReachability(pair.first, pair.second);
400 }
401 } else if (! action.compare("REACHABILITY_SUCCEEDED", Qt::CaseInsensitive)) {
402 if (args.contains("ORADDRESS")) {
403 QPair<QHostAddress,quint16> pair = splitAddress(args.value("ORADDRESS"));
404 if (! pair.first.isNull())
405 emit orPortReachabilityFinished(pair.first, pair.second, true);
406 } else if (args.contains("DIRADDRESS")) {
407 QPair<QHostAddress,quint16> pair = splitAddress(args.value("DIRADDRESS"));
408 if (! pair.first.isNull())
409 emit dirPortReachabilityFinished(pair.first, pair.second, true);
410 }
411 } else if (! action.compare("REACHABILITY_FAILED", Qt::CaseInsensitive)) {
412 if (args.contains("ORADDRESS")) {
413 QPair<QHostAddress,quint16> pair = splitAddress(args.value("ORADDRESS"));
414 if (! pair.first.isNull())
415 emit orPortReachabilityFinished(pair.first, pair.second, false);
416 } else if (args.contains("DIRADDRESS")) {
417 QPair<QHostAddress,quint16> pair = splitAddress(args.value("DIRADDRESS"));
418 if (! pair.first.isNull())
419 emit dirPortReachabilityFinished(pair.first, pair.second, false);
420 }
421 } else if (! action.compare("GOOD_SERVER_DESCRIPTOR", Qt::CaseInsensitive)) {
423 } else if (! action.compare("ACCEPTED_SERVER_DESCRIPTOR", Qt::CaseInsensitive)) {
424 QPair<QHostAddress,quint16> pair = splitAddress(args.value("DIRAUTH"));
425 if (! pair.first.isNull())
426 emit serverDescriptorAccepted(pair.first, pair.second);
427 } else if (! action.compare("BAD_SERVER_DESCRIPTOR", Qt::CaseInsensitive)) {
428 QPair<QHostAddress,quint16> pair = splitAddress(args.value("DIRAUTH"));
429 if (! pair.first.isNull())
430 emit serverDescriptorRejected(pair.first, pair.second,
431 args.value("REASON"));
432 } else if (! action.compare("DNS_HIJACKED", Qt::CaseInsensitive)) {
433 emit dnsHijacked();
434 } else if (! action.compare("DNS_USELESS", Qt::CaseInsensitive)) {
435 emit dnsUseless();
436 }
437}
438
439/** Splits a string in the form "IP:PORT" into a QHostAddress and quint16
440 * pair. If either portion is invalid, a default-constructed QPair() is
441 * returned. */
442QPair<QHostAddress,quint16>
443TorEvents::splitAddress(const QString &address)
444{
445 bool ok;
446 int idx = address.indexOf(":");
447 if (idx <= 0 || idx >= address.length()-1)
448 return QPair<QHostAddress,quint16>();
449
450 QHostAddress ip = QHostAddress(address.mid(0, idx));
451 quint16 port = static_cast<quint16>(address.mid(idx+1).toUInt(&ok));
452 if (ip.isNull() || ! ok)
453 return QPair<QHostAddress,quint16>();
454 return QPair<QHostAddress,quint16>(ip, port);
455}
456
457
#define DATE_FMT
Definition: TorEvents.cpp:30
static Status statusFromString(const QString &tag)
static Recommendation actionFromString(const QString &str)
bool isValid() const
Definition: Circuit.h:48
QList< ReplyLine > getLines() const
QString getMessage() const
Definition: ReplyLine.cpp:64
QStringList getData() const
Definition: ReplyLine.cpp:78
Definition: Stream.h:32
bool isValid() const
Definition: Stream.cpp:146
static Stream fromString(const QString &stream)
Definition: Stream.cpp:63
@ GeneralStatus
Definition: TorEvents.h:56
@ LogError
Definition: TorEvents.h:50
@ ServerStatus
Definition: TorEvents.h:58
@ Bandwidth
Definition: TorEvents.h:45
@ LogDebug
Definition: TorEvents.h:46
@ AddressMap
Definition: TorEvents.h:55
@ ClientStatus
Definition: TorEvents.h:57
@ StreamStatus
Definition: TorEvents.h:52
@ NewDescriptor
Definition: TorEvents.h:54
@ CircuitStatus
Definition: TorEvents.h:51
@ LogNotice
Definition: TorEvents.h:48
void serverDescriptorRejected(const QHostAddress &ip, quint16 port, const QString &reason)
void handleEvent(const ControlReply &reply)
Definition: TorEvents.cpp:124
void streamStatusChanged(const Stream &stream)
void handleAddressMap(const ReplyLine &line)
Definition: TorEvents.cpp:266
void handleGeneralStatusEvent(tc::Severity severity, const QString &action, const QHash< QString, QString > &args)
Definition: TorEvents.cpp:321
void dangerousPort(quint16 port, bool rejected)
void orPortReachabilityFinished(const QHostAddress &ip, quint16 port, bool reachable)
void logMessage(tc::Severity level, const QString &msg)
void handleClientStatusEvent(tc::Severity severity, const QString &action, const QHash< QString, QString > &args)
Definition: TorEvents.cpp:353
void circuitEstablished()
void externalAddressChanged(const QHostAddress &ip, const QString &hostname)
void addressMapped(const QString &from, const QString &to, const QDateTime &expires)
static QPair< QHostAddress, quint16 > splitAddress(const QString &address)
Definition: TorEvents.cpp:443
void clockSkewed(int skew, const QString &source)
static Event parseEventType(const ReplyLine &line)
Definition: TorEvents.cpp:113
void serverDescriptorAccepted()
TorEvents(QObject *parent=0)
Definition: TorEvents.cpp:34
void bootstrapStatusChanged(const BootstrapStatus &status)
void handleBandwidthUpdate(const ReplyLine &line)
Definition: TorEvents.cpp:160
void dangerousTorVersion(tc::TorVersionStatus reason, const QString &version, const QStringList &recommended)
void socksError(tc::SocksError error, const QString &destination)
void handleNewDescriptor(const ReplyLine &line)
Definition: TorEvents.cpp:250
void checkingOrPortReachability(const QHostAddress &ip, quint16 port)
static QString toString(TorEvents::Event e)
Definition: TorEvents.cpp:51
void bug(const QString &reason)
void handleStreamStatus(const ReplyLine &line)
Definition: TorEvents.cpp:213
void dnsHijacked()
static Event toTorEvent(const QString &event)
Definition: TorEvents.cpp:75
void newDescriptors(const QStringList &ids)
void dnsUseless()
void circuitStatusChanged(const Circuit &circuit)
void checkingDirPortReachability(const QHostAddress &ip, quint16 port)
void dirPortReachabilityFinished(const QHostAddress &ip, quint16 port, bool reachable)
void handleCircuitStatus(const ReplyLine &line)
Definition: TorEvents.cpp:184
void handleLogMessage(const ReplyLine &line)
Definition: TorEvents.cpp:233
void handleStatusEvent(Event type, const ReplyLine &line)
Definition: TorEvents.cpp:291
void bandwidthUpdate(quint64 bytesReceived, quint64 bytesSent)
void handleServerStatusEvent(tc::Severity severity, const QString &action, const QHash< QString, QString > &args)
Definition: TorEvents.cpp:384
QString i(QString str)
Definition: html.cpp:32
@ NewTorVersion
Definition: tcglobal.h:89
@ UnrecommendedTorVersion
Definition: tcglobal.h:88
@ ObsoleteTorVersion
Definition: tcglobal.h:87
@ UnknownSocksProtocolError
Definition: tcglobal.h:81
@ DangerousSocksTypeError
Definition: tcglobal.h:80
@ BadSocksHostnameError
Definition: tcglobal.h:82
Severity severityFromString(const QString &str)
Definition: tcglobal.cpp:82
Severity
Definition: tcglobal.h:69
ConnectionStatusReason connectionStatusReasonFromString(const QString &str)
Definition: tcglobal.cpp:55
QHash< QString, QString > string_parse_keyvals(const QString &str, bool *ok)
Definition: stringutil.cpp:244