2023-02-11 20:49:42 +00:00
|
|
|
// This file Copyright © 2008-2023 Mnemosyne LLC.
|
2022-02-07 16:25:02 +00:00
|
|
|
// It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only),
|
2022-01-20 18:27:56 +00:00
|
|
|
// or any future license endorsed by Mnemosyne LLC.
|
|
|
|
// License text can be found in the licenses/ folder.
|
2007-01-19 04:42:31 +00:00
|
|
|
|
2021-09-19 20:41:35 +00:00
|
|
|
#include <algorithm>
|
2022-08-11 17:28:37 +00:00
|
|
|
#include <chrono>
|
2022-08-17 16:08:36 +00:00
|
|
|
#include <memory>
|
2010-05-19 16:17:51 +00:00
|
|
|
|
2022-03-15 14:52:16 +00:00
|
|
|
#include <fmt/core.h>
|
|
|
|
|
2022-09-30 13:59:10 +00:00
|
|
|
#define LIBTRANSMISSION_PORT_FORWARDING_MODULE
|
|
|
|
|
2023-04-14 19:33:23 +00:00
|
|
|
#include "libtransmission/transmission.h"
|
|
|
|
|
|
|
|
#include "libtransmission/log.h"
|
|
|
|
#include "libtransmission/net.h"
|
|
|
|
#include "libtransmission/port-forwarding-natpmp.h"
|
|
|
|
#include "libtransmission/port-forwarding-upnp.h"
|
|
|
|
#include "libtransmission/port-forwarding.h"
|
|
|
|
#include "libtransmission/timer.h"
|
|
|
|
#include "libtransmission/torrent.h"
|
|
|
|
#include "libtransmission/tr-assert.h"
|
|
|
|
#include "libtransmission/utils.h" // for _()
|
2022-08-11 17:28:37 +00:00
|
|
|
|
|
|
|
using namespace std::literals;
|
2007-01-19 04:42:31 +00:00
|
|
|
|
2022-09-30 13:59:10 +00:00
|
|
|
class tr_port_forwarding_impl final : public tr_port_forwarding
|
2007-01-19 04:42:31 +00:00
|
|
|
{
|
2022-09-30 13:59:10 +00:00
|
|
|
public:
|
2022-10-01 14:12:49 +00:00
|
|
|
explicit tr_port_forwarding_impl(Mediator& mediator)
|
|
|
|
: mediator_{ mediator }
|
2022-08-11 17:28:37 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2022-09-30 13:59:10 +00:00
|
|
|
~tr_port_forwarding_impl() override
|
2007-12-08 19:34:15 +00:00
|
|
|
{
|
2022-09-30 13:59:10 +00:00
|
|
|
is_shutting_down_ = true;
|
|
|
|
stopForwarding();
|
|
|
|
}
|
2017-04-19 12:04:45 +00:00
|
|
|
|
2022-09-30 13:59:10 +00:00
|
|
|
tr_port_forwarding_impl(tr_port_forwarding_impl&&) = delete;
|
|
|
|
tr_port_forwarding_impl(tr_port_forwarding_impl const&) = delete;
|
|
|
|
tr_port_forwarding_impl& operator=(tr_port_forwarding_impl&&) = delete;
|
|
|
|
tr_port_forwarding_impl& operator=(tr_port_forwarding_impl const&) = delete;
|
2017-04-19 12:04:45 +00:00
|
|
|
|
2023-05-06 04:11:05 +00:00
|
|
|
void local_port_changed() override
|
2022-09-30 13:59:10 +00:00
|
|
|
{
|
|
|
|
if (!is_enabled_)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
2017-04-19 12:04:45 +00:00
|
|
|
|
2022-09-30 13:59:10 +00:00
|
|
|
stopTimer();
|
|
|
|
natPulse(false);
|
|
|
|
startTimer();
|
2007-12-08 19:34:15 +00:00
|
|
|
}
|
2008-09-23 19:11:04 +00:00
|
|
|
|
2023-05-06 04:11:05 +00:00
|
|
|
void set_enabled(bool enabled) override
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
2022-09-30 13:59:10 +00:00
|
|
|
if (enabled)
|
|
|
|
{
|
|
|
|
is_enabled_ = true;
|
|
|
|
startTimer();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
is_enabled_ = false;
|
|
|
|
stopForwarding();
|
|
|
|
}
|
2017-04-19 12:04:45 +00:00
|
|
|
}
|
2009-05-26 20:52:08 +00:00
|
|
|
|
2023-05-06 04:11:05 +00:00
|
|
|
[[nodiscard]] bool is_enabled() const noexcept override
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
2022-09-30 13:59:10 +00:00
|
|
|
return is_enabled_;
|
2017-04-19 12:04:45 +00:00
|
|
|
}
|
2010-07-05 21:04:17 +00:00
|
|
|
|
2022-09-30 13:59:10 +00:00
|
|
|
[[nodiscard]] tr_port_forwarding_state state() const noexcept override
|
|
|
|
{
|
|
|
|
return std::max(natpmp_state_, upnp_state_);
|
|
|
|
}
|
2010-07-05 21:04:17 +00:00
|
|
|
|
2022-09-30 13:59:10 +00:00
|
|
|
private:
|
|
|
|
void stopTimer()
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
2022-09-30 13:59:10 +00:00
|
|
|
timer_.reset();
|
2017-04-19 12:04:45 +00:00
|
|
|
}
|
2008-04-12 21:47:10 +00:00
|
|
|
|
2022-09-30 13:59:10 +00:00
|
|
|
void stopForwarding()
|
|
|
|
{
|
|
|
|
tr_logAddTrace("stopped");
|
|
|
|
natPulse(false);
|
2013-01-24 23:59:52 +00:00
|
|
|
|
2022-09-30 13:59:10 +00:00
|
|
|
natpmp_.reset();
|
|
|
|
natpmp_state_ = TR_PORT_UNMAPPED;
|
2017-04-19 12:04:45 +00:00
|
|
|
|
2022-09-30 13:59:10 +00:00
|
|
|
tr_upnpClose(upnp_);
|
|
|
|
upnp_ = nullptr;
|
|
|
|
upnp_state_ = TR_PORT_UNMAPPED;
|
|
|
|
|
|
|
|
stopTimer();
|
2017-04-19 12:04:45 +00:00
|
|
|
}
|
2007-01-19 04:42:31 +00:00
|
|
|
|
2022-09-30 13:59:10 +00:00
|
|
|
void startTimer()
|
2022-08-11 17:28:37 +00:00
|
|
|
{
|
2023-05-06 04:11:05 +00:00
|
|
|
timer_ = mediator_.timer_maker().create([this]() { this->onTimer(); });
|
2022-09-30 13:59:10 +00:00
|
|
|
restartTimer();
|
2022-08-11 17:28:37 +00:00
|
|
|
}
|
2007-09-21 05:31:29 +00:00
|
|
|
|
2022-09-30 13:59:10 +00:00
|
|
|
void restartTimer()
|
2008-12-15 00:17:08 +00:00
|
|
|
{
|
2022-09-30 13:59:10 +00:00
|
|
|
if (!timer_)
|
2022-08-11 17:28:37 +00:00
|
|
|
{
|
2022-09-30 13:59:10 +00:00
|
|
|
return;
|
2022-08-11 17:28:37 +00:00
|
|
|
}
|
2022-09-30 13:59:10 +00:00
|
|
|
|
|
|
|
// when to wake up again
|
|
|
|
switch (state())
|
2022-08-11 17:28:37 +00:00
|
|
|
{
|
2022-09-30 13:59:10 +00:00
|
|
|
case TR_PORT_MAPPED:
|
|
|
|
// if we're mapped, everything is fine... check back at `renew_time`
|
|
|
|
// to renew the port forwarding if it's expired
|
|
|
|
do_port_check_ = true;
|
|
|
|
if (auto const now = tr_time(); natpmp_->renewTime() > now)
|
|
|
|
{
|
2023-05-06 04:11:05 +00:00
|
|
|
timer_->start_single_shot(std::chrono::seconds{ natpmp_->renewTime() - now });
|
2022-09-30 13:59:10 +00:00
|
|
|
}
|
|
|
|
else // ???
|
|
|
|
{
|
2023-05-06 04:11:05 +00:00
|
|
|
timer_->start_single_shot(1min);
|
2022-09-30 13:59:10 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case TR_PORT_ERROR:
|
|
|
|
// some kind of an error. wait a minute and retry
|
2023-05-06 04:11:05 +00:00
|
|
|
timer_->start_single_shot(1min);
|
2022-09-30 13:59:10 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
// in progress. pulse frequently.
|
2023-05-06 04:11:05 +00:00
|
|
|
timer_->start_single_shot(333ms);
|
2022-09-30 13:59:10 +00:00
|
|
|
break;
|
2022-08-11 17:28:37 +00:00
|
|
|
}
|
2007-01-19 04:42:31 +00:00
|
|
|
}
|
2009-04-22 16:00:45 +00:00
|
|
|
|
2022-09-30 13:59:10 +00:00
|
|
|
void onTimer()
|
|
|
|
{
|
|
|
|
TR_ASSERT(timer_);
|
2009-10-29 17:25:03 +00:00
|
|
|
|
2022-09-30 13:59:10 +00:00
|
|
|
// do something
|
|
|
|
natPulse(do_port_check_);
|
|
|
|
do_port_check_ = false;
|
2009-10-29 17:25:03 +00:00
|
|
|
|
2022-09-30 13:59:10 +00:00
|
|
|
// set up the timer for the next pulse
|
|
|
|
restartTimer();
|
|
|
|
}
|
2009-10-29 17:25:03 +00:00
|
|
|
|
2022-09-30 13:59:10 +00:00
|
|
|
static constexpr char const* getNatStateStr(int state)
|
|
|
|
{
|
|
|
|
switch (state)
|
|
|
|
{
|
|
|
|
case TR_PORT_MAPPING:
|
|
|
|
return _("Starting");
|
2009-10-29 17:25:03 +00:00
|
|
|
|
2022-09-30 13:59:10 +00:00
|
|
|
case TR_PORT_MAPPED:
|
|
|
|
return _("Forwarded");
|
2007-01-19 04:42:31 +00:00
|
|
|
|
2022-09-30 13:59:10 +00:00
|
|
|
case TR_PORT_UNMAPPING:
|
|
|
|
return _("Stopping");
|
2007-09-20 16:32:01 +00:00
|
|
|
|
2022-09-30 13:59:10 +00:00
|
|
|
case TR_PORT_UNMAPPED:
|
|
|
|
return _("Not forwarded");
|
2007-01-19 04:42:31 +00:00
|
|
|
|
2022-09-30 13:59:10 +00:00
|
|
|
default:
|
|
|
|
return "???";
|
|
|
|
}
|
|
|
|
}
|
2010-02-11 14:28:40 +00:00
|
|
|
|
2022-09-30 13:59:10 +00:00
|
|
|
void natPulse(bool do_check)
|
|
|
|
{
|
|
|
|
auto const is_enabled = is_enabled_ && !is_shutting_down_;
|
2010-02-11 14:28:40 +00:00
|
|
|
|
2022-09-30 13:59:10 +00:00
|
|
|
if (!natpmp_)
|
|
|
|
{
|
|
|
|
natpmp_ = std::make_unique<tr_natpmp>();
|
|
|
|
}
|
2010-02-11 14:28:40 +00:00
|
|
|
|
2022-09-30 13:59:10 +00:00
|
|
|
if (upnp_ == nullptr)
|
|
|
|
{
|
|
|
|
upnp_ = tr_upnpInit();
|
|
|
|
}
|
2007-01-27 21:17:10 +00:00
|
|
|
|
2022-09-30 13:59:10 +00:00
|
|
|
auto const old_state = state();
|
2007-01-27 21:17:10 +00:00
|
|
|
|
2023-05-06 04:11:05 +00:00
|
|
|
auto const result = natpmp_->pulse(mediator_.local_peer_port(), is_enabled);
|
2022-09-30 13:59:10 +00:00
|
|
|
natpmp_state_ = result.state;
|
2022-11-02 00:32:26 +00:00
|
|
|
if (!std::empty(result.local_port) && !std::empty(result.advertised_port))
|
2022-09-30 13:59:10 +00:00
|
|
|
{
|
2023-05-06 04:11:05 +00:00
|
|
|
mediator_.on_port_forwarded(result.advertised_port);
|
2022-09-30 13:59:10 +00:00
|
|
|
tr_logAddInfo(fmt::format(
|
|
|
|
_("Mapped private port {private_port} to public port {public_port}"),
|
2022-11-02 00:32:26 +00:00
|
|
|
fmt::arg("private_port", result.local_port.host()),
|
|
|
|
fmt::arg("public_port", result.advertised_port.host())));
|
2022-09-30 13:59:10 +00:00
|
|
|
}
|
2007-01-27 21:17:10 +00:00
|
|
|
|
2022-10-01 14:12:49 +00:00
|
|
|
upnp_state_ = tr_upnpPulse(
|
|
|
|
upnp_,
|
2023-05-06 04:11:05 +00:00
|
|
|
mediator_.local_peer_port(),
|
2022-10-01 14:12:49 +00:00
|
|
|
is_enabled,
|
|
|
|
do_check,
|
2023-05-06 04:11:05 +00:00
|
|
|
mediator_.incoming_peer_address().display_name());
|
2009-05-20 17:35:41 +00:00
|
|
|
|
2022-09-30 13:59:10 +00:00
|
|
|
if (auto const new_state = state(); new_state != old_state)
|
|
|
|
{
|
|
|
|
tr_logAddInfo(fmt::format(
|
|
|
|
_("State changed from '{old_state}' to '{state}'"),
|
|
|
|
fmt::arg("old_state", getNatStateStr(old_state)),
|
|
|
|
fmt::arg("state", getNatStateStr(new_state))));
|
|
|
|
}
|
2017-04-19 12:04:45 +00:00
|
|
|
}
|
2007-01-19 04:42:31 +00:00
|
|
|
|
2022-10-01 14:12:49 +00:00
|
|
|
Mediator& mediator_;
|
2009-05-14 13:42:29 +00:00
|
|
|
|
2022-09-30 13:59:10 +00:00
|
|
|
bool is_enabled_ = false;
|
|
|
|
bool is_shutting_down_ = false;
|
|
|
|
bool do_port_check_ = false;
|
2007-01-19 04:42:31 +00:00
|
|
|
|
2022-09-30 13:59:10 +00:00
|
|
|
tr_port_forwarding_state natpmp_state_ = TR_PORT_UNMAPPED;
|
|
|
|
tr_port_forwarding_state upnp_state_ = TR_PORT_UNMAPPED;
|
|
|
|
|
|
|
|
tr_upnp* upnp_ = nullptr;
|
|
|
|
std::unique_ptr<tr_natpmp> natpmp_;
|
|
|
|
|
|
|
|
std::unique_ptr<libtransmission::Timer> timer_;
|
|
|
|
};
|
2008-05-12 16:33:17 +00:00
|
|
|
|
2022-10-01 14:12:49 +00:00
|
|
|
std::unique_ptr<tr_port_forwarding> tr_port_forwarding::create(Mediator& mediator)
|
2007-12-08 19:34:15 +00:00
|
|
|
{
|
2022-10-01 14:12:49 +00:00
|
|
|
return std::make_unique<tr_port_forwarding_impl>(mediator);
|
2007-01-19 04:42:31 +00:00
|
|
|
}
|