blob: 6fa2b78d7bcd23bc4ee180fc1c6dd6546326d711 [file] [log] [blame]
/*
* Copyright (C) 2004-2020 Savoir-faire Linux Inc.
*
* Author: Stepan Salenikovich <stepan.salenikovich@savoirfairelinux.com>
* Author: Eden Abitbol <eden.abitbol@savoirfairelinux.com>
*
* 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 3 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.
*/
#pragma once
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef _WIN32
#define UPNP_USE_MSVCPP
#define UPNP_STATIC_LIB
#endif
#include "../upnp_protocol.h"
#include "../global_mapping.h"
#include "../igd.h"
#include "upnp_igd.h"
#include "logger.h"
#include "ip_utils.h"
#include "noncopyable.h"
#include "compiler_intrinsics.h"
#include <upnp/upnp.h>
#include <upnp/upnptools.h>
#ifdef _WIN32
#include <windows.h>
#include <wincrypt.h>
#endif
#include <atomic>
#include <thread>
#include <list>
#include <map>
#include <set>
#include <string>
#include <memory>
#include <future>
// Action identifiers.
constexpr static const char* ACTION_ADD_PORT_MAPPING {"AddPortMapping"};
constexpr static const char* ACTION_DELETE_PORT_MAPPING {"DeletePortMapping"};
constexpr static const char* ACTION_GET_GENERIC_PORT_MAPPING_ENTRY {"GetGenericPortMappingEntry"};
constexpr static const char* ACTION_GET_STATUS_INFO {"GetStatusInfo"};
constexpr static const char* ACTION_GET_EXTERNAL_IP_ADDRESS {"GetExternalIPAddress"};
namespace jami {
class IpAddr;
}
namespace jami { namespace upnp {
// Error codes returned by router when trying to remove ports.
constexpr static int ARRAY_IDX_INVALID = 713;
constexpr static int CONFLICT_IN_MAPPING = 718;
// Timeout values (in seconds).
constexpr static unsigned int SEARCH_TIMEOUT {5};
constexpr static unsigned int SUBSCRIBE_TIMEOUT {300};
class PUPnP : public UPnPProtocol
{
public:
using XMLDocument = std::unique_ptr<IXML_Document, decltype(ixmlDocument_free)&>;
struct IGDInfo {
std::string location;
XMLDocument document;
};
enum class CtrlAction {
UNKNOWN,
ADD_PORT_MAPPING,
DELETE_PORT_MAPPING,
GET_GENERIC_PORT_MAPPING_ENTRY,
GET_STATUS_INFO,
GET_EXTERNAL_IP_ADDRESS
};
using pIGDInfo = std::unique_ptr<IGDInfo>;
PUPnP();
~PUPnP();
// Returns the protocol type.
Type getType() const override { return Type::PUPNP; }
// Notifies a change in network.
void clearIgds() override;
// Sends out async search for IGD.
void searchForIgd() override;
// Tries to add mapping. Assumes mutex is already locked.
void requestMappingAdd(IGD* igd, uint16_t port_external, uint16_t port_internal, PortType type) override;
// Treats the reception of an add mapping action answer.
void processAddMapAction(const std::string& ctrlURL, IXML_Document* actionRequest);
// Returns control point action callback based on xml node.
CtrlAction getAction(char* xmlNode);
// Removes a mapping.
void requestMappingRemove(const Mapping& igdMapping) override;
// Treats the reception of a remove mapping action answer.
void processRemoveMapAction(const std::string& ctrlURL, IXML_Document* actionRequest);
// Removes all local mappings of IGD that we're added by the application.
void removeAllLocalMappings(IGD* igd) override;
private:
// Validate IGD from the xml document received from the router.
bool validateIgd(const IGDInfo&);
// Control point callback.
static int ctrlPtCallback(Upnp_EventType event_type, const void* event, void* user_data);
#if UPNP_VERSION < 10800
static inline int ctrlPtCallback(Upnp_EventType event_type, void* event, void* user_data) {
return ctrlPtCallback(event_type, (const void*)event, user_data);
};
#endif
// Callback event handler function for the UPnP client (control point).
int handleCtrlPtUPnPEvents(Upnp_EventType event_type, const void* event);
// Subscription event callback.
static int subEventCallback(Upnp_EventType event_type, const void* event, void* user_data);
#if UPNP_VERSION < 10800
static inline int subEventCallback(Upnp_EventType event_type, void* event, void* user_data) {
return subEventCallback(event_type, (const void*)event, user_data);
};
#endif
// Callback subscription event function for handling subscription request.
int handleSubscriptionUPnPEvent(Upnp_EventType event_type, const void* event);
// Parses the IGD candidate.
std::unique_ptr<UPnPIGD> parseIgd(IXML_Document* doc, std::string locationUrl);
// These functions directly create UPnP actions and make synchronous UPnP control point calls. Assumes mutex is already locked.
bool actionIsIgdConnected(const UPnPIGD& igd);
IpAddr actionGetExternalIP(const UPnPIGD& igd);
void actionDeletePortMappingsByDesc(const UPnPIGD& igd, const std::string& description);
bool actionDeletePortMapping(const UPnPIGD& igd, const std::string& port_external, const std::string& protocol);
bool actionAddPortMapping(const UPnPIGD& igd, const Mapping& mapping, UPnPProtocol::UpnpError& upnp_error);
bool actionAddPortMappingAsync(const UPnPIGD& igd, const Mapping& mapping);
bool actionDeletePortMappingAsync(const UPnPIGD& igd, const std::string& port_external, const std::string& protocol);
private:
NON_COPYABLE(PUPnP);
std::condition_variable pupnpCv_ {}; // Condition variable for thread-safe signaling.
std::atomic_bool pupnpRun_ { true }; // Variable to allow the thread to run.
std::thread pupnpThread_ {}; // PUPnP thread for non-blocking client registration.
std::map<std::string, std::shared_ptr<IGD>> validIgdList_; // Map of valid IGDs with their UDN (universal Id).
std::set<std::string> cpDeviceList_; // Control point device list containing the device ID and device subscription event url.
std::list<std::future<pIGDInfo>> dwnldlXmlList_; // List of futures for blocking xml download function calls.
std::list<std::future<pIGDInfo>> cancelXmlList_; // List of abandoned documents
std::mutex ctrlptMutex_; // Mutex for client handle protection.
UpnpClient_Handle ctrlptHandle_ {-1}; // Control point handle.
std::atomic_bool clientRegistered_ { false }; // Indicates of the client is registered.
std::atomic_bool searchForIgd_ { false }; // Variable to signal thread for a search.
};
}} // namespace jami::upnp