Vidalia
0.2.15
|
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 }