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.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 

Generated on Mon Aug 30 19:14:02 2010 for Vidalia by  doxygen 1.5.9