/* * Copyright (c) 2011 Tobias Markmann * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ #include "PlatformNATTraversalWorker.h" #include <boost/smart_ptr/make_shared.hpp> #include <boost/enable_shared_from_this.hpp> #include <boost/numeric/conversion/cast.hpp> #include <Swiften/Base/Log.h> #include <Swiften/Network/NATTraversalGetPublicIPRequest.h> #include <Swiften/Network/NATTraversalForwardPortRequest.h> #include <Swiften/Network/NATTraversalRemovePortForwardingRequest.h> #ifdef HAVE_LIBNATPMP #include <Swiften/Network/NATPMPInterface.h> #endif #ifdef HAVE_LIBMINIUPNPC #include <Swiften/Network/MiniUPnPInterface.h> #endif namespace Swift { class PlatformNATTraversalRequest : public boost::enable_shared_from_this<PlatformNATTraversalRequest> { public: typedef boost::shared_ptr<PlatformNATTraversalRequest> ref; public: PlatformNATTraversalRequest(PlatformNATTraversalWorker* worker) : worker(worker) { } virtual ~PlatformNATTraversalRequest() { } virtual void doRun() { worker->addRequestToQueue(shared_from_this()); } NATTraversalInterface* getNATTraversalInterface() const { return worker->getNATTraversalInterface(); } virtual void runBlocking() = 0; private: PlatformNATTraversalWorker* worker; }; class PlatformNATTraversalGetPublicIPRequest : public NATTraversalGetPublicIPRequest, public PlatformNATTraversalRequest { public: PlatformNATTraversalGetPublicIPRequest(PlatformNATTraversalWorker* worker) : PlatformNATTraversalRequest(worker) { } virtual void start() { doRun(); } virtual void stop() { // TODO } virtual void runBlocking() { onResult(getNATTraversalInterface()->getPublicIP()); } }; class PlatformNATTraversalForwardPortRequest : public NATTraversalForwardPortRequest, public PlatformNATTraversalRequest { public: PlatformNATTraversalForwardPortRequest(PlatformNATTraversalWorker* worker, unsigned int localIP, unsigned int publicIP) : PlatformNATTraversalRequest(worker), localIP(localIP), publicIP(publicIP) { } virtual void start() { doRun(); } virtual void stop() { // TODO } virtual void runBlocking() { onResult(getNATTraversalInterface()->addPortForward(boost::numeric_cast<int>(localIP), boost::numeric_cast<int>(publicIP))); } private: unsigned int localIP; unsigned int publicIP; }; class PlatformNATTraversalRemovePortForwardingRequest : public NATTraversalRemovePortForwardingRequest, public PlatformNATTraversalRequest { public: PlatformNATTraversalRemovePortForwardingRequest(PlatformNATTraversalWorker* worker, const NATPortMapping& mapping) : PlatformNATTraversalRequest(worker), mapping(mapping) { } virtual void start() { doRun(); } virtual void stop() { // TODO } virtual void runBlocking() { onResult(getNATTraversalInterface()->removePortForward(mapping)); } private: NATPortMapping mapping; }; PlatformNATTraversalWorker::PlatformNATTraversalWorker(EventLoop* eventLoop) : eventLoop(eventLoop), stopRequested(false), natPMPSupported(boost::logic::indeterminate), natPMPInterface(NULL), miniUPnPSupported(boost::logic::indeterminate), miniUPnPInterface(NULL) { nullNATTraversalInterface = new NullNATTraversalInterface(); // FIXME: This should be done from start(), and the current start() should be an internal method thread = new boost::thread(boost::bind(&PlatformNATTraversalWorker::start, this)); } PlatformNATTraversalWorker::~PlatformNATTraversalWorker() { stopRequested = true; addRequestToQueue(boost::shared_ptr<PlatformNATTraversalRequest>()); thread->join(); delete thread; #ifdef HAVE_LIBNATPMP delete natPMPInterface; #endif #ifdef HAVE_LIBMINIUPNPC delete miniUPnPInterface; #endif delete nullNATTraversalInterface; } NATTraversalInterface* PlatformNATTraversalWorker::getNATTraversalInterface() const { #ifdef HAVE_LIBMINIUPNPC if (boost::logic::indeterminate(miniUPnPSupported)) { miniUPnPInterface = new MiniUPnPInterface(); miniUPnPSupported = miniUPnPInterface->isAvailable(); } if (miniUPnPSupported) { return miniUPnPInterface; } #endif #ifdef HAVE_LIBNATPMP if (boost::logic::indeterminate(natPMPSupported)) { natPMPInterface = new NATPMPInterface(); natPMPSupported = natPMPInterface->isAvailable(); } if (natPMPSupported) { return natPMPInterface; } #endif return nullNATTraversalInterface; } boost::shared_ptr<NATTraversalGetPublicIPRequest> PlatformNATTraversalWorker::createGetPublicIPRequest() { return boost::make_shared<PlatformNATTraversalGetPublicIPRequest>(this); } boost::shared_ptr<NATTraversalForwardPortRequest> PlatformNATTraversalWorker::createForwardPortRequest(int localPort, int publicPort) { return boost::make_shared<PlatformNATTraversalForwardPortRequest>(this, localPort, publicPort); } boost::shared_ptr<NATTraversalRemovePortForwardingRequest> PlatformNATTraversalWorker::createRemovePortForwardingRequest(int localPort, int publicPort) { NATPortMapping mapping(localPort, publicPort, NATPortMapping::TCP); // FIXME return boost::make_shared<PlatformNATTraversalRemovePortForwardingRequest>(this, mapping); } void PlatformNATTraversalWorker::start() { while (!stopRequested) { PlatformNATTraversalRequest::ref request; { boost::unique_lock<boost::mutex> lock(queueMutex); while (queue.empty()) { queueNonEmpty.wait(lock); } request = queue.front(); queue.pop_front(); } // Check whether we don't have a non-null request (used to stop the // worker) if (request) { request->runBlocking(); } } } void PlatformNATTraversalWorker::stop() { // TODO } void PlatformNATTraversalWorker::addRequestToQueue(PlatformNATTraversalRequest::ref request) { { boost::lock_guard<boost::mutex> lock(queueMutex); queue.push_back(request); } queueNonEmpty.notify_one(); } }