Vidalia
0.2.15
|
00001 /* 00002 ** This file is part of Vidalia, and is subject to the license terms in the 00003 ** LICENSE file, found in the top level directory of this distribution. If 00004 ** you did not receive the LICENSE file with this file, you may obtain it 00005 ** from the Vidalia source package distributed by the Vidalia Project at 00006 ** http://www.torproject.org/projects/vidalia.html. No part of Vidalia, 00007 ** including this file, may be copied, modified, propagated, or distributed 00008 ** except according to the terms described in the LICENSE file. 00009 */ 00010 00011 /* 00012 ** \file TorEvents.cpp 00013 ** \brief Parses and dispatches events from Tor 00014 */ 00015 00016 00017 #include "TorEvents.h" 00018 #include "ControlReply.h" 00019 #include "ReplyLine.h" 00020 #include "Circuit.h" 00021 #include "Stream.h" 00022 #include "BootstrapStatus.h" 00023 00024 #include "stringutil.h" 00025 00026 #include <QHostAddress> 00027 #include <QMetaType> 00028 00029 /** Format of expiry times in address map events. */ 00030 #define DATE_FMT "\"yyyy-MM-dd HH:mm:ss\"" 00031 00032 00033 /** Default constructor */ 00034 TorEvents::TorEvents(QObject *parent) 00035 : QObject(parent) 00036 { 00037 qRegisterMetaType<tc::Severity>(); 00038 qRegisterMetaType<tc::SocksError>(); 00039 qRegisterMetaType<tc::TorVersionStatus>(); 00040 00041 qRegisterMetaType<BootstrapStatus>("BootstrapStatus"); 00042 qRegisterMetaType<Circuit>("Circuit"); 00043 qRegisterMetaType<Stream>("Stream"); 00044 00045 qRegisterMetaType<QHostAddress>("QHostAddress"); 00046 qRegisterMetaType<QDateTime>("QDateTime"); 00047 } 00048 00049 /** Converts an event type to a string Tor understands */ 00050 QString 00051 TorEvents::toString(Event e) 00052 { 00053 QString event; 00054 switch (e) { 00055 case Bandwidth: event = "BW"; break; 00056 case LogDebug: event = "DEBUG"; break; 00057 case LogInfo: event = "INFO"; break; 00058 case LogNotice: event = "NOTICE"; break; 00059 case LogWarn: event = "WARN"; break; 00060 case LogError: event = "ERR"; break; 00061 case CircuitStatus: event = "CIRC"; break; 00062 case StreamStatus: event = "STREAM"; break; 00063 case NewDescriptor: event = "NEWDESC"; break; 00064 case AddressMap: event = "ADDRMAP"; break; 00065 case GeneralStatus: event = "STATUS_GENERAL"; break; 00066 case ClientStatus: event = "STATUS_CLIENT"; break; 00067 case ServerStatus: event = "STATUS_SERVER"; break; 00068 default: event = "UNKNOWN"; break; 00069 } 00070 return event; 00071 } 00072 00073 /** Converts an event in the string form sent by Tor to its enum value */ 00074 TorEvents::Event 00075 TorEvents::toTorEvent(const QString &event) 00076 { 00077 Event e; 00078 if (event == "BW") { 00079 e = Bandwidth; 00080 } else if (event == "CIRC") { 00081 e = CircuitStatus; 00082 } else if (event == "STREAM") { 00083 e = StreamStatus; 00084 } else if (event == "DEBUG") { 00085 e = LogDebug; 00086 } else if (event == "INFO") { 00087 e = LogInfo; 00088 } else if (event == "NOTICE") { 00089 e = LogNotice; 00090 } else if (event == "WARN") { 00091 e = LogWarn; 00092 } else if (event == "ERR") { 00093 e = LogError; 00094 } else if (event == "NEWDESC") { 00095 e = NewDescriptor; 00096 } else if (event == "ADDRMAP") { 00097 e = AddressMap; 00098 } else if (event == "STATUS_GENERAL") { 00099 e = GeneralStatus; 00100 } else if (event == "STATUS_CLIENT") { 00101 e = ClientStatus; 00102 } else if (event == "STATUS_SERVER") { 00103 e = ServerStatus; 00104 } else { 00105 e = Unknown; 00106 } 00107 return e; 00108 } 00109 00110 /** Parse the event type out of a message line and return the corresponding 00111 * Event enum value */ 00112 TorEvents::Event 00113 TorEvents::parseEventType(const ReplyLine &line) 00114 { 00115 QString msg = line.getMessage(); 00116 int i = msg.indexOf(" "); 00117 return toTorEvent(msg.mid(0, i)); 00118 } 00119 00120 /** Handles an event message from Tor. An event message can potentially have 00121 * more than one line, so we will iterate through them all and dispatch the 00122 * necessary events. */ 00123 void 00124 TorEvents::handleEvent(const ControlReply &reply) 00125 { 00126 foreach(ReplyLine line, reply.getLines()) { 00127 switch (parseEventType(line)) { 00128 case Bandwidth: handleBandwidthUpdate(line); break; 00129 case CircuitStatus: handleCircuitStatus(line); break; 00130 case StreamStatus: handleStreamStatus(line); break; 00131 case NewDescriptor: handleNewDescriptor(line); break; 00132 case AddressMap: handleAddressMap(line); break; 00133 00134 case GeneralStatus: 00135 handleStatusEvent(GeneralStatus, line); break; 00136 case ClientStatus: 00137 handleStatusEvent(ClientStatus, line); break; 00138 case ServerStatus: 00139 handleStatusEvent(ServerStatus, line); break; 00140 00141 case LogDebug: 00142 case LogInfo: 00143 case LogNotice: 00144 case LogWarn: 00145 case LogError: 00146 handleLogMessage(line); break; 00147 default: break; 00148 } 00149 } 00150 } 00151 00152 /** Handle a bandwidth update event, to inform the controller of the bandwidth 00153 * used in the last second. The format of this message is: 00154 * 00155 * "650" SP "BW" SP BytesRead SP BytesWritten 00156 * BytesRead = 1*DIGIT 00157 * BytesWritten = 1*DIGIT 00158 */ 00159 void 00160 TorEvents::handleBandwidthUpdate(const ReplyLine &line) 00161 { 00162 QStringList msg = line.getMessage().split(" "); 00163 if (msg.size() >= 3) { 00164 quint64 bytesIn = (quint64)msg.at(1).toULongLong(); 00165 quint64 bytesOut = (quint64)msg.at(2).toULongLong(); 00166 00167 /* Post the event to each of the interested targets */ 00168 emit bandwidthUpdate(bytesIn, bytesOut); 00169 } 00170 } 00171 00172 /** Handle a circuit status event. The format of this message is: 00173 * 00174 * "650" SP "CIRC" SP CircuitID SP CircStatus SP Path 00175 * CircStatus = 00176 * "LAUNCHED" / ; circuit ID assigned to new circuit 00177 * "BUILT" / ; all hops finished, can now accept streams 00178 * "EXTENDED" / ; one more hop has been completed 00179 * "FAILED" / ; circuit closed (was not built) 00180 * "CLOSED" ; circuit closed (was built) 00181 * Path = ServerID *("," ServerID) 00182 */ 00183 void 00184 TorEvents::handleCircuitStatus(const ReplyLine &line) 00185 { 00186 QString msg = line.getMessage().trimmed(); 00187 int i = msg.indexOf(" ") + 1; 00188 if (i > 0) { 00189 /* Post the event to each of the interested targets */ 00190 Circuit circ(msg.mid(i)); 00191 if (circ.isValid()) 00192 emit circuitStatusChanged(circ); 00193 } 00194 } 00195 00196 /** Handle a stream status event. The format of this message is: 00197 * 00198 * "650" SP "STREAM" SP StreamID SP StreamStatus SP CircID SP Target SP 00199 * StreamStatus = 00200 * "NEW" / ; New request to connect 00201 * "NEWRESOLVE" / ; New request to resolve an address 00202 * "SENTCONNECT" / ; Sent a connect cell along a circuit 00203 * "SENTRESOLVE" / ; Sent a resolve cell along a circuit 00204 * "SUCCEEDED" / ; Received a reply; stream established 00205 * "FAILED" / ; Stream failed and not retriable. 00206 * "CLOSED" / ; Stream closed 00207 * "DETACHED" ; Detached from circuit; still retriable. 00208 * Target = Address ":" Port 00209 * 00210 * If the circuit ID is 0, then the stream is unattached. 00211 */ 00212 void 00213 TorEvents::handleStreamStatus(const ReplyLine &line) 00214 { 00215 QString msg = line.getMessage().trimmed(); 00216 int i = msg.indexOf(" ") + 1; 00217 if (i > 0) { 00218 Stream stream = Stream::fromString(msg.mid(i)); 00219 if (stream.isValid()) 00220 emit streamStatusChanged(stream); 00221 } 00222 } 00223 00224 /** Handle a log message event. The format of this message is: 00225 * The syntax is: 00226 * 00227 * "650" SP Severity SP ReplyText 00228 * or 00229 * "650+" Severity CRLF Data 00230 * Severity = "DEBUG" / "INFO" / "NOTICE" / "WARN"/ "ERR" 00231 */ 00232 void 00233 TorEvents::handleLogMessage(const ReplyLine &line) 00234 { 00235 QString msg = line.getMessage(); 00236 int i = msg.indexOf(" "); 00237 tc::Severity severity = tc::severityFromString(msg.mid(0, i)); 00238 QString logLine = (line.getData().size() > 0 ? line.getData().join("\n") : 00239 msg.mid(i+1)); 00240 00241 emit logMessage(severity, logLine); 00242 } 00243 00244 /** Handles a new descriptor event. The format for event messages of this type 00245 * is: 00246 * 00247 * "650" SP "NEWDESC" 1*(SP ServerID) 00248 */ 00249 void 00250 TorEvents::handleNewDescriptor(const ReplyLine &line) 00251 { 00252 QString descs = line.getMessage(); 00253 QStringList descList = descs.mid(descs.indexOf(" ")+1).split(" "); 00254 emit newDescriptors(descList); 00255 } 00256 00257 /** Handles a new or updated address mapping event. The format for event 00258 * messages of this type is: 00259 * 00260 * "650" SP "ADDRMAP" SP Address SP Address SP Expiry 00261 * Expiry = DQUOTE ISOTime DQUOTE / "NEVER" 00262 * 00263 * Expiry is expressed as the local time (rather than GMT). 00264 */ 00265 void 00266 TorEvents::handleAddressMap(const ReplyLine &line) 00267 { 00268 QStringList msg = line.getMessage().split(" "); 00269 if (msg.size() >= 4) { 00270 QDateTime expires; 00271 if (msg.size() >= 5 && msg.at(3) != "NEVER") 00272 expires = QDateTime::fromString(msg.at(3) + " " + msg.at(4), DATE_FMT); 00273 emit addressMapped(msg.at(1), msg.at(2), expires); 00274 } 00275 } 00276 00277 /** Handles a Tor status event. The format for event messages of this type is: 00278 * 00279 * "650" SP StatusType SP StatusSeverity SP StatusAction 00280 * [SP StatusArguments] CRLF 00281 * 00282 * StatusType = "STATUS_GENERAL" / "STATUS_CLIENT" / "STATUS_SERVER" 00283 * StatusSeverity = "NOTICE" / "WARN" / "ERR" 00284 * StatusAction = 1*ALPHA 00285 * StatusArguments = StatusArgument *(SP StatusArgument) 00286 * StatusArgument = StatusKeyword '=' StatusValue 00287 * StatusKeyword = 1*(ALNUM / "_") 00288 * StatusValue = 1*(ALNUM / '_') / QuotedString 00289 */ 00290 void 00291 TorEvents::handleStatusEvent(Event e, const ReplyLine &line) 00292 { 00293 QString status; 00294 tc::Severity severity; 00295 QHash<QString,QString> args; 00296 QString msg = line.getMessage(); 00297 00298 severity = tc::severityFromString(msg.section(' ', 1, 1)); 00299 status = msg.section(' ', 2, 2); 00300 args = string_parse_keyvals(msg.section(' ', 3)); 00301 switch (e) { 00302 case ClientStatus: 00303 handleClientStatusEvent(severity, status, args); 00304 break; 00305 00306 case ServerStatus: 00307 handleServerStatusEvent(severity, status, args); 00308 break; 00309 00310 case GeneralStatus: 00311 handleGeneralStatusEvent(severity, status, args); 00312 break; 00313 00314 default: 00315 break; 00316 } 00317 } 00318 00319 /** Parses and emits a general Tor status event. */ 00320 void 00321 TorEvents::handleGeneralStatusEvent(tc::Severity severity, 00322 const QString &action, 00323 const QHash<QString,QString> &args) 00324 { 00325 if (! action.compare("DANGEROUS_TOR_VERSION", Qt::CaseInsensitive)) { 00326 QString reason = args.value("REASON"); 00327 QString current = args.value("CURRENT"); 00328 QStringList recommended = args.value("RECOMMENDED") 00329 .split(",", QString::SkipEmptyParts); 00330 if (! reason.compare("NEW", Qt::CaseInsensitive)) 00331 emit dangerousTorVersion(tc::NewTorVersion, current, recommended); 00332 else if (! reason.compare("UNRECOMMENDED", Qt::CaseInsensitive)) 00333 emit dangerousTorVersion(tc::UnrecommendedTorVersion, current, recommended); 00334 else if (! reason.compare("OBSOLETE", Qt::CaseInsensitive) 00335 || ! reason.compare("OLD", Qt::CaseInsensitive)) 00336 emit dangerousTorVersion(tc::ObsoleteTorVersion, current, recommended); 00337 } else if (! action.compare("CLOCK_SKEW", Qt::CaseInsensitive)) { 00338 int skew; 00339 bool ok = false; 00340 if (args.contains("SKEW")) 00341 skew = args.value("SKEW").toInt(&ok); 00342 else if (args.contains("MIN_SKEW")) 00343 skew = args.value("MIN_SKEW").toInt(&ok); 00344 if (ok) 00345 emit clockSkewed(skew, args.value("SOURCE")); 00346 } else if (! action.compare("BUG", Qt::CaseInsensitive)) { 00347 emit bug(args.value("REASON")); 00348 } 00349 } 00350 00351 /** Parses and emits a Tor client status event. */ 00352 void 00353 TorEvents::handleClientStatusEvent(tc::Severity severity, 00354 const QString &action, 00355 const QHash<QString,QString> &args) 00356 { 00357 if (! action.compare("CIRCUIT_ESTABLISHED", Qt::CaseInsensitive)) { 00358 emit circuitEstablished(); 00359 } else if (! action.compare("DANGEROUS_PORT", Qt::CaseInsensitive)) { 00360 bool reject = ! args.value("RESULT").compare("REJECT", Qt::CaseInsensitive); 00361 emit dangerousPort(args.value("PORT").toUInt(), reject); 00362 } else if (! action.compare("DANGEROUS_SOCKS", Qt::CaseInsensitive)) { 00363 emit socksError(tc::DangerousSocksTypeError, args.value("ADDRESS")); 00364 } else if (! action.compare("SOCKS_UNKNOWN_PROTOCOL", Qt::CaseInsensitive)) { 00365 emit socksError(tc::UnknownSocksProtocolError, QString()); 00366 } else if (! action.compare("SOCKS_BAD_HOSTNAME", Qt::CaseInsensitive)) { 00367 emit socksError(tc::BadSocksHostnameError, args.value("HOSTNAME")); 00368 } else if (! action.compare("BOOTSTRAP", Qt::CaseInsensitive)) { 00369 BootstrapStatus status 00370 = BootstrapStatus(severity, 00371 BootstrapStatus::statusFromString(args.value("TAG")), 00372 args.value("PROGRESS").toInt(), 00373 args.value("SUMMARY"), 00374 args.value("WARNING"), 00375 tc::connectionStatusReasonFromString(args.value("REASON")), 00376 BootstrapStatus::actionFromString( 00377 args.value("RECOMMENDATION"))); 00378 emit bootstrapStatusChanged(status); 00379 } 00380 } 00381 00382 /** Parses and emits a Tor server status event. */ 00383 void 00384 TorEvents::handleServerStatusEvent(tc::Severity severity, 00385 const QString &action, 00386 const QHash<QString,QString> &args) 00387 { 00388 if (! action.compare("EXTERNAL_ADDRESS", Qt::CaseInsensitive)) { 00389 emit externalAddressChanged(QHostAddress(args.value("ADDRESS")), 00390 args.value("HOSTNAME")); 00391 } else if (! action.compare("CHECKING_REACHABILITY", Qt::CaseInsensitive)) { 00392 if (args.contains("ORADDRESS")) { 00393 QPair<QHostAddress,quint16> pair = splitAddress(args.value("ORADDRESS")); 00394 if (! pair.first.isNull()) 00395 emit checkingOrPortReachability(pair.first, pair.second); 00396 } else if (args.contains("DIRADDRESS")) { 00397 QPair<QHostAddress,quint16> pair = splitAddress(args.value("DIRADDRESS")); 00398 if (! pair.first.isNull()) 00399 emit checkingDirPortReachability(pair.first, pair.second); 00400 } 00401 } else if (! action.compare("REACHABILITY_SUCCEEDED", Qt::CaseInsensitive)) { 00402 if (args.contains("ORADDRESS")) { 00403 QPair<QHostAddress,quint16> pair = splitAddress(args.value("ORADDRESS")); 00404 if (! pair.first.isNull()) 00405 emit orPortReachabilityFinished(pair.first, pair.second, true); 00406 } else if (args.contains("DIRADDRESS")) { 00407 QPair<QHostAddress,quint16> pair = splitAddress(args.value("DIRADDRESS")); 00408 if (! pair.first.isNull()) 00409 emit dirPortReachabilityFinished(pair.first, pair.second, true); 00410 } 00411 } else if (! action.compare("REACHABILITY_FAILED", Qt::CaseInsensitive)) { 00412 if (args.contains("ORADDRESS")) { 00413 QPair<QHostAddress,quint16> pair = splitAddress(args.value("ORADDRESS")); 00414 if (! pair.first.isNull()) 00415 emit orPortReachabilityFinished(pair.first, pair.second, false); 00416 } else if (args.contains("DIRADDRESS")) { 00417 QPair<QHostAddress,quint16> pair = splitAddress(args.value("DIRADDRESS")); 00418 if (! pair.first.isNull()) 00419 emit dirPortReachabilityFinished(pair.first, pair.second, false); 00420 } 00421 } else if (! action.compare("GOOD_SERVER_DESCRIPTOR", Qt::CaseInsensitive)) { 00422 emit serverDescriptorAccepted(); 00423 } else if (! action.compare("ACCEPTED_SERVER_DESCRIPTOR", Qt::CaseInsensitive)) { 00424 QPair<QHostAddress,quint16> pair = splitAddress(args.value("DIRAUTH")); 00425 if (! pair.first.isNull()) 00426 emit serverDescriptorAccepted(pair.first, pair.second); 00427 } else if (! action.compare("BAD_SERVER_DESCRIPTOR", Qt::CaseInsensitive)) { 00428 QPair<QHostAddress,quint16> pair = splitAddress(args.value("DIRAUTH")); 00429 if (! pair.first.isNull()) 00430 emit serverDescriptorRejected(pair.first, pair.second, 00431 args.value("REASON")); 00432 } else if (! action.compare("DNS_HIJACKED", Qt::CaseInsensitive)) { 00433 emit dnsHijacked(); 00434 } else if (! action.compare("DNS_USELESS", Qt::CaseInsensitive)) { 00435 emit dnsUseless(); 00436 } 00437 } 00438 00439 /** Splits a string in the form "IP:PORT" into a QHostAddress and quint16 00440 * pair. If either portion is invalid, a default-constructed QPair() is 00441 * returned. */ 00442 QPair<QHostAddress,quint16> 00443 TorEvents::splitAddress(const QString &address) 00444 { 00445 bool ok; 00446 int idx = address.indexOf(":"); 00447 if (idx <= 0 || idx >= address.length()-1) 00448 return QPair<QHostAddress,quint16>(); 00449 00450 QHostAddress ip = QHostAddress(address.mid(0, idx)); 00451 quint16 port = static_cast<quint16>(address.mid(idx+1).toUInt(&ok)); 00452 if (ip.isNull() || ! ok) 00453 return QPair<QHostAddress,quint16>(); 00454 return QPair<QHostAddress,quint16>(ip, port); 00455 } 00456 00457