2007-03-16 06:29:23 +01:00
|
|
|
/*
|
|
|
|
|
2018-04-09 09:04:33 +02:00
|
|
|
Copyright (c) 2007-2018, Arvid Norberg
|
2007-03-16 06:29:23 +01:00
|
|
|
All rights reserved.
|
|
|
|
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
|
|
modification, are permitted provided that the following conditions
|
|
|
|
are met:
|
|
|
|
|
|
|
|
* Redistributions of source code must retain the above copyright
|
|
|
|
notice, this list of conditions and the following disclaimer.
|
|
|
|
* Redistributions in binary form must reproduce the above copyright
|
|
|
|
notice, this list of conditions and the following disclaimer in
|
|
|
|
the documentation and/or other materials provided with the distribution.
|
|
|
|
* Neither the name of the author nor the names of its
|
|
|
|
contributors may be used to endorse or promote products derived
|
|
|
|
from this software without specific prior written permission.
|
|
|
|
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
|
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
|
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
|
|
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
|
|
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
|
|
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
|
|
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
|
|
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
|
|
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
|
|
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
|
|
POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
|
|
|
|
*/
|
2012-09-24 18:13:57 +02:00
|
|
|
#include "libtorrent/config.hpp"
|
2015-08-20 01:33:20 +02:00
|
|
|
|
|
|
|
#include "libtorrent/aux_/disable_warnings_push.hpp"
|
|
|
|
|
2012-09-24 18:13:57 +02:00
|
|
|
#if defined TORRENT_OS2
|
|
|
|
#include <pthread.h>
|
|
|
|
#endif
|
2007-03-16 06:29:23 +01:00
|
|
|
|
2016-04-23 23:29:25 +02:00
|
|
|
#include <boost/asio/ip/host_name.hpp>
|
2008-05-20 18:43:09 +02:00
|
|
|
|
2015-08-20 01:33:20 +02:00
|
|
|
#include "libtorrent/aux_/disable_warnings_pop.hpp"
|
|
|
|
|
2016-05-17 15:24:06 +02:00
|
|
|
#include <cstdio> // for snprintf
|
2016-06-23 07:23:59 +02:00
|
|
|
#include <cinttypes> // for PRId64 et.al.
|
2016-05-18 07:54:37 +02:00
|
|
|
#include <cstdarg>
|
2016-05-25 06:31:52 +02:00
|
|
|
#include <functional>
|
2017-09-12 23:10:11 +02:00
|
|
|
#include <cstring> // for memcpy
|
2016-05-17 15:24:06 +02:00
|
|
|
|
2007-09-10 08:12:41 +02:00
|
|
|
#include "libtorrent/natpmp.hpp"
|
|
|
|
#include "libtorrent/io.hpp"
|
|
|
|
#include "libtorrent/assert.hpp"
|
2007-10-01 19:21:19 +02:00
|
|
|
#include "libtorrent/enum_net.hpp"
|
2009-09-16 05:46:36 +02:00
|
|
|
#include "libtorrent/socket_io.hpp"
|
2009-11-23 09:38:50 +01:00
|
|
|
#include "libtorrent/io_service.hpp"
|
2015-03-12 05:34:54 +01:00
|
|
|
#include "libtorrent/aux_/time.hpp"
|
2016-04-23 23:29:25 +02:00
|
|
|
#include "libtorrent/debug.hpp"
|
2018-05-30 01:25:40 +02:00
|
|
|
#include "libtorrent/random.hpp"
|
|
|
|
#include "libtorrent/broadcast_socket.hpp" // for is_local
|
2015-03-15 00:10:20 +01:00
|
|
|
#include "libtorrent/aux_/escape_string.hpp"
|
2017-02-07 06:22:30 +01:00
|
|
|
#include "libtorrent/aux_/numeric_cast.hpp"
|
2007-09-10 08:12:41 +02:00
|
|
|
|
2016-09-18 16:11:56 +02:00
|
|
|
namespace libtorrent {
|
|
|
|
|
2019-01-19 22:47:48 +01:00
|
|
|
struct pcp_error_category final : boost::system::error_category
|
2018-05-30 01:25:40 +02:00
|
|
|
{
|
|
|
|
const char* name() const BOOST_SYSTEM_NOEXCEPT override
|
|
|
|
{ return "pcp error"; }
|
|
|
|
std::string message(int ev) const override
|
|
|
|
{
|
|
|
|
static char const* msgs[] =
|
|
|
|
{
|
|
|
|
"success",
|
|
|
|
"unsupported version",
|
|
|
|
"not authorized",
|
|
|
|
"malformed request",
|
|
|
|
"unsupported opcode",
|
|
|
|
"unsupported option",
|
|
|
|
"malformed option",
|
|
|
|
"network failure",
|
|
|
|
"no resources",
|
|
|
|
"unsupported protocol",
|
|
|
|
"user exceeded quota",
|
|
|
|
"cannot provide external",
|
|
|
|
"address mismatch",
|
|
|
|
"excessive remote peers",
|
|
|
|
};
|
|
|
|
if (ev < 0 || ev >= int(sizeof(msgs)/sizeof(msgs[0])))
|
|
|
|
return "Unknown error";
|
|
|
|
return msgs[ev];
|
|
|
|
}
|
|
|
|
boost::system::error_condition default_error_condition(
|
|
|
|
int ev) const BOOST_SYSTEM_NOEXCEPT override
|
|
|
|
{ return boost::system::error_condition(ev, *this); }
|
|
|
|
};
|
|
|
|
|
|
|
|
boost::system::error_category& pcp_category()
|
|
|
|
{
|
|
|
|
static pcp_error_category pcp_category;
|
|
|
|
return pcp_category;
|
|
|
|
}
|
|
|
|
|
|
|
|
namespace errors
|
|
|
|
{
|
|
|
|
// hidden
|
|
|
|
boost::system::error_code make_error_code(pcp_errors e)
|
|
|
|
{
|
|
|
|
return boost::system::error_code(e, pcp_category());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
error_code natpmp::from_result_code(int const version, int result)
|
|
|
|
{
|
|
|
|
if (version == version_natpmp)
|
|
|
|
{
|
|
|
|
// a few nat-pmp result codes map to different codes
|
|
|
|
// in pcp
|
|
|
|
switch (result)
|
|
|
|
{
|
|
|
|
case 3:result = 7; break;
|
|
|
|
case 4:result = 8; break;
|
|
|
|
case 5:result = 4; break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return errors::pcp_errors(result);
|
|
|
|
}
|
|
|
|
|
2018-06-10 00:04:38 +02:00
|
|
|
char const* natpmp::version_to_string(protocol_version version)
|
|
|
|
{
|
|
|
|
return version == version_natpmp ? "NAT-PMP" : "PCP";
|
|
|
|
}
|
|
|
|
|
2016-09-18 16:11:56 +02:00
|
|
|
using namespace aux;
|
2016-05-25 06:31:52 +02:00
|
|
|
using namespace std::placeholders;
|
2007-03-15 23:03:56 +01:00
|
|
|
|
2015-01-06 09:08:49 +01:00
|
|
|
natpmp::natpmp(io_service& ios
|
2016-09-16 15:53:17 +02:00
|
|
|
, aux::portmap_callback& cb)
|
2007-03-15 23:03:56 +01:00
|
|
|
: m_callback(cb)
|
|
|
|
, m_socket(ios)
|
|
|
|
, m_send_timer(ios)
|
|
|
|
, m_refresh_timer(ios)
|
|
|
|
{
|
2010-12-24 04:30:52 +01:00
|
|
|
// unfortunately async operations rely on the storage
|
|
|
|
// for this array not to be reallocated, by passing
|
|
|
|
// around pointers to its elements. so reserve size for now
|
|
|
|
m_mappings.reserve(10);
|
2007-03-16 22:04:58 +01:00
|
|
|
}
|
|
|
|
|
2018-05-30 01:25:40 +02:00
|
|
|
void natpmp::start(address local_address, std::string device)
|
2007-03-16 22:04:58 +01:00
|
|
|
{
|
2016-05-01 08:21:30 +02:00
|
|
|
TORRENT_ASSERT(is_single_thread());
|
2008-04-06 21:17:58 +02:00
|
|
|
|
2008-05-03 18:05:42 +02:00
|
|
|
error_code ec;
|
2018-05-21 00:31:10 +02:00
|
|
|
|
2018-05-30 01:25:40 +02:00
|
|
|
// assume servers support PCP and fall back to NAT-PMP
|
|
|
|
// if necessary
|
|
|
|
m_version = version_pcp;
|
|
|
|
|
2018-07-29 09:05:50 +02:00
|
|
|
// we really want a device name to get the right default gateway
|
|
|
|
// try to find one even if the listen socket isn't bound to a device
|
|
|
|
if (device.empty())
|
|
|
|
{
|
|
|
|
device = device_for_address(local_address, m_socket.get_io_service(), ec);
|
|
|
|
// if this fails fall back to using the first default gateway in the
|
|
|
|
// routing table
|
|
|
|
ec.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
auto const route = get_default_route(m_socket.get_io_service()
|
|
|
|
, device, local_address.is_v6(), ec);
|
|
|
|
|
|
|
|
if (!route)
|
|
|
|
{
|
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
|
|
|
if (should_log())
|
|
|
|
{
|
|
|
|
log("failed to find default route for \"%s\" %s: %s"
|
|
|
|
, device.c_str(), local_address.to_string().c_str()
|
|
|
|
, convert_from_native(ec.message()).c_str());
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
disable(ec);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (device.empty()) device = route->name;
|
|
|
|
|
|
|
|
TORRENT_ASSERT(!device.empty());
|
|
|
|
|
2018-05-30 01:25:40 +02:00
|
|
|
// PCP requires reporting the source address at the application
|
|
|
|
// layer so the socket MUST be bound to a specific address
|
|
|
|
// if the caller didn't specify one then get the first suitable
|
|
|
|
// address from the OS
|
|
|
|
if (local_address.is_unspecified())
|
|
|
|
{
|
2018-07-29 09:05:50 +02:00
|
|
|
std::vector<ip_interface> const net = enum_net_interfaces(
|
|
|
|
m_socket.get_io_service(), ec);
|
|
|
|
|
|
|
|
auto const it = std::find_if(net.begin(), net.end(), [&](ip_interface const& i)
|
2018-05-30 01:25:40 +02:00
|
|
|
{
|
2018-07-29 09:05:50 +02:00
|
|
|
return i.interface_address.is_v4() == local_address.is_v4()
|
|
|
|
&& (i.interface_address.is_v4() || !is_local(i.interface_address))
|
|
|
|
&& i.name == device;
|
|
|
|
});
|
2018-05-30 01:25:40 +02:00
|
|
|
|
2018-07-29 09:05:50 +02:00
|
|
|
if (it != net.end())
|
|
|
|
{
|
|
|
|
local_address = it->interface_address;
|
|
|
|
}
|
|
|
|
else
|
2018-05-30 01:25:40 +02:00
|
|
|
{
|
|
|
|
// if we can't get a specific address to bind to we'll have
|
|
|
|
// to fall back to NAT-PMP
|
|
|
|
// but NAT-PMP doesn't support IPv6 so if that's what is being
|
|
|
|
// requested we can't do anything
|
|
|
|
if (local_address.is_v6())
|
|
|
|
{
|
|
|
|
if (!ec) ec = boost::asio::error::address_family_not_supported;
|
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
|
|
|
if (should_log())
|
|
|
|
{
|
|
|
|
log("cannot map IPv6 without a local address, %s"
|
|
|
|
, convert_from_native(ec.message()).c_str());
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
disable(ec);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_version = version_natpmp;
|
|
|
|
ec.clear();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-03-16 22:04:58 +01:00
|
|
|
m_disabled = false;
|
|
|
|
|
2018-07-29 09:05:50 +02:00
|
|
|
udp::endpoint const nat_endpoint(route->gateway, 5351);
|
2007-03-16 22:04:58 +01:00
|
|
|
if (nat_endpoint == m_nat_endpoint) return;
|
|
|
|
m_nat_endpoint = nat_endpoint;
|
2007-03-15 23:03:56 +01:00
|
|
|
|
2016-05-18 07:54:37 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2016-09-16 15:53:17 +02:00
|
|
|
if (should_log())
|
|
|
|
{
|
|
|
|
log("found router at: %s"
|
|
|
|
, print_address(m_nat_endpoint.address()).c_str());
|
|
|
|
}
|
2016-05-18 07:54:37 +02:00
|
|
|
#endif
|
2007-03-15 23:03:56 +01:00
|
|
|
|
2018-05-21 00:31:10 +02:00
|
|
|
m_socket.open(local_address.is_v4() ? udp::v4() : udp::v6(), ec);
|
2007-12-29 08:26:36 +01:00
|
|
|
if (ec)
|
|
|
|
{
|
2016-05-01 08:21:30 +02:00
|
|
|
disable(ec);
|
2007-12-29 08:26:36 +01:00
|
|
|
return;
|
|
|
|
}
|
2018-05-21 00:31:10 +02:00
|
|
|
m_socket.bind({local_address, 0}, ec);
|
2007-12-29 08:26:36 +01:00
|
|
|
if (ec)
|
|
|
|
{
|
2016-05-01 08:21:30 +02:00
|
|
|
disable(ec);
|
2007-12-29 08:26:36 +01:00
|
|
|
return;
|
|
|
|
}
|
2007-05-16 23:36:27 +02:00
|
|
|
|
2016-04-23 23:29:25 +02:00
|
|
|
ADD_OUTSTANDING_ASYNC("natpmp::on_reply");
|
2016-05-24 04:46:46 +02:00
|
|
|
m_socket.async_receive_from(boost::asio::buffer(&m_response_buffer[0]
|
|
|
|
, sizeof(m_response_buffer))
|
2016-05-25 06:31:52 +02:00
|
|
|
, m_remote, std::bind(&natpmp::on_reply, self(), _1, _2));
|
2018-05-30 01:25:40 +02:00
|
|
|
if (m_version == version_natpmp)
|
|
|
|
send_get_ip_address_request();
|
2008-08-20 10:34:32 +02:00
|
|
|
|
2017-09-27 19:58:03 +02:00
|
|
|
for (auto i = m_mappings.begin(), end(m_mappings.end()); i != end; ++i)
|
2007-03-16 22:04:58 +01:00
|
|
|
{
|
2017-04-21 07:21:31 +02:00
|
|
|
if (i->protocol == portmap_protocol::none
|
2017-05-28 02:26:47 +02:00
|
|
|
|| i->act != portmap_action::none)
|
2007-03-16 22:04:58 +01:00
|
|
|
continue;
|
2017-05-28 02:26:47 +02:00
|
|
|
i->act = portmap_action::add;
|
2017-08-31 00:49:49 +02:00
|
|
|
update_mapping(port_mapping_t(int(i - m_mappings.begin())));
|
2007-03-16 22:04:58 +01:00
|
|
|
}
|
2007-03-15 23:03:56 +01:00
|
|
|
}
|
2007-12-29 08:26:36 +01:00
|
|
|
|
2016-05-01 08:21:30 +02:00
|
|
|
void natpmp::send_get_ip_address_request()
|
2010-12-05 21:40:28 +01:00
|
|
|
{
|
2016-05-01 08:21:30 +02:00
|
|
|
TORRENT_ASSERT(is_single_thread());
|
2010-12-05 21:40:28 +01:00
|
|
|
using namespace libtorrent::detail;
|
|
|
|
|
2018-05-30 01:25:40 +02:00
|
|
|
// this opcode only exists in NAT-PMP
|
|
|
|
// PCP routers report the external IP in the response to a MAP operation
|
|
|
|
TORRENT_ASSERT(m_version == version_natpmp);
|
|
|
|
if (m_version != version_natpmp)
|
|
|
|
return;
|
|
|
|
|
2010-12-05 21:40:28 +01:00
|
|
|
char buf[2];
|
|
|
|
char* out = buf;
|
2018-05-30 01:25:40 +02:00
|
|
|
write_uint8(version_natpmp, out);
|
2010-12-05 21:40:28 +01:00
|
|
|
write_uint8(0, out); // public IP address request opcode
|
2016-05-18 07:54:37 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2016-05-01 08:21:30 +02:00
|
|
|
log("==> get public IP address");
|
2016-05-18 07:54:37 +02:00
|
|
|
#endif
|
2010-12-05 21:40:28 +01:00
|
|
|
|
|
|
|
error_code ec;
|
2015-06-06 07:22:53 +02:00
|
|
|
m_socket.send_to(boost::asio::buffer(buf, sizeof(buf)), m_nat_endpoint, 0, ec);
|
2010-12-05 21:40:28 +01:00
|
|
|
}
|
|
|
|
|
2017-08-31 00:49:49 +02:00
|
|
|
bool natpmp::get_mapping(port_mapping_t const index, int& local_port
|
2016-09-18 16:11:56 +02:00
|
|
|
, int& external_port, portmap_protocol& protocol) const
|
2008-11-16 03:11:04 +01:00
|
|
|
{
|
2016-05-01 08:21:30 +02:00
|
|
|
TORRENT_ASSERT(is_single_thread());
|
2009-06-13 10:04:53 +02:00
|
|
|
|
2017-08-31 00:49:49 +02:00
|
|
|
TORRENT_ASSERT(index < m_mappings.end_index() && index >= port_mapping_t{});
|
|
|
|
if (index >= m_mappings.end_index() || index < port_mapping_t{}) return false;
|
2008-11-16 03:11:04 +01:00
|
|
|
mapping_t const& m = m_mappings[index];
|
2016-09-18 16:11:56 +02:00
|
|
|
if (m.protocol == portmap_protocol::none) return false;
|
2008-11-16 03:11:04 +01:00
|
|
|
local_port = m.local_port;
|
|
|
|
external_port = m.external_port;
|
|
|
|
protocol = m.protocol;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2016-05-18 07:54:37 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2016-09-16 15:53:17 +02:00
|
|
|
bool natpmp::should_log() const
|
|
|
|
{
|
2017-08-30 20:47:49 +02:00
|
|
|
return m_callback.should_log_portmap(portmap_transport::natpmp);
|
2016-09-16 15:53:17 +02:00
|
|
|
}
|
|
|
|
|
2017-05-28 02:26:47 +02:00
|
|
|
void natpmp::mapping_log(char const* op, mapping_t const& m) const
|
|
|
|
{
|
|
|
|
if (should_log())
|
|
|
|
{
|
|
|
|
log("%s-mapping: proto: %s port: %d local-port: %d action: %s ttl: %" PRId64
|
|
|
|
, op
|
|
|
|
, m.protocol == portmap_protocol::none
|
|
|
|
? "none" : to_string(m.protocol)
|
|
|
|
, m.external_port
|
|
|
|
, m.local_port
|
|
|
|
, to_string(m.act)
|
|
|
|
, total_seconds(m.expires - aux::time_now()));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-05-18 07:54:37 +02:00
|
|
|
TORRENT_FORMAT(2, 3)
|
|
|
|
void natpmp::log(char const* fmt, ...) const
|
2008-10-22 09:06:58 +02:00
|
|
|
{
|
2016-05-01 08:21:30 +02:00
|
|
|
TORRENT_ASSERT(is_single_thread());
|
2016-09-16 15:53:17 +02:00
|
|
|
if (!should_log()) return;
|
2016-05-18 07:54:37 +02:00
|
|
|
char msg[200];
|
|
|
|
va_list v;
|
|
|
|
va_start(v, fmt);
|
|
|
|
std::vsnprintf(msg, sizeof(msg), fmt, v);
|
|
|
|
va_end(v);
|
2017-08-30 20:47:49 +02:00
|
|
|
m_callback.log_portmap(portmap_transport::natpmp, msg);
|
2008-10-22 09:06:58 +02:00
|
|
|
}
|
2016-05-18 07:54:37 +02:00
|
|
|
#endif
|
2008-10-22 09:06:58 +02:00
|
|
|
|
2016-05-01 08:21:30 +02:00
|
|
|
void natpmp::disable(error_code const& ec)
|
2007-04-14 17:51:36 +02:00
|
|
|
{
|
2016-05-01 08:21:30 +02:00
|
|
|
TORRENT_ASSERT(is_single_thread());
|
2007-04-14 17:51:36 +02:00
|
|
|
m_disabled = true;
|
2008-04-06 21:17:58 +02:00
|
|
|
|
2017-09-27 19:58:03 +02:00
|
|
|
for (auto i = m_mappings.begin(), end(m_mappings.end()); i != end; ++i)
|
2008-04-06 21:17:58 +02:00
|
|
|
{
|
2016-09-18 16:11:56 +02:00
|
|
|
if (i->protocol == portmap_protocol::none) continue;
|
|
|
|
portmap_protocol const proto = i->protocol;
|
|
|
|
i->protocol = portmap_protocol::none;
|
2017-08-31 00:49:49 +02:00
|
|
|
port_mapping_t const index(static_cast<int>(i - m_mappings.begin()));
|
2016-09-16 15:53:17 +02:00
|
|
|
m_callback.on_port_mapping(index, address(), 0, proto, ec
|
2017-08-30 20:47:49 +02:00
|
|
|
, portmap_transport::natpmp);
|
2008-04-06 21:17:58 +02:00
|
|
|
}
|
2016-05-01 08:21:30 +02:00
|
|
|
close_impl();
|
2008-04-06 21:17:58 +02:00
|
|
|
}
|
2008-11-16 03:11:04 +01:00
|
|
|
|
2017-08-31 00:49:49 +02:00
|
|
|
void natpmp::delete_mapping(port_mapping_t const index)
|
2008-04-06 21:17:58 +02:00
|
|
|
{
|
2016-05-01 08:21:30 +02:00
|
|
|
TORRENT_ASSERT(is_single_thread());
|
2009-06-13 10:04:53 +02:00
|
|
|
|
2017-08-31 00:49:49 +02:00
|
|
|
TORRENT_ASSERT(index < m_mappings.end_index() && index >= port_mapping_t{});
|
|
|
|
if (index >= m_mappings.end_index() || index < port_mapping_t{}) return;
|
2008-04-06 21:17:58 +02:00
|
|
|
mapping_t& m = m_mappings[index];
|
|
|
|
|
2016-09-18 16:11:56 +02:00
|
|
|
if (m.protocol == portmap_protocol::none) return;
|
2008-11-16 03:11:04 +01:00
|
|
|
if (!m.map_sent)
|
|
|
|
{
|
2017-05-28 02:26:47 +02:00
|
|
|
m.act = portmap_action::none;
|
2016-09-18 16:11:56 +02:00
|
|
|
m.protocol = portmap_protocol::none;
|
2008-11-16 03:11:04 +01:00
|
|
|
return;
|
|
|
|
}
|
2008-04-06 21:17:58 +02:00
|
|
|
|
2017-05-28 02:26:47 +02:00
|
|
|
m.act = portmap_action::del;
|
2016-05-01 08:21:30 +02:00
|
|
|
update_mapping(index);
|
2007-12-29 08:26:36 +01:00
|
|
|
}
|
2007-03-15 23:03:56 +01:00
|
|
|
|
2017-08-31 00:49:49 +02:00
|
|
|
port_mapping_t natpmp::add_mapping(portmap_protocol const p, int const external_port
|
2017-05-13 03:46:45 +02:00
|
|
|
, tcp::endpoint const local_ep)
|
2007-03-15 23:03:56 +01:00
|
|
|
{
|
2016-05-01 08:21:30 +02:00
|
|
|
TORRENT_ASSERT(is_single_thread());
|
2008-04-06 21:17:58 +02:00
|
|
|
|
2017-08-31 00:49:49 +02:00
|
|
|
if (m_disabled) return port_mapping_t{-1};
|
2008-04-06 21:17:58 +02:00
|
|
|
|
2016-12-12 02:50:30 +01:00
|
|
|
auto i = std::find_if(m_mappings.begin()
|
2016-09-18 16:11:56 +02:00
|
|
|
, m_mappings.end(), [] (mapping_t const& m) { return m.protocol == portmap_protocol::none; });
|
2008-04-06 21:17:58 +02:00
|
|
|
if (i == m_mappings.end())
|
|
|
|
{
|
|
|
|
m_mappings.push_back(mapping_t());
|
|
|
|
i = m_mappings.end() - 1;
|
|
|
|
}
|
2018-05-30 01:25:40 +02:00
|
|
|
aux::random_bytes(i->nonce);
|
2008-04-06 21:17:58 +02:00
|
|
|
i->protocol = p;
|
|
|
|
i->external_port = external_port;
|
2017-05-13 03:46:45 +02:00
|
|
|
i->local_port = local_ep.port();
|
2017-05-28 02:26:47 +02:00
|
|
|
i->act = portmap_action::add;
|
2008-04-06 21:17:58 +02:00
|
|
|
|
2017-08-31 00:49:49 +02:00
|
|
|
port_mapping_t const mapping_index(static_cast<int>(i - m_mappings.begin()));
|
2016-06-23 07:23:59 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2017-05-28 02:26:47 +02:00
|
|
|
mapping_log("add",*i);
|
2010-09-30 08:33:42 +02:00
|
|
|
#endif
|
|
|
|
|
2016-05-01 08:21:30 +02:00
|
|
|
update_mapping(mapping_index);
|
2017-08-31 00:49:49 +02:00
|
|
|
return port_mapping_t{mapping_index};
|
2007-03-15 23:03:56 +01:00
|
|
|
}
|
|
|
|
|
2017-08-31 00:49:49 +02:00
|
|
|
void natpmp::try_next_mapping(port_mapping_t const i)
|
2007-03-15 23:03:56 +01:00
|
|
|
{
|
2016-05-01 08:21:30 +02:00
|
|
|
TORRENT_ASSERT(is_single_thread());
|
2017-08-31 00:49:49 +02:00
|
|
|
if (i < prev(m_mappings.end_index()))
|
2008-04-06 21:17:58 +02:00
|
|
|
{
|
2017-08-31 00:49:49 +02:00
|
|
|
update_mapping(next(i));
|
2008-04-06 21:17:58 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-12-12 02:50:30 +01:00
|
|
|
auto const m = std::find_if(
|
2008-04-06 21:17:58 +02:00
|
|
|
m_mappings.begin(), m_mappings.end()
|
2017-05-28 02:26:47 +02:00
|
|
|
, [] (mapping_t const& ma) { return ma.act != portmap_action::none
|
2017-05-20 21:21:01 +02:00
|
|
|
&& ma.protocol != portmap_protocol::none; });
|
2008-04-06 21:17:58 +02:00
|
|
|
|
|
|
|
if (m == m_mappings.end())
|
|
|
|
{
|
|
|
|
if (m_abort)
|
|
|
|
{
|
2008-05-03 18:05:42 +02:00
|
|
|
error_code ec;
|
2008-04-06 21:17:58 +02:00
|
|
|
m_send_timer.cancel(ec);
|
|
|
|
m_socket.close(ec);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-08-31 00:49:49 +02:00
|
|
|
update_mapping(port_mapping_t(static_cast<int>(m - m_mappings.begin())));
|
2008-04-06 21:17:58 +02:00
|
|
|
}
|
|
|
|
|
2017-08-31 00:49:49 +02:00
|
|
|
void natpmp::update_mapping(port_mapping_t const i)
|
2008-04-06 21:17:58 +02:00
|
|
|
{
|
2016-05-01 08:21:30 +02:00
|
|
|
TORRENT_ASSERT(is_single_thread());
|
2017-08-31 00:49:49 +02:00
|
|
|
if (i == m_mappings.end_index())
|
2008-09-24 04:20:13 +02:00
|
|
|
{
|
|
|
|
if (m_abort)
|
|
|
|
{
|
|
|
|
error_code ec;
|
|
|
|
m_send_timer.cancel(ec);
|
|
|
|
m_socket.close(ec);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-09-27 19:58:03 +02:00
|
|
|
mapping_t const& m = m_mappings[i];
|
2016-06-23 07:23:59 +02:00
|
|
|
|
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2017-05-28 02:26:47 +02:00
|
|
|
mapping_log("update", m);
|
2016-06-23 07:23:59 +02:00
|
|
|
#endif
|
|
|
|
|
2017-05-28 02:26:47 +02:00
|
|
|
if (m.act == portmap_action::none
|
2016-09-18 16:11:56 +02:00
|
|
|
|| m.protocol == portmap_protocol::none)
|
2008-04-06 21:17:58 +02:00
|
|
|
{
|
2016-05-01 08:21:30 +02:00
|
|
|
try_next_mapping(i);
|
2008-04-06 21:17:58 +02:00
|
|
|
return;
|
|
|
|
}
|
2007-03-15 23:03:56 +01:00
|
|
|
|
2017-08-31 00:49:49 +02:00
|
|
|
if (m_currently_mapping == port_mapping_t{-1})
|
2007-03-15 23:03:56 +01:00
|
|
|
{
|
|
|
|
// the socket is not currently in use
|
|
|
|
// send out a mapping request
|
|
|
|
m_retry_count = 0;
|
2016-05-01 08:21:30 +02:00
|
|
|
send_map_request(i);
|
2007-03-15 23:03:56 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-31 00:49:49 +02:00
|
|
|
void natpmp::send_map_request(port_mapping_t const i)
|
2007-03-15 23:03:56 +01:00
|
|
|
{
|
2016-05-01 08:21:30 +02:00
|
|
|
TORRENT_ASSERT(is_single_thread());
|
2007-03-15 23:03:56 +01:00
|
|
|
using namespace libtorrent::detail;
|
|
|
|
|
2017-08-31 00:49:49 +02:00
|
|
|
TORRENT_ASSERT(m_currently_mapping == port_mapping_t{-1}
|
2007-03-15 23:03:56 +01:00
|
|
|
|| m_currently_mapping == i);
|
|
|
|
m_currently_mapping = i;
|
2008-04-06 21:17:58 +02:00
|
|
|
mapping_t& m = m_mappings[i];
|
2017-05-28 02:26:47 +02:00
|
|
|
TORRENT_ASSERT(m.act != portmap_action::none);
|
2018-05-30 01:25:40 +02:00
|
|
|
char buf[60];
|
2007-03-15 23:03:56 +01:00
|
|
|
char* out = buf;
|
2017-05-28 02:26:47 +02:00
|
|
|
int ttl = m.act == portmap_action::add ? 3600 : 0;
|
2018-05-30 01:25:40 +02:00
|
|
|
if (m_version == version_natpmp)
|
|
|
|
{
|
|
|
|
write_uint8(m_version, out);
|
|
|
|
write_uint8(m.protocol == portmap_protocol::udp ? 1 : 2, out); // map "protocol"
|
|
|
|
write_uint16(0, out); // reserved
|
|
|
|
write_uint16(m.local_port, out); // private port
|
|
|
|
write_uint16(m.external_port, out); // requested public port
|
|
|
|
write_uint32(ttl, out); // port mapping lifetime
|
|
|
|
}
|
|
|
|
else if (m_version == version_pcp)
|
|
|
|
{
|
|
|
|
// PCP requires the use of IPv6 addresses even for IPv4 messages
|
|
|
|
write_uint8(m_version, out);
|
|
|
|
write_uint8(opcode_map, out);
|
|
|
|
write_uint16(0, out); // reserved
|
|
|
|
write_uint32(ttl, out);
|
|
|
|
address const local_addr = m_socket.local_endpoint().address();
|
|
|
|
auto const local_bytes = local_addr.is_v4()
|
|
|
|
? address_v6::v4_mapped(local_addr.to_v4()).to_bytes()
|
|
|
|
: local_addr.to_v6().to_bytes();
|
|
|
|
out = std::copy(local_bytes.begin(), local_bytes.end(), out);
|
|
|
|
out = std::copy(m.nonce.begin(), m.nonce.end(), out);
|
|
|
|
// translate portmap_protocol to an IANA protocol number
|
|
|
|
int const protocol =
|
|
|
|
(m.protocol == portmap_protocol::tcp) ? 6
|
|
|
|
: (m.protocol == portmap_protocol::udp) ? 17
|
|
|
|
: 0;
|
|
|
|
write_int8(protocol, out);
|
|
|
|
write_uint8(0, out); // reserved
|
|
|
|
write_uint16(0, out); // reserved
|
|
|
|
write_uint16(m.local_port, out);
|
|
|
|
write_uint16(m.external_port, out);
|
2018-07-19 14:29:30 +02:00
|
|
|
address_v6 external_addr;
|
2018-05-30 01:25:40 +02:00
|
|
|
if (!m.external_address.is_unspecified())
|
|
|
|
{
|
|
|
|
external_addr = m.external_address.is_v4()
|
2018-07-19 14:29:30 +02:00
|
|
|
? address_v6::v4_mapped(m.external_address.to_v4())
|
|
|
|
: m.external_address.to_v6();
|
2018-05-30 01:25:40 +02:00
|
|
|
}
|
|
|
|
else if (is_local(local_addr))
|
|
|
|
{
|
2018-07-19 14:29:30 +02:00
|
|
|
external_addr = local_addr.is_v4()
|
|
|
|
? address_v6::v4_mapped(address_v4())
|
|
|
|
: address_v6();
|
2018-05-30 01:25:40 +02:00
|
|
|
}
|
|
|
|
else if (local_addr.is_v4())
|
|
|
|
{
|
2018-07-19 14:29:30 +02:00
|
|
|
external_addr = address_v6::v4_mapped(local_addr.to_v4());
|
2018-05-30 01:25:40 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2018-07-19 14:29:30 +02:00
|
|
|
external_addr = local_addr.to_v6();
|
2018-05-30 01:25:40 +02:00
|
|
|
}
|
2018-07-19 14:29:30 +02:00
|
|
|
write_address(external_addr, out);
|
2018-05-30 01:25:40 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
TORRENT_ASSERT_FAIL();
|
|
|
|
}
|
2007-03-15 23:03:56 +01:00
|
|
|
|
2016-05-18 07:54:37 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2016-09-16 15:53:17 +02:00
|
|
|
if (should_log())
|
|
|
|
{
|
|
|
|
log("==> port map [ mapping: %d action: %s"
|
2018-06-10 00:04:38 +02:00
|
|
|
" transport: %s proto: %s local: %u external: %u ttl: %u ]"
|
2017-08-31 00:49:49 +02:00
|
|
|
, static_cast<int>(i), to_string(m.act)
|
2018-06-10 00:04:38 +02:00
|
|
|
, version_to_string(m_version)
|
2017-05-28 02:26:47 +02:00
|
|
|
, to_string(m.protocol)
|
2016-09-16 15:53:17 +02:00
|
|
|
, m.local_port, m.external_port, ttl);
|
|
|
|
}
|
2016-05-18 07:54:37 +02:00
|
|
|
#endif
|
2007-03-15 23:03:56 +01:00
|
|
|
|
2008-05-03 18:05:42 +02:00
|
|
|
error_code ec;
|
2018-05-30 01:25:40 +02:00
|
|
|
m_socket.send_to(boost::asio::buffer(buf, std::size_t(out - buf)), m_nat_endpoint, 0, ec);
|
2008-11-16 03:11:04 +01:00
|
|
|
m.map_sent = true;
|
2010-09-30 08:33:42 +02:00
|
|
|
m.outstanding_request = true;
|
2008-12-07 21:25:04 +01:00
|
|
|
if (m_abort)
|
|
|
|
{
|
2008-12-19 20:27:58 +01:00
|
|
|
// when we're shutting down, ignore the
|
|
|
|
// responses and just remove all mappings
|
|
|
|
// immediately
|
2017-08-31 00:49:49 +02:00
|
|
|
m_currently_mapping = port_mapping_t{-1};
|
2017-05-28 02:26:47 +02:00
|
|
|
m.act = portmap_action::none;
|
2016-05-01 08:21:30 +02:00
|
|
|
try_next_mapping(i);
|
2008-12-07 21:25:04 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2016-04-23 23:29:25 +02:00
|
|
|
ADD_OUTSTANDING_ASYNC("natpmp::resend_request");
|
2008-12-19 20:27:58 +01:00
|
|
|
// linear back-off instead of exponential
|
2008-12-07 21:25:04 +01:00
|
|
|
++m_retry_count;
|
|
|
|
m_send_timer.expires_from_now(milliseconds(250 * m_retry_count), ec);
|
2018-11-08 01:01:12 +01:00
|
|
|
m_send_timer.async_wait(std::bind(&natpmp::on_resend_request, self(), i, _1));
|
2008-12-07 21:25:04 +01:00
|
|
|
}
|
2007-03-15 23:03:56 +01:00
|
|
|
}
|
|
|
|
|
2018-11-08 01:01:12 +01:00
|
|
|
void natpmp::on_resend_request(port_mapping_t const i, error_code const& e)
|
2007-03-15 23:03:56 +01:00
|
|
|
{
|
2016-05-01 08:21:30 +02:00
|
|
|
TORRENT_ASSERT(is_single_thread());
|
2016-04-23 23:29:25 +02:00
|
|
|
COMPLETE_ASYNC("natpmp::resend_request");
|
2007-03-15 23:03:56 +01:00
|
|
|
if (e) return;
|
2018-11-08 01:01:12 +01:00
|
|
|
resend_request(i);
|
|
|
|
}
|
|
|
|
|
|
|
|
void natpmp::resend_request(port_mapping_t const i)
|
|
|
|
{
|
2007-04-04 10:56:58 +02:00
|
|
|
if (m_currently_mapping != i) return;
|
2008-08-20 10:34:32 +02:00
|
|
|
|
|
|
|
// if we're shutting down, don't retry, just move on
|
|
|
|
// to the next mapping
|
|
|
|
if (m_retry_count >= 9 || m_abort)
|
2007-03-15 23:03:56 +01:00
|
|
|
{
|
2017-08-31 00:49:49 +02:00
|
|
|
m_currently_mapping = port_mapping_t{-1};
|
2017-05-28 02:26:47 +02:00
|
|
|
m_mappings[i].act = portmap_action::none;
|
2007-03-15 23:03:56 +01:00
|
|
|
// try again in two hours
|
2015-03-12 05:34:54 +01:00
|
|
|
m_mappings[i].expires = aux::time_now() + hours(2);
|
2016-05-01 08:21:30 +02:00
|
|
|
try_next_mapping(i);
|
2007-03-15 23:03:56 +01:00
|
|
|
return;
|
|
|
|
}
|
2016-05-01 08:21:30 +02:00
|
|
|
send_map_request(i);
|
2007-03-15 23:03:56 +01:00
|
|
|
}
|
|
|
|
|
2008-05-03 18:05:42 +02:00
|
|
|
void natpmp::on_reply(error_code const& e
|
2017-09-27 19:58:03 +02:00
|
|
|
, std::size_t const bytes_transferred)
|
2007-03-15 23:03:56 +01:00
|
|
|
{
|
2016-05-01 08:21:30 +02:00
|
|
|
TORRENT_ASSERT(is_single_thread());
|
2009-06-13 10:04:53 +02:00
|
|
|
|
2016-04-23 23:29:25 +02:00
|
|
|
COMPLETE_ASYNC("natpmp::on_reply");
|
2010-11-28 02:47:30 +01:00
|
|
|
|
2007-03-15 23:03:56 +01:00
|
|
|
using namespace libtorrent::detail;
|
2008-08-20 10:34:32 +02:00
|
|
|
if (e)
|
|
|
|
{
|
2016-05-18 07:54:37 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2016-09-16 15:53:17 +02:00
|
|
|
if (should_log())
|
|
|
|
{
|
|
|
|
log("error on receiving reply: %s"
|
|
|
|
, convert_from_native(e.message()).c_str());
|
|
|
|
}
|
2016-05-18 07:54:37 +02:00
|
|
|
#endif
|
2008-08-20 10:34:32 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-11-13 11:00:18 +01:00
|
|
|
if (m_abort) return;
|
|
|
|
|
2016-04-23 23:29:25 +02:00
|
|
|
ADD_OUTSTANDING_ASYNC("natpmp::on_reply");
|
2010-12-12 19:16:35 +01:00
|
|
|
// make a copy of the response packet buffer
|
|
|
|
// to avoid overwriting it in the next receive call
|
2018-07-19 14:29:30 +02:00
|
|
|
std::array<char, sizeof(m_response_buffer)> msg_buf;
|
|
|
|
std::memcpy(msg_buf.data(), m_response_buffer, bytes_transferred);
|
2010-12-12 19:16:35 +01:00
|
|
|
|
2016-05-24 04:46:46 +02:00
|
|
|
m_socket.async_receive_from(boost::asio::buffer(&m_response_buffer[0]
|
|
|
|
, sizeof(m_response_buffer))
|
2016-05-25 06:31:52 +02:00
|
|
|
, m_remote, std::bind(&natpmp::on_reply, self(), _1, _2));
|
2007-03-15 23:03:56 +01:00
|
|
|
|
2007-12-29 08:26:36 +01:00
|
|
|
if (m_remote != m_nat_endpoint)
|
2007-03-15 23:03:56 +01:00
|
|
|
{
|
2016-05-18 07:54:37 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2016-09-16 15:53:17 +02:00
|
|
|
if (should_log())
|
|
|
|
{
|
|
|
|
log("received packet from wrong IP: %s"
|
|
|
|
, print_endpoint(m_remote).c_str());
|
|
|
|
}
|
2016-05-18 07:54:37 +02:00
|
|
|
#endif
|
2007-12-29 08:26:36 +01:00
|
|
|
return;
|
|
|
|
}
|
2007-03-15 23:03:56 +01:00
|
|
|
|
2008-05-03 18:05:42 +02:00
|
|
|
error_code ec;
|
2007-12-29 08:26:36 +01:00
|
|
|
m_send_timer.cancel(ec);
|
2007-03-15 23:03:56 +01:00
|
|
|
|
2018-06-09 17:55:49 +02:00
|
|
|
if (bytes_transferred < 4)
|
2010-12-05 21:40:28 +01:00
|
|
|
{
|
2016-05-18 07:54:37 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
|
|
|
log("received packet of invalid size: %d", int(bytes_transferred));
|
|
|
|
#endif
|
2010-12-05 21:40:28 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-07-19 14:29:30 +02:00
|
|
|
char* in = msg_buf.data();
|
2017-09-27 19:58:03 +02:00
|
|
|
int const version = read_uint8(in);
|
2018-05-30 01:25:40 +02:00
|
|
|
|
|
|
|
if (version != version_natpmp && version != version_pcp)
|
|
|
|
{
|
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
|
|
|
log("unexpected version: %u", version);
|
|
|
|
#endif
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
int cmd = read_uint8(in);
|
|
|
|
if (version == version_pcp)
|
|
|
|
{
|
|
|
|
cmd &= 0x7f;
|
|
|
|
}
|
|
|
|
int result;
|
|
|
|
if (version == version_pcp)
|
|
|
|
{
|
|
|
|
++in; // reserved
|
|
|
|
result = read_uint8(in);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
result = read_uint16(in);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (result == errors::pcp_unsupp_version)
|
|
|
|
{
|
2018-06-10 00:04:38 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
|
|
|
log("unsupported version");
|
|
|
|
#endif
|
2018-05-30 01:25:40 +02:00
|
|
|
if (m_version == version_pcp && !is_v6(m_socket.local_endpoint()))
|
|
|
|
{
|
|
|
|
m_version = version_natpmp;
|
2018-11-08 01:01:12 +01:00
|
|
|
resend_request(m_currently_mapping);
|
2018-05-30 01:25:40 +02:00
|
|
|
send_get_ip_address_request();
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-06-09 17:55:49 +02:00
|
|
|
if ((version == version_natpmp && bytes_transferred < 12)
|
|
|
|
|| (version == version_pcp && bytes_transferred < 24))
|
|
|
|
{
|
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
|
|
|
log("received packet of invalid size: %d", int(bytes_transferred));
|
|
|
|
#endif
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-05-30 01:25:40 +02:00
|
|
|
int lifetime = 0;
|
|
|
|
if (version == version_pcp)
|
|
|
|
{
|
|
|
|
lifetime = aux::numeric_cast<int>(read_uint32(in));
|
|
|
|
}
|
2017-09-27 19:58:03 +02:00
|
|
|
int const time = aux::numeric_cast<int>(read_uint32(in));
|
2018-05-30 01:25:40 +02:00
|
|
|
if (version == version_pcp) in += 12; // reserved
|
2016-05-18 07:54:37 +02:00
|
|
|
TORRENT_UNUSED(time);
|
2010-12-05 21:40:28 +01:00
|
|
|
|
2018-05-30 01:25:40 +02:00
|
|
|
if (version == version_natpmp && cmd == 128)
|
2010-12-05 21:40:28 +01:00
|
|
|
{
|
|
|
|
// public IP request response
|
2010-12-12 19:16:35 +01:00
|
|
|
m_external_ip = read_v4_address(in);
|
2010-12-05 21:40:28 +01:00
|
|
|
|
2016-05-18 07:54:37 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2016-09-16 15:53:17 +02:00
|
|
|
if (should_log())
|
|
|
|
{
|
|
|
|
log("<== public IP address [ %s ]", print_address(m_external_ip).c_str());
|
|
|
|
}
|
2016-05-18 07:54:37 +02:00
|
|
|
#endif
|
2010-12-05 21:40:28 +01:00
|
|
|
return;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2018-05-30 01:25:40 +02:00
|
|
|
if ((version == version_natpmp && bytes_transferred != 16)
|
|
|
|
|| (version == version_pcp && bytes_transferred != 60))
|
2010-12-05 21:40:28 +01:00
|
|
|
{
|
2016-05-18 07:54:37 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
|
|
|
log("received packet of invalid size: %d", int(bytes_transferred));
|
|
|
|
#endif
|
2010-12-05 21:40:28 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-05-30 01:25:40 +02:00
|
|
|
std::array<char, 12> nonce;
|
|
|
|
portmap_protocol protocol = portmap_protocol::none;
|
|
|
|
if (version == version_pcp)
|
|
|
|
{
|
|
|
|
std::memcpy(nonce.data(), in, nonce.size());
|
|
|
|
in += nonce.size();
|
|
|
|
int p = read_uint8(in);
|
|
|
|
protocol = p == 6 ? portmap_protocol::tcp
|
|
|
|
: portmap_protocol::udp;
|
|
|
|
in += 3; // reserved
|
|
|
|
}
|
2016-05-18 07:54:37 +02:00
|
|
|
int const private_port = read_uint16(in);
|
|
|
|
int const public_port = read_uint16(in);
|
2018-05-30 01:25:40 +02:00
|
|
|
if (version == version_natpmp)
|
|
|
|
lifetime = aux::numeric_cast<int>(read_uint32(in));
|
|
|
|
address external_addr;
|
|
|
|
if (version == version_pcp)
|
|
|
|
{
|
2018-07-19 14:29:30 +02:00
|
|
|
external_addr = read_v6_address(in);
|
2018-05-30 01:25:40 +02:00
|
|
|
if (external_addr.to_v6().is_v4_mapped())
|
|
|
|
external_addr = external_addr.to_v6().to_v4();
|
|
|
|
}
|
2007-03-27 09:04:31 +02:00
|
|
|
|
2018-05-30 01:25:40 +02:00
|
|
|
if (version == version_natpmp)
|
|
|
|
{
|
|
|
|
protocol = (cmd - 128 == 1)
|
|
|
|
? portmap_protocol::udp
|
|
|
|
: portmap_protocol::tcp;
|
|
|
|
}
|
2008-12-02 09:20:29 +01:00
|
|
|
|
2016-05-18 07:54:37 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2009-04-13 06:22:03 +02:00
|
|
|
char msg[200];
|
2017-09-27 19:58:03 +02:00
|
|
|
int const num_chars = std::snprintf(msg, sizeof(msg), "<== port map ["
|
2018-11-26 02:20:31 +01:00
|
|
|
" transport: %s protocol: %s local: %d external: %d ttl: %d ]"
|
2018-06-10 00:04:38 +02:00
|
|
|
, version_to_string(protocol_version(version))
|
2018-05-30 01:25:40 +02:00
|
|
|
, (protocol == portmap_protocol::udp ? "udp" : "tcp")
|
2009-04-13 06:22:03 +02:00
|
|
|
, private_port, public_port, lifetime);
|
2016-05-18 07:54:37 +02:00
|
|
|
#endif
|
2007-03-15 23:03:56 +01:00
|
|
|
|
2016-07-09 22:26:26 +02:00
|
|
|
mapping_t* m = nullptr;
|
2017-08-31 00:49:49 +02:00
|
|
|
port_mapping_t index{-1};
|
2017-02-07 06:22:30 +01:00
|
|
|
for (auto i = m_mappings.begin(), end(m_mappings.end()); i != end; ++i)
|
2007-12-29 08:26:36 +01:00
|
|
|
{
|
2008-12-02 09:20:29 +01:00
|
|
|
if (private_port != i->local_port) continue;
|
|
|
|
if (protocol != i->protocol) continue;
|
|
|
|
if (!i->map_sent) continue;
|
2010-09-30 08:33:42 +02:00
|
|
|
if (!i->outstanding_request) continue;
|
2018-05-30 01:25:40 +02:00
|
|
|
if (version == version_pcp && nonce != i->nonce) continue;
|
2008-12-02 09:20:29 +01:00
|
|
|
m = &*i;
|
2017-08-31 00:49:49 +02:00
|
|
|
index = port_mapping_t(static_cast<int>(i - m_mappings.begin()));
|
2008-12-02 09:20:29 +01:00
|
|
|
break;
|
2007-12-29 08:26:36 +01:00
|
|
|
}
|
2007-03-15 23:03:56 +01:00
|
|
|
|
2016-07-09 22:26:26 +02:00
|
|
|
if (m == nullptr)
|
2007-12-29 08:26:36 +01:00
|
|
|
{
|
2016-05-18 07:54:37 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2017-02-07 06:22:30 +01:00
|
|
|
snprintf(msg + num_chars, sizeof(msg) - aux::numeric_cast<std::size_t>(num_chars), " not found in map table");
|
2016-05-18 07:54:37 +02:00
|
|
|
log("%s", msg);
|
|
|
|
#endif
|
2008-12-02 09:20:29 +01:00
|
|
|
return;
|
2007-12-29 08:26:36 +01:00
|
|
|
}
|
2010-09-30 08:33:42 +02:00
|
|
|
m->outstanding_request = false;
|
|
|
|
|
2016-05-18 07:54:37 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
|
|
|
log("%s", msg);
|
|
|
|
#endif
|
2007-03-15 23:03:56 +01:00
|
|
|
|
2007-12-29 08:26:36 +01:00
|
|
|
if (public_port == 0 || lifetime == 0)
|
|
|
|
{
|
|
|
|
// this means the mapping was
|
|
|
|
// successfully closed
|
2016-09-18 16:11:56 +02:00
|
|
|
m->protocol = portmap_protocol::none;
|
2007-12-29 08:26:36 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2015-03-12 05:34:54 +01:00
|
|
|
m->expires = aux::time_now() + seconds(int(lifetime * 0.7f));
|
2008-12-02 09:20:29 +01:00
|
|
|
m->external_port = public_port;
|
2018-05-30 01:25:40 +02:00
|
|
|
if (!external_addr.is_unspecified())
|
|
|
|
m->external_address = external_addr;
|
2007-12-29 08:26:36 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (result != 0)
|
|
|
|
{
|
2015-03-12 05:34:54 +01:00
|
|
|
m->expires = aux::time_now() + hours(2);
|
2016-09-18 16:11:56 +02:00
|
|
|
portmap_protocol const proto = m->protocol;
|
2017-08-31 00:49:49 +02:00
|
|
|
m_callback.on_port_mapping(port_mapping_t{index}, address(), 0, proto
|
2018-05-30 01:25:40 +02:00
|
|
|
, from_result_code(version, result), portmap_transport::natpmp);
|
2007-03-15 23:03:56 +01:00
|
|
|
}
|
2017-05-28 02:26:47 +02:00
|
|
|
else if (m->act == portmap_action::add)
|
2007-03-15 23:03:56 +01:00
|
|
|
{
|
2016-09-18 16:11:56 +02:00
|
|
|
portmap_protocol const proto = m->protocol;
|
2018-05-30 01:25:40 +02:00
|
|
|
address const ext_ip = version == version_pcp ? m->external_address : m_external_ip;
|
|
|
|
m_callback.on_port_mapping(port_mapping_t{index}, ext_ip, m->external_port, proto
|
|
|
|
, errors::pcp_success, portmap_transport::natpmp);
|
2007-03-15 23:03:56 +01:00
|
|
|
}
|
2007-12-29 08:26:36 +01:00
|
|
|
|
2017-08-31 00:49:49 +02:00
|
|
|
m_currently_mapping = port_mapping_t{-1};
|
2017-05-28 02:26:47 +02:00
|
|
|
m->act = portmap_action::none;
|
2007-12-29 08:26:36 +01:00
|
|
|
m_send_timer.cancel(ec);
|
2016-05-01 08:21:30 +02:00
|
|
|
update_expiration_timer();
|
|
|
|
try_next_mapping(index);
|
2007-03-15 23:03:56 +01:00
|
|
|
}
|
|
|
|
|
2016-05-01 08:21:30 +02:00
|
|
|
void natpmp::update_expiration_timer()
|
2007-03-15 23:03:56 +01:00
|
|
|
{
|
2016-05-01 08:21:30 +02:00
|
|
|
TORRENT_ASSERT(is_single_thread());
|
2008-04-06 21:17:58 +02:00
|
|
|
if (m_abort) return;
|
|
|
|
|
2016-12-10 20:15:25 +01:00
|
|
|
time_point const now = aux::time_now() + milliseconds(100);
|
2015-03-12 05:34:54 +01:00
|
|
|
time_point min_expire = now + seconds(3600);
|
2017-08-31 00:49:49 +02:00
|
|
|
port_mapping_t min_index{-1};
|
2017-09-27 19:58:03 +02:00
|
|
|
for (auto i = m_mappings.begin(), end(m_mappings.end()); i != end; ++i)
|
2008-04-06 21:17:58 +02:00
|
|
|
{
|
2016-09-18 16:11:56 +02:00
|
|
|
if (i->protocol == portmap_protocol::none
|
2017-05-28 02:26:47 +02:00
|
|
|
|| i->act != portmap_action::none) continue;
|
2017-08-31 00:49:49 +02:00
|
|
|
port_mapping_t const index(static_cast<int>(i - m_mappings.begin()));
|
2010-06-18 06:43:20 +02:00
|
|
|
if (i->expires < now)
|
|
|
|
{
|
2016-05-18 07:54:37 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2017-08-31 00:49:49 +02:00
|
|
|
log("mapping %u expired", static_cast<int>(index));
|
2016-05-18 07:54:37 +02:00
|
|
|
#endif
|
2017-05-28 02:26:47 +02:00
|
|
|
i->act = portmap_action::add;
|
2017-08-31 00:49:49 +02:00
|
|
|
if (m_next_refresh == index) m_next_refresh = port_mapping_t{-1};
|
2016-05-01 08:21:30 +02:00
|
|
|
update_mapping(index);
|
2010-06-18 06:43:20 +02:00
|
|
|
}
|
|
|
|
else if (i->expires < min_expire)
|
2007-03-15 23:03:56 +01:00
|
|
|
{
|
2008-04-06 21:17:58 +02:00
|
|
|
min_expire = i->expires;
|
2010-06-18 06:43:20 +02:00
|
|
|
min_index = index;
|
2007-03-15 23:03:56 +01:00
|
|
|
}
|
2008-04-06 21:17:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// this is already the mapping we're waiting for
|
|
|
|
if (m_next_refresh == min_index) return;
|
2007-03-15 23:03:56 +01:00
|
|
|
|
2017-08-31 00:49:49 +02:00
|
|
|
if (min_index >= port_mapping_t{})
|
2007-03-15 23:03:56 +01:00
|
|
|
{
|
2016-06-23 07:23:59 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
|
|
|
log("next expiration [ idx: %d ttl: %" PRId64 " ]"
|
2017-08-31 00:49:49 +02:00
|
|
|
, static_cast<int>(min_index), total_seconds(min_expire - aux::time_now()));
|
2008-04-06 21:17:58 +02:00
|
|
|
#endif
|
2008-05-03 18:05:42 +02:00
|
|
|
error_code ec;
|
2017-08-31 00:49:49 +02:00
|
|
|
if (m_next_refresh >= port_mapping_t{}) m_refresh_timer.cancel(ec);
|
2010-11-28 02:47:30 +01:00
|
|
|
|
2016-04-23 23:29:25 +02:00
|
|
|
ADD_OUTSTANDING_ASYNC("natpmp::mapping_expired");
|
2007-12-29 08:26:36 +01:00
|
|
|
m_refresh_timer.expires_from_now(min_expire - now, ec);
|
2016-05-25 06:31:52 +02:00
|
|
|
m_refresh_timer.async_wait(std::bind(&natpmp::mapping_expired, self(), _1, min_index));
|
2008-04-06 21:17:58 +02:00
|
|
|
m_next_refresh = min_index;
|
2007-03-15 23:03:56 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-31 00:49:49 +02:00
|
|
|
void natpmp::mapping_expired(error_code const& e, port_mapping_t const i)
|
2007-03-15 23:03:56 +01:00
|
|
|
{
|
2016-05-01 08:21:30 +02:00
|
|
|
TORRENT_ASSERT(is_single_thread());
|
2016-04-23 23:29:25 +02:00
|
|
|
COMPLETE_ASYNC("natpmp::mapping_expired");
|
2007-03-15 23:03:56 +01:00
|
|
|
if (e) return;
|
2016-05-18 07:54:37 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2017-08-31 00:49:49 +02:00
|
|
|
log("mapping %u expired", static_cast<int>(i));
|
2016-05-18 07:54:37 +02:00
|
|
|
#endif
|
2017-05-28 02:26:47 +02:00
|
|
|
m_mappings[i].act = portmap_action::add;
|
2017-08-31 00:49:49 +02:00
|
|
|
if (m_next_refresh == i) m_next_refresh = port_mapping_t{-1};
|
2016-05-01 08:21:30 +02:00
|
|
|
update_mapping(i);
|
2007-03-15 23:03:56 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void natpmp::close()
|
|
|
|
{
|
2016-05-01 08:21:30 +02:00
|
|
|
TORRENT_ASSERT(is_single_thread());
|
|
|
|
close_impl();
|
2009-09-22 06:01:01 +02:00
|
|
|
}
|
|
|
|
|
2016-05-01 08:21:30 +02:00
|
|
|
void natpmp::close_impl()
|
2009-09-22 06:01:01 +02:00
|
|
|
{
|
2016-05-01 08:21:30 +02:00
|
|
|
TORRENT_ASSERT(is_single_thread());
|
2008-04-06 21:17:58 +02:00
|
|
|
m_abort = true;
|
2016-05-18 07:54:37 +02:00
|
|
|
#ifndef TORRENT_DISABLE_LOGGING
|
2016-05-01 08:21:30 +02:00
|
|
|
log("closing");
|
2008-04-06 21:17:58 +02:00
|
|
|
#endif
|
2007-03-15 23:03:56 +01:00
|
|
|
if (m_disabled) return;
|
2016-06-23 07:23:59 +02:00
|
|
|
for (auto& m : m_mappings)
|
2007-03-15 23:03:56 +01:00
|
|
|
{
|
2016-09-18 16:11:56 +02:00
|
|
|
if (m.protocol == portmap_protocol::none) continue;
|
2017-05-28 02:26:47 +02:00
|
|
|
m.act = portmap_action::del;
|
2007-03-15 23:03:56 +01:00
|
|
|
}
|
2008-11-16 03:11:04 +01:00
|
|
|
error_code ec;
|
2007-12-29 08:26:36 +01:00
|
|
|
m_refresh_timer.cancel(ec);
|
2017-08-31 00:49:49 +02:00
|
|
|
m_currently_mapping = port_mapping_t{-1};
|
|
|
|
update_mapping(port_mapping_t{});
|
2007-03-15 23:03:56 +01:00
|
|
|
}
|
2016-09-18 16:11:56 +02:00
|
|
|
|
|
|
|
} // namespace libtorrent
|