diff options
author | Slávek Banko <[email protected]> | 2013-07-27 16:34:45 +0200 |
---|---|---|
committer | Slávek Banko <[email protected]> | 2013-07-27 16:34:45 +0200 |
commit | d76ff81b7c1beffef0b84e570914c8f2d47834e6 (patch) | |
tree | 284b80ce7c5456fbb041f7979ac2c0baeead8902 /src/upnp/upnprouter.cpp | |
download | tork-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.cpp | 531 |
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 * + * [email protected] * + * * + * 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" |