forked from premiere/premiere-libtorrent
add support for the Port Control Protocol (PCP)
This commit is contained in:
parent
f6720e31ed
commit
e523566622
|
@ -1,3 +1,4 @@
|
||||||
|
* add support for Port Control Protocol (PCP)
|
||||||
* deliver notification of alerts being dropped via alerts_dropped_alert
|
* deliver notification of alerts being dropped via alerts_dropped_alert
|
||||||
* deprecated alert::progress_notification alert category, split into
|
* deprecated alert::progress_notification alert category, split into
|
||||||
finer grained categories
|
finer grained categories
|
||||||
|
|
|
@ -326,17 +326,19 @@ namespace libtorrent {
|
||||||
// the listen socket associated with this request was closed
|
// the listen socket associated with this request was closed
|
||||||
invalid_listen_socket,
|
invalid_listen_socket,
|
||||||
|
|
||||||
|
// these error codes are deprecated, NAT-PMP/PCP error codes have
|
||||||
|
// been moved to their own category
|
||||||
|
|
||||||
// The NAT-PMP router responded with an unsupported protocol version
|
// The NAT-PMP router responded with an unsupported protocol version
|
||||||
unsupported_protocol_version = 120,
|
unsupported_protocol_version TORRENT_DEPRECATED_ENUM = 120,
|
||||||
// You are not authorized to map ports on this NAT-PMP router
|
// You are not authorized to map ports on this NAT-PMP router
|
||||||
natpmp_not_authorized,
|
natpmp_not_authorized TORRENT_DEPRECATED_ENUM,
|
||||||
// The NAT-PMP router failed because of a network failure
|
// The NAT-PMP router failed because of a network failure
|
||||||
network_failure,
|
network_failure TORRENT_DEPRECATED_ENUM,
|
||||||
// The NAT-PMP router failed because of lack of resources
|
// The NAT-PMP router failed because of lack of resources
|
||||||
no_resources,
|
no_resources TORRENT_DEPRECATED_ENUM,
|
||||||
// The NAT-PMP router failed because an unsupported opcode was sent
|
// The NAT-PMP router failed because an unsupported opcode was sent
|
||||||
unsupported_opcode,
|
unsupported_opcode TORRENT_DEPRECATED_ENUM,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -43,6 +43,40 @@ POSSIBILITY OF SUCH DAMAGE.
|
||||||
#include "libtorrent/aux_/portmap.hpp"
|
#include "libtorrent/aux_/portmap.hpp"
|
||||||
#include "libtorrent/aux_/vector.hpp"
|
#include "libtorrent/aux_/vector.hpp"
|
||||||
|
|
||||||
|
namespace libtorrent {
|
||||||
|
|
||||||
|
namespace errors
|
||||||
|
{
|
||||||
|
// See RFC 6887 Section 7.4
|
||||||
|
enum pcp_errors
|
||||||
|
{
|
||||||
|
pcp_success = 0,
|
||||||
|
pcp_unsupp_version,
|
||||||
|
pcp_not_authorized,
|
||||||
|
pcp_malformed_request,
|
||||||
|
pcp_unsupp_opcode,
|
||||||
|
pcp_unsupp_option,
|
||||||
|
pcp_malformed_option,
|
||||||
|
pcp_network_failure,
|
||||||
|
pcp_no_resources,
|
||||||
|
pcp_unsupp_protocol,
|
||||||
|
pcp_user_ex_quota,
|
||||||
|
pcp_cannot_provide_external,
|
||||||
|
pcp_address_mismatch,
|
||||||
|
pcp_excessive_remote_peers,
|
||||||
|
};
|
||||||
|
|
||||||
|
boost::system::error_code make_error_code(pcp_errors e);
|
||||||
|
}
|
||||||
|
|
||||||
|
boost::system::error_category& pcp_category();
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace boost { namespace system {
|
||||||
|
template<> struct is_error_code_enum<libtorrent::errors::pcp_errors>
|
||||||
|
{ static const bool value = true; };
|
||||||
|
} }
|
||||||
|
|
||||||
namespace libtorrent {
|
namespace libtorrent {
|
||||||
|
|
||||||
struct TORRENT_EXTRA_EXPORT natpmp
|
struct TORRENT_EXTRA_EXPORT natpmp
|
||||||
|
@ -51,7 +85,7 @@ struct TORRENT_EXTRA_EXPORT natpmp
|
||||||
{
|
{
|
||||||
natpmp(io_service& ios, aux::portmap_callback& cb);
|
natpmp(io_service& ios, aux::portmap_callback& cb);
|
||||||
|
|
||||||
void start(address const& local_address, std::string device);
|
void start(address local_address, std::string device);
|
||||||
|
|
||||||
// maps the ports, if a port is set to 0
|
// maps the ports, if a port is set to 0
|
||||||
// it will not be mapped
|
// it will not be mapped
|
||||||
|
@ -63,6 +97,7 @@ struct TORRENT_EXTRA_EXPORT natpmp
|
||||||
void close();
|
void close();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
static error_code from_result_code(int version, int result);
|
||||||
|
|
||||||
std::shared_ptr<natpmp> self() { return shared_from_this(); }
|
std::shared_ptr<natpmp> self() { return shared_from_this(); }
|
||||||
|
|
||||||
|
@ -79,8 +114,28 @@ private:
|
||||||
|
|
||||||
void disable(error_code const& ec);
|
void disable(error_code const& ec);
|
||||||
|
|
||||||
|
enum protocol_version
|
||||||
|
{
|
||||||
|
version_natpmp = 0,
|
||||||
|
version_pcp = 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
// See RFC 6887 Section 19.2
|
||||||
|
enum pcp_opcode
|
||||||
|
{
|
||||||
|
opcode_announce = 0,
|
||||||
|
opcode_map,
|
||||||
|
opcode_peer,
|
||||||
|
};
|
||||||
|
|
||||||
struct mapping_t : aux::base_mapping
|
struct mapping_t : aux::base_mapping
|
||||||
{
|
{
|
||||||
|
// random identifier, used by PCP
|
||||||
|
std::array<char, 12> nonce;
|
||||||
|
|
||||||
|
// only valid if the router supports PCP
|
||||||
|
address external_address;
|
||||||
|
|
||||||
// the local port for this mapping. If this is set
|
// the local port for this mapping. If this is set
|
||||||
// to 0, the mapping is not in use
|
// to 0, the mapping is not in use
|
||||||
int local_port = 0;
|
int local_port = 0;
|
||||||
|
@ -100,6 +155,8 @@ private:
|
||||||
|
|
||||||
aux::portmap_callback& m_callback;
|
aux::portmap_callback& m_callback;
|
||||||
|
|
||||||
|
protocol_version m_version = version_pcp;
|
||||||
|
|
||||||
aux::vector<mapping_t, port_mapping_t> m_mappings;
|
aux::vector<mapping_t, port_mapping_t> m_mappings;
|
||||||
|
|
||||||
// the endpoint to the nat router
|
// the endpoint to the nat router
|
||||||
|
@ -114,9 +171,12 @@ private:
|
||||||
int m_retry_count = 0;
|
int m_retry_count = 0;
|
||||||
|
|
||||||
// used to receive responses in
|
// used to receive responses in
|
||||||
char m_response_buffer[16];
|
// 1100 octets is the maximum size of a PCP packet
|
||||||
|
char m_response_buffer[1100];
|
||||||
|
|
||||||
// router external IP address
|
// router external IP address
|
||||||
|
// this is only used if the router does not support PCP
|
||||||
|
// with PCP the external IP is stored with the mapping
|
||||||
address m_external_ip;
|
address m_external_ip;
|
||||||
|
|
||||||
// the endpoint we received the message from
|
// the endpoint we received the message from
|
||||||
|
|
|
@ -40,6 +40,7 @@ namespace libtorrent {
|
||||||
|
|
||||||
enum class portmap_transport : std::uint8_t
|
enum class portmap_transport : std::uint8_t
|
||||||
{
|
{
|
||||||
|
// natpmp can be NAT-PMP or PCP
|
||||||
natpmp, upnp
|
natpmp, upnp
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
299
src/natpmp.cpp
299
src/natpmp.cpp
|
@ -55,11 +55,76 @@ POSSIBILITY OF SUCH DAMAGE.
|
||||||
#include "libtorrent/io_service.hpp"
|
#include "libtorrent/io_service.hpp"
|
||||||
#include "libtorrent/aux_/time.hpp"
|
#include "libtorrent/aux_/time.hpp"
|
||||||
#include "libtorrent/debug.hpp"
|
#include "libtorrent/debug.hpp"
|
||||||
|
#include "libtorrent/random.hpp"
|
||||||
|
#include "libtorrent/broadcast_socket.hpp" // for is_local
|
||||||
#include "libtorrent/aux_/escape_string.hpp"
|
#include "libtorrent/aux_/escape_string.hpp"
|
||||||
#include "libtorrent/aux_/numeric_cast.hpp"
|
#include "libtorrent/aux_/numeric_cast.hpp"
|
||||||
|
|
||||||
namespace libtorrent {
|
namespace libtorrent {
|
||||||
|
|
||||||
|
struct TORRENT_EXPORT pcp_error_category : boost::system::error_category
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
using namespace aux;
|
using namespace aux;
|
||||||
using namespace std::placeholders;
|
using namespace std::placeholders;
|
||||||
|
|
||||||
|
@ -76,18 +141,64 @@ natpmp::natpmp(io_service& ios
|
||||||
m_mappings.reserve(10);
|
m_mappings.reserve(10);
|
||||||
}
|
}
|
||||||
|
|
||||||
void natpmp::start(address const& local_address, std::string device)
|
void natpmp::start(address local_address, std::string device)
|
||||||
{
|
{
|
||||||
TORRENT_ASSERT(is_single_thread());
|
TORRENT_ASSERT(is_single_thread());
|
||||||
|
|
||||||
error_code ec;
|
error_code ec;
|
||||||
|
|
||||||
|
// assume servers support PCP and fall back to NAT-PMP
|
||||||
|
// if necessary
|
||||||
|
m_version = version_pcp;
|
||||||
|
|
||||||
|
// 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())
|
||||||
|
{
|
||||||
|
for (auto const& a : enum_net_interfaces(m_socket.get_io_service(), ec))
|
||||||
|
{
|
||||||
|
if (a.interface_address.is_loopback()) continue;
|
||||||
|
if (a.interface_address.is_v4() != local_address.is_v4()) continue;
|
||||||
|
if (a.interface_address.is_v6() && is_local(a.interface_address)) continue;
|
||||||
|
if (!device.empty() && a.name != device) continue;
|
||||||
|
local_address = a.interface_address;
|
||||||
|
device = a.name;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (local_address.is_unspecified())
|
||||||
|
{
|
||||||
|
// 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// we really want a device name to get the right default gateway
|
// 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
|
// try to find one even if the listen socket isn't bound to a device
|
||||||
if (device.empty())
|
if (device.empty())
|
||||||
{
|
{
|
||||||
device = device_for_address(local_address, m_socket.get_io_service(), ec);
|
device = device_for_address(local_address, m_socket.get_io_service(), ec);
|
||||||
// if this failes fall back to using the first default gateway in the
|
// if this fails fall back to using the first default gateway in the
|
||||||
// routing table
|
// routing table
|
||||||
ec.clear();
|
ec.clear();
|
||||||
}
|
}
|
||||||
|
@ -138,6 +249,7 @@ void natpmp::start(address const& local_address, std::string device)
|
||||||
m_socket.async_receive_from(boost::asio::buffer(&m_response_buffer[0]
|
m_socket.async_receive_from(boost::asio::buffer(&m_response_buffer[0]
|
||||||
, sizeof(m_response_buffer))
|
, sizeof(m_response_buffer))
|
||||||
, m_remote, std::bind(&natpmp::on_reply, self(), _1, _2));
|
, m_remote, std::bind(&natpmp::on_reply, self(), _1, _2));
|
||||||
|
if (m_version == version_natpmp)
|
||||||
send_get_ip_address_request();
|
send_get_ip_address_request();
|
||||||
|
|
||||||
for (auto i = m_mappings.begin(), end(m_mappings.end()); i != end; ++i)
|
for (auto i = m_mappings.begin(), end(m_mappings.end()); i != end; ++i)
|
||||||
|
@ -155,9 +267,15 @@ void natpmp::send_get_ip_address_request()
|
||||||
TORRENT_ASSERT(is_single_thread());
|
TORRENT_ASSERT(is_single_thread());
|
||||||
using namespace libtorrent::detail;
|
using namespace libtorrent::detail;
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
|
||||||
char buf[2];
|
char buf[2];
|
||||||
char* out = buf;
|
char* out = buf;
|
||||||
write_uint8(0, out); // NAT-PMP version
|
write_uint8(version_natpmp, out);
|
||||||
write_uint8(0, out); // public IP address request opcode
|
write_uint8(0, out); // public IP address request opcode
|
||||||
#ifndef TORRENT_DISABLE_LOGGING
|
#ifndef TORRENT_DISABLE_LOGGING
|
||||||
log("==> get public IP address");
|
log("==> get public IP address");
|
||||||
|
@ -268,6 +386,7 @@ port_mapping_t natpmp::add_mapping(portmap_protocol const p, int const external_
|
||||||
m_mappings.push_back(mapping_t());
|
m_mappings.push_back(mapping_t());
|
||||||
i = m_mappings.end() - 1;
|
i = m_mappings.end() - 1;
|
||||||
}
|
}
|
||||||
|
aux::random_bytes(i->nonce);
|
||||||
i->protocol = p;
|
i->protocol = p;
|
||||||
i->external_port = external_port;
|
i->external_port = external_port;
|
||||||
i->local_port = local_ep.port();
|
i->local_port = local_ep.port();
|
||||||
|
@ -356,15 +475,70 @@ void natpmp::send_map_request(port_mapping_t const i)
|
||||||
m_currently_mapping = i;
|
m_currently_mapping = i;
|
||||||
mapping_t& m = m_mappings[i];
|
mapping_t& m = m_mappings[i];
|
||||||
TORRENT_ASSERT(m.act != portmap_action::none);
|
TORRENT_ASSERT(m.act != portmap_action::none);
|
||||||
char buf[12];
|
char buf[60];
|
||||||
char* out = buf;
|
char* out = buf;
|
||||||
write_uint8(0, out); // NAT-PMP version
|
int ttl = m.act == portmap_action::add ? 3600 : 0;
|
||||||
|
if (m_version == version_natpmp)
|
||||||
|
{
|
||||||
|
write_uint8(m_version, out);
|
||||||
write_uint8(m.protocol == portmap_protocol::udp ? 1 : 2, out); // map "protocol"
|
write_uint8(m.protocol == portmap_protocol::udp ? 1 : 2, out); // map "protocol"
|
||||||
write_uint16(0, out); // reserved
|
write_uint16(0, out); // reserved
|
||||||
write_uint16(m.local_port, out); // private port
|
write_uint16(m.local_port, out); // private port
|
||||||
write_uint16(m.external_port, out); // requested public port
|
write_uint16(m.external_port, out); // requested public port
|
||||||
int ttl = m.act == portmap_action::add ? 3600 : 0;
|
|
||||||
write_uint32(ttl, out); // port mapping lifetime
|
write_uint32(ttl, out); // port mapping lifetime
|
||||||
|
}
|
||||||
|
else if (m_version == version_pcp)
|
||||||
|
{
|
||||||
|
// PCP requires the use of IPv6 addresses even for IPv4 messages
|
||||||
|
// reference asio's address_v6 class directly so that we can use it
|
||||||
|
// even if TORRENT_USE_IPV6 is false
|
||||||
|
using boost::asio::ip::address_v6;
|
||||||
|
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);
|
||||||
|
address_v6::bytes_type external_addr;
|
||||||
|
if (!m.external_address.is_unspecified())
|
||||||
|
{
|
||||||
|
external_addr = m.external_address.is_v4()
|
||||||
|
? address_v6::v4_mapped(m.external_address.to_v4()).to_bytes()
|
||||||
|
: m.external_address.to_v6().to_bytes();
|
||||||
|
}
|
||||||
|
else if (is_local(local_addr))
|
||||||
|
{
|
||||||
|
external_addr = local_addr.is_v4() ? address_v6::v4_mapped(address_v4()).to_bytes()
|
||||||
|
: address_v6().to_bytes();
|
||||||
|
}
|
||||||
|
else if (local_addr.is_v4())
|
||||||
|
{
|
||||||
|
external_addr = address_v6::v4_mapped(local_addr.to_v4()).to_bytes();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
external_addr = local_addr.to_v6().to_bytes();
|
||||||
|
}
|
||||||
|
out = std::copy(external_addr.begin(), external_addr.end(), out);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
TORRENT_ASSERT_FAIL();
|
||||||
|
}
|
||||||
|
|
||||||
#ifndef TORRENT_DISABLE_LOGGING
|
#ifndef TORRENT_DISABLE_LOGGING
|
||||||
if (should_log())
|
if (should_log())
|
||||||
|
@ -378,7 +552,7 @@ void natpmp::send_map_request(port_mapping_t const i)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
error_code ec;
|
error_code ec;
|
||||||
m_socket.send_to(boost::asio::buffer(buf, sizeof(buf)), m_nat_endpoint, 0, ec);
|
m_socket.send_to(boost::asio::buffer(buf, std::size_t(out - buf)), m_nat_endpoint, 0, ec);
|
||||||
m.map_sent = true;
|
m.map_sent = true;
|
||||||
m.outstanding_request = true;
|
m.outstanding_request = true;
|
||||||
if (m_abort)
|
if (m_abort)
|
||||||
|
@ -476,13 +650,52 @@ void natpmp::on_reply(error_code const& e
|
||||||
|
|
||||||
char* in = msg_buf;
|
char* in = msg_buf;
|
||||||
int const version = read_uint8(in);
|
int const version = read_uint8(in);
|
||||||
int const cmd = read_uint8(in);
|
|
||||||
int const result = read_uint16(in);
|
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)
|
||||||
|
{
|
||||||
|
if (m_version == version_pcp && !is_v6(m_socket.local_endpoint()))
|
||||||
|
{
|
||||||
|
m_version = version_natpmp;
|
||||||
|
resend_request(m_currently_mapping, error_code());
|
||||||
|
send_get_ip_address_request();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int lifetime = 0;
|
||||||
|
if (version == version_pcp)
|
||||||
|
{
|
||||||
|
lifetime = aux::numeric_cast<int>(read_uint32(in));
|
||||||
|
}
|
||||||
int const time = aux::numeric_cast<int>(read_uint32(in));
|
int const time = aux::numeric_cast<int>(read_uint32(in));
|
||||||
TORRENT_UNUSED(version);
|
if (version == version_pcp) in += 12; // reserved
|
||||||
TORRENT_UNUSED(time);
|
TORRENT_UNUSED(time);
|
||||||
|
|
||||||
if (cmd == 128)
|
if (version == version_natpmp && cmd == 128)
|
||||||
{
|
{
|
||||||
// public IP request response
|
// public IP request response
|
||||||
m_external_ip = read_v4_address(in);
|
m_external_ip = read_v4_address(in);
|
||||||
|
@ -497,7 +710,8 @@ void natpmp::on_reply(error_code const& e
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bytes_transferred != 16)
|
if ((version == version_natpmp && bytes_transferred != 16)
|
||||||
|
|| (version == version_pcp && bytes_transferred != 60))
|
||||||
{
|
{
|
||||||
#ifndef TORRENT_DISABLE_LOGGING
|
#ifndef TORRENT_DISABLE_LOGGING
|
||||||
log("received packet of invalid size: %d", int(bytes_transferred));
|
log("received packet of invalid size: %d", int(bytes_transferred));
|
||||||
|
@ -505,27 +719,46 @@ void natpmp::on_reply(error_code const& e
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
int const private_port = read_uint16(in);
|
int const private_port = read_uint16(in);
|
||||||
int const public_port = read_uint16(in);
|
int const public_port = read_uint16(in);
|
||||||
int const lifetime = aux::numeric_cast<int>(read_uint32(in));
|
if (version == version_natpmp)
|
||||||
|
lifetime = aux::numeric_cast<int>(read_uint32(in));
|
||||||
|
address external_addr;
|
||||||
|
if (version == version_pcp)
|
||||||
|
{
|
||||||
|
using boost::asio::ip::address_v6;
|
||||||
|
address_v6::bytes_type addr;
|
||||||
|
std::memcpy(addr.data(), in, addr.size());
|
||||||
|
in += addr.size();
|
||||||
|
external_addr = address_v6(addr);
|
||||||
|
if (external_addr.to_v6().is_v4_mapped())
|
||||||
|
external_addr = external_addr.to_v6().to_v4();
|
||||||
|
}
|
||||||
|
|
||||||
portmap_protocol const protocol = (cmd - 128 == 1)
|
if (version == version_natpmp)
|
||||||
|
{
|
||||||
|
protocol = (cmd - 128 == 1)
|
||||||
? portmap_protocol::udp
|
? portmap_protocol::udp
|
||||||
: portmap_protocol::tcp;
|
: portmap_protocol::tcp;
|
||||||
|
}
|
||||||
|
|
||||||
#ifndef TORRENT_DISABLE_LOGGING
|
#ifndef TORRENT_DISABLE_LOGGING
|
||||||
char msg[200];
|
char msg[200];
|
||||||
int const num_chars = std::snprintf(msg, sizeof(msg), "<== port map ["
|
int const num_chars = std::snprintf(msg, sizeof(msg), "<== port map ["
|
||||||
" protocol: %s local: %u external: %u ttl: %u ]"
|
" protocol: %s local: %u external: %u ttl: %u ]"
|
||||||
, (cmd - 128 == 1 ? "udp" : "tcp")
|
, (protocol == portmap_protocol::udp ? "udp" : "tcp")
|
||||||
, private_port, public_port, lifetime);
|
, private_port, public_port, lifetime);
|
||||||
|
|
||||||
if (version != 0)
|
|
||||||
{
|
|
||||||
std::snprintf(msg + num_chars, sizeof(msg) - aux::numeric_cast<std::size_t>(num_chars), "unexpected version: %u"
|
|
||||||
, version);
|
|
||||||
log("%s", msg);
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
mapping_t* m = nullptr;
|
mapping_t* m = nullptr;
|
||||||
|
@ -536,6 +769,7 @@ void natpmp::on_reply(error_code const& e
|
||||||
if (protocol != i->protocol) continue;
|
if (protocol != i->protocol) continue;
|
||||||
if (!i->map_sent) continue;
|
if (!i->map_sent) continue;
|
||||||
if (!i->outstanding_request) continue;
|
if (!i->outstanding_request) continue;
|
||||||
|
if (version == version_pcp && nonce != i->nonce) continue;
|
||||||
m = &*i;
|
m = &*i;
|
||||||
index = port_mapping_t(static_cast<int>(i - m_mappings.begin()));
|
index = port_mapping_t(static_cast<int>(i - m_mappings.begin()));
|
||||||
break;
|
break;
|
||||||
|
@ -565,32 +799,23 @@ void natpmp::on_reply(error_code const& e
|
||||||
{
|
{
|
||||||
m->expires = aux::time_now() + seconds(int(lifetime * 0.7f));
|
m->expires = aux::time_now() + seconds(int(lifetime * 0.7f));
|
||||||
m->external_port = public_port;
|
m->external_port = public_port;
|
||||||
|
if (!external_addr.is_unspecified())
|
||||||
|
m->external_address = external_addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result != 0)
|
if (result != 0)
|
||||||
{
|
{
|
||||||
// TODO: 3 it would be nice to have a separate NAT-PMP error category
|
|
||||||
static errors::error_code_enum const errors[] =
|
|
||||||
{
|
|
||||||
errors::unsupported_protocol_version,
|
|
||||||
errors::natpmp_not_authorized,
|
|
||||||
errors::network_failure,
|
|
||||||
errors::no_resources,
|
|
||||||
errors::unsupported_opcode
|
|
||||||
};
|
|
||||||
errors::error_code_enum ev = errors::no_error;
|
|
||||||
if (result >= 1 && result <= 5) ev = errors[result - 1];
|
|
||||||
|
|
||||||
m->expires = aux::time_now() + hours(2);
|
m->expires = aux::time_now() + hours(2);
|
||||||
portmap_protocol const proto = m->protocol;
|
portmap_protocol const proto = m->protocol;
|
||||||
m_callback.on_port_mapping(port_mapping_t{index}, address(), 0, proto
|
m_callback.on_port_mapping(port_mapping_t{index}, address(), 0, proto
|
||||||
, ev, portmap_transport::natpmp);
|
, from_result_code(version, result), portmap_transport::natpmp);
|
||||||
}
|
}
|
||||||
else if (m->act == portmap_action::add)
|
else if (m->act == portmap_action::add)
|
||||||
{
|
{
|
||||||
portmap_protocol const proto = m->protocol;
|
portmap_protocol const proto = m->protocol;
|
||||||
m_callback.on_port_mapping(port_mapping_t{index}, m_external_ip, m->external_port, proto
|
address const ext_ip = version == version_pcp ? m->external_address : m_external_ip;
|
||||||
, errors::no_error, portmap_transport::natpmp);
|
m_callback.on_port_mapping(port_mapping_t{index}, ext_ip, m->external_port, proto
|
||||||
|
, errors::pcp_success, portmap_transport::natpmp);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_abort) return;
|
if (m_abort) return;
|
||||||
|
|
Loading…
Reference in New Issue