[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
[vidalia-svn] r2638: Merge the UPnP backend changes into trunk. (in vidalia: . trunk/src/vidalia trunk/src/vidalia/config)
Author: edmanm
Date: 2008-06-01 19:42:53 -0400 (Sun, 01 Jun 2008)
New Revision: 2638
Added:
vidalia/trunk/src/vidalia/config/upnpcontrolthread.cpp
vidalia/trunk/src/vidalia/config/upnpcontrolthread.h
Modified:
vidalia/
vidalia/trunk/src/vidalia/CMakeLists.txt
vidalia/trunk/src/vidalia/config/serversettings.cpp
vidalia/trunk/src/vidalia/config/serversettings.h
vidalia/trunk/src/vidalia/config/upnpcontrol.cpp
vidalia/trunk/src/vidalia/config/upnpcontrol.h
Log:
r412@thebe: edmanm | 2008-06-01 19:23:32 -0400
Merge the UPnP backend changes into trunk.
Property changes on: vidalia
___________________________________________________________________
svk:merge ticket from /local/vidalia [r412] on 45a62a8a-8088-484c-baad-c7b3e776dd32
Modified: vidalia/trunk/src/vidalia/CMakeLists.txt
===================================================================
--- vidalia/trunk/src/vidalia/CMakeLists.txt 2008-06-01 23:42:50 UTC (rev 2637)
+++ vidalia/trunk/src/vidalia/CMakeLists.txt 2008-06-01 23:42:53 UTC (rev 2638)
@@ -93,9 +93,11 @@
include_directories(${MINIUPNPC_INCLUDE_DIR})
set(vidalia_SRCS ${vidalia_SRCS}
config/upnpcontrol.cpp
+ config/upnpcontrolthread.cpp
)
qt4_wrap_cpp(vidalia_SRCS
config/upnpcontrol.h
+ config/upnpcontrolthread.h
)
endif(USE_MINIUPNPC)
@@ -257,7 +259,7 @@
torcontrol
)
if (USE_MINIUPNPC)
- target_link_libraries(${vidalia_BIN} ${MINIUPNPC_LIBRARY})
+ target_link_libraries(${vidalia_BIN} miniupnpc)
endif(USE_MINIUPNPC)
if (MINGW)
Modified: vidalia/trunk/src/vidalia/config/serversettings.cpp
===================================================================
--- vidalia/trunk/src/vidalia/config/serversettings.cpp 2008-06-01 23:42:50 UTC (rev 2637)
+++ vidalia/trunk/src/vidalia/config/serversettings.cpp 2008-06-01 23:42:53 UTC (rev 2638)
@@ -43,6 +43,7 @@
#define SETTING_BANDWIDTH_RATE "BandwidthRate"
#define SETTING_BANDWIDTH_BURST "BandwidthBurst"
#define SETTING_BRIDGE_RELAY "BridgeRelay"
+#define SETTING_ENABLE_UPNP "EnableUPnP"
#define SETTING_RELAY_BANDWIDTH_RATE "RelayBandwidthRate"
#define SETTING_RELAY_BANDWIDTH_BURST "RelayBandwidthBurst"
#define SETTING_PUBLISH_DESCRIPTOR "PublishServerDescriptor"
@@ -72,6 +73,7 @@
setDefault(SETTING_PUBLISH_DESCRIPTOR, "1");
setDefault(SETTING_EXITPOLICY,
ExitPolicy(ExitPolicy::Default).toString());
+ setDefault(SETTING_ENABLE_UPNP, false);
}
/** Returns a QHash of Tor-recognizable configuratin keys to their current
@@ -134,10 +136,9 @@
{
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);
rc = torControl()->setConf(confValues(), errmsg);
} else {
QStringList resetKeys;
@@ -156,7 +157,6 @@
<< SETTING_BANDWIDTH_BURST;
}
rc = torControl()->resetConf(resetKeys, errmsg);
- configurePortForwarding(false);
}
return rc;
}
@@ -164,21 +164,42 @@
/* TODO: We should call this periodically, in case the router gets rebooted or forgets its UPnP settings */
/* TODO: Remove port forwarding when Tor is shutdown or the ORPort changes */
/* TODO: init_upnp() will block for up to 2 seconds. We should fire off a thread */
-/** Configure UPnP device to forward DirPort and ORPort */
+
+/** Configure UPnP device to forward DirPort and ORPort. If enable is
+true, will forward ORPort and DirPort; otherwise will remove exising
+port mappings */
void
-ServerSettings::configurePortForwarding(bool enable)
+ServerSettings::configurePortForwarding()
{
#ifdef USE_MINIUPNPC
- UPNPControl *pUNPControl = UPNPControl::Instance();
+ quint16 ORPort, DirPort;
+ bool enableORPort, enableDirPort;
- if (enable) {
- pUNPControl->forwardPort(getORPort());
- } else {
- pUNPControl->disableForwarding();
- }
+ // This is how the tickbox should control UPNP
+ if (!isUpnpEnabled())
+ return;
+
+ ORPort = getORPort();
+ if (!isServerEnabled())
+ ORPort = 0;
+
+ DirPort = getDirPort();
+ if (!isServerEnabled() || !isDirectoryMirror())
+ DirPort = 0;
+
+ UPNPControl *control = UPNPControl::instance();
+ control->setDesiredState(DirPort, ORPort);
#endif
}
+void
+ServerSettings::cleanupPortForwarding()
+{
+#ifdef USE_MINIUPNPC
+ UPNPControl::cleanup();
+#endif
+}
+
/** Virtual method called when we retrieve a server-related setting from Tor.
* Currently this just translates BandwidthFoo to RelayBandwidthFoo when
* appropriate. */
@@ -348,3 +369,24 @@
setValue(SETTING_BANDWIDTH_BURST, rate);
}
+/** Returns true if UPnP support is available and enabled. */
+bool
+ServerSettings::isUpnpEnabled()
+{
+#if defined(USE_MINIUPNPC)
+ return localValue(SETTING_ENABLE_UPNP).toBool();
+#else
+ return false;
+#endif
+}
+
+/** Sets whether Vidalia should try to configure port forwarding using UPnP.
+ * If Vidalia was compiled without UPnP support, this method has no effect. */
+void
+ServerSettings::setUpnpEnabled(bool enabled)
+{
+#if defined(USE_MINIUPNPC)
+ setValue(SETTING_ENABLE_UPNP, enabled);
+#endif
+}
+
Modified: vidalia/trunk/src/vidalia/config/serversettings.h
===================================================================
--- vidalia/trunk/src/vidalia/config/serversettings.h 2008-06-01 23:42:50 UTC (rev 2637)
+++ vidalia/trunk/src/vidalia/config/serversettings.h 2008-06-01 23:42:53 UTC (rev 2638)
@@ -82,6 +82,16 @@
/** Gets the maximum burst rate (in B/s) of this server. */
quint32 getBandwidthBurstRate();
+ /** Configure port forwarding. */
+ void configurePortForwarding();
+
+ void cleanupPortForwarding();
+
+ /** Returns true if UPnP support is enabled. */
+ bool isUpnpEnabled();
+ /** Sets whether Vidalia should try to configure port forwarding using UPnP. */
+ void setUpnpEnabled(bool enabled);
+
protected:
/** Virtual method called when we retrieve a server-related setting from Tor.
* Currently this just translates BandwidthFoo to RelayBandwidthFoo when
@@ -91,9 +101,6 @@
private:
/** Returns Tor-recognizable configuration keys and current values. */
QHash<QString,QString> confValues();
-
- /** Configure UPnP device to forward DirPort and ORPort */
- void configurePortForwarding(bool enable);
};
#endif
Modified: vidalia/trunk/src/vidalia/config/upnpcontrol.cpp
===================================================================
--- vidalia/trunk/src/vidalia/config/upnpcontrol.cpp 2008-06-01 23:42:50 UTC (rev 2637)
+++ vidalia/trunk/src/vidalia/config/upnpcontrol.cpp 2008-06-01 23:42:53 UTC (rev 2638)
@@ -16,109 +16,149 @@
#include "upnpcontrol.h"
-UPNPControl* UPNPControl::pInstance = 0;
-UPNPControl* UPNPControl::Instance()
+#include <QMutex>
+#include <QMetaType>
+
+#ifdef Q_OS_WIN32
+#include <winsock2.h>
+#endif
+
+#include "upnpcontrolthread.h"
+
+/** UPNPControl singleton instance. */
+UPNPControl* UPNPControl::_instance = 0;
+
+/** Returns a pointer to this object's singleton instance. */
+UPNPControl* UPNPControl::instance()
{
- if (0 == pInstance)
- pInstance = new UPNPControl;
- return pInstance;
+ if (0 == _instance)
+ _instance = new UPNPControl;
+ return _instance;
}
+/** Constructor. Initializes and starts a thread in which all blocking UPnP
+ * operations will be performed. */
UPNPControl::UPNPControl()
{
- init_upnp();
- forwardedPort = 0;
+ _forwardedORPort = 0;
+ _forwardedDirPort = 0;
+ _error = UnknownError;
+ _state = IdleState;
+
+ qRegisterMetaType<UPNPControl::UPNPError>("UPNPControl::UPNPError");
+ qRegisterMetaType<UPNPControl::UPNPState>("UPNPControl::UPNPState");
+
+ _mutex = new QMutex();
+ _controlThread = new UPNPControlThread(this);
+ _controlThread->start();
}
-int
-UPNPControl::forwardPort(quint16 port)
+/** Destructor. cleanup() should be called before the object is destroyed.
+ * \sa cleanup()
+ */
+UPNPControl::~UPNPControl()
{
- int retval;
-
- char sPort[6];
-
- char intClient[16];
- char intPort[6];
+ delete _mutex;
+ delete _controlThread;
+}
- // Convert the port number to a string
- snprintf(sPort, sizeof(sPort), "%d", port);
+/** Terminates the UPnP control thread and frees memory allocated to this
+ * object's singleton instance. */
+void
+UPNPControl::cleanup()
+{
+ _instance->_controlThread->stop();
+ delete _instance;
+ _instance = 0;
+}
- // 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) {
- printf("AddPortMapping(%s, %s, %s) failed with code %d\n",
- sPort, sPort, lanaddr, retval);
- return 1;
- }
+/** Sets <b>desiredDirPort</b> and <b>desiredOrPort</b> to the currently
+ * forwarded DirPort and ORPort values. */
+void
+UPNPControl::getDesiredState(quint16 *desiredDirPort, quint16 *desiredOrPort)
+{
+ _mutex->lock();
+ *desiredDirPort = _forwardedDirPort;
+ *desiredOrPort = _forwardedORPort;
+ _mutex->unlock();
+}
+
+/** Sets the desired DirPort and ORPort port mappings to <b>desiredDirPort</b>
+ * and <b>desiredOrPort</b>, respectively. */
+void
+UPNPControl::setDesiredState(quint16 desiredDirPort, quint16 desiredOrPort)
+{
+ _mutex->lock();
+ _forwardedDirPort = desiredDirPort;
+ _forwardedORPort = desiredOrPort;
+ _mutex->unlock();
- // Check if the port mapping was accepted
- retval = UPNP_GetSpecificPortMappingEntry(urls.controlURL,
- data.servicetype,
- sPort, "TCP",
- intClient, intPort);
- if(UPNPCOMMAND_SUCCESS != retval) {
- printf("GetSpecificPortMappingEntry() failed with code %d\n", retval);
- return 2;
- }
+ _controlThread->wakeup();
+}
+
+/** Sets the most recent UPnP-related error to <b>error</b> and emits the
+ * error() signal. */
+void
+UPNPControl::setError(UPNPError upnpError)
+{
+ _mutex->lock();
+ _error = upnpError;
+ _mutex->unlock();
- if(! intClient[0]) {
- printf("GetSpecificPortMappingEntry failed.\n");
- return 3;
- }
-
- // Output the mapping
- printf("(external):%s -> %s:%s\n", sPort, intClient, intPort);
- fflush(stdout);
+ emit error(upnpError);
+}
- // Save the mapping
- forwardedPort = port;
+/** Sets the current UPnP state to <b>state</b> and emits the stateChanged()
+ * signal. */
+void
+UPNPControl::setState(UPNPState state)
+{
+ _mutex->lock();
+ _state = state;
+ _mutex->unlock();
- return 0;
+ emit stateChanged(state);
}
-int
-UPNPControl::disableForwarding()
+/** Returns the type of error that occurred last. */
+UPNPControl::UPNPError
+UPNPControl::error() const
{
- char sPort[6];
+ QMutexLocker locker(_mutex);
+ return _error;
+}
- if (0 == forwardedPort)
- return 0;
+/** Returns a QString describing the type of error that occurred last. */
+QString
+UPNPControl::errorString() const
+{
+ UPNPError error = this->error();
- // Convert the port number to a string
- snprintf(sPort, sizeof(sPort), "%d", forwardedPort);
-
- int retval = UPNP_DeletePortMapping(urls.controlURL, data.servicetype, sPort, "TCP");
- if(UPNPCOMMAND_SUCCESS != retval) {
- printf("DeletePortMapping() failed with code %d\n", retval);
- return 1;
+ switch (error) {
+ case Success:
+ return tr("Success");
+ case NoUPNPDevicesFound:
+ return tr("No UPnP-enabled devices found");
+ case NoValidIGDsFound:
+ return tr("No valid UPnP-enabled Internet gateway devices found");
+ case WSAStartupFailed:
+ return tr("WSAStartup failed");
+ case AddPortMappingFailed:
+ return tr("Failed to add a port mapping");
+ case GetPortMappingFailed:
+ return tr("Failed to retrieve a port mapping");
+ case DeletePortMappingFailed:
+ return tr("Failed to remove a port mapping");
+ default:
+ return tr("Unknown error");
}
-
- // Output the cancelled mapping
- printf("(external):%s -> <>\n", sPort);
- fflush(stdout);
-
- // Save the mapping
- forwardedPort = 0;
-
- return 0;
}
-
-/** Based on http://miniupnp.free.fr/files/download.php?file=xchat-upnp20061022.patch */
-void
-UPNPControl::init_upnp()
+/** Returns the number of milliseconds to wait for devices to respond
+ * when attempting to discover UPnP-enabled IGDs. */
+int
+UPNPControl::discoverTimeout() const
{
- struct UPNPDev * devlist;
- int retval;
+ return UPNPControlThread::UPNPCONTROL_DISCOVER_TIMEOUT;
+}
- 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));
- printf("GetValidIGD returned: %d\n", retval);
- fflush(stdout);
-
- freeUPNPDevlist(devlist);
-}
Modified: vidalia/trunk/src/vidalia/config/upnpcontrol.h
===================================================================
--- vidalia/trunk/src/vidalia/config/upnpcontrol.h 2008-06-01 23:42:50 UTC (rev 2637)
+++ vidalia/trunk/src/vidalia/config/upnpcontrol.h 2008-06-01 23:42:53 UTC (rev 2638)
@@ -18,35 +18,97 @@
#define _UPNPCONTROL_H
#include <QObject>
+#include <QMutex>
-#define STATICLIB
-#include <miniupnpc/miniwget.h>
-#include <miniupnpc/miniupnpc.h>
-#include <miniupnpc/upnpcommands.h>
+/* Forward declaration to make it build */
+class UPNPControlThread;
+
class UPNPControl : public QObject
{
Q_OBJECT
public:
- static UPNPControl* Instance();
- int forwardPort(quint16 port);
- int disableForwarding();
+ /** UPnP-related error values. */
+ enum UPNPError {
+ Success,
+ NoUPNPDevicesFound,
+ NoValidIGDsFound,
+ WSAStartupFailed,
+ AddPortMappingFailed,
+ GetPortMappingFailed,
+ DeletePortMappingFailed,
+ UnknownError
+ };
+ /** UPnP port forwarding state. */
+ enum UPNPState {
+ IdleState,
+ ErrorState,
+ DiscoverState,
+ UpdatingORPortState,
+ UpdatingDirPortState,
+ ForwardingCompleteState
+ };
+
+ /** Returns a pointer to this object's singleton instance. */
+ static UPNPControl* instance();
+ /** Terminates the UPnP control thread and frees memory allocated to this
+ * object's singleton instance. */
+ static void cleanup();
+ /** Sets <b>desiredDirPort</b> and <b>desiredOrPort</b> to the currently
+ * forwarded DirPort and ORPort values. */
+ void getDesiredState(quint16 *desiredDirPort, quint16 *desiredOrPort);
+ /** Sets the desired DirPort and ORPort port mappings to
+ * <b>desiredDirPort</b> and <b>desiredOrPort</b>, respectively. */
+ void setDesiredState(quint16 desiredDirPort, quint16 desiredOrPort);
+
+ /** Returns the type of error that occurred last. */
+ UPNPError error() const;
+ /** Returns a QString describing the type of error that occurred last. */
+ QString errorString() const;
+
+ /** Returns the number of milliseconds to wait for devices to respond
+ * when attempting to discover UPnP-enabled IGDs. */
+ int discoverTimeout() const;
+
+signals:
+ /** Emitted when the UPnP control thread status changes. */
+ void stateChanged(UPNPControl::UPNPState state);
+
+ /** Emitted when a UPnP error occurs. */
+ void error(UPNPControl::UPNPError error);
+
protected:
+ /** Constructor. Initializes and starts a thread in which all blocking UPnP
+ * operations will be performed. */
UPNPControl();
+ /** Destructor. cleanup() should be called before the object is destroyed. */
+ ~UPNPControl();
+
+ /** Sets the most recent UPnP-related error to <b>error</b> and emits the
+ * error() signal.
+ * \sa error
+ */
+ void setError(UPNPError error);
+
+ /** Sets the current UPnP state to <b>state</b> and emits the stateChanged()
+ * signal.
+ * \sa stateChanged
+ */
+ void setState(UPNPState state);
+
private:
- static UPNPControl* pInstance;
+ static UPNPControl* _instance; /**< UPNPControl singleton instance. */
- /** 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);
+ quint16 _forwardedORPort; /**< Currently forwarded ORPort. */
+ quint16 _forwardedDirPort; /**< Currently forwarded DirPort. */
+ QMutex* _mutex; /**< Mutex around variables shared with UPNPControlThread. */
+ UPNPError _error; /**< Most recent UPNP error. */
+ UPNPState _state; /**< Current UPNP status. */
- /* Currently forwarded port */
- quint16 forwardedPort;
+ friend class UPNPControlThread;
+ UPNPControlThread* _controlThread; /**< Thread used for UPnP operations. */
};
-#endif
+#endif
+
Copied: vidalia/trunk/src/vidalia/config/upnpcontrolthread.cpp (from rev 2635, vidalia/branches/upnp/src/vidalia/config/upnpcontrolthread.cpp)
===================================================================
--- vidalia/trunk/src/vidalia/config/upnpcontrolthread.cpp (rev 0)
+++ vidalia/trunk/src/vidalia/config/upnpcontrolthread.cpp 2008-06-01 23:42:53 UTC (rev 2638)
@@ -0,0 +1,338 @@
+/*
+** This file is part of Vidalia, and is subject to the license terms in the
+** LICENSE file, found in the top level directory of this distribution. If
+** you did not receive the LICENSE file with this file, you may obtain it
+** from the Vidalia source package distributed by the Vidalia Project at
+** http://www.vidalia-project.net/. No part of Vidalia, including this file,
+** may be copied, modified, propagated, or distributed except according to
+** the terms described in the LICENSE file.
+*/
+
+/*
+** \file upnpcontrolthread.cpp
+** \version $Id$
+** \brief Thread for configuring UPnP in the background
+*/
+
+#include "upnpcontrolthread.h"
+
+#include <QWaitCondition>
+#include <QMutex>
+#include <QTime>
+#include <QTextStream>
+#include <QString>
+#include <QMessageBox>
+#include <vidalia.h>
+
+#ifdef Q_OS_WIN32
+#include <winsock2.h>
+#endif
+
+#include "upnpcontrol.h"
+
+#define UPNPCONTROL_REINIT_MSEC 300000 // 5 minutes
+#define UPNPCONTROL_MAX_WAIT_MSEC 60000 // 1 minute
+
+
+/** Constructor. <b>control</b> will be used for retrieving the desired port
+ * forwarding state. */
+UPNPControlThread::UPNPControlThread(UPNPControl *control)
+{
+ _upnpInitialized = QTime();
+ _keepRunning = true;
+ _control = control;
+
+ _dirPort = 0;
+ _orPort = 0;
+
+ _waitCondition = new QWaitCondition();
+ _waitMutex = new QMutex();
+}
+
+/** Destructor. The UPnP control thread must be stopped prior to destroying
+ * this object.
+ * \sa stop()
+ */
+UPNPControlThread::~UPNPControlThread()
+{
+ delete _waitCondition;
+ delete _waitMutex;
+}
+
+/** Thread entry point. The thread has a main loop that periodically wakes up
+ * and updates the configured port mappings. Upon exiting, all port mappings
+ * will be removed. */
+void
+UPNPControlThread::run()
+{
+ 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 */
+ UPNPControl::instance()->setState(UPNPControl::IdleState);
+ _waitCondition->wait(_waitMutex, UPNPCONTROL_MAX_WAIT_MSEC);
+
+ /* 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);
+}
+
+/** Sets up port forwarding according the previously-configured desired state.
+ * The desired state is set using UPNPControl's setDesiredState() method.
+ * \sa UPNPControl::setDesiredState
+ */
+void
+UPNPControlThread::configurePorts() {
+ quint16 desiredDirPort, desiredOrPort;
+ bool force_init = false;
+ UPNPControl::UPNPError retval = UPNPControl::Success;
+
+ /* Get desired state */
+ _control->getDesiredState(&desiredDirPort, &desiredOrPort);
+
+ /* If it's been a while since we initialized the router, or time has gone
+ backward, then maybe the router has gone away or forgotten the forwards.
+ Reset UPnP state, and re-do the port forwarding */
+ if (_upnpInitialized.isNull() || // Is this the first time we have used UPNP?
+ _upnpInitialized>QTime::currentTime() || // Has time gone backwards?
+ _upnpInitialized.addMSecs(UPNPCONTROL_REINIT_MSEC)<QTime::currentTime() // Has it been REINIT_MSEC since initialization
+ ) {
+ _upnpInitialized = QTime();
+ force_init = true;
+ }
+
+ if (!force_init) {
+ /* Configure DirPort */
+ if (desiredDirPort != _dirPort) {
+ UPNPControl::instance()->setState(UPNPControl::UpdatingDirPortState);
+ retval = updatePort(_dirPort, desiredDirPort);
+ if (retval == UPNPControl::Success)
+ _dirPort = desiredDirPort;
+ else
+ goto err;
+ }
+
+ /* Configure ORPort */
+ if (desiredOrPort != _orPort) {
+ UPNPControl::instance()->setState(UPNPControl::UpdatingORPortState);
+ retval = updatePort(_orPort, desiredOrPort);
+ if (retval == UPNPControl::Success)
+ _orPort = desiredOrPort;
+ else
+ goto err;
+ }
+ } else {
+ /* Add the mapping even if they exist already */
+ UPNPControl::instance()->setState(UPNPControl::UpdatingDirPortState);
+ retval = updatePort(0, desiredDirPort);
+ if (retval == UPNPControl::Success)
+ _dirPort = desiredDirPort;
+ else
+ goto err;
+
+ UPNPControl::instance()->setState(UPNPControl::UpdatingORPortState);
+ retval = updatePort(0, desiredOrPort);
+ if (retval == UPNPControl::Success)
+ _orPort = desiredOrPort;
+ else goto err;
+ }
+
+ UPNPControl::instance()->setState(UPNPControl::ForwardingCompleteState);
+ return;
+
+err:
+ UPNPControl::instance()->setError(retval);
+ UPNPControl::instance()->setState(UPNPControl::ErrorState);
+}
+
+/** Terminates the UPnP control thread's run() loop.
+ * \sa run()
+ */
+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();
+}
+
+/** Wakes up the UPnP control thread's run() loop.
+ * \sa run()
+ */
+void
+UPNPControlThread::wakeup()
+{
+ _waitMutex->lock();
+ _waitCondition->wakeAll();
+ _waitMutex->unlock();
+}
+
+/** Updates the port mapping for <b>oldPort</b>, changing it to
+ * <b>newPort</b>. */
+UPNPControl::UPNPError
+UPNPControlThread::updatePort(quint16 oldPort, quint16 newPort)
+{
+ UPNPControl::UPNPError retval;
+
+#ifdef Q_OS_WIN32
+ // Workaround from http://trolltech.com/developer/knowledgebase/579
+ WSAData wsadata;
+ if (WSAStartup(MAKEWORD(2,0), &wsadata) != 0) {
+ vWarn("WSAStartup failure while updating UPnP port forwarding");
+ return UPNPControl::WSAStartupFailed;
+ }
+#endif
+
+ if (_upnpInitialized.isNull() && (oldPort != 0 || newPort != 0)) {
+ retval = initializeUPNP();
+ if (retval == UPNPControl::Success)
+ _upnpInitialized = QTime::currentTime();
+ else
+ _upnpInitialized = QTime();
+ } else {
+ retval = UPNPControl::Success;
+ }
+
+ if (retval == UPNPControl::Success && oldPort != 0)
+ retval = disablePort(oldPort);
+
+ if (retval == UPNPControl::Success && newPort != 0)
+ retval = forwardPort(newPort);
+
+#ifdef Q_OS_WIN32
+ WSACleanup();
+#endif
+
+ return retval;
+}
+
+/** Discovers UPnP-enabled IGDs on the network. Based on
+ * http://miniupnp.free.fr/files/download.php?file=xchat-upnp20061022.patch
+ * This method will block for UPNPCONTROL_DISCOVER_TIMEOUT milliseconds. */
+UPNPControl::UPNPError
+UPNPControlThread::initializeUPNP()
+{
+ struct UPNPDev *devlist;
+ int retval;
+
+ memset(&urls, 0, sizeof(struct UPNPUrls));
+ memset(&data, 0, sizeof(struct IGDdatas));
+
+ UPNPControl::instance()->setState(UPNPControl::DiscoverState);
+
+ devlist = upnpDiscover(UPNPCONTROL_DISCOVER_TIMEOUT, NULL, NULL);
+ if (NULL == devlist) {
+ vWarn("upnpDiscover returned: NULL");
+ return UPNPControl::NoUPNPDevicesFound;
+ }
+
+ retval = UPNP_GetValidIGD(devlist, &urls, &data, lanaddr, sizeof(lanaddr));
+
+ vInfo("GetValidIGD returned: %1").arg(retval);
+
+ freeUPNPDevlist(devlist);
+
+ if (retval != 1 && retval != 2)
+ return UPNPControl::NoValidIGDsFound;
+
+ return UPNPControl::Success;
+}
+
+/** Adds a port forwarding mapping from external:<b>port</b> to
+ * internal:<b>port</b>. Returns 0 on success, or non-zero on failure. */
+UPNPControl::UPNPError
+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 relay", "TCP");
+ if(UPNPCOMMAND_SUCCESS != retval) {
+ vWarn("AddPortMapping(%1, %2, %3) failed with code %4")
+ .arg(sPort).arg(sPort).arg(lanaddr).arg(retval);
+ return UPNPControl::AddPortMappingFailed;
+ }
+
+ // Check if the port mapping was accepted
+ retval = UPNP_GetSpecificPortMappingEntry(urls.controlURL, data.servicetype,
+ sPort, "TCP", intClient, intPort);
+ if(UPNPCOMMAND_SUCCESS != retval) {
+ vWarn("GetSpecificPortMappingEntry() failed with code %1").arg(retval);
+ return UPNPControl::GetPortMappingFailed;
+ }
+
+ if(! intClient[0]) {
+ vWarn("GetSpecificPortMappingEntry failed.");
+ return UPNPControl::GetPortMappingFailed;
+ }
+
+ // Output the mapping
+ vInfo("(external):%1 -> %2:%3").arg(sPort).arg(intClient).arg(intPort);
+
+ return UPNPControl::Success;
+}
+
+/** Removes the port mapping for <b>port</b>. Returns 0 on success or non-zero
+ * on failure. */
+UPNPControl::UPNPError
+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) {
+ vWarn("DeletePortMapping() failed with code %1").arg(retval);
+ return UPNPControl::DeletePortMappingFailed;
+ }
+
+ // Output the cancelled mapping
+ vInfo("(external):%1 -> <>").arg(sPort);
+
+ return UPNPControl::Success;
+}
+
Copied: vidalia/trunk/src/vidalia/config/upnpcontrolthread.h (from rev 2628, vidalia/branches/upnp/src/vidalia/config/upnpcontrolthread.h)
===================================================================
--- vidalia/trunk/src/vidalia/config/upnpcontrolthread.h (rev 0)
+++ vidalia/trunk/src/vidalia/config/upnpcontrolthread.h 2008-06-01 23:42:53 UTC (rev 2638)
@@ -0,0 +1,90 @@
+/*
+** This file is part of Vidalia, and is subject to the license terms in the
+** LICENSE file, found in the top level directory of this distribution. If
+** you did not receive the LICENSE file with this file, you may obtain it
+** from the Vidalia source package distributed by the Vidalia Project at
+** http://www.vidalia-project.net/. No part of Vidalia, including this file,
+** may be copied, modified, propagated, or distributed except according to
+** the terms described in the LICENSE file.
+*/
+
+/*
+** \file upnpcontrolthread.h
+** \version $Id$
+** \brief Thread for configuring UPnP in the background
+*/
+
+#ifndef _UPNPCONTROLTHREAD_H
+#define _UPNPCONTROLTHREAD_H
+
+#include <QThread>
+#include <QMutex>
+#include <QWaitCondition>
+#include <QTime>
+#include "upnpcontrol.h"
+
+#define STATICLIB
+#include <miniupnpc/miniwget.h>
+#include <miniupnpc/miniupnpc.h>
+#include <miniupnpc/upnpcommands.h>
+
+
+class UPNPControlThread : public QThread
+{
+ Q_OBJECT
+
+public:
+ /** Specifies the number of milliseconds to wait for devices to respond
+ * when attempting to discover UPnP-enabled IGDs. */
+ static const int UPNPCONTROL_DISCOVER_TIMEOUT = 2000;
+
+ /** Constructor. <b>control</b> will be used for retrieving the desired port
+ * forwarding state. */
+ UPNPControlThread(UPNPControl *control);
+ /** Destructor. The UPnP control thread must be stopped prior to destroying
+ * this object. */
+ ~UPNPControlThread();
+ /** Terminates the UPnP control thread's run() loop. */
+ void stop();
+ /** Wakes up the UPnP control thread's run() loop. */
+ void wakeup();
+
+protected:
+ /** Thread entry point. The thread has a main loop that periodically wakes
+ * up and updates the configured port mappings. Upon exiting, all port
+ * mappings will be removed. */
+ void run();
+
+private:
+ /** Sets up port forwarding according the previously-configured desired
+ * state. The desired state is set using UPNPControl's setDesiredState()
+ * method. */
+ void configurePorts();
+ /** Discovers UPnP-enabled IGDs on the network. This method will block for
+ * UPNPCONTROL_DISCOVER_TIMEOUT milliseconds. */
+ UPNPControl::UPNPError initializeUPNP();
+ /** Updates the port mapping for <b>oldPort</b>, changing it to
+ * <b>newPort</b>. */
+ UPNPControl::UPNPError updatePort(quint16 oldPort, quint16 newPort);
+ /** Adds a port forwarding mapping from external:<b>port</b> to
+ * internal:<b>port</b>. Returns 0 on success, or non-zero on failure. */
+ UPNPControl::UPNPError forwardPort(quint16 port);
+ /** Removes the port mapping for <b>port</b>. Returns 0 on success or
+ * non-zero on failure. */
+ UPNPControl::UPNPError disablePort(quint16 port);
+
+ QTime _upnpInitialized; /**< Time at which the UPnP state was last set. */
+ bool _keepRunning; /**< True if the control thread should keep running. */
+ UPNPControl *_control; /**< Stores desired UPnP state. */
+ QWaitCondition *_waitCondition; /**< Used to wake up the control thread. */
+ QMutex *_waitMutex; /**< Mutex around shared variables. */
+ quint16 _dirPort; /**< Desired DirPort. */
+ quint16 _orPort; /**< Desired ORPort. */
+
+ /* Used by miniupnpc library */
+ struct UPNPUrls urls;
+ struct IGDdatas data;
+ char lanaddr[16];
+};
+#endif
+