forked from premiere/premiere-libtorrent
rely less on the ability to enumerate the routing table reliably. Any IP address
explicitly specified is assumed to represent an externally available IP, unless marked with the l-flag. If a device name or an unspecified address is used, they are expanded and only for such expanded entries is there a heuristic to determine which addresses are externally available and which are local. The default is to assume it's local only, unless it has a globally routable IP address or a default route can be found for the device.
This commit is contained in:
parent
47012b506a
commit
1e4083b3fb
|
@ -1,3 +1,4 @@
|
||||||
|
* fix issue with knowing which interfaces to announce to trackers and DHT
|
||||||
* undeprecate settings_pack::dht_upload_rate_limit
|
* undeprecate settings_pack::dht_upload_rate_limit
|
||||||
|
|
||||||
1.2.4 release
|
1.2.4 release
|
||||||
|
|
|
@ -556,3 +556,4 @@ netmask
|
||||||
fe80
|
fe80
|
||||||
vcpkg
|
vcpkg
|
||||||
leecher
|
leecher
|
||||||
|
6881l
|
||||||
|
|
105
docs/manual.rst
105
docs/manual.rst
|
@ -733,12 +733,11 @@ setting.
|
||||||
Multi-homed hosts
|
Multi-homed hosts
|
||||||
=================
|
=================
|
||||||
|
|
||||||
libtorrent has solid support for multi-homed hosts, starting with version 1.2.4.
|
The settings_pack::listen_interfaces setting is used to specify which interfaces/IP addresses
|
||||||
the settings_pack::listen_interfaces setting is used to specify which interfaces/IP addresses
|
|
||||||
to listen on, and accept incoming connections via.
|
to listen on, and accept incoming connections via.
|
||||||
|
|
||||||
Each entry in ``listen_interfaces`` is an IP address or a device name, followed
|
Each item in ``listen_interfaces`` is an IP address or a device name, followed
|
||||||
by a listen port number. Each entry (called ``listen_socket_t``) will have the
|
by a listen port number. Each item (called ``listen_socket_t``) will have the
|
||||||
following objects associated with it:
|
following objects associated with it:
|
||||||
|
|
||||||
* a listen socket accepting incoming TCP connections
|
* a listen socket accepting incoming TCP connections
|
||||||
|
@ -754,70 +753,58 @@ following objects associated with it:
|
||||||
* a UPnP port mapper (if applicable), to map ports on any
|
* a UPnP port mapper (if applicable), to map ports on any
|
||||||
* ``InternetGatewayDevice`` found on the specified local subnet.
|
* ``InternetGatewayDevice`` found on the specified local subnet.
|
||||||
|
|
||||||
|
A ``listen_socket_t`` item may be specified to only be a local network (with
|
||||||
|
the ``l`` suffix). Such listen socket will only be used to talk to peers and
|
||||||
|
trackers within the same local network. The netmask defining the network is
|
||||||
|
queried from the operating system by enumerating network interfaces.
|
||||||
|
|
||||||
|
An item that's considered to be "local network" will not be used to announce to
|
||||||
|
trackers outside of that network. For example, ``10.0.0.2:6881l`` is marked as "local
|
||||||
|
network" and it will only be used as the source address announcing to a tracker
|
||||||
|
if the tracker is also within the same local network (e.g. ``10.0.0.0/8``).
|
||||||
|
|
||||||
|
If an IP address is the *unspecified* address (i.e. ``0.0.0.0`` or ``::``),
|
||||||
|
libtorrent will enumerate all addresses it can find for the corresponding
|
||||||
|
address family. If a device name is specified instead of an IP, it will expand
|
||||||
|
to all IP addresses associated with that device.
|
||||||
|
|
||||||
|
Listen IP addresses that are automatically expanded by libtorrent have some
|
||||||
|
special rules. They are all assumed to be restricted to be "local network"
|
||||||
|
unless the following conditions are met:
|
||||||
|
|
||||||
|
* the IP address is not in a known link-local range
|
||||||
|
* the IP address is not in a known loopback range
|
||||||
|
* the item the IP address was expanded from was not marked local (``l``)
|
||||||
|
* the IP address is in a globally reachable IP address range OR the routing
|
||||||
|
table contains a default route with a gateway for the corresponding network
|
||||||
|
|
||||||
The NAT-PMP/PCP and UPnP port mapper objects are only created for networks that
|
The NAT-PMP/PCP and UPnP port mapper objects are only created for networks that
|
||||||
have a gateway configured. This typically means the network that's connected to
|
are expected to be externally available (i.e. not "local network"). If there are
|
||||||
the internet. If there are multiple subnets connected to the internet, they will
|
multiple subnets connected to the internet, they will each have a separate
|
||||||
each have a separate gateway, and separate port mappings.
|
gateway, and separate port mappings.
|
||||||
|
|
||||||
Networks that are not connected to the internet, like loopback, won't have a
|
|
||||||
gateway configured and will hence not have any port-mappers associated with
|
|
||||||
them. They will still be able to accept incoming connections from within the
|
|
||||||
local network.
|
|
||||||
|
|
||||||
gateways
|
default routes
|
||||||
--------
|
--------------
|
||||||
|
|
||||||
The logic for IPv4 and IPv6 are slightly different. An IPv4 network is
|
This section describes the logic for determining whether an address has a
|
||||||
considered having a gateway if:
|
default route associated with it or not. This is only used for listen addresses that
|
||||||
|
are *expanded* from either an unspecified listen address (``0.0.0.0`` or ``::``)
|
||||||
|
or from a device name (e.g. ``eth0``).
|
||||||
|
|
||||||
* there is a default route with a matching egress network device name
|
A network is considered having a default route if there is a default route with
|
||||||
* the gateway configured for the route is inside the local network.
|
a matching egress network device name and address family.
|
||||||
* if the route has a source-hint, it matches the network's address
|
|
||||||
|
|
||||||
For example, if there are two ``listen_socket_t`` entries associated with the
|
|
||||||
following networks::
|
|
||||||
|
|
||||||
1: 192.168.0.10/16 eth0
|
|
||||||
2: 10.0.0.10/8 eth0
|
|
||||||
|
|
||||||
And these two entries in the routing table::
|
|
||||||
|
|
||||||
0.0.0.0 eth0 gateway: 192.168.0.1
|
|
||||||
0.0.0.0 eth0 gateway: 10.0.0.1
|
|
||||||
|
|
||||||
Network (1) will be associated with gateway 192.168.0.1 and network (2) will be
|
|
||||||
associated with gateway 10.0.0.1.
|
|
||||||
|
|
||||||
An IPv6 network is considered having a gateway if:
|
|
||||||
|
|
||||||
* there is a default route with a matching egress network device name
|
|
||||||
* the IPv6 network address is not a local IPv6 address
|
|
||||||
* if the route has a source-hint, it matches the network's address
|
|
||||||
|
|
||||||
For example, if there are two ``listen_socket_t`` entries associated with the
|
|
||||||
following networks::
|
|
||||||
|
|
||||||
1. 2a00:5678:::2/64 eth0
|
|
||||||
2. 2b00:1234:::3/64 eth1
|
|
||||||
|
|
||||||
And these two routes::
|
|
||||||
|
|
||||||
:: eth0 gateway: fe80::1%eth0
|
|
||||||
:: eth1 gateway: fe80::2%eth1
|
|
||||||
|
|
||||||
Network (1) will be associated with ``fe80::1%eth0``, and network (2) will be
|
|
||||||
associated with ``fe80::2%eth1``, because of the interface name matching.
|
|
||||||
|
|
||||||
routing
|
routing
|
||||||
-------
|
-------
|
||||||
|
|
||||||
A ``listen_socket_t`` entry can route to a destination address if any of these
|
A ``listen_socket_t`` item can route to a destination address if any of these
|
||||||
hold:
|
hold:
|
||||||
|
|
||||||
* the destination address falls inside its subnet (i.e. interface address masked
|
* the destination address falls inside its subnet (i.e. interface address masked
|
||||||
by netmask is the same as the destination address masked by the netmask).
|
by netmask is the same as the destination address masked by the netmask).
|
||||||
* the ``listen_socket_t`` has a gateway associated with it, and the address
|
* the ``listen_socket_t`` does not have the "local network" flag set, and the
|
||||||
family matches the destination address.
|
address family matches the destination address.
|
||||||
|
|
||||||
The ability to route to an address is used when determining whether to announce
|
The ability to route to an address is used when determining whether to announce
|
||||||
to a tracker from a ``listen_socket_t`` and whether to open a SOCKS5 UDP tunnel
|
to a tracker from a ``listen_socket_t`` and whether to open a SOCKS5 UDP tunnel
|
||||||
|
@ -829,9 +816,9 @@ tracker announces
|
||||||
Trackers are announced to from all network interfaces listening for incoming
|
Trackers are announced to from all network interfaces listening for incoming
|
||||||
connections. However, interfaces that cannot be used to reach the tracker, such
|
connections. However, interfaces that cannot be used to reach the tracker, such
|
||||||
as loopback, are not used as the source address for announces. A
|
as loopback, are not used as the source address for announces. A
|
||||||
``listen_socket_t`` entry that can route to at least one of the tracker IP
|
``listen_socket_t`` item that can route to at least one of the tracker IP
|
||||||
addresses will be used as the source address for an announce. Each such entry
|
addresses will be used as the source address for an announce. Each such item
|
||||||
will also have an announce_endpoint entry associated with it, in the tracker
|
will also have an announce_endpoint item associated with it, in the tracker
|
||||||
list.
|
list.
|
||||||
|
|
||||||
If a tracker can be reached on a loopback address, then the loopback interface
|
If a tracker can be reached on a loopback address, then the loopback interface
|
||||||
|
@ -848,7 +835,7 @@ SOCKS5 UDP tunnels
|
||||||
When using a SOCKS5 proxy, each interface that can route to one of the SOCKS5
|
When using a SOCKS5 proxy, each interface that can route to one of the SOCKS5
|
||||||
proxy's addresses will be used to open a UDP tunnel, via that proxy. For
|
proxy's addresses will be used to open a UDP tunnel, via that proxy. For
|
||||||
example, if a client has both IPv4 and IPv6 connectivity, but the socks5 proxy
|
example, if a client has both IPv4 and IPv6 connectivity, but the socks5 proxy
|
||||||
only resolves to IPv4, only the IPv4 entry will have a UDP tunnel. In that case,
|
only resolves to IPv4, only the IPv4 address will have a UDP tunnel. In that case,
|
||||||
the IPv6 connection will not be used, since it cannot use the proxy.
|
the IPv6 connection will not be used, since it cannot use the proxy.
|
||||||
|
|
||||||
predictive piece announce
|
predictive piece announce
|
||||||
|
|
|
@ -142,9 +142,10 @@ namespace aux {
|
||||||
// we accept incoming connections on this interface
|
// we accept incoming connections on this interface
|
||||||
static constexpr listen_socket_flags_t accept_incoming = 0_bit;
|
static constexpr listen_socket_flags_t accept_incoming = 0_bit;
|
||||||
|
|
||||||
// this interface has a gateway associated with it, and can
|
// this interface was specified to be just the local network. If this flag
|
||||||
// route to the internet (of the same address family)
|
// is not set, this interface is assumed to have a path to the internet
|
||||||
static constexpr listen_socket_flags_t has_gateway = 1_bit;
|
// (i.e. have a gateway configured)
|
||||||
|
static constexpr listen_socket_flags_t local_network = 1_bit;
|
||||||
|
|
||||||
// this interface was expanded from the user requesting to
|
// this interface was expanded from the user requesting to
|
||||||
// listen on an unspecified address (either IPv4 or IPv6)
|
// listen on an unspecified address (either IPv4 or IPv6)
|
||||||
|
@ -260,7 +261,11 @@ namespace aux {
|
||||||
|
|
||||||
bool operator==(listen_endpoint_t const& o) const
|
bool operator==(listen_endpoint_t const& o) const
|
||||||
{
|
{
|
||||||
return addr == o.addr && port == o.port && device == o.device && ssl == o.ssl;
|
return addr == o.addr
|
||||||
|
&& port == o.port
|
||||||
|
&& device == o.device
|
||||||
|
&& ssl == o.ssl
|
||||||
|
&& flags == o.flags;
|
||||||
}
|
}
|
||||||
|
|
||||||
address addr;
|
address addr;
|
||||||
|
@ -283,13 +288,20 @@ namespace aux {
|
||||||
std::vector<listen_endpoint_t>& eps
|
std::vector<listen_endpoint_t>& eps
|
||||||
, std::vector<std::shared_ptr<aux::listen_socket_t>>& sockets);
|
, std::vector<std::shared_ptr<aux::listen_socket_t>>& sockets);
|
||||||
|
|
||||||
|
TORRENT_EXTRA_EXPORT void interface_to_endpoints(
|
||||||
|
listen_interface_t const& iface
|
||||||
|
, listen_socket_flags_t flags
|
||||||
|
, span<ip_interface const> const ifs
|
||||||
|
, span<ip_route const> const routes
|
||||||
|
, std::vector<listen_endpoint_t>& eps);
|
||||||
|
|
||||||
// expand [::] to all IPv6 interfaces for BEP 45 compliance
|
// expand [::] to all IPv6 interfaces for BEP 45 compliance
|
||||||
TORRENT_EXTRA_EXPORT void expand_unspecified_address(
|
TORRENT_EXTRA_EXPORT void expand_unspecified_address(
|
||||||
span<ip_interface const> ifs
|
span<ip_interface const> ifs
|
||||||
|
, span<ip_route const> routes
|
||||||
, std::vector<listen_endpoint_t>& eps);
|
, std::vector<listen_endpoint_t>& eps);
|
||||||
|
|
||||||
TORRENT_EXTRA_EXPORT void expand_devices(span<ip_interface const>
|
TORRENT_EXTRA_EXPORT void expand_devices(span<ip_interface const>
|
||||||
, span<ip_route const> routes
|
|
||||||
, std::vector<listen_endpoint_t>& eps);
|
, std::vector<listen_endpoint_t>& eps);
|
||||||
|
|
||||||
// this is the link between the main thread and the
|
// this is the link between the main thread and the
|
||||||
|
@ -851,9 +863,6 @@ namespace aux {
|
||||||
void set_external_address(std::shared_ptr<listen_socket_t> const& sock, address const& ip
|
void set_external_address(std::shared_ptr<listen_socket_t> const& sock, address const& ip
|
||||||
, ip_source_t source_type, address const& source);
|
, ip_source_t source_type, address const& source);
|
||||||
|
|
||||||
void interface_to_endpoints(std::string const& device, int port
|
|
||||||
, transport ssl, listen_socket_flags_t flags, std::vector<listen_endpoint_t>& eps);
|
|
||||||
|
|
||||||
counters m_stats_counters;
|
counters m_stats_counters;
|
||||||
|
|
||||||
// this is a pool allocator for torrent_peer objects
|
// this is a pool allocator for torrent_peer objects
|
||||||
|
|
|
@ -48,7 +48,9 @@ POSSIBILITY OF SUCH DAMAGE.
|
||||||
namespace libtorrent {
|
namespace libtorrent {
|
||||||
|
|
||||||
// TODO: 2 factor these functions out
|
// TODO: 2 factor these functions out
|
||||||
|
TORRENT_EXTRA_EXPORT bool is_global(address const& a);
|
||||||
TORRENT_EXTRA_EXPORT bool is_local(address const& a);
|
TORRENT_EXTRA_EXPORT bool is_local(address const& a);
|
||||||
|
TORRENT_EXTRA_EXPORT bool is_link_local(address const& addr);
|
||||||
TORRENT_EXTRA_EXPORT bool is_loopback(address const& addr);
|
TORRENT_EXTRA_EXPORT bool is_loopback(address const& addr);
|
||||||
TORRENT_EXTRA_EXPORT bool is_any(address const& addr);
|
TORRENT_EXTRA_EXPORT bool is_any(address const& addr);
|
||||||
TORRENT_EXTRA_EXPORT bool is_teredo(address const& addr);
|
TORRENT_EXTRA_EXPORT bool is_teredo(address const& addr);
|
||||||
|
|
|
@ -87,6 +87,9 @@ namespace libtorrent {
|
||||||
TORRENT_EXTRA_EXPORT std::vector<ip_route> enum_routes(io_service& ios
|
TORRENT_EXTRA_EXPORT std::vector<ip_route> enum_routes(io_service& ios
|
||||||
, error_code& ec);
|
, error_code& ec);
|
||||||
|
|
||||||
|
// returns AF_INET or AF_INET6, depending on the address' family
|
||||||
|
TORRENT_EXTRA_EXPORT int family(address const& a);
|
||||||
|
|
||||||
// return (a1 & mask) == (a2 & mask)
|
// return (a1 & mask) == (a2 & mask)
|
||||||
TORRENT_EXTRA_EXPORT bool match_addr_mask(address const& a1
|
TORRENT_EXTRA_EXPORT bool match_addr_mask(address const& a1
|
||||||
, address const& a2, address const& mask);
|
, address const& a2, address const& mask);
|
||||||
|
@ -101,6 +104,9 @@ namespace libtorrent {
|
||||||
TORRENT_EXTRA_EXPORT boost::optional<address> get_gateway(
|
TORRENT_EXTRA_EXPORT boost::optional<address> get_gateway(
|
||||||
ip_interface const& iface, span<ip_route const> routes);
|
ip_interface const& iface, span<ip_route const> routes);
|
||||||
|
|
||||||
|
TORRENT_EXTRA_EXPORT bool has_default_route(char const* device, int family
|
||||||
|
, span<ip_route const> routes);
|
||||||
|
|
||||||
// attempt to bind socket to the device with the specified name. For systems
|
// attempt to bind socket to the device with the specified name. For systems
|
||||||
// that don't support SO_BINDTODEVICE the socket will be bound to one of the
|
// that don't support SO_BINDTODEVICE the socket will be bound to one of the
|
||||||
// IP addresses of the specified device. In this case it is necessary to
|
// IP addresses of the specified device. In this case it is necessary to
|
||||||
|
|
|
@ -222,6 +222,11 @@ namespace aux {
|
||||||
// a port that has an "s" suffix will accept SSL connections. (note
|
// a port that has an "s" suffix will accept SSL connections. (note
|
||||||
// that SSL sockets are not enabled by default).
|
// that SSL sockets are not enabled by default).
|
||||||
//
|
//
|
||||||
|
// a port that has an "l" suffix will be considered a local network.
|
||||||
|
// i.e. it's assumed to only be able to reach hosts in the same local
|
||||||
|
// network as the IP address (based on the netmask associated with the
|
||||||
|
// IP, queried from the operating system).
|
||||||
|
//
|
||||||
// if binding fails, the listen_failed_alert is posted. If or once a
|
// if binding fails, the listen_failed_alert is posted. If or once a
|
||||||
// socket binding succeeds, the listen_succeeded_alert is posted. There
|
// socket binding succeeds, the listen_succeeded_alert is posted. There
|
||||||
// may be multiple failures before a success.
|
// may be multiple failures before a success.
|
||||||
|
@ -236,7 +241,13 @@ namespace aux {
|
||||||
// ``[::]:0s`` - will accept SSL connections on a port chosen by the
|
// ``[::]:0s`` - will accept SSL connections on a port chosen by the
|
||||||
// OS. And not accept non-SSL connections at all.
|
// OS. And not accept non-SSL connections at all.
|
||||||
//
|
//
|
||||||
// ``0.0.0.0:6881,[::]:6881`` - binds to all interfaces on port 6881
|
// ``0.0.0.0:6881,[::]:6881`` - binds to all interfaces on port 6881.
|
||||||
|
//
|
||||||
|
// ``10.0.1.13:6881l`` - binds to the local IP address, port 6881, but
|
||||||
|
// only allow talking to peers on the same local network. The netmask
|
||||||
|
// is queried from the operating system. Interfaces marked ``l`` are
|
||||||
|
// not announced to trackers, unless the tracker is also on the same
|
||||||
|
// local network.
|
||||||
//
|
//
|
||||||
// Windows OS network adapter device name can be specified with GUID.
|
// Windows OS network adapter device name can be specified with GUID.
|
||||||
// It can be obtained from "netsh lan show interfaces" command output.
|
// It can be obtained from "netsh lan show interfaces" command output.
|
||||||
|
|
|
@ -84,11 +84,13 @@ namespace libtorrent {
|
||||||
std::string device;
|
std::string device;
|
||||||
int port;
|
int port;
|
||||||
bool ssl;
|
bool ssl;
|
||||||
|
bool local;
|
||||||
friend bool operator==(listen_interface_t const& lhs, listen_interface_t const& rhs)
|
friend bool operator==(listen_interface_t const& lhs, listen_interface_t const& rhs)
|
||||||
{
|
{
|
||||||
return lhs.device == rhs.device
|
return lhs.device == rhs.device
|
||||||
&& lhs.port == rhs.port
|
&& lhs.port == rhs.port
|
||||||
&& lhs.ssl == rhs.ssl;
|
&& lhs.ssl == rhs.ssl
|
||||||
|
&& lhs.local == rhs.local;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -53,6 +53,34 @@ namespace libtorrent {
|
||||||
return !ec;
|
return !ec;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool is_global(address const& a)
|
||||||
|
{
|
||||||
|
if (a.is_v6())
|
||||||
|
{
|
||||||
|
// https://www.iana.org/assignments/ipv6-address-space/ipv6-address-space.xhtml
|
||||||
|
address_v6 const a6 = a.to_v6();
|
||||||
|
return (a6.to_bytes()[0] & 0xe0) == 0x20;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
address_v4 const a4 = a.to_v4();
|
||||||
|
return !(a4.is_multicast() || a4.is_unspecified() || is_local(a));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_link_local(address const& a)
|
||||||
|
{
|
||||||
|
if (a.is_v6())
|
||||||
|
{
|
||||||
|
address_v6 const a6 = a.to_v6();
|
||||||
|
return a6.is_link_local()
|
||||||
|
|| a6.is_multicast_link_local();
|
||||||
|
}
|
||||||
|
address_v4 const a4 = a.to_v4();
|
||||||
|
unsigned long ip = a4.to_ulong();
|
||||||
|
return (ip & 0xffff0000) == 0xa9fe0000; // 169.254.x.x
|
||||||
|
}
|
||||||
|
|
||||||
bool is_local(address const& a)
|
bool is_local(address const& a)
|
||||||
{
|
{
|
||||||
if (a.is_v6())
|
if (a.is_v6())
|
||||||
|
|
|
@ -249,8 +249,7 @@ namespace {
|
||||||
{
|
{
|
||||||
rtmsg* rt_msg = reinterpret_cast<rtmsg*>(NLMSG_DATA(nl_hdr));
|
rtmsg* rt_msg = reinterpret_cast<rtmsg*>(NLMSG_DATA(nl_hdr));
|
||||||
|
|
||||||
if (!valid_addr_family(rt_msg->rtm_family) || (rt_msg->rtm_table != RT_TABLE_MAIN
|
if (!valid_addr_family(rt_msg->rtm_family))
|
||||||
&& rt_msg->rtm_table != RT_TABLE_LOCAL))
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// make sure the defaults have the right address family
|
// make sure the defaults have the right address family
|
||||||
|
@ -451,6 +450,8 @@ int _System __libsocket_sysctl(int* mib, u_int namelen, void *oldp, size_t *oldl
|
||||||
|
|
||||||
} // <anonymous>
|
} // <anonymous>
|
||||||
|
|
||||||
|
int family(address const& a) { return a.is_v4() ? AF_INET : AF_INET6; }
|
||||||
|
|
||||||
address build_netmask(int prefix_bits, int const family)
|
address build_netmask(int prefix_bits, int const family)
|
||||||
{
|
{
|
||||||
if (family == AF_INET)
|
if (family == AF_INET)
|
||||||
|
@ -798,25 +799,32 @@ int _System __libsocket_sysctl(int* mib, u_int namelen, void *oldp, size_t *oldl
|
||||||
return r.destination.is_unspecified()
|
return r.destination.is_unspecified()
|
||||||
&& r.destination.is_v4() == iface.interface_address.is_v4()
|
&& r.destination.is_v4() == iface.interface_address.is_v4()
|
||||||
&& !r.gateway.is_unspecified()
|
&& !r.gateway.is_unspecified()
|
||||||
// IPv6 gateways aren't addressed in the same network as the
|
|
||||||
// interface, but they are addressed by the local network address
|
|
||||||
// space. So this check only works for IPv4.
|
|
||||||
&& (!v4 || match_addr_mask(r.gateway, iface.interface_address, r.netmask))
|
|
||||||
// in case there are multiple networks on the same networking
|
// in case there are multiple networks on the same networking
|
||||||
// device, the source hint may be the only thing telling them
|
// device, the source hint may be the only thing telling them
|
||||||
// apart
|
// apart
|
||||||
&& (r.source_hint.is_unspecified() || r.source_hint == iface.interface_address)
|
&& (r.source_hint.is_unspecified() || r.source_hint == iface.interface_address)
|
||||||
&& strcmp(r.name, iface.name) == 0;
|
&& std::strcmp(r.name, iface.name) == 0;
|
||||||
});
|
});
|
||||||
if (it != routes.end()) return it->gateway;
|
if (it != routes.end()) return it->gateway;
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool has_default_route(char const* device, int const fam, span<ip_route const> routes)
|
||||||
|
{
|
||||||
|
return std::find_if(routes.begin(), routes.end()
|
||||||
|
, [&](ip_route const& r) -> bool
|
||||||
|
{
|
||||||
|
return r.destination.is_unspecified()
|
||||||
|
&& family(r.destination) == fam
|
||||||
|
&& std::strcmp(r.name, device) == 0;
|
||||||
|
}) != routes.end();
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<ip_route> enum_routes(io_service& ios, error_code& ec)
|
std::vector<ip_route> enum_routes(io_service& ios, error_code& ec)
|
||||||
{
|
{
|
||||||
std::vector<ip_route> ret;
|
std::vector<ip_route> ret;
|
||||||
TORRENT_UNUSED(ios);
|
TORRENT_UNUSED(ios);
|
||||||
TORRENT_UNUSED(ec);
|
ec.clear();
|
||||||
|
|
||||||
#ifdef TORRENT_BUILD_SIMULATOR
|
#ifdef TORRENT_BUILD_SIMULATOR
|
||||||
|
|
||||||
|
|
|
@ -199,7 +199,7 @@ namespace libtorrent {
|
||||||
namespace aux {
|
namespace aux {
|
||||||
|
|
||||||
constexpr listen_socket_flags_t listen_socket_t::accept_incoming;
|
constexpr listen_socket_flags_t listen_socket_t::accept_incoming;
|
||||||
constexpr listen_socket_flags_t listen_socket_t::has_gateway;
|
constexpr listen_socket_flags_t listen_socket_t::local_network;
|
||||||
constexpr listen_socket_flags_t listen_socket_t::was_expanded;
|
constexpr listen_socket_flags_t listen_socket_t::was_expanded;
|
||||||
|
|
||||||
constexpr ip_source_t session_interface::source_dht;
|
constexpr ip_source_t session_interface::source_dht;
|
||||||
|
@ -243,6 +243,7 @@ namespace aux {
|
||||||
// by prohibiting creating a listen socket on [::] and 0.0.0.0. Instead the list of
|
// by prohibiting creating a listen socket on [::] and 0.0.0.0. Instead the list of
|
||||||
// interfaces is enumerated and sockets are created for each of them.
|
// interfaces is enumerated and sockets are created for each of them.
|
||||||
void expand_unspecified_address(span<ip_interface const> const ifs
|
void expand_unspecified_address(span<ip_interface const> const ifs
|
||||||
|
, span<ip_route const> const routes
|
||||||
, std::vector<listen_endpoint_t>& eps)
|
, std::vector<listen_endpoint_t>& eps)
|
||||||
{
|
{
|
||||||
auto unspecified_begin = std::partition(eps.begin(), eps.end()
|
auto unspecified_begin = std::partition(eps.begin(), eps.end()
|
||||||
|
@ -273,14 +274,23 @@ namespace aux {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// record whether the device has a gateway associated with it
|
||||||
|
// (which indicates it can be used to reach the internet)
|
||||||
|
// if the IP address tell us it's loopback or link-local, don't
|
||||||
|
// bother looking for the gateway
|
||||||
|
bool const local = ipface.interface_address.is_loopback()
|
||||||
|
|| is_link_local(ipface.interface_address)
|
||||||
|
|| (!is_global(ipface.interface_address)
|
||||||
|
&& !has_default_route(ipface.name, family(ipface.interface_address), routes));
|
||||||
|
|
||||||
eps.emplace_back(ipface.interface_address, uep.port, uep.device
|
eps.emplace_back(ipface.interface_address, uep.port, uep.device
|
||||||
, uep.ssl, uep.flags | listen_socket_t::was_expanded);
|
, uep.ssl, uep.flags | listen_socket_t::was_expanded
|
||||||
|
| (local ? listen_socket_t::local_network : listen_socket_flags_t{}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void expand_devices(span<ip_interface const> const ifs
|
void expand_devices(span<ip_interface const> const ifs
|
||||||
, span<ip_route const> const routes
|
|
||||||
, std::vector<listen_endpoint_t>& eps)
|
, std::vector<listen_endpoint_t>& eps)
|
||||||
{
|
{
|
||||||
for (auto& ep : eps)
|
for (auto& ep : eps)
|
||||||
|
@ -305,13 +315,6 @@ namespace aux {
|
||||||
}
|
}
|
||||||
|
|
||||||
ep.netmask = iface->netmask;
|
ep.netmask = iface->netmask;
|
||||||
|
|
||||||
// also record whether the device has a gateway associated with it
|
|
||||||
// (which indicates it can be used to reach the internet)
|
|
||||||
// only gateways inside the interface's network count
|
|
||||||
if(get_gateway(*iface, routes))
|
|
||||||
ep.flags |= listen_socket_t::has_gateway;
|
|
||||||
|
|
||||||
ep.device = iface->name;
|
ep.device = iface->name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -324,12 +327,10 @@ namespace aux {
|
||||||
&& local_endpoint.address().to_v6().scope_id() != addr.to_v6().scope_id())
|
&& local_endpoint.address().to_v6().scope_id() != addr.to_v6().scope_id())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (flags & has_gateway) return true;
|
|
||||||
if (local_endpoint.address() == addr) return true;
|
if (local_endpoint.address() == addr) return true;
|
||||||
if (local_endpoint.address().is_unspecified()) return true;
|
if (local_endpoint.address().is_unspecified()) return true;
|
||||||
if (match_addr_mask(addr, local_endpoint.address(), netmask)) return true;
|
if (match_addr_mask(addr, local_endpoint.address(), netmask)) return true;
|
||||||
|
return !(flags & local_network);
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void session_impl::init_peer_class_filter(bool unlimited_local)
|
void session_impl::init_peer_class_filter(bool unlimited_local)
|
||||||
|
@ -1362,7 +1363,7 @@ namespace aux {
|
||||||
session_log("attempting to open listen socket to: %s on device: %s %s%s%s%s"
|
session_log("attempting to open listen socket to: %s on device: %s %s%s%s%s"
|
||||||
, print_endpoint(bind_ep).c_str(), lep.device.c_str()
|
, print_endpoint(bind_ep).c_str(), lep.device.c_str()
|
||||||
, (lep.ssl == transport::ssl) ? "ssl " : ""
|
, (lep.ssl == transport::ssl) ? "ssl " : ""
|
||||||
, (lep.flags & listen_socket_t::has_gateway) ? "has-gateway " : ""
|
, (lep.flags & listen_socket_t::local_network) ? "local-network " : ""
|
||||||
, (lep.flags & listen_socket_t::accept_incoming) ? "accept-incoming " : "no-incoming "
|
, (lep.flags & listen_socket_t::accept_incoming) ? "accept-incoming " : "no-incoming "
|
||||||
, (lep.flags & listen_socket_t::was_expanded) ? "expanded-ip " : "");
|
, (lep.flags & listen_socket_t::was_expanded) ? "expanded-ip " : "");
|
||||||
}
|
}
|
||||||
|
@ -1740,48 +1741,50 @@ namespace aux {
|
||||||
reopen_network_sockets({});
|
reopen_network_sockets({});
|
||||||
}
|
}
|
||||||
|
|
||||||
void session_impl::interface_to_endpoints(std::string const& device, int const port
|
// TODO: could this function be merged with expand_unspecified_addresses?
|
||||||
, transport const ssl, listen_socket_flags_t const flags, std::vector<listen_endpoint_t>& eps)
|
// right now both listen_endpoint_t and listen_interface_t are almost
|
||||||
|
// identical, maybe the latter could be removed too
|
||||||
|
void interface_to_endpoints(listen_interface_t const& iface
|
||||||
|
, listen_socket_flags_t flags
|
||||||
|
, span<ip_interface const> const ifs
|
||||||
|
, span<ip_route const> const routes
|
||||||
|
, std::vector<listen_endpoint_t>& eps)
|
||||||
{
|
{
|
||||||
|
flags |= iface.local ? listen_socket_t::local_network : listen_socket_flags_t{};
|
||||||
|
transport const ssl = iface.ssl ? transport::ssl : transport::plaintext;
|
||||||
|
|
||||||
// First, check to see if it's an IP address
|
// First, check to see if it's an IP address
|
||||||
error_code err;
|
error_code err;
|
||||||
address const adr = make_address(device.c_str(), err);
|
address const adr = make_address(iface.device.c_str(), err);
|
||||||
if (!err)
|
if (!err)
|
||||||
{
|
{
|
||||||
eps.emplace_back(adr, port, std::string(), ssl, flags);
|
eps.emplace_back(adr, iface.port, std::string{}, ssl, flags);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
flags |= listen_socket_t::was_expanded;
|
||||||
|
|
||||||
// this is the case where device names a network device. We need to
|
// this is the case where device names a network device. We need to
|
||||||
// enumerate all IPs associated with this device
|
// enumerate all IPs associated with this device
|
||||||
|
|
||||||
// TODO: 3 only run this once in the caller
|
|
||||||
std::vector<ip_interface> const ifs = enum_net_interfaces(m_io_service, err);
|
|
||||||
if (err)
|
|
||||||
{
|
|
||||||
#ifndef TORRENT_DISABLE_LOGGING
|
|
||||||
if (should_log())
|
|
||||||
{
|
|
||||||
session_log("failed to enumerate IPs on device: \"%s\": %s"
|
|
||||||
, device.c_str(), err.message().c_str());
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
if (m_alerts.should_post<listen_failed_alert>())
|
|
||||||
{
|
|
||||||
m_alerts.emplace_alert<listen_failed_alert>(device
|
|
||||||
, operation_t::enum_if, err
|
|
||||||
, socket_type_t::tcp);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto const& ipface : ifs)
|
for (auto const& ipface : ifs)
|
||||||
{
|
{
|
||||||
// we're looking for a specific interface, and its address
|
// we're looking for a specific interface, and its address
|
||||||
// (which must be of the same family as the address we're
|
// (which must be of the same family as the address we're
|
||||||
// connecting to)
|
// connecting to)
|
||||||
if (device != ipface.name) continue;
|
if (iface.device != ipface.name) continue;
|
||||||
eps.emplace_back(ipface.interface_address, port, device, ssl, flags);
|
|
||||||
|
// record whether the device has a gateway associated with it
|
||||||
|
// (which indicates it can be used to reach the internet)
|
||||||
|
// if the IP address tell us it's loopback or link-local, don't
|
||||||
|
// bother looking for the gateway
|
||||||
|
bool const local = iface.local
|
||||||
|
|| ipface.interface_address.is_loopback()
|
||||||
|
|| is_link_local(ipface.interface_address)
|
||||||
|
|| (!is_global(ipface.interface_address)
|
||||||
|
&& !has_default_route(ipface.name, family(ipface.interface_address), routes));
|
||||||
|
|
||||||
|
eps.emplace_back(ipface.interface_address, iface.port, iface.device
|
||||||
|
, ssl, flags | (local ? listen_socket_t::local_network : listen_socket_flags_t{}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1826,20 +1829,16 @@ namespace aux {
|
||||||
// expand device names and populate eps
|
// expand device names and populate eps
|
||||||
for (auto const& iface : m_listen_interfaces)
|
for (auto const& iface : m_listen_interfaces)
|
||||||
{
|
{
|
||||||
std::string const& device = iface.device;
|
|
||||||
int const port = iface.port;
|
|
||||||
transport const ssl = iface.ssl ? transport::ssl : transport::plaintext;
|
|
||||||
|
|
||||||
#ifndef TORRENT_USE_OPENSSL
|
#ifndef TORRENT_USE_OPENSSL
|
||||||
if (ssl == transport::ssl)
|
if (iface.ssl)
|
||||||
{
|
{
|
||||||
#ifndef TORRENT_DISABLE_LOGGING
|
#ifndef TORRENT_DISABLE_LOGGING
|
||||||
session_log("attempted to listen ssl with no library support on device: \"%s\""
|
session_log("attempted to listen ssl with no library support on device: \"%s\""
|
||||||
, device.c_str());
|
, iface.device.c_str());
|
||||||
#endif
|
#endif
|
||||||
if (m_alerts.should_post<listen_failed_alert>())
|
if (m_alerts.should_post<listen_failed_alert>())
|
||||||
{
|
{
|
||||||
m_alerts.emplace_alert<listen_failed_alert>(device
|
m_alerts.emplace_alert<listen_failed_alert>(iface.device
|
||||||
, operation_t::sock_open
|
, operation_t::sock_open
|
||||||
, boost::asio::error::operation_not_supported
|
, boost::asio::error::operation_not_supported
|
||||||
, socket_type_t::tcp_ssl);
|
, socket_type_t::tcp_ssl);
|
||||||
|
@ -1852,7 +1851,7 @@ namespace aux {
|
||||||
// IP address or a device name. In case it's a device name, we want to
|
// IP address or a device name. In case it's a device name, we want to
|
||||||
// (potentially) end up binding a socket for each IP address associated
|
// (potentially) end up binding a socket for each IP address associated
|
||||||
// with that device.
|
// with that device.
|
||||||
interface_to_endpoints(device, port, ssl, flags, eps);
|
interface_to_endpoints(iface, flags, ifs, routes, eps);
|
||||||
}
|
}
|
||||||
|
|
||||||
// if no listen interfaces are specified, create sockets to use
|
// if no listen interfaces are specified, create sockets to use
|
||||||
|
@ -1865,8 +1864,8 @@ namespace aux {
|
||||||
, listen_socket_flags_t{});
|
, listen_socket_flags_t{});
|
||||||
}
|
}
|
||||||
|
|
||||||
expand_unspecified_address(ifs, eps);
|
expand_unspecified_address(ifs, routes, eps);
|
||||||
expand_devices(ifs, routes, eps);
|
expand_devices(ifs, eps);
|
||||||
|
|
||||||
auto remove_iter = partition_listen_sockets(eps, m_listen_sockets);
|
auto remove_iter = partition_listen_sockets(eps, m_listen_sockets);
|
||||||
|
|
||||||
|
@ -1915,15 +1914,9 @@ namespace aux {
|
||||||
m_listen_sockets.emplace_back(s);
|
m_listen_sockets.emplace_back(s);
|
||||||
|
|
||||||
#ifndef TORRENT_DISABLE_DHT
|
#ifndef TORRENT_DISABLE_DHT
|
||||||
// addresses that we expanded from an
|
|
||||||
// unspecified address don't get a DHT running
|
|
||||||
// on them, unless they have a gateway (in
|
|
||||||
// which case we believe they can reach the
|
|
||||||
// internet)
|
|
||||||
if (m_dht
|
if (m_dht
|
||||||
&& s->ssl != transport::ssl
|
&& s->ssl != transport::ssl
|
||||||
&& ((s->flags & listen_socket_t::has_gateway)
|
&& !(s->flags & listen_socket_t::local_network))
|
||||||
|| !(s->flags & listen_socket_t::was_expanded)))
|
|
||||||
{
|
{
|
||||||
m_dht->new_socket(m_listen_sockets.back());
|
m_dht->new_socket(m_listen_sockets.back());
|
||||||
}
|
}
|
||||||
|
@ -4955,7 +4948,7 @@ namespace aux {
|
||||||
{
|
{
|
||||||
if (is_v4(ls->local_endpoint) != remote_address.is_v4()) continue;
|
if (is_v4(ls->local_endpoint) != remote_address.is_v4()) continue;
|
||||||
if (ls->ssl != ssl) continue;
|
if (ls->ssl != ssl) continue;
|
||||||
if (ls->flags & listen_socket_t::has_gateway)
|
if (!(ls->flags & listen_socket_t::local_network))
|
||||||
with_gateways.push_back(ls);
|
with_gateways.push_back(ls);
|
||||||
|
|
||||||
if (match_addr_mask(ls->local_endpoint.address(), remote_address, ls->netmask))
|
if (match_addr_mask(ls->local_endpoint.address(), remote_address, ls->netmask))
|
||||||
|
@ -5510,7 +5503,7 @@ namespace aux {
|
||||||
if (is_v6(s.local_endpoint) && is_local(s.local_endpoint.address()))
|
if (is_v6(s.local_endpoint) && is_local(s.local_endpoint.address()))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!s.natpmp_mapper && (s.flags & listen_socket_t::has_gateway))
|
if (!s.natpmp_mapper && !(s.flags & listen_socket_t::local_network))
|
||||||
{
|
{
|
||||||
// the natpmp constructor may fail and call the callbacks
|
// the natpmp constructor may fail and call the callbacks
|
||||||
// into the session_impl.
|
// into the session_impl.
|
||||||
|
@ -5783,8 +5776,7 @@ namespace aux {
|
||||||
for (auto& s : m_listen_sockets)
|
for (auto& s : m_listen_sockets)
|
||||||
{
|
{
|
||||||
if (s->ssl != transport::ssl
|
if (s->ssl != transport::ssl
|
||||||
&& ((s->flags & listen_socket_t::has_gateway)
|
&& !(s->flags & listen_socket_t::local_network))
|
||||||
|| !(s->flags & listen_socket_t::was_expanded)))
|
|
||||||
{
|
{
|
||||||
m_dht->new_socket(s);
|
m_dht->new_socket(s);
|
||||||
}
|
}
|
||||||
|
@ -6740,9 +6732,10 @@ namespace aux {
|
||||||
if (is_v6(s.local_endpoint))
|
if (is_v6(s.local_endpoint))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// there's no point in starting the UPnP mapper for a network that doesn't
|
// there's no point in starting the UPnP mapper for a network that isn't
|
||||||
// have a gateway. The whole point is to forward ports through the gateway
|
// connected to the internet. The whole point is to forward ports through
|
||||||
if (!(s.flags & listen_socket_t::has_gateway))
|
// the gateway
|
||||||
|
if (s.flags & listen_socket_t::local_network)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!s.upnp_mapper)
|
if (!s.upnp_mapper)
|
||||||
|
|
|
@ -174,6 +174,7 @@ namespace libtorrent {
|
||||||
ret += ':';
|
ret += ':';
|
||||||
ret += to_string(i.port).data();
|
ret += to_string(i.port).data();
|
||||||
if (i.ssl) ret += 's';
|
if (i.ssl) ret += 's';
|
||||||
|
if (i.local) ret += 'l';
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -211,6 +212,7 @@ namespace libtorrent {
|
||||||
|
|
||||||
listen_interface_t iface;
|
listen_interface_t iface;
|
||||||
iface.ssl = false;
|
iface.ssl = false;
|
||||||
|
iface.local = false;
|
||||||
|
|
||||||
string_view port;
|
string_view port;
|
||||||
if (element.front() == '[')
|
if (element.front() == '[')
|
||||||
|
@ -268,6 +270,7 @@ namespace libtorrent {
|
||||||
switch (c)
|
switch (c)
|
||||||
{
|
{
|
||||||
case 's': iface.ssl = true; break;
|
case 's': iface.ssl = true; break;
|
||||||
|
case 'l': iface.local = true; break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -215,10 +215,6 @@ TORRENT_TEST(get_gateway_basic)
|
||||||
TEST_CHECK(get_gateway(ip("192.168.0.130", "eth1"), routes) == none);
|
TEST_CHECK(get_gateway(ip("192.168.0.130", "eth1"), routes) == none);
|
||||||
TEST_CHECK(get_gateway(ip("2a02::4567", "eth1"), routes) == none);
|
TEST_CHECK(get_gateway(ip("2a02::4567", "eth1"), routes) == none);
|
||||||
|
|
||||||
// the gateway for the route is outside of this local network, it cannot be
|
|
||||||
// used for this network
|
|
||||||
TEST_CHECK(get_gateway(ip("192.168.1.130", "eth0"), routes) == none);
|
|
||||||
|
|
||||||
// for IPv6, the address family and device name matches, so it's a match
|
// for IPv6, the address family and device name matches, so it's a match
|
||||||
TEST_CHECK(get_gateway(ip("2a02:8000::0123:4567", "eth0"), routes) == address::from_string("2a02::1234"));
|
TEST_CHECK(get_gateway(ip("2a02:8000::0123:4567", "eth0"), routes) == address::from_string("2a02::1234"));
|
||||||
}
|
}
|
||||||
|
@ -261,24 +257,34 @@ TORRENT_TEST(get_gateway_loopback)
|
||||||
TEST_CHECK(get_gateway(ip("::1", "lo"), routes) == none);
|
TEST_CHECK(get_gateway(ip("::1", "lo"), routes) == none);
|
||||||
}
|
}
|
||||||
|
|
||||||
TORRENT_TEST(get_gateway_netmask)
|
|
||||||
{
|
|
||||||
std::vector<ip_route> const routes = {
|
|
||||||
rt("0.0.0.0", "eth0", "192.168.1.1", "255.255.255.0"),
|
|
||||||
rt("0.0.0.0", "eth0", "192.168.2.1", "0.0.0.0")
|
|
||||||
};
|
|
||||||
|
|
||||||
TEST_CHECK(get_gateway(ip("192.168.0.130", "eth0"), routes) == address::from_string("192.168.2.1"));
|
|
||||||
TEST_CHECK(get_gateway(ip("192.168.1.130", "eth0"), routes) == address::from_string("192.168.1.1"));
|
|
||||||
}
|
|
||||||
|
|
||||||
TORRENT_TEST(get_gateway_multi_homed)
|
TORRENT_TEST(get_gateway_multi_homed)
|
||||||
{
|
{
|
||||||
std::vector<ip_route> const routes = {
|
std::vector<ip_route> const routes = {
|
||||||
rt("0.0.0.0", "eth0", "192.168.0.1", "255.255.0.0"),
|
rt("0.0.0.0", "eth0", "192.168.0.1", "255.255.0.0"),
|
||||||
rt("0.0.0.0", "eth0", "10.0.0.1", "255.0.0.0")
|
rt("0.0.0.0", "eth1", "10.0.0.1", "255.0.0.0")
|
||||||
};
|
};
|
||||||
|
|
||||||
TEST_CHECK(get_gateway(ip("192.168.0.130", "eth0"), routes) == address::from_string("192.168.0.1"));
|
TEST_CHECK(get_gateway(ip("192.168.0.130", "eth0"), routes) == address::from_string("192.168.0.1"));
|
||||||
TEST_CHECK(get_gateway(ip("10.0.1.130", "eth0"), routes) == address::from_string("10.0.0.1"));
|
TEST_CHECK(get_gateway(ip("10.0.1.130", "eth1"), routes) == address::from_string("10.0.0.1"));
|
||||||
|
}
|
||||||
|
|
||||||
|
TORRENT_TEST(has_default_route)
|
||||||
|
{
|
||||||
|
std::vector<ip_route> const routes = {
|
||||||
|
rt("0.0.0.0", "eth0", "192.168.0.1", "255.255.0.0"),
|
||||||
|
rt("0.0.0.0", "eth1", "0.0.0.0", "255.0.0.0"),
|
||||||
|
rt("127.0.0.0", "lo", "0.0.0.0", "255.0.0.0")
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_CHECK(has_default_route("eth0", AF_INET, routes));
|
||||||
|
TEST_CHECK(!has_default_route("eth0", AF_INET6, routes));
|
||||||
|
|
||||||
|
TEST_CHECK(has_default_route("eth1", AF_INET, routes));
|
||||||
|
TEST_CHECK(!has_default_route("eth1", AF_INET6, routes));
|
||||||
|
|
||||||
|
TEST_CHECK(!has_default_route("lo", AF_INET, routes));
|
||||||
|
TEST_CHECK(!has_default_route("lo", AF_INET6, routes));
|
||||||
|
|
||||||
|
TEST_CHECK(!has_default_route("eth2", AF_INET, routes));
|
||||||
|
TEST_CHECK(!has_default_route("eth2", AF_INET6, routes));
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,6 +32,7 @@ POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
#include "test.hpp"
|
#include "test.hpp"
|
||||||
#include "libtorrent/aux_/session_impl.hpp"
|
#include "libtorrent/aux_/session_impl.hpp"
|
||||||
|
#include "libtorrent/string_util.hpp"
|
||||||
|
|
||||||
using namespace lt;
|
using namespace lt;
|
||||||
|
|
||||||
|
@ -76,6 +77,13 @@ namespace
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
aux::listen_endpoint_t ep(char const* ip, int port
|
||||||
|
, tp ssl, aux::listen_socket_flags_t const flags)
|
||||||
|
{
|
||||||
|
return aux::listen_endpoint_t(address::from_string(ip), port, std::string{}
|
||||||
|
, ssl, flags);
|
||||||
|
}
|
||||||
|
|
||||||
aux::listen_endpoint_t ep(char const* ip, int port
|
aux::listen_endpoint_t ep(char const* ip, int port
|
||||||
, tp ssl = tp::plaintext
|
, tp ssl = tp::plaintext
|
||||||
, std::string device = {})
|
, std::string device = {})
|
||||||
|
@ -92,6 +100,21 @@ namespace
|
||||||
, aux::listen_socket_t::accept_incoming);
|
, aux::listen_socket_t::accept_incoming);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
aux::listen_endpoint_t ep(char const* ip, int port
|
||||||
|
, std::string device
|
||||||
|
, aux::listen_socket_flags_t const flags)
|
||||||
|
{
|
||||||
|
return aux::listen_endpoint_t(address::from_string(ip), port, device
|
||||||
|
, tp::plaintext, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
aux::listen_endpoint_t ep(char const* ip, int port
|
||||||
|
, aux::listen_socket_flags_t const flags)
|
||||||
|
{
|
||||||
|
return aux::listen_endpoint_t(address::from_string(ip), port, std::string{}
|
||||||
|
, tp::plaintext, flags);
|
||||||
|
}
|
||||||
|
|
||||||
std::shared_ptr<aux::listen_socket_t> sock(char const* ip, int const port
|
std::shared_ptr<aux::listen_socket_t> sock(char const* ip, int const port
|
||||||
, int const original_port, char const* device = "")
|
, int const original_port, char const* device = "")
|
||||||
{
|
{
|
||||||
|
@ -242,13 +265,6 @@ TORRENT_TEST(partition_listen_sockets_op_ports)
|
||||||
|
|
||||||
TORRENT_TEST(expand_devices)
|
TORRENT_TEST(expand_devices)
|
||||||
{
|
{
|
||||||
|
|
||||||
// this causes us to only expand IPv6 addresses on eth0
|
|
||||||
std::vector<ip_route> const routes = {
|
|
||||||
rt("0.0.0.0", "eth0", "1.2.3.4"),
|
|
||||||
rt("::", "eth0", "1234:5678::1"),
|
|
||||||
};
|
|
||||||
|
|
||||||
std::vector<ip_interface> const ifs = {
|
std::vector<ip_interface> const ifs = {
|
||||||
ifc("127.0.0.1", "lo", "255.0.0.0")
|
ifc("127.0.0.1", "lo", "255.0.0.0")
|
||||||
, ifc("192.168.1.2", "eth0", "255.255.255.0")
|
, ifc("192.168.1.2", "eth0", "255.255.255.0")
|
||||||
|
@ -273,7 +289,7 @@ TORRENT_TEST(expand_devices)
|
||||||
aux::listen_socket_flags_t{} }
|
aux::listen_socket_flags_t{} }
|
||||||
};
|
};
|
||||||
|
|
||||||
expand_devices(ifs, routes, eps);
|
expand_devices(ifs, eps);
|
||||||
|
|
||||||
TEST_CHECK((eps == std::vector<aux::listen_endpoint_t>{
|
TEST_CHECK((eps == std::vector<aux::listen_endpoint_t>{
|
||||||
{
|
{
|
||||||
|
@ -295,6 +311,12 @@ TORRENT_TEST(expand_devices)
|
||||||
|
|
||||||
TORRENT_TEST(expand_unspecified)
|
TORRENT_TEST(expand_unspecified)
|
||||||
{
|
{
|
||||||
|
// this causes us to only expand IPv6 addresses on eth0
|
||||||
|
std::vector<ip_route> const routes = {
|
||||||
|
rt("0.0.0.0", "eth0", "1.2.3.4"),
|
||||||
|
rt("::", "eth0", "1234:5678::1"),
|
||||||
|
};
|
||||||
|
|
||||||
std::vector<ip_interface> const ifs = {
|
std::vector<ip_interface> const ifs = {
|
||||||
ifc("127.0.0.1", "lo")
|
ifc("127.0.0.1", "lo")
|
||||||
, ifc("192.168.1.2", "eth0")
|
, ifc("192.168.1.2", "eth0")
|
||||||
|
@ -304,28 +326,34 @@ TORRENT_TEST(expand_unspecified)
|
||||||
, ifc("2601:646:c600:a3:d250:99ff:fe0c:9b74", "eth0")
|
, ifc("2601:646:c600:a3:d250:99ff:fe0c:9b74", "eth0")
|
||||||
};
|
};
|
||||||
|
|
||||||
|
aux::listen_socket_flags_t const global = aux::listen_socket_t::accept_incoming
|
||||||
|
| aux::listen_socket_t::was_expanded;
|
||||||
|
aux::listen_socket_flags_t const local = aux::listen_socket_t::accept_incoming
|
||||||
|
| aux::listen_socket_t::was_expanded
|
||||||
|
| aux::listen_socket_t::local_network;
|
||||||
|
|
||||||
auto v4_nossl = ep("0.0.0.0", 6881);
|
auto v4_nossl = ep("0.0.0.0", 6881);
|
||||||
auto v4_ssl = ep("0.0.0.0", 6882, tp::ssl);
|
auto v4_ssl = ep("0.0.0.0", 6882, tp::ssl);
|
||||||
auto v4_loopb_nossl= ep("127.0.0.1", 6881);
|
auto v4_loopb_nossl= ep("127.0.0.1", 6881, local);
|
||||||
auto v4_loopb_ssl = ep("127.0.0.1", 6882, tp::ssl);
|
auto v4_loopb_ssl = ep("127.0.0.1", 6882, tp::ssl, local);
|
||||||
auto v4_g1_nossl = ep("192.168.1.2", 6881);
|
auto v4_g1_nossl = ep("192.168.1.2", 6881, global);
|
||||||
auto v4_g1_ssl = ep("192.168.1.2", 6882, tp::ssl);
|
auto v4_g1_ssl = ep("192.168.1.2", 6882, tp::ssl, global);
|
||||||
auto v4_g2_nossl = ep("24.172.48.90", 6881);
|
auto v4_g2_nossl = ep("24.172.48.90", 6881, global);
|
||||||
auto v4_g2_ssl = ep("24.172.48.90", 6882, tp::ssl);
|
auto v4_g2_ssl = ep("24.172.48.90", 6882, tp::ssl, global);
|
||||||
auto v6_unsp_nossl = ep("::", 6883);
|
auto v6_unsp_nossl = ep("::", 6883, global);
|
||||||
auto v6_unsp_ssl = ep("::", 6884, tp::ssl);
|
auto v6_unsp_ssl = ep("::", 6884, tp::ssl, global);
|
||||||
auto v6_ll_nossl = ep("fe80::d250:99ff:fe0c:9b74", 6883);
|
auto v6_ll_nossl = ep("fe80::d250:99ff:fe0c:9b74", 6883, local);
|
||||||
auto v6_ll_ssl = ep("fe80::d250:99ff:fe0c:9b74", 6884, tp::ssl);
|
auto v6_ll_ssl = ep("fe80::d250:99ff:fe0c:9b74", 6884, tp::ssl, local);
|
||||||
auto v6_g_nossl = ep("2601:646:c600:a3:d250:99ff:fe0c:9b74", 6883);
|
auto v6_g_nossl = ep("2601:646:c600:a3:d250:99ff:fe0c:9b74", 6883, global);
|
||||||
auto v6_g_ssl = ep("2601:646:c600:a3:d250:99ff:fe0c:9b74", 6884, tp::ssl);
|
auto v6_g_ssl = ep("2601:646:c600:a3:d250:99ff:fe0c:9b74", 6884, tp::ssl, global);
|
||||||
auto v6_loopb_ssl = ep("::1", 6884, tp::ssl);
|
auto v6_loopb_ssl = ep("::1", 6884, tp::ssl, local);
|
||||||
auto v6_loopb_nossl= ep("::1", 6883);
|
auto v6_loopb_nossl= ep("::1", 6883, local);
|
||||||
|
|
||||||
std::vector<aux::listen_endpoint_t> eps = {
|
std::vector<aux::listen_endpoint_t> eps = {
|
||||||
v4_nossl, v4_ssl, v6_unsp_nossl, v6_unsp_ssl
|
v4_nossl, v4_ssl, v6_unsp_nossl, v6_unsp_ssl
|
||||||
};
|
};
|
||||||
|
|
||||||
aux::expand_unspecified_address(ifs, eps);
|
aux::expand_unspecified_address(ifs, routes, eps);
|
||||||
|
|
||||||
TEST_EQUAL(eps.size(), 12);
|
TEST_EQUAL(eps.size(), 12);
|
||||||
TEST_CHECK(std::count(eps.begin(), eps.end(), v4_g1_nossl) == 1);
|
TEST_CHECK(std::count(eps.begin(), eps.end(), v4_g1_nossl) == 1);
|
||||||
|
@ -352,7 +380,7 @@ TORRENT_TEST(expand_unspecified)
|
||||||
eps.push_back(v6_unsp_nossl);
|
eps.push_back(v6_unsp_nossl);
|
||||||
eps.push_back(v6_g_nossl_dev);
|
eps.push_back(v6_g_nossl_dev);
|
||||||
|
|
||||||
aux::expand_unspecified_address(ifs, eps);
|
aux::expand_unspecified_address(ifs, routes, eps);
|
||||||
|
|
||||||
TEST_EQUAL(eps.size(), 3);
|
TEST_EQUAL(eps.size(), 3);
|
||||||
TEST_CHECK(std::count(eps.begin(), eps.end(), v6_ll_nossl) == 1);
|
TEST_CHECK(std::count(eps.begin(), eps.end(), v6_ll_nossl) == 1);
|
||||||
|
@ -361,3 +389,59 @@ TORRENT_TEST(expand_unspecified)
|
||||||
TEST_CHECK(std::count(eps.begin(), eps.end(), v6_g_nossl_dev) == 1);
|
TEST_CHECK(std::count(eps.begin(), eps.end(), v6_g_nossl_dev) == 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
std::vector<aux::listen_endpoint_t> to_endpoint(listen_interface_t const& iface
|
||||||
|
, span<ip_interface const> const ifs
|
||||||
|
, span<ip_route const> const routes)
|
||||||
|
{
|
||||||
|
std::vector<aux::listen_endpoint_t> ret;
|
||||||
|
interface_to_endpoints(iface, aux::listen_socket_t::accept_incoming, ifs, routes, ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
using eps = std::vector<aux::listen_endpoint_t>;
|
||||||
|
|
||||||
|
listen_interface_t ift(char const* dev, int const port, bool const ssl = false
|
||||||
|
, bool const local = false)
|
||||||
|
{
|
||||||
|
return {std::string(dev), port, ssl, local};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
using ls = aux::listen_socket_t;
|
||||||
|
|
||||||
|
TORRENT_TEST(interface_to_endpoint)
|
||||||
|
{
|
||||||
|
TEST_CHECK(to_endpoint(ift("10.0.1.1", 6881), {}, {}) == eps{ep("10.0.1.1", 6881)});
|
||||||
|
|
||||||
|
|
||||||
|
std::vector<ip_interface> const ifs = {
|
||||||
|
// this is a global IPv4 address, not a private network
|
||||||
|
ifc("185.0.1.2", "eth0")
|
||||||
|
, ifc("192.168.2.2", "eth1")
|
||||||
|
, ifc("fe80::d250:99ff:fe0c:9b74", "eth0")
|
||||||
|
// this is a global IPv6 address, not a private network
|
||||||
|
, ifc("2601:646:c600:a3:d250:99ff:fe0c:9b74", "eth1")
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_CHECK((to_endpoint(ift("eth0", 1234), ifs, {})
|
||||||
|
== eps{ep("185.0.1.2", 1234, "eth0", ls::was_expanded | ls::accept_incoming)
|
||||||
|
, ep("fe80::d250:99ff:fe0c:9b74", 1234, "eth0", ls::was_expanded | ls::accept_incoming | ls::local_network)}));
|
||||||
|
|
||||||
|
TEST_CHECK((to_endpoint(ift("eth1", 1234), ifs, {})
|
||||||
|
== eps{ep("192.168.2.2", 1234, "eth1", ls::was_expanded | ls::accept_incoming | ls::local_network)
|
||||||
|
, ep("2601:646:c600:a3:d250:99ff:fe0c:9b74", 1234, "eth1", ls::was_expanded | ls::accept_incoming)}));
|
||||||
|
|
||||||
|
std::vector<ip_route> const routes = {
|
||||||
|
rt("0.0.0.0", "eth1", "3.4.5.6"),
|
||||||
|
rt("0.0.0.0", "eth0", "1.2.3.4"),
|
||||||
|
rt("::", "eth0", "1234:5678::1"),
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<ip_interface> const ifs2 = {
|
||||||
|
ifc("10.0.1.1", "eth0")
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_CHECK((to_endpoint(ift("eth0", 1234), ifs2, routes)
|
||||||
|
== eps{ep("10.0.1.1", 1234, "eth0", ls::was_expanded | ls::accept_incoming)}));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -578,7 +578,7 @@ TORRENT_TEST(reopen_network_sockets)
|
||||||
|
|
||||||
settings_pack p = settings();
|
settings_pack p = settings();
|
||||||
p.set_int(settings_pack::alert_mask, alert::all_categories);
|
p.set_int(settings_pack::alert_mask, alert::all_categories);
|
||||||
p.set_str(settings_pack::listen_interfaces, "127.0.0.1:6881");
|
p.set_str(settings_pack::listen_interfaces, "127.0.0.1:6881l");
|
||||||
|
|
||||||
p.set_bool(settings_pack::enable_upnp, true);
|
p.set_bool(settings_pack::enable_upnp, true);
|
||||||
p.set_bool(settings_pack::enable_natpmp, true);
|
p.set_bool(settings_pack::enable_natpmp, true);
|
||||||
|
|
|
@ -377,26 +377,30 @@ TORRENT_TEST(parse_list)
|
||||||
TORRENT_TEST(parse_interface)
|
TORRENT_TEST(parse_interface)
|
||||||
{
|
{
|
||||||
test_parse_interface(" a:4,b:35, c : 1000s, d: 351 ,e \t:42,foobar:1337s\n\r,[2001::1]:6881"
|
test_parse_interface(" a:4,b:35, c : 1000s, d: 351 ,e \t:42,foobar:1337s\n\r,[2001::1]:6881"
|
||||||
, {{"a", 4, false}, {"b", 35, false}, {"c", 1000, true}, {"d", 351, false}
|
, {{"a", 4, false, false}, {"b", 35, false, false}
|
||||||
, {"e", 42, false}, {"foobar", 1337, true}, {"2001::1", 6881, false}}
|
, {"c", 1000, true, false}
|
||||||
|
, {"d", 351, false, false}
|
||||||
|
, {"e", 42, false, false}
|
||||||
|
, {"foobar", 1337, true, false}
|
||||||
|
, {"2001::1", 6881, false, false}}
|
||||||
, {}
|
, {}
|
||||||
, "a:4,b:35,c:1000s,d:351,e:42,foobar:1337s,[2001::1]:6881");
|
, "a:4,b:35,c:1000s,d:351,e:42,foobar:1337s,[2001::1]:6881");
|
||||||
|
|
||||||
// IPv6 address
|
// IPv6 address
|
||||||
test_parse_interface("[2001:ffff::1]:6882s"
|
test_parse_interface("[2001:ffff::1]:6882s"
|
||||||
, {{"2001:ffff::1", 6882, true}}
|
, {{"2001:ffff::1", 6882, true, false}}
|
||||||
, {}
|
, {}
|
||||||
, "[2001:ffff::1]:6882s");
|
, "[2001:ffff::1]:6882s");
|
||||||
|
|
||||||
// IPv4 address
|
// IPv4 address
|
||||||
test_parse_interface("127.0.0.1:6882"
|
test_parse_interface("127.0.0.1:6882"
|
||||||
, {{"127.0.0.1", 6882, false}}
|
, {{"127.0.0.1", 6882, false, false}}
|
||||||
, {}
|
, {}
|
||||||
, "127.0.0.1:6882");
|
, "127.0.0.1:6882");
|
||||||
|
|
||||||
// maximum padding
|
// maximum padding
|
||||||
test_parse_interface(" nic\r\n:\t 12\r s "
|
test_parse_interface(" nic\r\n:\t 12\r s "
|
||||||
, {{"nic", 12, true}}
|
, {{"nic", 12, true, false}}
|
||||||
, {}
|
, {}
|
||||||
, "nic:12s");
|
, "nic:12s");
|
||||||
|
|
||||||
|
@ -409,7 +413,19 @@ TORRENT_TEST(parse_interface)
|
||||||
test_parse_interface("nic s", {}, {"nic s"}, "");
|
test_parse_interface("nic s", {}, {"nic s"}, "");
|
||||||
|
|
||||||
// parse interface with port 0
|
// parse interface with port 0
|
||||||
test_parse_interface("127.0.0.1:0", {{"127.0.0.1", 0, false}}, {}, "127.0.0.1:0");
|
test_parse_interface("127.0.0.1:0", {{"127.0.0.1", 0, false, false}}
|
||||||
|
, {}, "127.0.0.1:0");
|
||||||
|
|
||||||
|
// SSL flag
|
||||||
|
test_parse_interface("127.0.0.1:1234s", {{"127.0.0.1", 1234, true, false}}
|
||||||
|
, {}, "127.0.0.1:1234s");
|
||||||
|
// local flag
|
||||||
|
test_parse_interface("127.0.0.1:1234l", {{"127.0.0.1", 1234, false, true}}
|
||||||
|
, {}, "127.0.0.1:1234l");
|
||||||
|
|
||||||
|
// both
|
||||||
|
test_parse_interface("127.0.0.1:1234ls", {{"127.0.0.1", 1234, true, true}}
|
||||||
|
, {}, "127.0.0.1:1234sl");
|
||||||
|
|
||||||
// IPv6 error
|
// IPv6 error
|
||||||
test_parse_interface("[aaaa::1", {}, {"[aaaa::1"}, "");
|
test_parse_interface("[aaaa::1", {}, {"[aaaa::1"}, "");
|
||||||
|
@ -417,10 +433,10 @@ TORRENT_TEST(parse_interface)
|
||||||
test_parse_interface("[aaaa::1]:", {}, {"[aaaa::1]:"}, "");
|
test_parse_interface("[aaaa::1]:", {}, {"[aaaa::1]:"}, "");
|
||||||
test_parse_interface("[aaaa::1]:s", {}, {"[aaaa::1]:s"}, "");
|
test_parse_interface("[aaaa::1]:s", {}, {"[aaaa::1]:s"}, "");
|
||||||
test_parse_interface("[aaaa::1] :6881", {}, {"[aaaa::1] :6881"}, "");
|
test_parse_interface("[aaaa::1] :6881", {}, {"[aaaa::1] :6881"}, "");
|
||||||
test_parse_interface("[aaaa::1]:6881", {{"aaaa::1", 6881, false}}, {}, "[aaaa::1]:6881");
|
test_parse_interface("[aaaa::1]:6881", {{"aaaa::1", 6881, false, false}}, {}, "[aaaa::1]:6881");
|
||||||
|
|
||||||
// unterminated [
|
// unterminated [
|
||||||
test_parse_interface("[aaaa::1,foobar:0", {{"foobar", 0, false}}, {"[aaaa::1"}, "foobar:0");
|
test_parse_interface("[aaaa::1,foobar:0", {{"foobar", 0, false, false}}, {"[aaaa::1"}, "foobar:0");
|
||||||
|
|
||||||
// multiple errors
|
// multiple errors
|
||||||
test_parse_interface("foo:,bar", {}, {"foo:", "bar"}, "");
|
test_parse_interface("foo:,bar", {}, {"foo:", "bar"}, "");
|
||||||
|
@ -432,7 +448,7 @@ TORRENT_TEST(parse_interface)
|
||||||
test_parse_interface("\"", {}, {"\""}, "");
|
test_parse_interface("\"", {}, {"\""}, "");
|
||||||
|
|
||||||
// multiple errors and one correct
|
// multiple errors and one correct
|
||||||
test_parse_interface("foo,bar,0.0.0.0:6881", {{"0.0.0.0", 6881, false}}, {"foo", "bar"}, "0.0.0.0:6881");
|
test_parse_interface("foo,bar,0.0.0.0:6881", {{"0.0.0.0", 6881, false, false}}, {"foo", "bar"}, "0.0.0.0:6881");
|
||||||
}
|
}
|
||||||
|
|
||||||
TORRENT_TEST(split_string)
|
TORRENT_TEST(split_string)
|
||||||
|
|
Loading…
Reference in New Issue