Vidalia  0.3.1
UPNPControlThread.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 UPNPControlThread.cpp
13 ** \brief Thread for configuring UPnP in the background
14 */
15 
16 #include "UPNPControlThread.h"
17 #include "UPNPControl.h"
18 #include "Vidalia.h"
19 
20 #include <QWaitCondition>
21 #include <QMutex>
22 #include <QTime>
23 #include <QTextStream>
24 #include <QString>
25 #include <QMessageBox>
26 
27 #define UPNPCONTROL_REINIT_MSEC 300000 // 5 minutes
28 #define UPNPCONTROL_MAX_WAIT_MSEC 60000 // 1 minute
29 
30 
31 /** Constructor. <b>control</b> will be used for retrieving the desired port
32  * forwarding state. */
34 {
35  _upnpInitialized = QTime();
36  _keepRunning = true;
37  _control = control;
38 
39  _dirPort = 0;
40  _orPort = 0;
41 
42  _waitCondition = new QWaitCondition();
43  _waitMutex = new QMutex();
44 }
45 
46 /** Destructor. The UPnP control thread must be stopped prior to destroying
47  * this object.
48  * \sa stop()
49  */
51 {
52  delete _waitCondition;
53  delete _waitMutex;
54 }
55 
56 /** Thread entry point. The thread has a main loop that periodically wakes up
57  * and updates the configured port mappings. Upon exiting, all port mappings
58  * will be removed. */
59 void
61 {
62  bool shouldExit = false;
63 
64  forever {
65  /* TODO: Check for switching OR/Dir port */
66  /* TODO: Check for router losing state */
67 
69 
70  /* Wait for something to happen */
71  _waitMutex->lock();
72  if (_keepRunning) {
73  /* We should continue */
76 
77  /* Maybe we were asked to exit while waiting */
78  shouldExit = !_keepRunning;
79  _waitMutex->unlock();
80  if (shouldExit)
81  break;
82  } else {
83  /* We should exit */
84  _waitMutex->unlock();
85  break;
86  }
87  }
88 
89  /* Remove the existing port forwards */
90  updatePort(_dirPort, 0);
91  updatePort(_orPort, 0);
92 }
93 
94 /** Sets up port forwarding according the previously-configured desired state.
95  * The desired state is set using UPNPControl's setDesiredState() method.
96  * \sa UPNPControl::setDesiredState
97  */
98 void
100 {
101  quint16 desiredDirPort, desiredOrPort;
102  bool force_init = false;
104 
105  /* Get desired state */
106  _control->getDesiredState(&desiredDirPort, &desiredOrPort);
107 
108  /* If it's been a while since we initialized the router, or time has gone
109  backward, then maybe the router has gone away or forgotten the forwards.
110  Reset UPnP state, and re-do the port forwarding */
111  if (_upnpInitialized.isNull() || // Is this the first time we have used UPNP?
112  _upnpInitialized>QTime::currentTime() || // Has time gone backwards?
113  _upnpInitialized.addMSecs(UPNPCONTROL_REINIT_MSEC)<QTime::currentTime() // Has it been REINIT_MSEC since initialization
114  ) {
115  _upnpInitialized = QTime();
116  force_init = true;
117  }
118 
119  if (!force_init) {
120  /* Configure DirPort */
121  if (desiredDirPort != _dirPort) {
123  retval = updatePort(_dirPort, desiredDirPort);
124  if (retval == UPNPControl::Success)
125  _dirPort = desiredDirPort;
126  else
127  goto err;
128  }
129 
130  /* Configure ORPort */
131  if (desiredOrPort != _orPort) {
133  retval = updatePort(_orPort, desiredOrPort);
134  if (retval == UPNPControl::Success)
135  _orPort = desiredOrPort;
136  else
137  goto err;
138  }
139  } else {
140  /* Add the mapping even if they exist already */
142  retval = updatePort(0, desiredDirPort);
143  if (retval == UPNPControl::Success)
144  _dirPort = desiredDirPort;
145  else
146  goto err;
147 
149  retval = updatePort(0, desiredOrPort);
150  if (retval == UPNPControl::Success)
151  _orPort = desiredOrPort;
152  else goto err;
153  }
154 
156  return;
157 
158 err:
159  UPNPControl::instance()->setError(retval);
161 }
162 
163 /** Terminates the UPnP control thread's run() loop.
164  * \sa run()
165  */
166 void
168 {
169  /* Lock access to _keepRunning */
170  _waitMutex->lock();
171 
172  /* Ask the thread to stop */
173  _keepRunning = false;
174 
175  /* Wake it up if needed */
176  _waitCondition->wakeAll();
177 
178  /* Unlock shared state */
179  _waitMutex->unlock();
180 
181  /* Wait for it to finish */
182  wait();
183 }
184 
185 /** Wakes up the UPnP control thread's run() loop.
186  * \sa run()
187  */
188 void
190 {
191  _waitMutex->lock();
192  _waitCondition->wakeAll();
193  _waitMutex->unlock();
194 }
195 
196 /** Updates the port mapping for <b>oldPort</b>, changing it to
197  * <b>newPort</b>. */
199 UPNPControlThread::updatePort(quint16 oldPort, quint16 newPort)
200 {
201  UPNPControl::UPNPError retval;
202 
203 #ifdef Q_OS_WIN32
204  // Workaround from http://trolltech.com/developer/knowledgebase/579
205  WSAData wsadata;
206  if (WSAStartup(MAKEWORD(2,0), &wsadata) != 0) {
207  vWarn("WSAStartup failure while updating UPnP port forwarding");
209  }
210 #endif
211 
212  if (_upnpInitialized.isNull() && (oldPort != 0 || newPort != 0)) {
213  retval = initializeUPNP();
214  if (retval == UPNPControl::Success)
215  _upnpInitialized = QTime::currentTime();
216  else
217  _upnpInitialized = QTime();
218  } else {
219  retval = UPNPControl::Success;
220  }
221 
222  if (retval == UPNPControl::Success && oldPort != 0)
223  retval = disablePort(oldPort);
224 
225  if (retval == UPNPControl::Success && newPort != 0)
226  retval = forwardPort(newPort);
227 
228 #ifdef Q_OS_WIN32
229  WSACleanup();
230 #endif
231 
232  return retval;
233 }
234 
235 /** Discovers UPnP-enabled IGDs on the network. Based on
236  * http://miniupnp.free.fr/files/download.php?file=xchat-upnp20061022.patch
237  * This method will block for UPNPCONTROL_DISCOVER_TIMEOUT milliseconds. */
240 {
241  struct UPNPDev *devlist;
242  int retval;
243 
244  memset(&urls, 0, sizeof(struct UPNPUrls));
245  memset(&data, 0, sizeof(struct IGDdatas));
246 
248 
249  devlist = upnpDiscover(UPNPCONTROL_DISCOVER_TIMEOUT, NULL, NULL, 0);
250  if (NULL == devlist) {
251  vWarn("upnpDiscover returned: NULL");
253  }
254 
255  retval = UPNP_GetValidIGD(devlist, &urls, &data, lanaddr, sizeof(lanaddr));
256 
257  vInfo("GetValidIGD returned: %1").arg(retval);
258 
259  freeUPNPDevlist(devlist);
260 
261  if (retval != 1 && retval != 2)
263 
264  return UPNPControl::Success;
265 }
266 
267 /** Adds a port forwarding mapping from external:<b>port</b> to
268  * internal:<b>port</b>. Returns 0 on success, or non-zero on failure. */
271 {
272  QString sPort;
273  int retval;
274 
275  char intClient[16];
276  char intPort[6];
277 
278  // Convert the port number to a string
279  sPort = QString::number(port);
280 
281  // Add the port mapping of external:port -> internal:port
283  qPrintable(sPort), qPrintable(sPort), lanaddr,
284  "Tor relay", "TCP", NULL);
285  if(UPNPCOMMAND_SUCCESS != retval) {
286  vWarn("AddPortMapping(%1, %2, %3) failed with code %4")
287  .arg(sPort).arg(sPort).arg(lanaddr).arg(retval);
289  }
290 
291  // Check if the port mapping was accepted
293  qPrintable(sPort), "TCP",
294  intClient, intPort);
295  if(UPNPCOMMAND_SUCCESS != retval) {
296  vWarn("GetSpecificPortMappingEntry() failed with code %1").arg(retval);
298  }
299 
300  if(! intClient[0]) {
301  vWarn("GetSpecificPortMappingEntry failed.");
303  }
304 
305  // Output the mapping
306  vInfo("(external):%1 -> %2:%3").arg(sPort).arg(intClient).arg(intPort);
307 
308  return UPNPControl::Success;
309 }
310 
311 /** Removes the port mapping for <b>port</b>. Returns 0 on success or non-zero
312  * on failure. */
315 {
316  QString sPort = QString::number(port);
317 
318  // Remove the mapping
320  qPrintable(sPort), "TCP", NULL);
321  if(UPNPCOMMAND_SUCCESS != retval) {
322  vWarn("DeletePortMapping() failed with code %1").arg(retval);
324  }
325 
326  // Output the cancelled mapping
327  vInfo("(external):%1 -> <>").arg(sPort);
328 
329  return UPNPControl::Success;
330 }
331 
UPNPControlThread::configurePorts
void configurePorts()
Definition: UPNPControlThread.cpp:99
UPNPControlThread::~UPNPControlThread
~UPNPControlThread()
Definition: UPNPControlThread.cpp:50
UPNPControlThread::urls
struct UPNPUrls urls
Definition: UPNPControlThread.h:86
err
bool err(QString *str, const QString &errmsg)
Definition: stringutil.cpp:37
UPNPControl::AddPortMappingFailed
@ AddPortMappingFailed
Definition: UPNPControl.h:37
UPNPControlThread::lanaddr
char lanaddr[16]
Definition: UPNPControlThread.h:88
UPNPControl::instance
static UPNPControl * instance()
Definition: UPNPControl.cpp:31
UPNPControlThread::_dirPort
quint16 _dirPort
Definition: UPNPControlThread.h:82
UPNPControlThread::wakeup
void wakeup()
Definition: UPNPControlThread.cpp:189
UPNPUrls::controlURL
char * controlURL
Definition: miniupnpc.h:62
UPNPControlThread::data
struct IGDdatas data
Definition: UPNPControlThread.h:87
UPNPControl::ForwardingCompleteState
@ ForwardingCompleteState
Definition: UPNPControl.h:49
UPNPControl::setError
void setError(UPNPError error)
Definition: UPNPControl.cpp:102
UPNPControl::NoUPNPDevicesFound
@ NoUPNPDevicesFound
Definition: UPNPControl.h:34
UPNPControl::UpdatingDirPortState
@ UpdatingDirPortState
Definition: UPNPControl.h:48
IGDdatas::first
struct IGDdatas_service first
Definition: igd_desc_parse.h:32
UPNPControlThread::disablePort
UPNPControl::UPNPError disablePort(quint16 port)
Definition: UPNPControlThread.cpp:314
UPNPControl::IdleState
@ IdleState
Definition: UPNPControl.h:44
UPNPControlThread::_waitCondition
QWaitCondition * _waitCondition
Definition: UPNPControlThread.h:80
UPNPCONTROL_REINIT_MSEC
#define UPNPCONTROL_REINIT_MSEC
Definition: UPNPControlThread.cpp:27
UPNP_GetSpecificPortMappingEntry
LIBSPEC int UPNP_GetSpecificPortMappingEntry(const char *controlURL, const char *servicetype, const char *extPort, const char *proto, char *intClient, char *intPort)
upnpDiscover
LIBSPEC struct UPNPDev * upnpDiscover(int delay, const char *multicastif, const char *minissdpdsock, int sameport)
UPNPControlThread::initializeUPNP
UPNPControl::UPNPError initializeUPNP()
Definition: UPNPControlThread.cpp:239
UPNPCOMMAND_SUCCESS
#define UPNPCOMMAND_SUCCESS
Definition: upnpcommands.h:14
UPNPControl::setState
void setState(UPNPState state)
Definition: UPNPControl.cpp:114
UPNPDev
Definition: miniupnpc.h:25
UPNPControl::GetPortMappingFailed
@ GetPortMappingFailed
Definition: UPNPControl.h:38
Vidalia.h
UPNPControl::Success
@ Success
Definition: UPNPControl.h:33
UPNPControlThread.h
UPNPControlThread::_waitMutex
QMutex * _waitMutex
Definition: UPNPControlThread.h:81
vInfo
#define vInfo(fmt)
Definition: Vidalia.h:40
UPNPControl::NoValidIGDsFound
@ NoValidIGDsFound
Definition: UPNPControl.h:35
UPNPControlThread::_upnpInitialized
QTime _upnpInitialized
Definition: UPNPControlThread.h:77
UPNPControl::UPNPError
UPNPError
Definition: UPNPControl.h:32
UPNP_AddPortMapping
LIBSPEC int UPNP_AddPortMapping(const char *controlURL, const char *servicetype, const char *extPort, const char *inPort, const char *inClient, const char *desc, const char *proto, const char *remoteHost)
UPNPControl::ErrorState
@ ErrorState
Definition: UPNPControl.h:45
vWarn
#define vWarn(fmt)
Definition: Vidalia.h:42
IGDdatas_service::servicetype
char servicetype[MINIUPNPC_URL_MAXSIZE]
Definition: igd_desc_parse.h:19
UPNP_DeletePortMapping
LIBSPEC int UPNP_DeletePortMapping(const char *controlURL, const char *servicetype, const char *extPort, const char *proto, const char *remoteHost)
UPNPControl::UpdatingORPortState
@ UpdatingORPortState
Definition: UPNPControl.h:47
UPNPControl::DeletePortMappingFailed
@ DeletePortMappingFailed
Definition: UPNPControl.h:39
freeUPNPDevlist
LIBSPEC void freeUPNPDevlist(struct UPNPDev *devlist)
UPNPControl::WSAStartupFailed
@ WSAStartupFailed
Definition: UPNPControl.h:36
UPNPControl::DiscoverState
@ DiscoverState
Definition: UPNPControl.h:46
UPNPControlThread::UPNPControlThread
UPNPControlThread(UPNPControl *control)
Definition: UPNPControlThread.cpp:33
UPNPControlThread::updatePort
UPNPControl::UPNPError updatePort(quint16 oldPort, quint16 newPort)
Definition: UPNPControlThread.cpp:199
UPNPControlThread::_control
UPNPControl * _control
Definition: UPNPControlThread.h:79
UPNPUrls
Definition: miniupnpc.h:61
UPNPControl::getDesiredState
void getDesiredState(quint16 *desiredDirPort, quint16 *desiredOrPort)
Definition: UPNPControl.cpp:78
UPNPCONTROL_MAX_WAIT_MSEC
#define UPNPCONTROL_MAX_WAIT_MSEC
Definition: UPNPControlThread.cpp:28
UPNPControl.h
UPNPControlThread::forwardPort
UPNPControl::UPNPError forwardPort(quint16 port)
Definition: UPNPControlThread.cpp:270
UPNPControlThread::run
void run()
Definition: UPNPControlThread.cpp:60
UPNPControlThread::UPNPCONTROL_DISCOVER_TIMEOUT
static const int UPNPCONTROL_DISCOVER_TIMEOUT
Definition: UPNPControlThread.h:40
UPNPControlThread::stop
void stop()
Definition: UPNPControlThread.cpp:167
IGDdatas
Definition: igd_desc_parse.h:23
UPNPControl
Definition: UPNPControl.h:26
UPNPControlThread::_orPort
quint16 _orPort
Definition: UPNPControlThread.h:83
UPNPControlThread::_keepRunning
bool _keepRunning
Definition: UPNPControlThread.h:78
UPNP_GetValidIGD
LIBSPEC int UPNP_GetValidIGD(struct UPNPDev *devlist, struct UPNPUrls *urls, struct IGDdatas *data, char *lanaddr, int lanaddrlen)