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