blob: 0468352e08776488041f99142b5b6b34bb72692e [file] [log] [blame]
/*
* Copyright (C) 2004-2023 Savoir-faire Linux Inc.
*
* 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, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include "../ip_utils.h"
#include <map>
#include <string>
#include <chrono>
#include <functional>
#include <mutex>
#include <memory>
namespace dhtnet {
namespace upnp {
using sys_clock = std::chrono::system_clock;
enum class PortType { TCP, UDP };
/**
* State Machine:
*
* - PENDING: Initial state when a mapping is requested by the client.
* - IN_PROGRESS: Intermediate state while the mapping is being requested from an IGD.
* - OPEN: State when the mapping is successfully established.
* - FAILED: State when the mapping fails or is invalidated.
*
* State Transitions:
*
* - PENDING -> IN_PROGRESS: When the mapping request is sent to the IGD.
* - PENDING -> FAILED: When there is no valid IGD and it's not during the IGD discovery phase.
* - IN_PROGRESS -> OPEN: When the mapping is successfully established.
* - IN_PROGRESS -> FAILED: When the mapping fails.
* - OPEN -> FAILED: When the IGD becomes invalid or the mapping is removed.
* - FAILED -> PENDING: When auto-update is enabled and there is no valid IGD.
* - FAILED -> unregistered: When auto-update is disabled.
*
* If auto-update is enabled but there is a valid IGD, the mapping will be unregistered and a new mapping of the same type will be requested.
*/
enum class MappingState { PENDING, IN_PROGRESS, FAILED, OPEN };
enum class NatProtocolType;
class IGD;
class Mapping
{
friend class UPnPContext;
friend class NatPmp;
friend class PUPnP;
public:
using key_t = uint64_t;
using sharedPtr_t = std::shared_ptr<Mapping>;
using NotifyCallback = std::function<void(sharedPtr_t)>;
static constexpr char const* MAPPING_STATE_STR[4] {"PENDING", "IN_PROGRESS", "FAILED", "OPEN"};
static constexpr char const* UPNP_MAPPING_DESCRIPTION_PREFIX {"JAMI"};
Mapping(PortType type,
uint16_t portExternal = 0,
uint16_t portInternal = 0,
bool available = true);
Mapping(const Mapping& other);
Mapping(Mapping&& other) = delete;
~Mapping() = default;
// Delete operators with confusing semantic.
Mapping& operator=(Mapping&& other) = delete;
bool operator==(const Mapping& other) = delete;
bool operator!=(const Mapping& other) = delete;
bool operator<(const Mapping& other) = delete;
bool operator>(const Mapping& other) = delete;
bool operator<=(const Mapping& other) = delete;
bool operator>=(const Mapping& other) = delete;
inline explicit operator bool() const { return isValid(); }
void updateFrom(const Mapping& other);
void updateFrom(const Mapping::sharedPtr_t& other);
std::string getExternalAddress() const;
uint16_t getExternalPort() const;
std::string getExternalPortStr() const;
std::string getInternalAddress() const;
uint16_t getInternalPort() const;
std::string getInternalPortStr() const;
PortType getType() const;
const char* getTypeStr() const;
static const char* getTypeStr(PortType type) { return type == PortType::UDP ? "UDP" : "TCP"; }
std::shared_ptr<IGD> getIgd() const;
NatProtocolType getProtocol() const;
std::string_view getProtocolName() const;
bool isAvailable() const;
MappingState getState() const;
const char* getStateStr() const;
static const char* getStateStr(MappingState state)
{
return MAPPING_STATE_STR[static_cast<int>(state)];
}
std::string toString(bool extraInfo = false) const;
bool isValid() const;
bool hasValidHostAddress() const;
bool hasPublicAddress() const;
void setNotifyCallback(NotifyCallback cb);
void enableAutoUpdate(bool enable);
bool getAutoUpdate() const;
key_t getMapKey() const;
static PortType getTypeFromMapKey(key_t key);
sys_clock::time_point getRenewalTime() const;
sys_clock::time_point getExpiryTime() const;
private:
NotifyCallback getNotifyCallback() const;
void setInternalAddress(const std::string& addr);
void setExternalPort(uint16_t port);
void setInternalPort(uint16_t port);
void setIgd(const std::shared_ptr<IGD>& igd);
void setAvailable(bool val);
void setState(const MappingState& state);
void setRenewalTime(sys_clock::time_point time);
void setExpiryTime(sys_clock::time_point time);
mutable std::mutex mutex_;
PortType type_ {PortType::UDP};
uint16_t externalPort_ {0};
uint16_t internalPort_ {0};
std::string internalAddr_;
// Protocol and
std::shared_ptr<IGD> igd_;
// Track if the mapping is available to use.
bool available_;
// Track the state of the mapping
MappingState state_;
NotifyCallback notifyCb_;
// If true, a new mapping will be requested on behalf of the mapping
// owner when the mapping state changes from "OPEN" to "FAILED".
bool autoUpdate_;
sys_clock::time_point renewalTime_;
sys_clock::time_point expiryTime_;
};
struct MappingInfo
{
std::string remoteHost;
std::string protocol;
std::string internalClient;
std::string enabled;
std::string description;
uint16_t externalPort;
uint16_t internalPort;
uint32_t leaseDuration;
};
} // namespace upnp
} // namespace dhtnet