summaryrefslogtreecommitdiffstats
path: root/src/upnp/upnprouter.cpp
diff options
context:
space:
mode:
authorSlávek Banko <[email protected]>2013-07-27 16:34:45 +0200
committerSlávek Banko <[email protected]>2013-07-27 16:34:45 +0200
commitd76ff81b7c1beffef0b84e570914c8f2d47834e6 (patch)
tree284b80ce7c5456fbb041f7979ac2c0baeead8902 /src/upnp/upnprouter.cpp
downloadtork-d76ff81b7c1beffef0b84e570914c8f2d47834e6.tar.gz
tork-d76ff81b7c1beffef0b84e570914c8f2d47834e6.zip
Initial import of tork 0.33
Diffstat (limited to 'src/upnp/upnprouter.cpp')
-rw-r--r--src/upnp/upnprouter.cpp531
1 files changed, 531 insertions, 0 deletions
diff --git a/src/upnp/upnprouter.cpp b/src/upnp/upnprouter.cpp
new file mode 100644
index 0000000..4d8f5ad
--- /dev/null
+++ b/src/upnp/upnprouter.cpp
@@ -0,0 +1,531 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Joris Guisson *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+#include <stdlib.h>
+#include <klocale.h>
+#include <kdebug.h>
+#include <kglobal.h>
+#include <kstandarddirs.h>
+#include <qstringlist.h>
+#include <kio/netaccess.h>
+#include <kio/job.h>
+#include "httprequest.h"
+#include "upnprouter.h"
+#include "upnpdescriptionparser.h"
+#include "soap.h"
+#include "../functions.h"
+#include <fcntl.h>
+#include <qdir.h>
+#include <qfile.h>
+
+using namespace bt;
+using namespace net;
+
+namespace kt
+{
+ UPnPService::UPnPService()
+ {
+ }
+
+ UPnPService::UPnPService(const UPnPService & s)
+ {
+ this->servicetype = s.servicetype;
+ this->controlurl = s.controlurl;
+ this->eventsuburl = s.eventsuburl;
+ this->serviceid = s.serviceid;
+ this->scpdurl = s.scpdurl;
+ }
+
+ void UPnPService::setProperty(const QString & name,const QString & value)
+ {
+ if (name == "serviceType")
+ servicetype = value;
+ else if (name == "controlURL")
+ controlurl = value;
+ else if (name == "eventSubURL")
+ eventsuburl = value;
+ else if (name == "SCPDURL")
+ scpdurl = value;
+ else if (name == "serviceId")
+ serviceid = value;
+ }
+
+ void UPnPService::clear()
+ {
+ servicetype = controlurl = eventsuburl = scpdurl = serviceid = "";
+ }
+
+ void UPnPService::debugPrintData()
+ {
+ kdDebug() << " servicetype = " << servicetype << endl;
+ kdDebug() << " controlurl = " << controlurl << endl;
+ kdDebug() << " eventsuburl = " << eventsuburl << endl;
+ kdDebug() << " scpdurl = " << scpdurl << endl;
+ kdDebug() << " serviceid = " << serviceid << endl;
+ }
+
+ UPnPService & UPnPService::operator = (const UPnPService & s)
+ {
+ this->servicetype = s.servicetype;
+ this->controlurl = s.controlurl;
+ this->eventsuburl = s.eventsuburl;
+ this->serviceid = s.serviceid;
+ this->scpdurl = s.scpdurl;
+ return *this;
+ }
+
+ ///////////////////////////////////////
+
+ void UPnPDeviceDescription::setProperty(const QString & name,const QString & value)
+ {
+ if (name == "friendlyName")
+ friendlyName = value;
+ else if (name == "manufacturer")
+ manufacturer = value;
+ else if (name == "modelDescription")
+ modelDescription = value;
+ else if (name == "modelName")
+ modelName = value;
+ else if (name == "modelNumber")
+ modelNumber == value;
+ }
+
+ ///////////////////////////////////////
+
+ UPnPRouter::UPnPRouter(const QString & server,const KURL & location,bool verbose) : server(server),location(location),verbose(verbose)
+ {
+ forwardedPortList = new ForwardPortList();
+ // make the tmp_file unique, current time * a random number should be enough
+ tmp_file = QString("/tmp/tork_upnp_description-%1.xml").arg(bt::GetCurrentTime() * rand());
+ }
+
+
+ UPnPRouter::~UPnPRouter()
+ {
+ QValueList<HTTPRequest*>::iterator i = active_reqs.begin();
+ while (i != active_reqs.end())
+ {
+ (*i)->deleteLater();
+ i++;
+ }
+ }
+
+ void UPnPRouter::addService(const UPnPService & s)
+ {
+ QValueList<UPnPService>::iterator i = services.begin();
+ while (i != services.end())
+ {
+ UPnPService & os = *i;
+ if (s.servicetype == os.servicetype)
+ return;
+ i++;
+ }
+ services.append(s);
+ }
+
+ void UPnPRouter::downloadFinished(KIO::Job* j)
+ {
+ if (j->error())
+ {
+ kdDebug() << "Failed to download " << location << " : " << j->errorString() << endl;
+ return;
+ }
+
+ QString target = tmp_file;
+ // load in the file (target is always local)
+ UPnPDescriptionParser desc_parse;
+ bool ret = desc_parse.parse(target,this);
+ if (!ret)
+ {
+ kdDebug() << "Error parsing router description !" << endl;
+ QString dest = KGlobal::dirs()->saveLocation("data","tork") + "upnp_failure";
+ KIO::file_copy(target,dest,-1,true,false,false);
+ }
+ else
+ {
+ if (verbose)
+ debugPrintData();
+ }
+ xmlFileDownloaded(this,ret);
+ remove(QFile::encodeName(target));
+ }
+
+ void UPnPRouter::downloadXMLFile()
+ {
+ // downlaod XML description into a temporary file in /tmp
+ KIO::Job* job = KIO::file_copy(location,tmp_file,-1,true,false,false);
+ connect(job,SIGNAL(result(KIO::Job *)),this,SLOT(downloadFinished( KIO::Job* )));
+ }
+
+ void UPnPRouter::debugPrintData()
+ {
+ kdDebug() << "UPnPRouter : " << endl;
+ kdDebug() << "Friendly name = " << desc.friendlyName << endl;
+ kdDebug() << "Manufacterer = " << desc.manufacturer << endl;
+ kdDebug() << "Model description = " << desc.modelDescription << endl;
+ kdDebug() << "Model name = " << desc.modelName << endl;
+ kdDebug() << "Model number = " << desc.modelNumber << endl;
+ for (QValueList<UPnPService>::iterator i = services.begin();i != services.end();i++)
+ {
+ UPnPService & s = *i;
+ kdDebug() << "Service : " << endl;
+ s.debugPrintData();
+ kdDebug() << "Done" << endl;
+ }
+ kdDebug() << "Done" << endl;
+ }
+
+
+ void UPnPRouter::forward(UPnPService* srv,const net::Port & externalport,
+ const net::Port & internalport)
+ {
+
+
+ // add all the arguments for the command
+ QValueList<SOAP::Arg> args;
+ SOAP::Arg a;
+ a.element = "NewRemoteHost";
+ args.append(a);
+
+ // the external port
+ a.element = "NewExternalPort";
+ a.value = QString::number(externalport.number);
+ args.append(a);
+
+ // the protocol
+ a.element = "NewProtocol";
+ a.value = externalport.proto == TCP ? "TCP" : "UDP";
+ args.append(a);
+
+ // the local port
+ a.element = "NewInternalPort";
+ if (internalport.number)
+ a.value = QString::number(internalport.number);
+ else
+ a.value = QString::number(externalport.number);
+ args.append(a);
+
+ // the local IP address
+ a.element = "NewInternalClient";
+ a.value = "$LOCAL_IP";// will be replaced by our local ip in bt::HTTPRequest
+ args.append(a);
+
+ a.element = "NewEnabled";
+ a.value = "1";
+ args.append(a);
+
+ a.element = "NewPortMappingDescription";
+ static Uint32 cnt = 0;
+ a.value = QString("TorK UPNP %1").arg(cnt++); // TODO: change this
+ args.append(a);
+
+ a.element = "NewLeaseDuration";
+ a.value = "0";
+ args.append(a);
+
+ QString action = "AddPortMapping";
+ QString comm = SOAP::createCommand(action,srv->servicetype,args);
+
+ Forwarding fw = {externalport,internalport,0,srv};
+ // erase old forwarding if one exists
+ QValueList<Forwarding>::iterator itr = fwds.begin();
+ while (itr != fwds.end())
+ {
+ Forwarding & fwo = *itr;
+ if (fwo.extport == externalport && fwo.intport == internalport && fwo.service == srv)
+ itr = fwds.erase(itr);
+ else
+ itr++;
+ }
+
+ fw.pending_req = sendSoapQuery(comm,srv->servicetype + "#" + action,srv->controlurl, true);
+ fwds.append(fw);
+
+ //Track the forwarding request so we can know whether it was successful or not
+ //and keep a map of successfully forwarded ports in forwardedPorts
+ ForwardingRequest fwreq = {externalport,internalport,fw.pending_req};
+ QValueList<ForwardingRequest>::iterator itrq = fwdreqs.begin();
+ while (itrq != fwdreqs.end())
+ {
+ ForwardingRequest & fwo = *itrq;
+ if (fwo.extport == externalport && fwo.intport == internalport)
+ itrq = fwdreqs.erase(itrq);
+ else
+ itrq++;
+ }
+ fwdreqs.append(fwreq);
+
+ }
+
+ void UPnPRouter::forward(const net::Port & externalport,
+ const net::Port & internalport,
+ bool force)
+ {
+
+ if ((forwardedPortList->contains(net::ForwardPort(externalport.number,
+ internalport.number,
+ net::TCP,false)))
+ && (!force))
+ return;
+
+ kdDebug() << "Forwarding port " << externalport.number << " (" << (externalport.proto == UDP ? "UDP" : "TCP") << ")" << endl;
+ // first find the right service
+ QValueList<UPnPService>::iterator i = services.begin();
+ while (i != services.end())
+ {
+ UPnPService & s = *i;
+ if (s.servicetype == "urn:schemas-upnp-org:service:WANIPConnection:1" ||
+ s.servicetype == "urn:schemas-upnp-org:service:WANPPPConnection:1")
+ {
+ if (internalport.number)
+ forward(&s,externalport,internalport);
+ else
+ forward(&s,externalport);
+ }
+ i++;
+ }
+
+ }
+
+ void UPnPRouter::undoForward(UPnPService* srv,const net::Port & extport,
+ const net::Port & intport,bt::WaitJob* waitjob)
+ {
+ // add all the arguments for the command
+ QValueList<SOAP::Arg> args;
+ SOAP::Arg a;
+ a.element = "NewRemoteHost";
+ args.append(a);
+
+ // the external port
+ a.element = "NewExternalPort";
+ a.value = QString::number(extport.number);
+ args.append(a);
+
+ // the protocol
+ a.element = "NewProtocol";
+ a.value = extport.proto == TCP ? "TCP" : "UDP";
+ args.append(a);
+
+
+ QString action = "DeletePortMapping";
+ QString comm = SOAP::createCommand(action,srv->servicetype,args);
+ bt::HTTPRequest* r = sendSoapQuery(comm,srv->servicetype + "#" + action,srv->controlurl,waitjob != 0,false);
+
+ ForwardingRequest fwreq = {extport,intport,r};
+ QValueList<ForwardingRequest>::iterator itrq = fwdreqs.begin();
+ while (itrq != fwdreqs.end())
+ {
+ ForwardingRequest & fwo = *itrq;
+ if (fwo.extport == extport && fwo.intport == intport)
+ itrq = fwdreqs.erase(itrq);
+ else
+ itrq++;
+ }
+ fwdreqs.append(fwreq);
+
+
+ if (waitjob)
+ waitjob->addExitOperation(r);
+
+ updateGUI();
+ }
+
+
+ void UPnPRouter::undoForward(const net::Port & externalport,
+ const net::Port & ,bt::WaitJob* waitjob)
+ {
+ kdDebug() << "Undoing forward of port " << externalport.number
+ << " (" << (externalport.proto == UDP ? "UDP" : "TCP") << ")" << endl;
+
+ QValueList<Forwarding>::iterator itr = fwds.begin();
+ while (itr != fwds.end())
+ {
+ Forwarding & wd = *itr;
+ if (wd.extport == externalport)
+ {
+ undoForward(wd.service,wd.extport,wd.intport,waitjob);
+ itr = fwds.erase(itr);
+ }
+ else
+ {
+ itr++;
+ }
+ }
+ }
+
+ bt::HTTPRequest* UPnPRouter::sendSoapQuery(const QString & query,const QString & soapact,const QString & controlurl, bool fwd,bool at_exit)
+ {
+ // if port is not set, 0 will be returned
+ // thanks to Diego R. Brogna for spotting this bug
+ if (location.port()==0)
+ location.setPort(80);
+
+ QString http_hdr = QString(
+ "POST %1 HTTP/1.1\r\n"
+ "HOST: %2:%3\r\n"
+ "Content-length: $CONTENT_LENGTH\r\n"
+ "Content-Type: text/xml\r\n"
+ "SOAPAction: \"%4\"\r\n"
+ "\r\n").arg(controlurl).arg(location.host()).arg(location.port()).arg(soapact);
+
+
+ HTTPRequest* r = new HTTPRequest(http_hdr,query,location.host(),location.port(),verbose, fwd);
+ connect(r,SIGNAL(replyError(bt::HTTPRequest* ,const QString& ,bool)),
+ this,SLOT(onReplyError(bt::HTTPRequest* ,const QString& ,bool)));
+ connect(r,SIGNAL(replyOK(bt::HTTPRequest* ,const QString& ,bool)),
+ this,SLOT(onReplyOK(bt::HTTPRequest* ,const QString& ,bool)));
+ connect(r,SIGNAL(error(bt::HTTPRequest*, bool )),
+ this,SLOT(onError(bt::HTTPRequest*, bool )));
+ r->start();
+ if (!at_exit)
+ active_reqs.append(r);
+ return r;
+ }
+
+ void UPnPRouter::httpRequestDone(bt::HTTPRequest* r,bool erase_fwd)
+ {
+ QValueList<Forwarding>::iterator i = fwds.begin();
+ while (i != fwds.end())
+ {
+ Forwarding & fw = *i;
+ if (fw.pending_req == r)
+ {
+ fw.pending_req = 0;
+ if (erase_fwd)
+ fwds.erase(i);
+ break;
+ }
+ i++;
+ }
+
+ updateGUI();
+ active_reqs.remove(r);
+ r->deleteLater();
+ }
+
+ void UPnPRouter::onReplyOK(bt::HTTPRequest* r,const QString & s,bool fwd)
+ {
+ kdDebug() << "UPnPRouter : OK" << endl;
+ kdDebug() << "FWD : " << fwd << endl;
+ QValueList<ForwardingRequest>::iterator i = fwdreqs.begin();
+ while (i != fwdreqs.end())
+ {
+ ForwardingRequest & fw = *i;
+ if (fw.pending_req == r)
+ {
+ if (fwd)
+ forwardedPortList->addNewForwardPort(fw.extport.number,
+ fw.intport.number,net::TCP,false);
+ else
+ forwardedPortList->removeForwardPort(fw.extport.number,
+ fw.intport.number,net::TCP);
+ break;
+ }
+ i++;
+ }
+
+ emit replyOK(this,r,s,fwd);
+ httpRequestDone(r,false);
+ }
+
+ void UPnPRouter::onReplyError(bt::HTTPRequest* r,const QString & s,bool fwd)
+ {
+ if (verbose)
+ kdDebug() << "UPnPRouter : Error" << endl;
+ kdDebug() << r->showPayload() << endl;
+ httpRequestDone(r,true);
+ emit replyError(this,r,s,fwd);
+ }
+
+ void UPnPRouter::onError(bt::HTTPRequest* r,bool)
+ {
+ httpRequestDone(r,true);
+ }
+
+
+}
+
+
+namespace bt
+{
+
+ WaitJob::WaitJob(Uint32 millis) : KIO::Job(false)
+ {
+ connect(&timer,SIGNAL(timeout()),this,SLOT(timerDone()));
+ timer.start(millis,true);
+ }
+
+
+ WaitJob::~WaitJob()
+ {}
+
+ void WaitJob::kill(bool)
+ {
+ m_error = 0;
+ emitResult();
+ }
+
+ void WaitJob::timerDone()
+ {
+ // set the error to null and emit the result
+ m_error = 0;
+ emitResult();
+ }
+
+ void WaitJob::addExitOperation(kt::ExitOperation* op)
+ {
+ exit_ops.append(op);
+ connect(op,SIGNAL(operationFinished( kt::ExitOperation* )),
+ this,SLOT(operationFinished( kt::ExitOperation* )));
+ }
+
+ void WaitJob::operationFinished(kt::ExitOperation* op)
+ {
+ if (exit_ops.count() > 0)
+ {
+ exit_ops.remove(op);
+ if (op->deleteAllowed())
+ op->deleteLater();
+
+ if (exit_ops.count() == 0)
+ timerDone();
+ }
+ }
+
+ void WaitJob::execute(WaitJob* job)
+ {
+ KIO::NetAccess::synchronousRun(job,0);
+ }
+
+ void SynchronousWait(Uint32 millis)
+ {
+ kdDebug() << "SynchronousWait" << endl;
+ WaitJob* j = new WaitJob(millis);
+ KIO::NetAccess::synchronousRun(j,0);
+ }
+
+
+// void UpdateCurrentTime()
+// {
+// global_time_stamp = Now();
+// }
+
+}
+
+#include "upnprouter.moc"