Vidalia  0.2.15
ControlSocket.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 ControlSocket.cpp
00013 ** \brief Socket used to connect to Tor's control interface
00014 */
00015 
00016 #include "ControlSocket.h"
00017 #include "SendCommandEvent.h"
00018 #include "tcglobal.h"
00019 
00020 #include "stringutil.h"
00021 
00022 /** Timeout reads in 250ms. We can set this to a short value because if there
00023 * isn't any data to read, we want to return anyway. */
00024 #define READ_TIMEOUT  250
00025 
00026 
00027 /** Default constructor. */
00028 ControlSocket::ControlSocket(ControlMethod::Method method)
00029 {
00030   _tcpSocket = new QTcpSocket();
00031   _localSocket = new QLocalSocket();
00032   _method = method;
00033   switch(_method) {
00034     case ControlMethod::Port:
00035       _socket = _tcpSocket;
00036       break;
00037       
00038     case ControlMethod::Socket:
00039       _socket = _localSocket;
00040       break;
00041   }
00042 
00043   QObject::connect(_socket, SIGNAL(readyRead()), this, SIGNAL(readyRead()));
00044   QObject::connect(_socket, SIGNAL(disconnected()), this, SIGNAL(disconnected()));
00045   QObject::connect(_socket, SIGNAL(connected()), this, SIGNAL(connected()));
00046   QObject::connect(_socket, SIGNAL(error(QAbstractSocket::SocketError)), 
00047                    this, SIGNAL(error(QAbstractSocket::SocketError)));
00048 }
00049 
00050 /** Returns true if the control socket is connected and ready to send or
00051  * receive. */
00052 bool
00053 ControlSocket::isConnected()
00054 {
00055   switch(_method) {
00056     case ControlMethod::Port:
00057       return (_tcpSocket->isValid() && _tcpSocket->state() == QAbstractSocket::ConnectedState);
00058       break;
00059       
00060     default:
00061     case ControlMethod::Socket:
00062       return (_localSocket->isValid() && _localSocket->state() == QLocalSocket::ConnectedState);
00063       break;
00064   }
00065 }
00066 
00067 /** Connects to address:port */
00068 void 
00069 ControlSocket::connectToHost(const QHostAddress &address, quint16 port)
00070 {
00071   _tcpSocket->connectToHost(address, port);
00072 }
00073 
00074 /** Disconnects from host */
00075 void 
00076 ControlSocket::disconnectFromHost()
00077 {
00078   _tcpSocket->disconnectFromHost();
00079 }
00080 
00081 /** Connects to a unix socket file */
00082 void 
00083 ControlSocket::connectToServer(const QString &name)
00084 {
00085   _localSocket->connectToServer(name);
00086 }
00087 
00088 /** Disconnects from the socket */
00089 void 
00090 ControlSocket::disconnectFromServer()
00091 {
00092   _localSocket->disconnectFromServer();
00093 }
00094 
00095 /** Interface to QTcpSocket::canReadLine */
00096 bool
00097 ControlSocket::canReadLine()
00098 {
00099   return _socket->canReadLine();
00100 }
00101 
00102 /** Returns the string description of <b>error</b>. */
00103 QString
00104 ControlSocket::toString(const QAbstractSocket::SocketError error)
00105 {
00106   QString str;
00107   switch (error) {
00108     case QAbstractSocket::ConnectionRefusedError:
00109       str = "Connection refused by peer."; break;
00110     case QAbstractSocket::RemoteHostClosedError:
00111       str = "Remote host closed the connection."; break;
00112     case QAbstractSocket::HostNotFoundError:
00113       str = "Host address not found."; break;
00114     case QAbstractSocket::SocketAccessError:
00115       str = "Insufficient access privileges."; break;
00116     case QAbstractSocket::SocketResourceError:
00117       str = "Insufficient resources."; break;
00118     case QAbstractSocket::SocketTimeoutError:
00119       str = "Socket operation timed out."; break;
00120     case QAbstractSocket::DatagramTooLargeError:
00121       str = "Datagram size exceeded the operating system limit."; break;
00122     case QAbstractSocket::NetworkError:
00123       str = "Network error occurred."; break;
00124     case QAbstractSocket::AddressInUseError:
00125       str = "Specified address already in use."; break;
00126     case QAbstractSocket::SocketAddressNotAvailableError:
00127       str = "Specified address does not belong to the host."; break;
00128     case QAbstractSocket::UnsupportedSocketOperationError:
00129       str = "The requested operation is not supported."; break;
00130     default:
00131       str = "An unidentified error occurred."; break;
00132   }
00133   return str;
00134 }
00135 
00136 /** Processes custom events sent to this object (e.g. SendCommandEvents) from
00137  * other threads. */
00138 void
00139 ControlSocket::customEvent(QEvent *event)
00140 {
00141   if (event->type() == QEvent::User) {
00142     SendCommandEvent *sce = dynamic_cast<SendCommandEvent *>(event);
00143     if (! sce)
00144       return;
00145 
00146     QString errmsg;
00147     bool result = sendCommand(sce->command(), &errmsg);
00148     if (sce->waiter())
00149       sce->waiter()->setResult(result, errmsg);
00150     sce->accept();
00151   }
00152 }
00153 
00154 /** Send a control command to Tor on the control socket, conforming to Tor's
00155  * Control Protocol V1:
00156  *
00157  *   Command = Keyword Arguments CRLF / "+" Keyword Arguments CRLF Data
00158  *   Keyword = 1*ALPHA
00159  *   Arguments = *(SP / VCHAR)
00160  */
00161 bool
00162 ControlSocket::sendCommand(ControlCommand cmd, QString *errmsg)
00163 {  
00164   if (!isConnected()) {
00165     return err(errmsg, tr("Control socket is not connected."));
00166   }
00167   
00168   /* Format the control command */
00169   QString strCmd = cmd.toString();
00170   tc::debug("Control Command: %1").arg(strCmd.trimmed());
00171 
00172   /* Attempt to send the command to Tor */
00173   if (_socket->write(strCmd.toLocal8Bit()) != strCmd.length()) {
00174     return err(errmsg, tr("Error sending control command. [%1]")
00175                                             .arg(_socket->errorString()));
00176   }
00177   switch(_method) {
00178     case ControlMethod::Port:
00179       _tcpSocket->flush();
00180       break;
00181       
00182     case ControlMethod::Socket:
00183       _localSocket->flush();
00184       break;
00185   }
00186   return true;
00187 }
00188 
00189 
00190 /** Read a complete reply from the control socket. Replies take the following
00191  * form, based on Tor's Control Protocol v1:
00192  *
00193  *    Reply = *(MidReplyLine / DataReplyLine) EndReplyLine
00194  *
00195  *    MidReplyLine = "-" ReplyLine
00196  *    DataReplyLine = "+" ReplyLine Data
00197  *    EndReplyLine = SP ReplyLine
00198  *    ReplyLine = StatusCode [ SP ReplyText ]  CRLF
00199  *    ReplyText = XXXX
00200  *    StatusCode = XXiX
00201  */
00202 bool
00203 ControlSocket::readReply(ControlReply &reply, QString *errmsg)
00204 {
00205   QChar c;
00206   QString line;
00207 
00208   if (!isConnected()) {
00209     return false;
00210   }
00211 
00212   /* The implementation below is (loosely) based on the Java control library
00213    * from Tor */
00214   do {
00215     /* Read a line of the response */
00216     if (!readLine(line, errmsg)) {
00217       return false;
00218     }
00219     
00220     if (line.length() < 4) {
00221       return err(errmsg, tr("Invalid control reply. [%1]").arg(line));
00222     }
00223 
00224     /* Parse the status and message */
00225     ReplyLine replyLine(line.mid(0, 3), line.mid(4));
00226     c = line.at(3);
00227 
00228     /* If the reply line contains data, then parse out the data up until the
00229      * trailing CRLF "." CRLF */
00230     if (c == QChar('+') &&
00231         !line.startsWith("250+PROTOCOLINFO")) {
00232         /* XXX The second condition above is a hack to deal with Tor
00233          * 0.2.0.5-alpha that gives a malformed PROTOCOLINFO reply. This
00234          * should be removed once that version of Tor is sufficiently dead. */
00235       while (true) {
00236         if (!readLine(line, errmsg)) {
00237           return false;
00238         }
00239         if (line.trimmed() == ".") {
00240           break;
00241         }
00242         replyLine.appendData(line);
00243       }
00244     }
00245     reply.appendLine(replyLine);
00246   } while (c != QChar(' '));
00247   return true;
00248 }
00249 
00250 /** Reads line data, one chunk at a time, until a newline character is
00251  * encountered. */
00252 bool
00253 ControlSocket::readLineData(QString &line, QString *errmsg)
00254 {
00255   char buffer[1024];  /* Read in 1024 byte chunks at a time */
00256   int bytesRecv = _socket->readLine(buffer, 1024);
00257   while (bytesRecv != -1) {
00258     line.append(QString::fromLocal8Bit(buffer, bytesRecv));
00259     if (buffer[bytesRecv-1] == '\n') {
00260       break;
00261     }
00262     bytesRecv = _socket->readLine(buffer, 1024);
00263   }
00264   if (bytesRecv == -1) {
00265     return err(errmsg, _socket->errorString());
00266   }
00267   return true;
00268 }
00269 
00270 /** Reads a line of data from the socket and returns true if successful or
00271  * false if an error occurred while waiting for a line of data to become
00272  * available. */
00273 bool
00274 ControlSocket::readLine(QString &line, QString *errmsg)
00275 {
00276   /* Make sure we have data to read before attempting anything. Note that this
00277    * essentially makes our socket a blocking socket */
00278   while (!_socket->canReadLine()) {
00279     if (!isConnected()) {
00280       return err(errmsg, tr("Socket disconnected while attempting "
00281                             "to read a line of data."));
00282     }
00283     _socket->waitForReadyRead(READ_TIMEOUT);
00284   }
00285   line.clear();
00286   return readLineData(line, errmsg);
00287 }