[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
[vidalia-svn] r2557: Re-write UPNP code so that all UPNP operations occur from on (in vidalia/branches/upnp/src/vidalia: . config)
Author: sjmurdoch
Date: 2008-05-07 11:28:32 -0400 (Wed, 07 May 2008)
New Revision: 2557
Modified:
vidalia/branches/upnp/src/vidalia/config/serversettings.cpp
vidalia/branches/upnp/src/vidalia/config/serversettings.h
vidalia/branches/upnp/src/vidalia/config/upnpcontrol.cpp
vidalia/branches/upnp/src/vidalia/config/upnpcontrol.h
vidalia/branches/upnp/src/vidalia/config/upnpcontrolthread.cpp
vidalia/branches/upnp/src/vidalia/config/upnpcontrolthread.h
vidalia/branches/upnp/src/vidalia/mainwindow.cpp
Log:
Re-write UPNP code so that all UPNP operations occur from one thread. This makes the logic much cleaner, but there is some fairly subtle locking going on.
Modified: vidalia/branches/upnp/src/vidalia/config/serversettings.cpp
===================================================================
--- vidalia/branches/upnp/src/vidalia/config/serversettings.cpp 2008-05-06 21:21:51 UTC (rev 2556)
+++ vidalia/branches/upnp/src/vidalia/config/serversettings.cpp 2008-05-07 15:28:32 UTC (rev 2557)
@@ -134,10 +134,8 @@
{
bool rc;
+ configurePortForwarding();
if (isServerEnabled()) {
- /* Configure UPnP device to forward DirPort and OrPort */
- /* TODO: does isServerEnabled() return true when a server is just set up? */
- configurePortForwarding(true, true);
rc = torControl()->setConf(confValues(), errmsg);
} else {
QStringList resetKeys;
@@ -156,7 +154,6 @@
<< SETTING_BANDWIDTH_BURST;
}
rc = torControl()->resetConf(resetKeys, errmsg);
- configurePortForwarding(false, true);
}
return rc;
}
@@ -169,7 +166,7 @@
true, will forward ORPort and DirPort; otherwise will remove exising
port mappings */
void
-ServerSettings::configurePortForwarding(bool enable, bool async)
+ServerSettings::configurePortForwarding()
{
#ifdef USE_MINIUPNPC
quint16 ORPort, DirPort;
@@ -182,15 +179,15 @@
return;
ORPort = getORPort();
+ if (!isServerEnabled())
+ ORPort = 0;
+
DirPort = getDirPort();
+ if (!isServerEnabled() || !isDirectoryMirror())
+ DirPort = 0;
- enableORPort = isServerEnabled();
- enableDirPort = enableORPort && isDirectoryMirror();
-
- if (async)
- UPNPControl::configurePortForwardingAsync(enable, enableORPort, enableDirPort, ORPort, DirPort);
- else
- UPNPControl::configurePortForwardingSync(enable, enableORPort, enableDirPort, ORPort, DirPort);
+ UPNPControl *control = UPNPControl::Instance();
+ control->setDesiredState(DirPort, ORPort);
#endif
}
Modified: vidalia/branches/upnp/src/vidalia/config/serversettings.h
===================================================================
--- vidalia/branches/upnp/src/vidalia/config/serversettings.h 2008-05-06 21:21:51 UTC (rev 2556)
+++ vidalia/branches/upnp/src/vidalia/config/serversettings.h 2008-05-07 15:28:32 UTC (rev 2557)
@@ -82,8 +82,8 @@
/** Gets the maximum burst rate (in B/s) of this server. */
quint32 getBandwidthBurstRate();
- /** If enable is true, configure UPnP device to forward ORPort, otherwise remove mapping */
- void configurePortForwarding(bool enable, bool async);
+ /** Configure port forwarding */
+ void configurePortForwarding();
void cleanupPortForwarding();
protected:
Modified: vidalia/branches/upnp/src/vidalia/config/upnpcontrol.cpp
===================================================================
--- vidalia/branches/upnp/src/vidalia/config/upnpcontrol.cpp 2008-05-06 21:21:51 UTC (rev 2556)
+++ vidalia/branches/upnp/src/vidalia/config/upnpcontrol.cpp 2008-05-07 15:28:32 UTC (rev 2557)
@@ -16,14 +16,14 @@
#include "upnpcontrol.h"
+#include <QMutex>
+
#ifdef WIN32
#include <winsock2.h>
#endif
#include "upnpcontrolthread.h"
-#define UPNPCONTROL_DEBUG 1
-
UPNPControl* UPNPControl::pInstance = 0;
UPNPControl* UPNPControl::Instance()
{
@@ -34,264 +34,37 @@
UPNPControl::UPNPControl()
{
- init_upnp();
- forwardedORPort = 0;
- forwardedDirPort = 0;
- controlThread = NULL;
+ _forwardedORPort = 0;
+ _forwardedDirPort = 0;
+ mutex = new QMutex();
+ controlThread = new UPNPControlThread(this);
+ controlThread->start();
}
-int
-UPNPControl::forwardDirPort(quint16 port)
+UPNPControl::~UPNPControl()
{
- if (forwardedPorts.contains(port))
- return 0;
-
- int retval = forwardPort(port);
- if (retval)
- return retval;
- else
- forwardedDirPort = port;
-
- return 0;
+ delete mutex;
+ delete controlThread;
}
-int
-UPNPControl::forwardORPort(quint16 port)
-{
- if (forwardedPorts.contains(port))
- return 0;
+void UPNPControl::cleanup(){
+ pInstance->controlThread->stop();
+ delete pInstance;
+ pInstance = 0;
+}
- int retval = forwardPort(port);
- if (retval)
- return retval;
- else
- forwardedORPort = port;
-
- return 0;
+void UPNPControl::getDesiredState(quint16 *desiredDirPort, quint16 *desiredOrPort) {
+ mutex->lock();
+ *desiredDirPort = _forwardedDirPort;
+ *desiredOrPort = _forwardedORPort;
+ mutex->unlock();
+ controlThread->wakeup();
}
-int
-UPNPControl::disableDirPort()
-{
- int retval = disablePort(forwardedDirPort);
- if (retval)
- return retval;
- else
- forwardedDirPort = 0;
-
- return 0;
+void UPNPControl::setDesiredState(quint16 desiredDirPort, quint16 desiredOrPort) {
+ mutex->lock();
+ _forwardedDirPort = desiredDirPort;
+ _forwardedORPort = desiredOrPort;
+ mutex->unlock();
+ controlThread->wakeup();
}
-
-int
-UPNPControl::disableORPort()
-{
- int retval = disablePort(forwardedORPort);
- if (retval)
- return retval;
- else
- forwardedORPort = 0;
-
- return 0;
-}
-
-int
-UPNPControl::forwardPort(quint16 port)
-{
- int retval;
-
- char sPort[6];
-
- char intClient[16];
- char intPort[6];
-
- // Don't forward port 0
- if (0 == port)
- return 4;
-
- // Convert the port number to a string
- snprintf(sPort, sizeof(sPort), "%d", port);
-
- // Add the port mapping of external:port -> internal:port
- retval = UPNP_AddPortMapping(urls.controlURL, data.servicetype,
- sPort, sPort, lanaddr, "Tor server", "TCP");
- if(UPNPCOMMAND_SUCCESS != retval) {
- #ifdef UPNPCONTROL_DEBUG
- printf("AddPortMapping(%s, %s, %s) failed with code %d\n",
- sPort, sPort, lanaddr, retval);
- fflush(stdout);
- #endif
- return 1;
- }
-
- // Check if the port mapping was accepted
- retval = UPNP_GetSpecificPortMappingEntry(urls.controlURL,
- data.servicetype,
- sPort, "TCP",
- intClient, intPort);
- if(UPNPCOMMAND_SUCCESS != retval) {
- #ifdef UPNPCONTROL_DEBUG
- printf("GetSpecificPortMappingEntry() failed with code %d\n", retval);
- fflush(stdout);
- #endif
- return 2;
- }
-
- if(! intClient[0]) {
- #ifdef UPNPCONTROL_DEBUG
- printf("GetSpecificPortMappingEntry failed.\n");
- fflush(stdout);
- #endif
- return 3;
- }
-
- // Output the mapping
- #ifdef UPNPCONTROL_DEBUG
- printf("(external):%s -> %s:%s\n", sPort, intClient, intPort);
- fflush(stdout);
- #endif
-
- // Save the mapping
- forwardedPorts.append(port);
-
- return 0;
-}
-
-int
-UPNPControl::disablePort(quint16 port)
-{
- char sPort[6];
-
- // Port 0 should never be forwarded
- if (0 == port)
- return 4;
-
- // Convert the port number to a string
- snprintf(sPort, sizeof(sPort), "%d", port);
-
- // Remove the mapping
- int retval = UPNP_DeletePortMapping(urls.controlURL, data.servicetype, sPort, "TCP");
- if(UPNPCOMMAND_SUCCESS != retval) {
-#ifdef UPNPCONTROL_DEBUG
- printf("DeletePortMapping() failed with code %d\n", retval);
- fflush(stdout);
-#endif
- return 1;
- }
-
- // Output the cancelled mapping
-#ifdef UPNPCONTROL_DEBUG
- printf("(external):%s -> <>\n", sPort);
- fflush(stdout);
-#endif
-
-
- // Save the removal
- forwardedPorts.removeAll(port);
-
- return 0;
-}
-
-int
-UPNPControl::disableForwarding()
-{
- char sPort[6];
- quint16 port;
- int retval;
-
- while (!forwardedPorts.isEmpty()) {
- // Get the port number
- port = forwardedPorts.last();
-
- // Disable the port forward
- retval = disablePort(port);
-
- // Check if there was an error
- if (retval)
- return retval;
- }
-
- forwardedDirPort = forwardedORPort = 0;
-
- return 0;
-}
-
-
-/** Based on http://miniupnp.free.fr/files/download.php?file=xchat-upnp20061022.patch */
-void
-UPNPControl::init_upnp()
-{
- struct UPNPDev * devlist;
- int retval;
-
- memset(&urls, 0, sizeof(struct UPNPUrls));
- memset(&data, 0, sizeof(struct IGDdatas));
-
- devlist = upnpDiscover(2000, NULL, NULL);
- retval = UPNP_GetValidIGD(devlist, &urls, &data, lanaddr, sizeof(lanaddr));
-
- #ifdef UPNPCONTROL_DEBUG
- printf("GetValidIGD returned: %d\n", retval);
- fflush(stdout);
- #endif
-
- freeUPNPDevlist(devlist);
-}
-
-void
-UPNPControl::configurePortForwardingAsync(bool enable, bool enableORPort, bool enableDirPort, quint16 ORPort, quint16 DirPort)
-{
- UPNPControlThread *thread = new UPNPControlThread(enable, enableORPort, enableDirPort, ORPort, DirPort);
- thread->start();
- //TODO: How to de-allocate memory? What about locking?
-}
-
-void
-UPNPControl::cleanup()
-{
- if (pInstance!=0 && pInstance->controlThread != NULL) {
- delete pInstance->controlThread;
- pInstance->controlThread = 0;
- }
-}
-
-void
-UPNPControl::setThread(UPNPControlThread *thread)
-{
- UPNPControl::cleanup();
- controlThread = thread;
-}
-
-void
-UPNPControl::configurePortForwardingSync(bool enable, bool enableORPort, bool enableDirPort, quint16 ORPort, quint16 DirPort)
-{
-#ifdef WIN32
- // Workaround from http://trolltech.com/developer/knowledgebase/579
- WSAData wsadata;
- if (WSAStartup(MAKEWORD(2,0), &wsadata)!=0) {
- qFatal("WSAStartup failure while disabling UPnP port forwarding");
- }
-#endif
-
- UPNPControl *pUNPControl = UPNPControl::Instance();
-
- // Remove all port forwards
- if (!enable) {
- pUNPControl->disableForwarding();
- return;
- }
-
- // Configure port forwards, if Tor is using them
- if (enableORPort)
- pUNPControl->forwardORPort(ORPort);
- else
- pUNPControl->disableORPort();
-
- if (enableDirPort)
- pUNPControl->forwardDirPort(DirPort);
- else
- pUNPControl->disableDirPort();
-
-#ifdef WIN32
- WSACleanup();
-#endif
-}
Modified: vidalia/branches/upnp/src/vidalia/config/upnpcontrol.h
===================================================================
--- vidalia/branches/upnp/src/vidalia/config/upnpcontrol.h 2008-05-06 21:21:51 UTC (rev 2556)
+++ vidalia/branches/upnp/src/vidalia/config/upnpcontrol.h 2008-05-07 15:28:32 UTC (rev 2557)
@@ -18,57 +18,31 @@
#define _UPNPCONTROL_H
#include <QObject>
-#include <QList>
+#include <QMutex>
-#define STATICLIB
-#include <miniupnpc/miniwget.h>
-#include <miniupnpc/miniupnpc.h>
-#include <miniupnpc/upnpcommands.h>
+/* Forward declaration to make it build */
+class UPNPControlThread;
-#include "upnpcontrolthread.h"
-
class UPNPControl : public QObject
{
Q_OBJECT
public:
static UPNPControl* Instance();
-
- int disableForwarding();
-
- int forwardDirPort(quint16 port);
- int forwardORPort(quint16 port);
- int disableDirPort();
- int disableORPort();
-
- static void configurePortForwardingAsync(bool enable, bool enableORPort, bool enableDirPort, quint16 ORPort, quint16 DirPort);
- static void configurePortForwardingSync(bool enable, bool enableORPort, bool enableDirPort, quint16 ORPort, quint16 DirPort);
-
static void cleanup();
-
- void setThread(UPNPControlThread *thread);
-
+ void getDesiredState(quint16 *desiredDirPort, quint16 *desiredOrPort);
+ void setDesiredState(quint16 desiredDirPort, quint16 desiredOrPort);
protected:
UPNPControl();
+ ~UPNPControl();
+
private:
static UPNPControl* pInstance;
- int forwardPort(quint16 port);
- int disablePort(quint16 port);
-
- /** Used by miniupnpc library */
- struct UPNPUrls urls;
- struct IGDdatas data;
- char lanaddr[16];
- void init_upnp();
- void upnp_add_redir (const char * addr, int port);
- void upnp_rem_redir(int port);
-
/* Currently forwarded ports */
- QList<quint16> forwardedPorts;
- quint16 forwardedORPort;
- quint16 forwardedDirPort;
-
+ quint16 _forwardedORPort;
+ quint16 _forwardedDirPort;
+ QMutex *mutex;
UPNPControlThread *controlThread;
};
Modified: vidalia/branches/upnp/src/vidalia/config/upnpcontrolthread.cpp
===================================================================
--- vidalia/branches/upnp/src/vidalia/config/upnpcontrolthread.cpp 2008-05-06 21:21:51 UTC (rev 2556)
+++ vidalia/branches/upnp/src/vidalia/config/upnpcontrolthread.cpp 2008-05-07 15:28:32 UTC (rev 2557)
@@ -15,21 +15,245 @@
*/
#include "upnpcontrolthread.h"
+
+#include <QWaitCondition>
+#include <QMutex>
+
+#ifdef WIN32
+#include <winsock2.h>
+#endif
+
#include "upnpcontrol.h"
-UPNPControlThread::UPNPControlThread(bool enable, bool enableORPort, bool enableDirPort, quint16 ORPort, quint16 DirPort)
+#define UPNPCONTROL_DEBUG 1
+
+UPNPControlThread::UPNPControlThread(UPNPControl *control)
{
- _enable = enable;
- _enableORPort = enableORPort;
- _enableDirPort = enableDirPort;
- _ORPort = ORPort;
- _DirPort = DirPort;
+ _upnpInitialized = false;
+ _keepRunning = true;
+ _control = control;
+
+ _dirPort = 0;
+ _orPort = 0;
+
+ _waitCondition = new QWaitCondition();
+ _waitMutex = new QMutex();
}
+UPNPControlThread::~UPNPControlThread()
+{
+ delete _waitCondition;
+ delete _waitMutex;
+}
+
void
UPNPControlThread::run()
{
- UPNPControl *control = UPNPControl::Instance();
- control->setThread(this);
- UPNPControl::configurePortForwardingSync(_enable, _enableORPort, _enableDirPort, _ORPort, _DirPort);
+ bool shouldExit = false;
+
+ forever {
+ /* TODO: Check for switching OR/Dir port */
+ /* TODO: Check for router losing state */
+
+ configurePorts();
+
+ /* Wait for something to happen */
+ _waitMutex->lock();
+ if (_keepRunning) {
+ /* We should continue */
+ _waitCondition->wait(_waitMutex);
+ /* Maybe we were asked to exit while waiting */
+ shouldExit = !_keepRunning;
+ _waitMutex->unlock();
+ if (shouldExit)
+ break;
+ } else {
+ /* We should exit */
+ _waitMutex->unlock();
+ break;
+ }
+ }
+
+ /* Remove the existing port forwards */
+ updatePort(_dirPort, 0);
+ updatePort(_orPort, 0);
}
+
+void
+UPNPControlThread::configurePorts() {
+ quint16 desiredDirPort, desiredOrPort;
+
+ /* Get desired state */
+ _control->getDesiredState(&desiredDirPort, &desiredOrPort);
+
+ /* Configure DirPort */
+ if (desiredDirPort != _dirPort) {
+ updatePort(_dirPort, desiredDirPort);
+ _dirPort = desiredDirPort;
+ }
+
+ /* Configure ORPort */
+ if (desiredOrPort != _orPort) {
+ updatePort(_orPort, desiredOrPort);
+ _orPort = desiredOrPort;
+ }
+}
+
+void
+UPNPControlThread::stop()
+{
+ /* Lock access to _keepRunning */
+ _waitMutex->lock();
+
+ /* Ask the thread to stop */
+ _keepRunning = false;
+
+ /* Wake it up if needed */
+ _waitCondition->wakeAll();
+
+ /* Unlock shared state */
+ _waitMutex->unlock();
+
+ /* Wait for it to finish */
+ wait();
+}
+
+void
+UPNPControlThread::wakeup()
+{
+ _waitMutex->lock();
+ _waitCondition->wakeAll();
+ _waitMutex->unlock();
+}
+
+void
+UPNPControlThread::updatePort(quint16 oldPort, quint16 newPort)
+{
+
+#ifdef WIN32
+ // Workaround from http://trolltech.com/developer/knowledgebase/579
+ WSAData wsadata;
+ if (WSAStartup(MAKEWORD(2,0), &wsadata)!=0) {
+ qFatal("WSAStartup failure while disabling UPnP port forwarding");
+ }
+#endif
+
+ if (!_upnpInitialized) {
+ init_upnp();
+ _upnpInitialized = true;
+ }
+
+ if (oldPort != 0)
+ disablePort(oldPort);
+
+ if (newPort != 0)
+ forwardPort(newPort);
+
+#ifdef WIN32
+ WSACleanup();
+#endif
+
+}
+
+/** Based on http://miniupnp.free.fr/files/download.php?file=xchat-upnp20061022.patch */
+void
+UPNPControlThread::init_upnp()
+{
+ struct UPNPDev * devlist;
+ int retval;
+
+ memset(&urls, 0, sizeof(struct UPNPUrls));
+ memset(&data, 0, sizeof(struct IGDdatas));
+
+ devlist = upnpDiscover(2000, NULL, NULL);
+ retval = UPNP_GetValidIGD(devlist, &urls, &data, lanaddr, sizeof(lanaddr));
+
+ #ifdef UPNPCONTROL_DEBUG
+ printf("GetValidIGD returned: %d\n", retval);
+ fflush(stdout);
+ #endif
+
+ freeUPNPDevlist(devlist);
+}
+
+int
+UPNPControlThread::forwardPort(quint16 port)
+{
+ int retval;
+
+ char sPort[6];
+
+ char intClient[16];
+ char intPort[6];
+
+ // Convert the port number to a string
+ snprintf(sPort, sizeof(sPort), "%d", port);
+
+ // Add the port mapping of external:port -> internal:port
+ retval = UPNP_AddPortMapping(urls.controlURL, data.servicetype,
+ sPort, sPort, lanaddr, "Tor server", "TCP");
+ if(UPNPCOMMAND_SUCCESS != retval) {
+ #ifdef UPNPCONTROL_DEBUG
+ printf("AddPortMapping(%s, %s, %s) failed with code %d\n",
+ sPort, sPort, lanaddr, retval);
+ fflush(stdout);
+ #endif
+ return 1;
+ }
+
+ // Check if the port mapping was accepted
+ retval = UPNP_GetSpecificPortMappingEntry(urls.controlURL,
+ data.servicetype,
+ sPort, "TCP",
+ intClient, intPort);
+ if(UPNPCOMMAND_SUCCESS != retval) {
+ #ifdef UPNPCONTROL_DEBUG
+ printf("GetSpecificPortMappingEntry() failed with code %d\n", retval);
+ fflush(stdout);
+ #endif
+ return 2;
+ }
+
+ if(! intClient[0]) {
+ #ifdef UPNPCONTROL_DEBUG
+ printf("GetSpecificPortMappingEntry failed.\n");
+ fflush(stdout);
+ #endif
+ return 3;
+ }
+
+ // Output the mapping
+ #ifdef UPNPCONTROL_DEBUG
+ printf("(external):%s -> %s:%s\n", sPort, intClient, intPort);
+ fflush(stdout);
+ #endif
+
+ return 0;
+}
+
+int
+UPNPControlThread::disablePort(quint16 port)
+{
+ char sPort[6];
+
+ // Convert the port number to a string
+ snprintf(sPort, sizeof(sPort), "%d", port);
+
+ // Remove the mapping
+ int retval = UPNP_DeletePortMapping(urls.controlURL, data.servicetype, sPort, "TCP");
+ if(UPNPCOMMAND_SUCCESS != retval) {
+#ifdef UPNPCONTROL_DEBUG
+ printf("DeletePortMapping() failed with code %d\n", retval);
+ fflush(stdout);
+#endif
+ return 1;
+ }
+
+ // Output the cancelled mapping
+#ifdef UPNPCONTROL_DEBUG
+ printf("(external):%s -> <>\n", sPort);
+ fflush(stdout);
+#endif
+
+ return 0;
+}
Modified: vidalia/branches/upnp/src/vidalia/config/upnpcontrolthread.h
===================================================================
--- vidalia/branches/upnp/src/vidalia/config/upnpcontrolthread.h 2008-05-06 21:21:51 UTC (rev 2556)
+++ vidalia/branches/upnp/src/vidalia/config/upnpcontrolthread.h 2008-05-07 15:28:32 UTC (rev 2557)
@@ -18,19 +18,47 @@
#define _UPNPCONTROLTHREAD_H
#include <QThread>
+#include <QMutex>
+#include <QWaitCondition>
+#define STATICLIB
+#include <miniupnpc/miniwget.h>
+#include <miniupnpc/miniupnpc.h>
+#include <miniupnpc/upnpcommands.h>
+
+/* Forward declaration to make it build */
+class UPNPControl;
+
class UPNPControlThread : public QThread
{
Q_OBJECT
public:
- UPNPControlThread(bool enable, bool enableORPort, bool enableDirPort, quint16 ORPort, quint16 DirPort);
+ UPNPControlThread(UPNPControl *control);
+ ~UPNPControlThread();
+ void stop();
+ void wakeup();
protected:
void run();
private:
- bool _enable, _enableORPort, _enableDirPort;
- quint16 _ORPort, _DirPort;
+ bool _upnpInitialized;
+ bool _keepRunning;
+ UPNPControl *_control;
+ QWaitCondition *_waitCondition;
+ QMutex *_waitMutex;
+ quint16 _dirPort, _orPort;
+
+ /** Used by miniupnpc library */
+ struct UPNPUrls urls;
+ struct IGDdatas data;
+ char lanaddr[16];
+
+ void configurePorts();
+ void init_upnp();
+ void updatePort(quint16 oldPort, quint16 newPort);
+ int forwardPort(quint16 port);
+ int disablePort(quint16 port);
};
#endif
Modified: vidalia/branches/upnp/src/vidalia/mainwindow.cpp
===================================================================
--- vidalia/branches/upnp/src/vidalia/mainwindow.cpp 2008-05-06 21:21:51 UTC (rev 2556)
+++ vidalia/branches/upnp/src/vidalia/mainwindow.cpp 2008-05-07 15:28:32 UTC (rev 2557)
@@ -228,7 +228,6 @@
/* Disable port forwarding */
ServerSettings settings(_torControl);
- settings.configurePortForwarding(false, false);
settings.cleanupPortForwarding();
if (_proxyProcess->state() != QProcess::NotRunning) {
@@ -914,7 +913,7 @@
}
/* Configure UPnP port forwarding if needed */
- serverSettings.configurePortForwarding(true, true);
+ serverSettings.configurePortForwarding();
/* Check if Tor has a circuit established */
if (_torControl->circuitEstablished())