Vidalia 0.2.15
TorEvents.cpp
Go to the documentation of this file.
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