Vidalia 0.2.12

TorControl.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 TorControl.cpp
00013 ** \brief Object for interacting with the Tor process and control interface
00014 */
00015 
00016 #include "TorControl.h"
00017 #include "RouterDescriptor.h"
00018 #include "ProtocolInfo.h"
00019 #include "RouterStatus.h"
00020 #include "file.h"
00021 #include "stringutil.h"
00022 
00023 #include <QHostAddress>
00024 #include <QVariantMap>
00025 
00026 
00027 /** Default constructor */
00028 TorControl::TorControl(ControlMethod::Method method)
00029 {
00030 #define RELAY_SIGNAL(src, sig) \
00031   QObject::connect((src), (sig), this, (sig))
00032 
00033   /* Create a TorEvents object to receive and parse asynchronous events
00034    * from Tor's control port, and relay them as external signals from
00035    * this TorControl object. */
00036   _eventHandler = new TorEvents(this);
00037   RELAY_SIGNAL(_eventHandler, SIGNAL(circuitEstablished()));
00038   RELAY_SIGNAL(_eventHandler, SIGNAL(dangerousTorVersion(tc::TorVersionStatus,
00039                                                          QString, QStringList)));
00040   RELAY_SIGNAL(_eventHandler, SIGNAL(addressMapped(QString,QString,QDateTime)));
00041   RELAY_SIGNAL(_eventHandler, SIGNAL(bandwidthUpdate(quint64, quint64)));
00042   RELAY_SIGNAL(_eventHandler, SIGNAL(circuitStatusChanged(Circuit)));
00043   RELAY_SIGNAL(_eventHandler, SIGNAL(streamStatusChanged(Stream)));
00044   RELAY_SIGNAL(_eventHandler, SIGNAL(newDescriptors(QStringList)));
00045   RELAY_SIGNAL(_eventHandler, SIGNAL(logMessage(tc::Severity, QString)));
00046   RELAY_SIGNAL(_eventHandler, SIGNAL(dangerousPort(quint16, bool)));
00047   RELAY_SIGNAL(_eventHandler, SIGNAL(socksError(tc::SocksError, QString)));
00048   RELAY_SIGNAL(_eventHandler, SIGNAL(bootstrapStatusChanged(BootstrapStatus)));
00049   RELAY_SIGNAL(_eventHandler, SIGNAL(clockSkewed(int, QString)));
00050   RELAY_SIGNAL(_eventHandler, SIGNAL(bug(QString)));
00051   RELAY_SIGNAL(_eventHandler, SIGNAL(dnsHijacked()));
00052   RELAY_SIGNAL(_eventHandler, SIGNAL(dnsUseless()));
00053   RELAY_SIGNAL(_eventHandler,
00054                SIGNAL(externalAddressChanged(QHostAddress, QString)));
00055   RELAY_SIGNAL(_eventHandler,
00056                SIGNAL(checkingOrPortReachability(QHostAddress, quint16)));
00057   RELAY_SIGNAL(_eventHandler,
00058                SIGNAL(orPortReachabilityFinished(QHostAddress,quint16,bool)));
00059   RELAY_SIGNAL(_eventHandler,
00060                SIGNAL(checkingDirPortReachability(QHostAddress, quint16)));
00061   RELAY_SIGNAL(_eventHandler,
00062                SIGNAL(dirPortReachabilityFinished(QHostAddress,quint16,bool)));
00063   RELAY_SIGNAL(_eventHandler,
00064                SIGNAL(serverDescriptorRejected(QHostAddress, quint16, QString)));
00065   RELAY_SIGNAL(_eventHandler,
00066                SIGNAL(serverDescriptorAccepted(QHostAddress, quint16)));
00067   RELAY_SIGNAL(_eventHandler, SIGNAL(serverDescriptorAccepted()));
00068 
00069   /* Create an instance of a connection to Tor's control interface and give
00070    * it an object to use to handle asynchronous events. */
00071   _controlConn = new ControlConnection(method, _eventHandler);
00072   RELAY_SIGNAL(_controlConn, SIGNAL(connected()));
00073   RELAY_SIGNAL(_controlConn, SIGNAL(connectFailed(QString)));
00074   QObject::connect(_controlConn, SIGNAL(disconnected()),
00075                    this, SLOT(onDisconnected()));
00076 
00077   /* Create an object used to start and stop a Tor process. */
00078   _torProcess = new TorProcess(this);
00079   RELAY_SIGNAL(_torProcess, SIGNAL(started()));
00080   RELAY_SIGNAL(_torProcess, SIGNAL(startFailed(QString)));
00081   QObject::connect(_torProcess, SIGNAL(finished(int, QProcess::ExitStatus)),
00082                    this, SLOT(onStopped(int, QProcess::ExitStatus)));
00083   QObject::connect(_torProcess, SIGNAL(log(QString, QString)),
00084                    this, SLOT(onLogStdout(QString, QString)));
00085 
00086 #if defined(Q_OS_WIN32)
00087   _torService = new TorService(this);
00088   RELAY_SIGNAL(_torService, SIGNAL(started()));
00089   RELAY_SIGNAL(_torService, SIGNAL(startFailed(QString)));
00090   QObject::connect(_torService, SIGNAL(finished(int, QProcess::ExitStatus)),
00091                    this, SLOT(onStopped(int, QProcess::ExitStatus)));
00092 #endif
00093 #undef RELAY_SIGNAL
00094   _method = method;
00095 }
00096 
00097 /** Default destructor */
00098 TorControl::~TorControl()
00099 {
00100   /* Disconnect the control socket */
00101   if (isConnected()) {
00102     disconnect();
00103   }
00104   /* If we started our own Tor, stop it now */
00105   if (isVidaliaRunningTor()) {
00106     stop();
00107   }
00108   delete _controlConn;
00109 }
00110 
00111 /** Start the Tor process using the executable <b>tor</b> and the list of
00112  * arguments in <b>args</b>. */
00113 void
00114 TorControl::start(const QString &tor, const QStringList &args)
00115 {
00116   if (isRunning()) {
00117     emit started();
00118   } else {
00119 #if defined(Q_OS_WIN32)
00120     /* If the Tor service is installed, run that. Otherwise, start a new
00121      * Tor process. */
00122     if (TorService::isSupported() && _torService->isInstalled())
00123       _torService->start();
00124     else
00125       _torProcess->start(expand_filename(tor), args);
00126 #else
00127     /* Start a new Tor process */
00128     _torProcess->start(expand_filename(tor), args);
00129 #endif
00130   }
00131 }
00132 
00133 /** Stop the Tor process. */
00134 bool
00135 TorControl::stop(QString *errmsg)
00136 {
00137   bool rc = false;
00138   if (_controlConn->isConnected())
00139     rc = signal(TorSignal::Halt, errmsg);
00140   if (!rc)
00141     rc = _torProcess->stop(errmsg);
00142   return rc;
00143 }
00144 
00145 /** Emits a signal that the Tor process stopped */
00146 void
00147 TorControl::onStopped(int exitCode, QProcess::ExitStatus exitStatus)
00148 {
00149   if (_controlConn->status() == ControlConnection::Connecting)
00150     _controlConn->cancelConnect();
00151 
00152   emit stopped();
00153   emit stopped(exitCode, exitStatus);
00154 }
00155 
00156 /** Detects if the Tor process is running under Vidalia. Returns true if
00157  * Vidalia owns the Tor process, or false if it was an independent Tor. */
00158 bool
00159 TorControl::isVidaliaRunningTor()
00160 {
00161   return (_torProcess->state() != QProcess::NotRunning);
00162 }
00163 
00164 /** Detect if the Tor process is running. */
00165 bool
00166 TorControl::isRunning()
00167 {
00168   return (_torProcess->state() != QProcess::NotRunning
00169             || _controlConn->isConnected());
00170 }
00171 
00172 /** Stops reading log messages from the Tor process's stdout. This has no
00173  * effect if isVidaliaRunningTor() is false. */
00174 void
00175 TorControl::closeTorStdout()
00176 {
00177   if (_torProcess)
00178     _torProcess->closeStdout();
00179 }
00180 
00181 /** Called when Tor has printed a log message to stdout. */
00182 void
00183 TorControl::onLogStdout(const QString &severity, const QString &message)
00184 {
00185   emit logMessage(tc::severityFromString(severity), message.trimmed());
00186 }
00187 
00188 /** Connect to Tor's control port. The control port to use is determined by
00189  * Vidalia's configuration file. */
00190 void
00191 TorControl::connect(const QHostAddress &address, quint16 port)
00192 {
00193   _controlConn->connect(address, port);
00194 }
00195 
00196 /** Connect to Tor's control socket. The control socket to use is determined by
00197  * Vidalia's configuration file. */
00198 void
00199 TorControl::connect(const QString &path)
00200 {
00201   _controlConn->connect(path);
00202 }
00203 
00204 /** Disconnect from Tor's control port */
00205 void
00206 TorControl::disconnect()
00207 {
00208   if (isConnected())
00209     _controlConn->disconnect();
00210 }
00211 
00212 /** Emits a signal that the control socket disconnected from Tor */
00213 void
00214 TorControl::onDisconnected()
00215 {
00216   if (_torProcess) {
00217     /* If we're running a Tor process, then start reading logs from stdout
00218      * again, in case our control connection just died but Tor is still
00219      * running. In this case, there may be relevant information in the logs. */
00220     _torProcess->openStdout();
00221   }
00222   /* Tor isn't running, so it has no version */
00223   _torVersion = QString();
00224 
00225   /* Let interested parties know we lost our control connection */
00226   emit disconnected();
00227 }
00228 
00229 /** Check if the control socket is connected */
00230 bool
00231 TorControl::isConnected()
00232 {
00233   return _controlConn->isConnected();
00234 }
00235 
00236 /** Send a message to Tor and reads the response. If Vidalia was unable to
00237  * send the command to Tor or read its response, false is returned. If the
00238  * response was read and the status code is not 250 OK, false is also
00239  * returned. */
00240 bool
00241 TorControl::send(ControlCommand cmd, ControlReply &reply, QString *errmsg)
00242 {
00243   if (_controlConn->send(cmd, reply, errmsg)) {
00244     if (reply.getStatus() == "250") {
00245       return true;
00246     }
00247     if (errmsg) {
00248       *errmsg = reply.getMessage();
00249     }
00250   }
00251   return false;
00252 }
00253 
00254 /** Sends a message to Tor and discards the response. */
00255 bool
00256 TorControl::send(ControlCommand cmd, QString *errmsg)
00257 {
00258   ControlReply reply;
00259   return send(cmd, reply, errmsg);
00260 }
00261 
00262 /** Sends an authentication cookie to Tor. The syntax is:
00263  *
00264  *   "AUTHENTICATE" SP 1*HEXDIG CRLF
00265  */
00266 bool
00267 TorControl::authenticate(const QByteArray cookie, QString *errmsg)
00268 {
00269   ControlCommand cmd("AUTHENTICATE", base16_encode(cookie));
00270   ControlReply reply;
00271   QString str;
00272 
00273   if (!send(cmd, reply, &str)) {
00274     emit authenticationFailed(str);
00275     return err(errmsg, str);
00276   }
00277   onAuthenticated();
00278   return true;
00279 }
00280 
00281 /** Sends an authentication password to Tor. The syntax is:
00282  *
00283  *   "AUTHENTICATE" SP QuotedString CRLF
00284  */
00285 bool
00286 TorControl::authenticate(const QString &password, QString *errmsg)
00287 {
00288   ControlCommand cmd("AUTHENTICATE", string_escape(password));
00289   ControlReply reply;
00290   QString str;
00291 
00292   if (!send(cmd, reply, &str)) {
00293     emit authenticationFailed(str);
00294     return err(errmsg, str);
00295   }
00296   onAuthenticated();
00297   return true;
00298 }
00299 
00300 /** Called when the controller has successfully authenticated to Tor. */
00301 void
00302 TorControl::onAuthenticated()
00303 {
00304   /* The version of Tor isn't going to change while we're connected to it, so
00305    * save it for later. */
00306   getInfo("version", _torVersion);
00307   /* We want to use verbose names in events and GETINFO results. */
00308   useFeature("VERBOSE_NAMES");
00309   /* We want to use extended events in all async events */
00310   useFeature("EXTENDED_EVENTS");
00311 
00312   emit authenticated();
00313 }
00314 
00315 /** Sends a PROTOCOLINFO command to Tor and parses the response. */
00316 ProtocolInfo
00317 TorControl::protocolInfo(QString *errmsg)
00318 {
00319   ControlCommand cmd("PROTOCOLINFO", "1");
00320   ControlReply reply;
00321   ProtocolInfo pi;
00322   QString msg, topic;
00323   QHash<QString,QString> keyvals;
00324   int idx;
00325   bool ok;
00326 
00327   if (!send(cmd, reply, errmsg))
00328     return ProtocolInfo();
00329 
00330   foreach (ReplyLine line, reply.getLines()) {
00331     if (line.getStatus() != "250")
00332       continue;
00333 
00334     msg = line.getMessage().trimmed();
00335     idx = msg.indexOf(" ");
00336     topic = msg.mid(0, idx).toUpper();
00337 
00338     if (idx > 0) {
00339       keyvals = string_parse_keyvals(msg.mid(idx+1), &ok);
00340       if (!ok)
00341         continue; /* Ignore malformed lines */
00342     } else {
00343       keyvals = QHash<QString,QString>();
00344     }
00345 
00346     if (topic == "AUTH") {
00347       if (keyvals.contains("METHODS"))
00348         pi.setAuthMethods(keyvals.value("METHODS"));
00349       if (keyvals.contains("COOKIEFILE"))
00350         pi.setCookieAuthFile(keyvals.value("COOKIEFILE"));
00351     } else if (topic == "VERSION") {
00352       if (keyvals.contains("Tor"))
00353         pi.setTorVersion(keyvals.value("Tor"));
00354     }
00355   }
00356   return pi;
00357 }
00358 
00359 /** Tells Tor the controller wants to enable <b>feature</b> via the
00360  * USEFEATURE control command. Returns true if the given feature was
00361  * successfully enabled. */
00362 bool
00363 TorControl::useFeature(const QString &feature, QString *errmsg)
00364 {
00365   ControlCommand cmd("USEFEATURE", feature);
00366   return send(cmd, errmsg);
00367 }
00368 
00369 BootstrapStatus
00370 TorControl::bootstrapStatus(QString *errmsg)
00371 {
00372   QString str = getInfo("status/bootstrap-phase").toString();
00373   if (!str.isEmpty()) {
00374     tc::Severity severity = tc::severityFromString(str.section(' ', 0, 0));
00375     QHash<QString,QString> args = string_parse_keyvals(str);
00376     return BootstrapStatus(severity,
00377               BootstrapStatus::statusFromString(args.value("TAG")),
00378               args.value("PROGRESS").toInt(),
00379               args.value("SUMMARY"),
00380               args.value("WARNING"),
00381               tc::connectionStatusReasonFromString(args.value("REASON")),
00382               BootstrapStatus::actionFromString(
00383                 args.value("RECOMMENDATION")));
00384   }
00385   return BootstrapStatus();
00386 }
00387 
00388 /** Returns true if Tor either has an open circuit or (on Tor >=
00389  * 0.2.0.1-alpha) has previously decided it's able to establish a circuit. */
00390 bool
00391 TorControl::isCircuitEstablished()
00392 {
00393   /* If Tor is recent enough, we can 'getinfo status/circuit-established' to
00394    * see if Tor has an open circuit */
00395   if (getTorVersion() >= 0x020001) {
00396     QString tmp;
00397     if (getInfo("status/circuit-established", tmp))
00398       return (tmp == "1");
00399   }
00400 
00401   /* Either Tor was too old or our getinfo failed, so try to get a list of all
00402    * circuits and check their statuses. */
00403   CircuitList circs = getCircuits();
00404   foreach (Circuit circ, circs) {
00405     if (circ.status() == Circuit::Built)
00406       return true;
00407   }
00408   return false;
00409 }
00410 
00411 /** Sends a GETINFO message to Tor based on the given map of keyvals. The
00412  * syntax is:
00413  *
00414  *    "GETINFO" 1*(SP keyword) CRLF
00415  */
00416 bool
00417 TorControl::getInfo(QHash<QString,QString> &map, QString *errmsg)
00418 {
00419   ControlCommand cmd("GETINFO");
00420   ControlReply reply;
00421 
00422   /* Add the keys as arguments to the GETINFO message */
00423   foreach (QString key, map.keys()) {
00424     cmd.addArgument(key);
00425   }
00426 
00427   /* Ask Tor for the specified info values */
00428   if (send(cmd, reply, errmsg)) {
00429     /* Parse the response for the returned values */
00430     foreach (ReplyLine line, reply.getLines()) {
00431       /* Split the "key=val" line and map them */
00432       QStringList keyval = line.getMessage().split("=");
00433       if (keyval.size() == 2) {
00434         QString key = keyval.at(0);
00435         QString val = keyval.at(1);
00436         if (val.startsWith(QLatin1Char('\"')) &&
00437             val.endsWith(QLatin1Char('\"'))) {
00438           bool ok;
00439           val = string_unescape(val, &ok);
00440           if (! ok)
00441             continue;
00442         }
00443         map.insert(key, val);
00444       }
00445     }
00446     return true;
00447   }
00448   return false;
00449 }
00450 
00451 /** Sends a GETINFO message to Tor using the given list of <b>keys</b> and
00452  * returns a QVariantMap containing the specified keys and their values as
00453  * returned  by Tor. Returns a default constructed QVariantMap on failure. */
00454 QVariantMap
00455 TorControl::getInfo(const QStringList &keys, QString *errmsg)
00456 {
00457   ControlCommand cmd("GETINFO");
00458   ControlReply reply;
00459   QVariantMap infoMap;
00460 
00461   cmd.addArguments(keys);
00462   if (!send(cmd, reply, errmsg))
00463     return QVariantMap();
00464 
00465   foreach (ReplyLine line, reply.getLines()) {
00466     QString msg = line.getMessage();
00467     int index   = msg.indexOf("=");
00468     QString key = msg.mid(0, index);
00469     QStringList val;
00470 
00471     if (index > 0 && index < msg.length()-1) {
00472       QString str = msg.mid(index+1);
00473       if (str.startsWith(QLatin1Char('\"')) &&
00474           str.endsWith(QLatin1Char('\"'))) {
00475         bool ok;
00476         str = string_unescape(str, &ok);
00477         if (! ok)
00478           continue;
00479       }
00480       val << str;
00481     }
00482     if (line.hasData())
00483       val << line.getData();
00484 
00485     if (infoMap.contains(key)) {
00486       QStringList values = infoMap.value(key).toStringList();
00487       values << val;
00488       infoMap.insert(key, values);
00489     } else {
00490       infoMap.insert(key, val);
00491     }
00492   }
00493   return infoMap;
00494 }
00495 
00496 /** Sends a GETINFO message to Tor with a single <b>key</b> and returns a
00497  * QVariant containing the value returned by Tor. Returns a default
00498  * constructed QVariant on failure. */
00499 QVariant
00500 TorControl::getInfo(const QString &key, QString *errmsg)
00501 {
00502   QVariantMap map = getInfo(QStringList() << key, errmsg);
00503   return map.value(key);
00504 }
00505 
00506 /** Overloaded method to send a GETINFO command for a single info value */
00507 bool
00508 TorControl::getInfo(QString key, QString &val, QString *errmsg)
00509 {
00510   QHash<QString,QString> map;
00511   map.insert(key, "");
00512 
00513   if (getInfo(map, errmsg)) {
00514     val = map.value(key);
00515     return true;
00516   }
00517   return false;
00518 }
00519 
00520 /** Sends a signal to Tor */
00521 bool
00522 TorControl::signal(TorSignal::Signal sig, QString *errmsg)
00523 {
00524   ControlCommand cmd("SIGNAL");
00525   cmd.addArgument(TorSignal::toString(sig));
00526 
00527   if (sig == TorSignal::Shutdown || sig == TorSignal::Halt) {
00528     /* Tor closes the connection before giving us a response to any commands
00529      * asking it to stop running, so don't try to get a response. */
00530     return _controlConn->send(cmd, errmsg);
00531   }
00532   return send(cmd, errmsg);
00533 }
00534 
00535 /** Returns an address on which Tor is listening for application
00536  * requests. If none are available, a null QHostAddress is returned. */
00537 QHostAddress
00538 TorControl::getSocksAddress(QString *errmsg)
00539 {
00540   QHostAddress socksAddr;
00541 
00542   /* If SocksPort is 0, then Tor is not accepting any application requests. */
00543   if (getSocksPort() == 0) {
00544     return QHostAddress::Null;
00545   }
00546 
00547   /* Get a list of SocksListenAddress lines and return the first valid IP
00548    * address parsed from the list. */
00549   QStringList addrList = getSocksAddressList(errmsg);
00550   foreach (QString addr, addrList) {
00551     addr = addr.mid(0, addr.indexOf(":"));
00552     if (socksAddr.setAddress(addr)) {
00553       return socksAddr;
00554     }
00555   }
00556   /* Otherwise Tor is listening on its default 127.0.0.1 */
00557   return QHostAddress::LocalHost;
00558 }
00559 
00560 /** Returns a (possibly empty) list of all currently configured
00561  * SocksListenAddress entries. */
00562 QStringList
00563 TorControl::getSocksAddressList(QString *errmsg)
00564 {
00565   QStringList addrList;
00566   if (getConf("SocksListenAddress", addrList, errmsg)) {
00567     return addrList;
00568   }
00569   return QStringList();
00570 }
00571 
00572 /** Returns a valid SOCKS port for Tor, or 0 if Tor is not accepting
00573  * application requests. */
00574 quint16
00575 TorControl::getSocksPort(QString *errmsg)
00576 {
00577   QList<quint16> portList = getSocksPortList(errmsg);
00578   if (portList.size() > 0) {
00579     return portList.at(0);
00580   }
00581   return 0;
00582 }
00583 
00584 /** Returns a list of all currently configured SOCKS ports. If Tor is not
00585  * accepting any application connections, an empty list will be returned. */
00586 QList<quint16>
00587 TorControl::getSocksPortList(QString *errmsg)
00588 {
00589   bool valid;
00590   quint16 port, socksPort;
00591   QString portString;
00592   QList<quint16> portList;
00593 
00594   /* Get the value of the SocksPort configuration variable */
00595   if (getConf("SocksPort", portString, errmsg)) {
00596     socksPort = (quint16)portString.toUInt(&valid);
00597     if (valid) {
00598       if (socksPort == 0) {
00599         /* A SocksPort of 0 means Tor is not accepting any application
00600          * connections. */
00601         return QList<quint16>();
00602       }
00603     }
00604   }
00605   /* Get a list of SOCKS ports from SocksListenAddress entries */
00606   QStringList addrList = getSocksAddressList(errmsg);
00607   foreach (QString addr, addrList) {
00608     if (addr.contains(":")) {
00609       portString = addr.mid(addr.indexOf(":")+1);
00610       port = (quint16)portString.toUInt(&valid);
00611       if (valid) {
00612         portList << port;
00613       }
00614     }
00615   }
00616   /* If there were no SocksListenAddress entries, or one or more of them did
00617    * not specify a port, then add the value of SocksPort, too */
00618   if (!portList.size() || (portList.size() != addrList.size())) {
00619     portList << socksPort;
00620   }
00621   return portList;
00622 }
00623 
00624 /** Reeturns Tor's version as a string. */
00625 QString
00626 TorControl::getTorVersionString()
00627 {
00628   return _torVersion;
00629 }
00630 
00631 /** Returns Tor's version as a numeric value. Note that this discards any
00632  * version status flag, such as "-alpha" or "-rc". */
00633 quint32
00634 TorControl::getTorVersion()
00635 {
00636   static QString versionString;
00637   static quint32 version = 0;
00638   quint8 major, minor, micro, patch;
00639 
00640   /* Only recompute the version number if the version string changed */
00641   if (versionString == _torVersion)
00642     return version;
00643   versionString = _torVersion;
00644 
00645   /* Split the version string at either "." or "-" characters */
00646   QStringList parts = versionString.split(QRegExp("\\.|-|\\ "));
00647   if (parts.size() >= 4) {
00648     major = (quint8)parts.at(0).toUInt();
00649     minor = (quint8)parts.at(1).toUInt();
00650     micro = (quint8)parts.at(2).toUInt();
00651     patch = (quint8)parts.at(3).toUInt();
00652     version = ((major << 24) | (minor << 16) | (micro << 8) | patch);
00653   } else {
00654     /* Couldn't parse the version string */
00655     version = 0;
00656   }
00657   return version;
00658 }
00659 
00660 /** Sets an event and its handler. If add is true, then the event is added,
00661  * otherwise it is removed. If set is true, then the given event will be
00662  * registered with Tor. */
00663 bool
00664 TorControl::setEvent(TorEvents::Event e, bool add, bool set, QString *errmsg)
00665 {
00666   _events = (add ? (_events | e) : (_events & ~e));
00667   if (set && isConnected())
00668     return setEvents(errmsg);
00669   return true;
00670 }
00671 
00672 /** Register for the events currently in the event list */
00673 bool
00674 TorControl::setEvents(QString *errmsg)
00675 {
00676   ControlCommand cmd("SETEVENTS");
00677 
00678   for (TorEvents::Event e = TorEvents::EVENT_MIN; e <= TorEvents::EVENT_MAX;) {
00679     if (_events & e)
00680       cmd.addArgument(TorEvents::toString(e));
00681     e = static_cast<TorEvents::Event>(e << 1);
00682   }
00683   return send(cmd, errmsg);
00684 }
00685 
00686 /** Sets each configuration key in <b>map</b> to the value associated
00687  * with its key. */
00688 bool
00689 TorControl::setConf(QHash<QString,QString> map, QString *errmsg)
00690 {
00691   ControlCommand cmd("SETCONF");
00692 
00693   /* Add each keyvalue to the argument list */
00694   foreach (QString key, map.uniqueKeys()) {
00695     /* If a key has multiple values (e.g., a QMultiHash), they are stored
00696      * in order from most recently inserted to least recently inserted.
00697      * So, walk the list in reverse so that we append the configuration
00698      * values to the SETCONF command in the same order they were inserted
00699      * into the QHash. */
00700     QList<QString> values = map.values(key);
00701     for (int i = values.size()-1; i >= 0; i--) {
00702       QString value = values.at(i);
00703       if (value.length() > 0)
00704         cmd.addArgument(key + "=" + string_escape(value));
00705       else
00706         cmd.addArgument(key);
00707     }
00708   }
00709   return send(cmd, errmsg);
00710 }
00711 
00712 /** Sets a single configuration key to the given value. */
00713 bool
00714 TorControl::setConf(QString key, QString value, QString *errmsg)
00715 {
00716   QHash<QString,QString> map;
00717   map.insert(key, value);
00718   return setConf(map, errmsg);
00719 }
00720 
00721 /** Sets a single configuration string that is formatted <key=escaped value>.*/
00722 bool
00723 TorControl::setConf(QString keyAndValue, QString *errmsg)
00724 {
00725   QHash<QString,QString> map;
00726   map.insert(keyAndValue, "");
00727   return setConf(map, errmsg);
00728 }
00729 
00730 /** Gets values for a set of configuration keys, each of which has a single
00731  * value. */
00732 bool
00733 TorControl::getConf(QHash<QString,QString> &map, QString *errmsg)
00734 {
00735   QHash<QString,QStringList> multiMap;
00736   foreach (QString key, map.keys()) {
00737     multiMap.insert(key, QStringList());
00738   }
00739   if (getConf(multiMap, errmsg)) {
00740     foreach (QString key, multiMap.keys()) {
00741       if (map.contains(key)) {
00742         map.insert(key, multiMap.value(key).join("\n"));
00743       }
00744     }
00745   }
00746   return false;
00747 }
00748 
00749 /** Gets a set of configuration keyvalues and stores them in <b>map</b>. */
00750 bool
00751 TorControl::getConf(QHash<QString,QStringList> &map, QString *errmsg)
00752 {
00753   ControlCommand cmd("GETCONF");
00754   ControlReply reply;
00755   QStringList confValue;
00756   QString confKey;
00757 
00758   /* Add the keys as arguments to the GETINFO message */
00759   foreach (QString key, map.keys()) {
00760     cmd.addArgument(key);
00761   }
00762 
00763   /* Ask Tor for the specified info values */
00764   if (send(cmd, reply, errmsg)) {
00765     /* Parse the response for the returned values */
00766     foreach (ReplyLine line, reply.getLines()) {
00767       /* Split the "key=val" line and map them */
00768       QStringList keyval = line.getMessage().split("=");
00769       if (keyval.size() == 2) {
00770         confKey = keyval.at(0);
00771 
00772         if (map.contains(confKey)) {
00773           /* This configuration key has multiple values, so add this one to
00774            * the list. */
00775           confValue = map.value(confKey);
00776         }
00777         confValue << keyval.at(1);
00778         map.insert(keyval.at(0), confValue);
00779       }
00780     }
00781     return true;
00782   }
00783   return false;
00784 }
00785 
00786 /** Gets a single configuration value for <b>key</b>. */
00787 bool
00788 TorControl::getConf(QString key, QString &value, QString *errmsg)
00789 {
00790   QStringList confValues;
00791   if (getConf(key, confValues, errmsg)) {
00792     value = confValues.join("\n");
00793     return true;
00794   }
00795   return false;
00796 }
00797 
00798 /** Gets a list of configuration values for <b>key</b>. */
00799 bool
00800 TorControl::getConf(QString key, QStringList &value, QString *errmsg)
00801 {
00802   QHash<QString,QStringList> map;
00803   map.insert(key, QStringList());
00804 
00805   if (getConf(map, errmsg)) {
00806     value = map.value(key);
00807     return true;
00808   }
00809   return false;
00810 }
00811 
00812 /** Sends a GETICONF message to Tor using the given list of <b>keys</b> and
00813  * returns a QVariantMap containing the specified keys and their values as
00814  * returned  by Tor. Returns a default constructed QVariantMap on failure. */
00815 QVariantMap
00816 TorControl::getConf(const QStringList &keys, QString *errmsg)
00817 {
00818   ControlCommand cmd("GETCONF");
00819   ControlReply reply;
00820   QVariantMap confMap;
00821 
00822   cmd.addArguments(keys);
00823   if (!send(cmd, reply, errmsg))
00824     return QVariantMap();
00825 
00826   foreach (ReplyLine line, reply.getLines()) {
00827     QString msg = line.getMessage();
00828     int index   = msg.indexOf("=");
00829     QString key = msg.mid(0, index);
00830     QString val;
00831 
00832     if (index > 0 && index < msg.length()-1)
00833       val = msg.mid(index+1);
00834     if (val.startsWith(QLatin1Char('\"')) &&
00835         val.endsWith(QLatin1Char('\"'))) {
00836       bool ok;
00837       val = string_unescape(val, &ok);
00838       if (! ok)
00839         continue;
00840     }
00841 
00842     if (confMap.contains(key)) {
00843       QStringList values = confMap.value(key).toStringList();
00844       values << val;
00845       confMap.insert(key, values);
00846     } else {
00847       confMap.insert(key, val);
00848     }
00849   }
00850   return confMap;
00851 }
00852 
00853 /** Sends a GETCONF message to Tor with a single <b>key</b> and returns a
00854  * QVariant containing the value returned by Tor. Returns a default
00855  * constructed QVariant on failure. */
00856 QVariant
00857 TorControl::getConf(const QString &key, QString *errmsg)
00858 {
00859   QVariantMap map = getConf(QStringList() << key, errmsg);
00860   return map.value(key);
00861 }
00862 
00863 /** Sends a GETCONF message to Tor with the single key and returns a QString
00864  * containing the value returned by Tor */
00865 QString
00866 TorControl::getHiddenServiceConf(const QString &key, QString *errmsg)
00867 {
00868   ControlCommand cmd("GETCONF");
00869   ControlReply reply;
00870   QVariantMap confMap;
00871 
00872   cmd.addArgument(key);
00873   if (!send(cmd, reply, errmsg))
00874     return "";
00875 
00876   return reply.toString();
00877 }
00878 
00879 /** Asks Tor to save the current configuration to its torrc. */
00880 bool
00881 TorControl::saveConf(QString *errmsg)
00882 {
00883   ControlCommand cmd("SAVECONF");
00884   return send(cmd, errmsg);
00885 }
00886 
00887 /** Tells Tor to reset the given configuration keys back to defaults. */
00888 bool
00889 TorControl::resetConf(QStringList keys, QString *errmsg)
00890 {
00891   ControlCommand cmd("RESETCONF");
00892 
00893   /* Add each key to the argument list */
00894   foreach (QString key, keys) {
00895     cmd.addArgument(key);
00896   }
00897   return send(cmd, errmsg);
00898 }
00899 
00900 /** Tells Tor to reset a single given configuration key back to its default
00901  * value. */
00902 bool
00903 TorControl::resetConf(QString key, QString *errmsg)
00904 {
00905   return resetConf(QStringList() << key, errmsg);
00906 }
00907 
00908 /** Returns an unparsed router descriptor for the router whose fingerprint
00909  * matches <b>id</b>. The returned text can later be parsed by the
00910  * RouterDescriptor class. If <b>id</b> is invalid, then an empty
00911  * QStringList is returned. */
00912 QStringList
00913 TorControl::getRouterDescriptorText(const QString &id, QString *errmsg)
00914 {
00915   return getInfo("desc/id/" + id, errmsg).toStringList();
00916 }
00917 
00918 /** Returns the descriptor for the router whose fingerprint matches
00919  * <b>id</b>. If <b>id</b> is invalid or the router's descriptor cannot
00920  * be parsed, then an invalid RouterDescriptor is returned. */
00921 RouterDescriptor
00922 TorControl::getRouterDescriptor(const QString &id, QString *errmsg)
00923 {
00924   return RouterDescriptor(getRouterDescriptorText(id, errmsg));
00925 }
00926 
00927 /** Returns the status of the router whose fingerprint matches <b>id</b>. If
00928  * <b>id</b> is invalid or the router's status cannot be parsed, then an
00929  * invalid RouterStatus is returned. */
00930 RouterStatus
00931 TorControl::getRouterStatus(const QString &id, QString *errmsg)
00932 {
00933   QStringList status = getInfo("ns/id/" + id, errmsg).toStringList();
00934   return RouterStatus(status);
00935 }
00936 
00937 /** Returns a RouterStatus object for every known router in the network. If
00938  * the network status document cannot be parsed, then an empty NetworkStatus
00939  * is returned. */
00940 NetworkStatus
00941 TorControl::getNetworkStatus(QString *errmsg)
00942 {
00943   QStringList networkStatusLines = getInfo("ns/all", errmsg).toStringList();
00944   QList<RouterStatus> networkStatus;
00945   int len = networkStatusLines.size();
00946   int i = 0;
00947 
00948   while (i < len) {
00949     /* Extract the "r", "s", and whatever other status lines */
00950     QStringList routerStatusLines;
00951     do {
00952       routerStatusLines << networkStatusLines.at(i);
00953     } while (++i < len && ! networkStatusLines.at(i).startsWith("r "));
00954 
00955     /* Create a new RouterStatus object and add it to the network status, if
00956      * it's valid. */
00957     RouterStatus routerStatus(routerStatusLines);
00958     if (routerStatus.isValid())
00959       networkStatus << routerStatus;
00960   }
00961   return networkStatus;
00962 }
00963 
00964 /** Returns the annotations for the router whose fingerprint matches
00965  * <b>id</b>. If <b>id</b> is invalid or the router's annotations cannot be
00966  * parsed, then an empty DescriptorAnnotations is returned and <b>errmsg</b>
00967  * is set if it's not NULL. (Tor >= 0.2.0.13-alpha only) */
00968 DescriptorAnnotations
00969 TorControl::getDescriptorAnnotations(const QString &id, QString *errmsg)
00970 {
00971   QStringList lines = getInfo("desc-annotations/id/"+id, errmsg).toStringList();
00972   DescriptorAnnotations annotations;
00973   QString key, value;
00974 
00975   foreach (QString line, lines) {
00976     int idx = line.indexOf(" ");
00977 
00978     /* Extract the annotation key */
00979     key = line.mid(0, idx);
00980     if (key.startsWith("@"))
00981       key = key.remove(0, 1);
00982 
00983     /* Extract the annotation value (if present) */
00984     if (idx > 0 && idx < line.length()-1)
00985       annotations.insert(key, line.mid(idx + 1).trimmed());
00986     else
00987       annotations.insert(key, QString());
00988   }
00989   return annotations;
00990 }
00991 
00992 /** Gets a list of current circuits. */
00993 QList<Circuit>
00994 TorControl::getCircuits(QString *errmsg)
00995 {
00996   ControlCommand cmd("GETINFO", "circuit-status");
00997   ControlReply reply;
00998   CircuitList circuits;
00999 
01000   if (!send(cmd, reply, errmsg))
01001     return CircuitList();
01002 
01003   /* The rest of the circuits just come as data, one per line */
01004   foreach(QString line, reply.getData()) {
01005     Circuit circ(line);
01006     if (circ.isValid())
01007       circuits << circ;
01008   }
01009   return circuits;
01010 }
01011 
01012 /** Closes the circuit specified by <b>circId</b>. If <b>ifUnused</b> is
01013  * true, then the circuit will not be closed unless it is unused. */
01014 bool
01015 TorControl::closeCircuit(const CircuitId &circId, bool ifUnused, QString *errmsg)
01016 {
01017   ControlCommand cmd("CLOSECIRCUIT", circId);
01018   if (ifUnused) {
01019     cmd.addArgument("IfUnused");
01020   }
01021   return send(cmd, errmsg);
01022 }
01023 
01024 /** Gets a list of current streams. */
01025 QList<Stream>
01026 TorControl::getStreams(QString *errmsg)
01027 {
01028   ControlCommand cmd("GETINFO", "stream-status");
01029   ControlReply reply;
01030   QList<Stream> streams;
01031   Stream s;
01032 
01033   if (send(cmd, reply, errmsg)) {
01034     /* Sometimes there is a stream on the first message line */
01035     QString msg = reply.getMessage();
01036     s = Stream::fromString(msg.mid(msg.indexOf("=")+1));
01037     if (s.isValid())
01038       streams << s;
01039 
01040     /* The rest of the streams just come as data, one per line */
01041     foreach (QString line, reply.getData()) {
01042       s = Stream::fromString(line);
01043       if (s.isValid())
01044         streams << s;
01045     }
01046   }
01047   return streams;
01048 }
01049 
01050 /** Closes the stream specified by <b>streamId</b>. */
01051 bool
01052 TorControl::closeStream(const StreamId &streamId, QString *errmsg)
01053 {
01054   ControlCommand cmd("CLOSESTREAM", streamId);
01055   cmd.addArgument("1"); /* 1 == REASON_MISC (tor-spec.txt) */
01056   return send(cmd, errmsg);
01057 }
01058 
01059  /** Gets a list of address mappings of the type specified by <b>type</b>
01060   * (defaults to <i>AddressMapAll</i>. */
01061 AddressMap
01062 TorControl::getAddressMap(AddressMap::AddressMapType type, QString *errmsg)
01063 {
01064   AddressMap addressMap;
01065   QStringList entries;
01066 
01067   switch (type) {
01068     case AddressMap::AddressMapConfig:
01069       entries = getInfo("address-mappings/config").toStringList();
01070       break;
01071     case AddressMap::AddressMapCache:
01072       entries = getInfo("address-mappings/cache").toStringList();
01073       break;
01074     case AddressMap::AddressMapControl:
01075       entries = getInfo("address-mappings/control").toStringList();
01076       break;
01077     default:
01078       entries = getInfo("address-mappings/all").toStringList();
01079   }
01080 
01081   foreach (QString entry, entries) {
01082     addressMap.add(entry);
01083   }
01084   return addressMap;
01085 }
01086 
01087 /** Gets the ISO-3166 two-letter country code for <b>ip</b> from Tor.
01088  * Returns a default-constructed QString on failure or if a country code
01089  * is not known for <b>ip</b>. On failure, <b>errmsg</b> will be set if
01090  * it's not NULL. */
01091 QString
01092 TorControl::ipToCountry(const QHostAddress &ip, QString *errmsg)
01093 {
01094   QString request   = QString("ip-to-country/%1").arg(ip.toString());
01095   QVariant response = getInfo(request, errmsg);
01096   if (! response.isNull())
01097     return response.toString();
01098   return QString();
01099 }
01100