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