Vidalia  0.3.1
ControlConnection.cpp
Go to the documentation of this file.
1 /*
2 ** This file is part of Vidalia, and is subject to the license terms in the
3 ** LICENSE file, found in the top level directory of this distribution. If
4 ** you did not receive the LICENSE file with this file, you may obtain it
5 ** from the Vidalia source package distributed by the Vidalia Project at
6 ** http://www.torproject.org/projects/vidalia.html. No part of Vidalia,
7 ** including this file, may be copied, modified, propagated, or distributed
8 ** except according to the terms described in the LICENSE file.
9 */
10 
11 /*
12 ** \file ControlConnection.cpp
13 ** \brief A connection to Tor's control interface, responsible for sending and
14 ** receiving commands and events
15 */
16 
17 #include "ControlConnection.h"
18 #include "tcglobal.h"
19 #include "stringutil.h"
20 
21 #include <QCoreApplication>
22 #include <QMutexLocker>
23 
24 /** Maximum number of times we'll try to connect to Tor before giving up.*/
25 #define MAX_CONNECT_ATTEMPTS 5
26 /** Time to wait between control connection attempts (in milliseconds). */
27 #define CONNECT_RETRY_DELAY 2*1000
28 
29 
30 /** Default constructor. */
32 {
33  _events = events;
34  _status = Unset;
35  _sock = 0;
37  _method = method;
38 }
39 
40 /** Destructor. */
42 {
43  /* Exit the event loop */
44  exit();
45  /* Wait for the thread to finish */
46  wait();
47  /* Clean up after the send waiter */
48  delete _sendWaiter;
49 }
50 
51 /** Connect to the specified Tor control interface. */
52 void
53 ControlConnection::connect(const QHostAddress &addr, quint16 port)
54 {
55  if (isRunning()) {
56  tc::error("Bug: Tried to call ControlConnection::connect() when the "
57  "control thread is already running.");
58  return;
59  }
60 
61  /* Save the destination information */
62  _addr = addr;
63  _port = port;
64  _sock = 0;
65  _connectAttempt = 0;
67 
68  /* Kick off the thread in which the control socket will live */
69  QThread::start();
70 }
71 
72 /** Connect to the specified Tor control socket interface. */
73 void
74 ControlConnection::connect(const QString &addr)
75 {
76  if (isRunning()) {
77  tc::error("Bug: Tried to call ControlConnection::connect() when the "
78  "control thread is already running.");
79  return;
80  }
81 
82  _path = addr;
83  _connectAttempt = 0;
85 
86  /* Kick off the thread in which the control socket will live */
87  QThread::start();
88 }
89 
90 /** Attempt to establish a connection to Tor's control interface. We will try
91  * a maximum of MAX_CONNECT_ATTEMPTS, waiting CONNECT_RETRY_DELAY between each
92  * attempt, to give slow Tors a chance to finish binding their control port. */
93 void
95 {
97  tc::debug("Connecting to Tor (Attempt %1 of %2)").arg(_connectAttempt)
99 
100  _connMutex.lock();
101  switch(_method) {
104  break;
105 
106  default:
107  case ControlMethod::Port:
109  break;
110  }
111  _connMutex.unlock();
112 }
113 
114 /** Disconnect from Tor's control interface. */
115 void
117 {
119  _connMutex.lock();
120  switch(_method) {
123  break;
124 
125  default:
126  case ControlMethod::Port:
128  break;
129  }
130  _connMutex.unlock();
131 }
132 
133 /** Called when the control socket is connected. This method checks that the
134  * control protocol version of the Tor we connected to is at least V1. */
135 void
137 {
139  emit connected();
140 }
141 
142 /** Called when the control socket is disconnected and stops the control
143  * thread's event loop. */
144 void
146 {
148  emit disconnected();
149  exit(0);
150 }
151 
152 /** Called when the control socket encounters <b>error</b>. */
153 void
154 ControlConnection::onError(QAbstractSocket::SocketError error)
155 {
156  if (status() == Connecting) {
157  /* If we got a 'connection refused' and we haven't exceeded
158  * MAX_CONNECT_ATTEMPTS, then try to reconnect since Tor is probably
159  * running, but it doesn't have a ControlSocket open yet. */
160  if (error == QAbstractSocket::ConnectionRefusedError &&
162  tc::debug("Control connection refused. Retrying in %1ms.")
165  } else {
166  /* Exceeded maximum number of connect attempts. Give up. */
167  QString errstr = ControlSocket::toString(error);
168  tc::error("Vidalia was unable to connect to Tor: %1").arg(errstr);
169  emit connectFailed(tr("Vidalia was unable to connect to Tor. (%1)")
170  .arg(errstr));
172  }
173  } else if (error == QAbstractSocket::RemoteHostClosedError) {
174  /* Tor closed the connection. This is common when we send a 'shutdown' or
175  * 'halt' signal to Tor and doesn't need to be logged as loudly. */
176  tc::warn("Tor closed the control connection.");
177  } else {
178  /* Some other error. */
179  /*XXX We may want to be emitting these so the GUI thread can learn about
180  * them and display an error message. */
181  tc::error("Control socket error: %1").arg(ControlSocket::toString(error));
182  }
183 }
184 
185 /** Cancels a pending control connection to Tor. */
186 void
188 {
189  tc::warn("Control connection attempt cancelled.");
191  exit(0);
192 }
193 
194 /** Returns true if the control socket is connected to Tor. */
195 bool
197 {
198  return (status() == Connected);
199 }
200 
201 /** Returns the status of the control connection. */
204 {
205  QMutexLocker locker(&_statusMutex);
206  return _status;
207 }
208 
209 /** Returns a string description of the control Status value
210  * <b>status</b>. */
211 QString
213 {
214  QString str;
215  switch (status) {
216  case Unset: str = "Unset"; break;
217  case Disconnected: str = "Disconnected"; break;
218  case Disconnecting: str = "Disconnecting"; break;
219  case Connecting: str = "Connecting"; break;
220  case Connected: str = "Connected"; break;
221  default: str = "unknown";
222  }
223  return str;
224 }
225 
226 /** Sets the control connection status. */
227 void
229 {
230  QMutexLocker locker(&_statusMutex);
231  tc::debug("Control connection status changed from '%1' to '%2'")
234  _status = status;
235 }
236 
237 /** Sends a control command to Tor and waits for the reply. */
238 bool
240  ControlReply &reply, QString *errmsg)
241 {
242  bool result = false;
243  QString errstr;
244 
245  _recvMutex.lock();
246  if (send(cmd, &errstr)) {
247  /* Create and enqueue a new receive waiter */
248  ReceiveWaiter *w = new ReceiveWaiter();
249  _recvQueue.enqueue(w);
250  _recvMutex.unlock();
251 
252  /* Wait for and get the result, clean up, and return */
253  result = w->getResult(&reply, &errstr);
254  if (!result)
255  tc::error("Failed to receive control reply: %1").arg(errstr);
256  delete w;
257  } else {
258  tc::error("Failed to send control command (%1): %2").arg(cmd.keyword())
259  .arg(errstr);
260  _recvMutex.unlock();
261  }
262 
263  if (!result && errmsg)
264  *errmsg = errstr;
265  return result;
266 }
267 
268 /** Sends a control command to Tor and returns true if the command was sent
269  * successfully. Otherwise, returns false and <b>*errmsg</b> (if supplied)
270  * will be set. */
271 bool
272 ControlConnection::send(const ControlCommand &cmd, QString *errmsg)
273 {
274  _connMutex.lock();
275  if (!_sock || !_sock->isConnected()) {
276  _connMutex.unlock();
277  return err(errmsg, tr("Control socket is not connected."));
278  }
279  QCoreApplication::postEvent(_sock, new SendCommandEvent(cmd, _sendWaiter));
280  _connMutex.unlock();
281 
282  return _sendWaiter->getResult(errmsg);
283 }
284 
285 /** Called when there is data on the control socket. */
286 void
288 {
289  QMutexLocker locker(&_connMutex);
290  ReceiveWaiter *waiter;
291  QString errmsg;
292 
293  while (_sock->canReadLine()) {
294  ControlReply reply;
295  if (_sock->readReply(reply, &errmsg)) {
296  if (reply.getStatus() == "650") {
297  /* Asynchronous event message */
298  tc::debug("Control Event: %1").arg(reply.toString());
299 
300  if (_events) {
301  _events->handleEvent(reply);
302  }
303  } else {
304  /* Response to a previous command */
305  tc::debug("Control Reply: %1").arg(reply.toString());
306 
307  _recvMutex.lock();
308  if (!_recvQueue.isEmpty()) {
309  waiter = _recvQueue.dequeue();
310  waiter->setResult(true, reply);
311  }
312  _recvMutex.unlock();
313  }
314  } else {
315  tc::error("Unable to read control reply: %1").arg(errmsg);
316  }
317  }
318 }
319 
320 /** Main thread implementation. Creates and connects a control socket, then
321  * spins up an event loop. */
322 void
324 {
325  /* Create a new control socket */
326  _connMutex.lock();
327  _sock = new ControlSocket(_method);
328 
329  _connectTimer = new QTimer();
330  _connectTimer->setSingleShot(true);
331 
332  QObject::connect(_sock, SIGNAL(readyRead()), this, SLOT(onReadyRead()),
333  Qt::DirectConnection);
334  QObject::connect(_sock, SIGNAL(disconnected()), this, SLOT(onDisconnected()),
335  Qt::DirectConnection);
336  QObject::connect(_sock, SIGNAL(connected()), this, SLOT(onConnected()),
337  Qt::DirectConnection);
338  QObject::connect(_sock, SIGNAL(error(QAbstractSocket::SocketError)),
339  this, SLOT(onError(QAbstractSocket::SocketError)),
340  Qt::DirectConnection);
341  QObject::connect(_connectTimer, SIGNAL(timeout()), this, SLOT(connect()),
342  Qt::DirectConnection);
343 
344  _connMutex.unlock();
345 
346  /* Attempt to connect to Tor */
347  connect();
348  tc::debug("Starting control connection event loop.");
349  exec();
350  tc::debug("Exited control connection event loop.");
351 
352  /* Clean up the socket */
353  _connMutex.lock();
354  _sock->disconnect(this);
355  delete _sock;
356  delete _connectTimer;
357  _sock = 0;
358  _connMutex.unlock();
359 
360  /* If there are any messages waiting for a response, clear them. */
362  _sendWaiter->setResult(false, tr("Control socket is not connected."));
363 
364  _recvMutex.lock();
365  while (!_recvQueue.isEmpty()) {
366  ReceiveWaiter *w = _recvQueue.dequeue();
367  w->setResult(false, ControlReply(),
368  tr("Control socket is not connected."));
369  }
370  _recvMutex.unlock();
371 }
372 
373 
374 /*
375  * ControlConnection::ReceiveWaiter
376  */
377 /** Waits for and gets the reply from a control command. */
378 bool
380  QString *errmsg)
381 {
382  forever {
383  _mutex.lock();
384  if (_status == Waiting) {
385  _waitCond.wait(&_mutex);
386  _mutex.unlock();
387  } else {
388  _mutex.unlock();
389  break;
390  }
391  }
392  if (errmsg) {
393  *errmsg = _errmsg;
394  }
395  *reply = _reply;
396  return (_status == Success);
397 }
398 
399 /** Sets the result and reply from a control command. */
400 void
402  const ControlReply &reply,
403  const QString &errmsg)
404 {
405  _mutex.lock();
406  _status = (success ? Success : Failed);
407  _reply = reply;
408  _errmsg = errmsg;
409  _mutex.unlock();
410  _waitCond.wakeAll();
411 }
412 
SendCommandEvent
Definition: SendCommandEvent.h:27
SendCommandEvent::SendWaiter::setResult
void setResult(bool success, const QString &errmsg=QString())
Definition: SendCommandEvent.cpp:31
ControlConnection::Connecting
@ Connecting
Definition: ControlConnection.h:42
ControlConnection::run
void run()
Definition: ControlConnection.cpp:323
ControlConnection::ReceiveWaiter::_status
enum ControlConnection::ReceiveWaiter::ReceiveStatus _status
err
bool err(QString *str, const QString &errmsg)
Definition: stringutil.cpp:37
ControlConnection::setStatus
void setStatus(Status status)
Definition: ControlConnection.cpp:228
ControlConnection.h
ControlConnection::ReceiveWaiter::_errmsg
QString _errmsg
Definition: ControlConnection.h:126
ControlConnection::ReceiveWaiter::_reply
ControlReply _reply
Definition: ControlConnection.h:123
ControlConnection::_connMutex
QMutex _connMutex
Definition: ControlConnection.h:103
CONNECT_RETRY_DELAY
#define CONNECT_RETRY_DELAY
Definition: ControlConnection.cpp:27
ControlConnection::ReceiveWaiter::Success
@ Success
Definition: ControlConnection.h:122
tcglobal.h
ControlConnection::_connectAttempt
int _connectAttempt
Definition: ControlConnection.h:106
ControlConnection::Disconnecting
@ Disconnecting
Definition: ControlConnection.h:41
ControlConnection::_recvQueue
QQueue< ReceiveWaiter * > _recvQueue
Definition: ControlConnection.h:128
ControlConnection::_addr
QHostAddress _addr
Definition: ControlConnection.h:101
ControlReply::toString
QString toString() const
Definition: ControlReply.cpp:68
ControlSocket::canReadLine
bool canReadLine()
Definition: ControlSocket.cpp:97
ControlConnection::disconnect
void disconnect()
Definition: ControlConnection.cpp:116
ControlConnection::_statusMutex
QMutex _statusMutex
Definition: ControlConnection.h:105
ControlConnection::ControlConnection
ControlConnection(ControlMethod::Method method, TorEvents *events=0)
Definition: ControlConnection.cpp:31
ControlConnection::connectFailed
void connectFailed(QString errmsg)
tc::warn
DebugMessage warn(const QString &fmt)
Definition: tcglobal.cpp:32
ControlSocket::connectToServer
void connectToServer(const QString &name)
Definition: ControlSocket.cpp:83
ControlConnection::connect
void connect()
Definition: ControlConnection.cpp:94
tc::DebugMessage::arg
DebugMessage arg(const QString &a)
Definition: tcglobal.h:48
ControlCommand
Definition: ControlCommand.h:22
ControlSocket::disconnectFromHost
void disconnectFromHost()
Definition: ControlSocket.cpp:76
ControlConnection::send
bool send(const ControlCommand &cmd, ControlReply &reply, QString *errmsg=0)
Definition: ControlConnection.cpp:239
ControlReply
Definition: ControlReply.h:24
ControlConnection::Connected
@ Connected
Definition: ControlConnection.h:43
ControlConnection::ReceiveWaiter
Definition: ControlConnection.h:111
ControlConnection::isConnected
bool isConnected()
Definition: ControlConnection.cpp:196
tc::debug
DebugMessage debug(const QString &fmt)
Definition: tcglobal.cpp:24
ControlConnection::_events
TorEvents * _events
Definition: ControlConnection.h:99
TorEvents
Definition: TorEvents.h:37
ControlConnection::ReceiveWaiter::Waiting
@ Waiting
Definition: ControlConnection.h:122
ControlConnection::onReadyRead
void onReadyRead()
Definition: ControlConnection.cpp:287
ControlConnection::_connectTimer
QTimer * _connectTimer
Definition: ControlConnection.h:108
ControlMethod::Port
@ Port
Definition: ControlMethod.h:24
stringutil.h
ControlSocket
Definition: ControlSocket.h:27
ControlConnection::cancelConnect
void cancelConnect()
Definition: ControlConnection.cpp:187
ControlConnection::_sock
ControlSocket * _sock
Definition: ControlConnection.h:96
ControlConnection::_status
Status _status
Definition: ControlConnection.h:100
ControlConnection::ReceiveWaiter::_waitCond
QWaitCondition _waitCond
Definition: ControlConnection.h:125
SendCommandEvent::SendWaiter::Waiting
@ Waiting
Definition: SendCommandEvent.h:33
ControlReply::getStatus
QString getStatus() const
Definition: ControlReply.cpp:47
TorEvents::handleEvent
void handleEvent(const ControlReply &reply)
Definition: TorEvents.cpp:124
ControlConnection::onError
void onError(QAbstractSocket::SocketError error)
Definition: ControlConnection.cpp:154
connect
stop errmsg connect(const QHostAddress &address, quint16 port)
SendCommandEvent::SendWaiter::status
SenderStatus status()
Definition: SendCommandEvent.cpp:62
ControlMethod::Socket
@ Socket
Definition: ControlMethod.h:24
ControlConnection::_method
ControlMethod::Method _method
Definition: ControlConnection.h:97
ControlSocket::disconnectFromServer
void disconnectFromServer()
Definition: ControlSocket.cpp:90
ControlConnection::status
Status status()
Definition: ControlConnection.cpp:203
ControlConnection::Unset
@ Unset
Definition: ControlConnection.h:39
MAX_CONNECT_ATTEMPTS
#define MAX_CONNECT_ATTEMPTS
Definition: ControlConnection.cpp:25
ControlConnection::disconnected
void disconnected()
ControlConnection::Disconnected
@ Disconnected
Definition: ControlConnection.h:40
ControlConnection::onConnected
void onConnected()
Definition: ControlConnection.cpp:136
ControlSocket::readReply
bool readReply(ControlReply &reply, QString *errmsg=0)
Definition: ControlSocket.cpp:203
SendCommandEvent::SendWaiter::getResult
bool getResult(QString *errmsg=0)
Definition: SendCommandEvent.cpp:42
ControlMethod::Method
Method
Definition: ControlMethod.h:24
ControlSocket::isConnected
bool isConnected()
Definition: ControlSocket.cpp:53
ControlConnection::connected
void connected()
ControlConnection::Status
Status
Definition: ControlConnection.h:38
ControlConnection::_sendWaiter
SendCommandEvent::SendWaiter * _sendWaiter
Definition: ControlConnection.h:129
ControlConnection::ReceiveWaiter::getResult
bool getResult(ControlReply *reply, QString *errmsg=0)
Definition: ControlConnection.cpp:379
ControlConnection::~ControlConnection
~ControlConnection()
Definition: ControlConnection.cpp:41
ControlCommand::keyword
QString keyword() const
Definition: ControlCommand.h:31
ControlConnection::onDisconnected
void onDisconnected()
Definition: ControlConnection.cpp:145
ControlSocket::connectToHost
void connectToHost(const QHostAddress &address, quint16 port)
Definition: ControlSocket.cpp:69
ControlConnection::statusString
QString statusString(Status status)
Definition: ControlConnection.cpp:212
tc::error
DebugMessage error(const QString &fmt)
Definition: tcglobal.cpp:40
ControlConnection::_recvMutex
QMutex _recvMutex
Definition: ControlConnection.h:104
ControlConnection::_port
quint16 _port
Definition: ControlConnection.h:102
ControlConnection::ReceiveWaiter::setResult
void setResult(bool success, const ControlReply &reply, const QString &errmsg=QString())
Definition: ControlConnection.cpp:401
SendCommandEvent::SendWaiter
Definition: SendCommandEvent.h:30
ControlConnection::_path
QString _path
Definition: ControlConnection.h:98
ControlConnection::ReceiveWaiter::_mutex
QMutex _mutex
Definition: ControlConnection.h:124
ControlSocket::toString
static QString toString(const QAbstractSocket::SocketError error)
Definition: ControlSocket.cpp:104