UPNPControlThread.cpp
Go to the documentation of this file.00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017 #include "UPNPControlThread.h"
00018 #include "UPNPControl.h"
00019 #include "Vidalia.h"
00020
00021 #include <QWaitCondition>
00022 #include <QMutex>
00023 #include <QTime>
00024 #include <QTextStream>
00025 #include <QString>
00026 #include <QMessageBox>
00027
00028 #define UPNPCONTROL_REINIT_MSEC 300000 // 5 minutes
00029 #define UPNPCONTROL_MAX_WAIT_MSEC 60000 // 1 minute
00030
00031
00032
00033
00034 UPNPControlThread::UPNPControlThread(UPNPControl *control)
00035 {
00036 _upnpInitialized = QTime();
00037 _keepRunning = true;
00038 _control = control;
00039
00040 _dirPort = 0;
00041 _orPort = 0;
00042
00043 _waitCondition = new QWaitCondition();
00044 _waitMutex = new QMutex();
00045 }
00046
00047
00048
00049
00050
00051 UPNPControlThread::~UPNPControlThread()
00052 {
00053 delete _waitCondition;
00054 delete _waitMutex;
00055 }
00056
00057
00058
00059
00060 void
00061 UPNPControlThread::run()
00062 {
00063 bool shouldExit = false;
00064
00065 forever {
00066
00067
00068
00069 configurePorts();
00070
00071
00072 _waitMutex->lock();
00073 if (_keepRunning) {
00074
00075 UPNPControl::instance()->setState(UPNPControl::IdleState);
00076 _waitCondition->wait(_waitMutex, UPNPCONTROL_MAX_WAIT_MSEC);
00077
00078
00079 shouldExit = !_keepRunning;
00080 _waitMutex->unlock();
00081 if (shouldExit)
00082 break;
00083 } else {
00084
00085 _waitMutex->unlock();
00086 break;
00087 }
00088 }
00089
00090
00091 updatePort(_dirPort, 0);
00092 updatePort(_orPort, 0);
00093 }
00094
00095
00096
00097
00098
00099 void
00100 UPNPControlThread::configurePorts()
00101 {
00102 quint16 desiredDirPort, desiredOrPort;
00103 bool force_init = false;
00104 UPNPControl::UPNPError retval = UPNPControl::Success;
00105
00106
00107 _control->getDesiredState(&desiredDirPort, &desiredOrPort);
00108
00109
00110
00111
00112 if (_upnpInitialized.isNull() ||
00113 _upnpInitialized>QTime::currentTime() ||
00114 _upnpInitialized.addMSecs(UPNPCONTROL_REINIT_MSEC)<QTime::currentTime()
00115 ) {
00116 _upnpInitialized = QTime();
00117 force_init = true;
00118 }
00119
00120 if (!force_init) {
00121
00122 if (desiredDirPort != _dirPort) {
00123 UPNPControl::instance()->setState(UPNPControl::UpdatingDirPortState);
00124 retval = updatePort(_dirPort, desiredDirPort);
00125 if (retval == UPNPControl::Success)
00126 _dirPort = desiredDirPort;
00127 else
00128 goto err;
00129 }
00130
00131
00132 if (desiredOrPort != _orPort) {
00133 UPNPControl::instance()->setState(UPNPControl::UpdatingORPortState);
00134 retval = updatePort(_orPort, desiredOrPort);
00135 if (retval == UPNPControl::Success)
00136 _orPort = desiredOrPort;
00137 else
00138 goto err;
00139 }
00140 } else {
00141
00142 UPNPControl::instance()->setState(UPNPControl::UpdatingDirPortState);
00143 retval = updatePort(0, desiredDirPort);
00144 if (retval == UPNPControl::Success)
00145 _dirPort = desiredDirPort;
00146 else
00147 goto err;
00148
00149 UPNPControl::instance()->setState(UPNPControl::UpdatingORPortState);
00150 retval = updatePort(0, desiredOrPort);
00151 if (retval == UPNPControl::Success)
00152 _orPort = desiredOrPort;
00153 else goto err;
00154 }
00155
00156 UPNPControl::instance()->setState(UPNPControl::ForwardingCompleteState);
00157 return;
00158
00159 err:
00160 UPNPControl::instance()->setError(retval);
00161 UPNPControl::instance()->setState(UPNPControl::ErrorState);
00162 }
00163
00164
00165
00166
00167 void
00168 UPNPControlThread::stop()
00169 {
00170
00171 _waitMutex->lock();
00172
00173
00174 _keepRunning = false;
00175
00176
00177 _waitCondition->wakeAll();
00178
00179
00180 _waitMutex->unlock();
00181
00182
00183 wait();
00184 }
00185
00186
00187
00188
00189 void
00190 UPNPControlThread::wakeup()
00191 {
00192 _waitMutex->lock();
00193 _waitCondition->wakeAll();
00194 _waitMutex->unlock();
00195 }
00196
00197
00198
00199 UPNPControl::UPNPError
00200 UPNPControlThread::updatePort(quint16 oldPort, quint16 newPort)
00201 {
00202 UPNPControl::UPNPError retval;
00203
00204 #ifdef Q_OS_WIN32
00205
00206 WSAData wsadata;
00207 if (WSAStartup(MAKEWORD(2,0), &wsadata) != 0) {
00208 vWarn("WSAStartup failure while updating UPnP port forwarding");
00209 return UPNPControl::WSAStartupFailed;
00210 }
00211 #endif
00212
00213 if (_upnpInitialized.isNull() && (oldPort != 0 || newPort != 0)) {
00214 retval = initializeUPNP();
00215 if (retval == UPNPControl::Success)
00216 _upnpInitialized = QTime::currentTime();
00217 else
00218 _upnpInitialized = QTime();
00219 } else {
00220 retval = UPNPControl::Success;
00221 }
00222
00223 if (retval == UPNPControl::Success && oldPort != 0)
00224 retval = disablePort(oldPort);
00225
00226 if (retval == UPNPControl::Success && newPort != 0)
00227 retval = forwardPort(newPort);
00228
00229 #ifdef Q_OS_WIN32
00230 WSACleanup();
00231 #endif
00232
00233 return retval;
00234 }
00235
00236
00237
00238
00239 UPNPControl::UPNPError
00240 UPNPControlThread::initializeUPNP()
00241 {
00242 struct UPNPDev *devlist;
00243 int retval;
00244
00245 memset(&urls, 0, sizeof(struct UPNPUrls));
00246 memset(&data, 0, sizeof(struct IGDdatas));
00247
00248 UPNPControl::instance()->setState(UPNPControl::DiscoverState);
00249
00250 devlist = upnpDiscover(UPNPCONTROL_DISCOVER_TIMEOUT, NULL, NULL, 0);
00251 if (NULL == devlist) {
00252 vWarn("upnpDiscover returned: NULL");
00253 return UPNPControl::NoUPNPDevicesFound;
00254 }
00255
00256 retval = UPNP_GetValidIGD(devlist, &urls, &data, lanaddr, sizeof(lanaddr));
00257
00258 vInfo("GetValidIGD returned: %1").arg(retval);
00259
00260 freeUPNPDevlist(devlist);
00261
00262 if (retval != 1 && retval != 2)
00263 return UPNPControl::NoValidIGDsFound;
00264
00265 return UPNPControl::Success;
00266 }
00267
00268
00269
00270 UPNPControl::UPNPError
00271 UPNPControlThread::forwardPort(quint16 port)
00272 {
00273 QString sPort;
00274 int retval;
00275
00276 char intClient[16];
00277 char intPort[6];
00278
00279
00280 sPort = QString::number(port);
00281
00282
00283 retval = UPNP_AddPortMapping(urls.controlURL, data.servicetype,
00284 qPrintable(sPort), qPrintable(sPort), lanaddr,
00285 "Tor relay", "TCP", NULL);
00286 if(UPNPCOMMAND_SUCCESS != retval) {
00287 vWarn("AddPortMapping(%1, %2, %3) failed with code %4")
00288 .arg(sPort).arg(sPort).arg(lanaddr).arg(retval);
00289 return UPNPControl::AddPortMappingFailed;
00290 }
00291
00292
00293 retval = UPNP_GetSpecificPortMappingEntry(urls.controlURL, data.servicetype,
00294 qPrintable(sPort), "TCP",
00295 intClient, intPort);
00296 if(UPNPCOMMAND_SUCCESS != retval) {
00297 vWarn("GetSpecificPortMappingEntry() failed with code %1").arg(retval);
00298 return UPNPControl::GetPortMappingFailed;
00299 }
00300
00301 if(! intClient[0]) {
00302 vWarn("GetSpecificPortMappingEntry failed.");
00303 return UPNPControl::GetPortMappingFailed;
00304 }
00305
00306
00307 vInfo("(external):%1 -> %2:%3").arg(sPort).arg(intClient).arg(intPort);
00308
00309 return UPNPControl::Success;
00310 }
00311
00312
00313
00314 UPNPControl::UPNPError
00315 UPNPControlThread::disablePort(quint16 port)
00316 {
00317 QString sPort = QString::number(port);
00318
00319
00320 int retval = UPNP_DeletePortMapping(urls.controlURL, data.servicetype,
00321 qPrintable(sPort), "TCP", NULL);
00322 if(UPNPCOMMAND_SUCCESS != retval) {
00323 vWarn("DeletePortMapping() failed with code %1").arg(retval);
00324 return UPNPControl::DeletePortMappingFailed;
00325 }
00326
00327
00328 vInfo("(external):%1 -> <>").arg(sPort);
00329
00330 return UPNPControl::Success;
00331 }
00332