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
|
||||
|
||||
1.2.4 release
|
||||
|
|
|
@ -556,3 +556,4 @@ netmask
|
|||
fe80
|
||||
vcpkg
|
||||
leecher
|
||||
6881l
|
||||
|
|
105
docs/manual.rst
105
docs/manual.rst
|
@ -733,12 +733,11 @@ setting.
|
|||
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.
|
||||
|
||||
Each entry 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
|
||||
Each item in ``listen_interfaces`` is an IP address or a device name, followed
|
||||
by a listen port number. Each item (called ``listen_socket_t``) will have the
|
||||
following objects associated with it:
|
||||
|
||||
* 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
|
||||
* ``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
|
||||
have a gateway configured. This typically means the network that's connected to
|
||||
the internet. If there are multiple subnets connected to the internet, they will
|
||||
each have a separate gateway, and separate port mappings.
|
||||
are expected to be externally available (i.e. not "local network"). If there are
|
||||
multiple subnets connected to the internet, they will each have a separate
|
||||
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
|
||||
considered having a gateway if:
|
||||
This section describes the logic for determining whether an address has a
|
||||
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
|
||||
* the gateway configured for the route is inside the local network.
|
||||
* 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.
|
||||
A network is considered having a default route if there is a default route with
|
||||
a matching egress network device name and address family.
|
||||
|
||||
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:
|
||||
|
||||
* 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).
|
||||
* the ``listen_socket_t`` has a gateway associated with it, and the address
|
||||
family matches the destination address.
|
||||
* the ``listen_socket_t`` does not have the "local network" flag set, and the
|
||||
address family matches the destination address.
|
||||
|
||||
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
|
||||
|
@ -829,9 +816,9 @@ tracker announces
|
|||
Trackers are announced to from all network interfaces listening for incoming
|
||||
connections. However, interfaces that cannot be used to reach the tracker, such
|
||||
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
|
||||
addresses will be used as the source address for an announce. Each such entry
|
||||
will also have an announce_endpoint entry associated with it, in the tracker
|
||||
``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 item
|
||||
will also have an announce_endpoint item associated with it, in the tracker
|
||||
list.
|
||||
|
||||
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
|
||||
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
|
||||
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.
|
||||
|
||||
predictive piece announce
|
||||
|
|
|
@ -142,9 +142,10 @@ namespace aux {
|
|||
// we accept incoming connections on this interface
|
||||
static constexpr listen_socket_flags_t accept_incoming = 0_bit;
|
||||
|
||||
// this interface has a gateway associated with it, and can
|
||||
// route to the internet (of the same address family)
|
||||
static constexpr listen_socket_flags_t has_gateway = 1_bit;
|
||||
// this interface was specified to be just the local network. If this flag
|
||||
// is not set, this interface is assumed to have a path to the internet
|
||||
// (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
|
||||
// listen on an unspecified address (either IPv4 or IPv6)
|
||||
|
@ -260,7 +261,11 @@ namespace aux {
|
|||
|
||||
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;
|
||||
|
@ -283,13 +288,20 @@ namespace aux {
|
|||
std::vector<listen_endpoint_t>& eps
|
||||
, 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
|
||||
TORRENT_EXTRA_EXPORT void expand_unspecified_address(
|
||||
span<ip_interface const> ifs
|
||||
, span<ip_route const> routes
|
||||
, std::vector<listen_endpoint_t>& eps);
|
||||
|
||||
TORRENT_EXTRA_EXPORT void expand_devices(span<ip_interface const>
|
||||
, span<ip_route const> routes
|
||||
, std::vector<listen_endpoint_t>& eps);
|
||||
|
||||
// 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
|
||||
, 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;
|
||||
|
||||
// this is a pool allocator for torrent_peer objects
|
||||
|
|
|
@ -48,7 +48,9 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
namespace libtorrent {
|
||||
|
||||
// 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_link_local(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_teredo(address const& addr);
|
||||
|
|
|
@ -87,6 +87,9 @@ namespace libtorrent {
|
|||
TORRENT_EXTRA_EXPORT std::vector<ip_route> enum_routes(io_service& ios
|
||||
, 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)
|
||||
TORRENT_EXTRA_EXPORT bool match_addr_mask(address const& a1
|
||||
, address const& a2, address const& mask);
|
||||
|
@ -101,6 +104,9 @@ namespace libtorrent {
|
|||
TORRENT_EXTRA_EXPORT boost::optional<address> get_gateway(
|
||||
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
|
||||
// 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
|
||||
|
|
|
@ -222,6 +222,11 @@ namespace aux {
|
|||
// a port that has an "s" suffix will accept SSL connections. (note
|
||||
// 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
|
||||
// socket binding succeeds, the listen_succeeded_alert is posted. There
|
||||
// may be multiple failures before a success.
|
||||
|
@ -236,7 +241,13 @@ namespace aux {
|
|||
// ``[::]:0s`` - will accept SSL connections on a port chosen by the
|
||||
// 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.
|
||||
// It can be obtained from "netsh lan show interfaces" command output.
|
||||
|
|
|
@ -84,11 +84,13 @@ namespace libtorrent {
|
|||
std::string device;
|
||||
int port;
|
||||
bool ssl;
|
||||
bool local;
|
||||
friend bool operator==(listen_interface_t const& lhs, listen_interface_t const& rhs)
|
||||
{
|
||||
return lhs.device == rhs.device
|
||||
&& lhs.port == rhs.port
|
||||
&& lhs.ssl == rhs.ssl;
|
||||
&& lhs.ssl == rhs.ssl
|
||||
&& lhs.local == rhs.local;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -53,6 +53,34 @@ namespace libtorrent {
|
|||
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)
|
||||
{
|
||||
if (a.is_v6())
|
||||
|
|
|
@ -249,8 +249,7 @@ namespace {
|
|||
{
|
||||
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
|
||||
&& rt_msg->rtm_table != RT_TABLE_LOCAL))
|
||||
if (!valid_addr_family(rt_msg->rtm_family))
|
||||
return false;
|
||||
|
||||
// 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>
|
||||
|
||||
int family(address const& a) { return a.is_v4() ? AF_INET : AF_INET6; }
|
||||
|
||||
address build_netmask(int prefix_bits, int const family)
|
||||
{
|
||||
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()
|
||||
&& r.destination.is_v4() == iface.interface_address.is_v4()
|
||||
&& !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
|
||||
// device, the source hint may be the only thing telling them
|
||||
// apart
|
||||
&& (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;
|
||||
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> ret;
|
||||
TORRENT_UNUSED(ios);
|
||||
TORRENT_UNUSED(ec);
|
||||
ec.clear();
|
||||
|
||||
#ifdef TORRENT_BUILD_SIMULATOR
|
||||
|
||||
|
|
|
@ -199,7 +199,7 @@ namespace libtorrent {
|
|||
namespace aux {
|
||||
|
||||
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 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
|
||||
// interfaces is enumerated and sockets are created for each of them.
|
||||
void expand_unspecified_address(span<ip_interface const> const ifs
|
||||
, span<ip_route const> const routes
|
||||
, std::vector<listen_endpoint_t>& eps)
|
||||
{
|
||||
auto unspecified_begin = std::partition(eps.begin(), eps.end()
|
||||
|
@ -273,14 +274,23 @@ namespace aux {
|
|||
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
|
||||
, 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
|
||||
, span<ip_route const> const routes
|
||||
, std::vector<listen_endpoint_t>& eps)
|
||||
{
|
||||
for (auto& ep : eps)
|
||||
|
@ -305,13 +315,6 @@ namespace aux {
|
|||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -324,12 +327,10 @@ namespace aux {
|
|||
&& local_endpoint.address().to_v6().scope_id() != addr.to_v6().scope_id())
|
||||
return false;
|
||||
|
||||
if (flags & has_gateway) return true;
|
||||
if (local_endpoint.address() == addr) return true;
|
||||
if (local_endpoint.address().is_unspecified()) return true;
|
||||
if (match_addr_mask(addr, local_endpoint.address(), netmask)) return true;
|
||||
|
||||
return false;
|
||||
return !(flags & local_network);
|
||||
}
|
||||
|
||||
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"
|
||||
, print_endpoint(bind_ep).c_str(), lep.device.c_str()
|
||||
, (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::was_expanded) ? "expanded-ip " : "");
|
||||
}
|
||||
|
@ -1740,48 +1741,50 @@ namespace aux {
|
|||
reopen_network_sockets({});
|
||||
}
|
||||
|
||||
void session_impl::interface_to_endpoints(std::string const& device, int const port
|
||||
, transport const ssl, listen_socket_flags_t const flags, std::vector<listen_endpoint_t>& eps)
|
||||
// TODO: could this function be merged with expand_unspecified_addresses?
|
||||
// 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
|
||||
error_code err;
|
||||
address const adr = make_address(device.c_str(), err);
|
||||
address const adr = make_address(iface.device.c_str(), err);
|
||||
if (!err)
|
||||
{
|
||||
eps.emplace_back(adr, port, std::string(), ssl, flags);
|
||||
eps.emplace_back(adr, iface.port, std::string{}, ssl, flags);
|
||||
}
|
||||
else
|
||||
{
|
||||
flags |= listen_socket_t::was_expanded;
|
||||
|
||||
// this is the case where device names a network device. We need to
|
||||
// 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)
|
||||
{
|
||||
// we're looking for a specific interface, and its address
|
||||
// (which must be of the same family as the address we're
|
||||
// connecting to)
|
||||
if (device != ipface.name) continue;
|
||||
eps.emplace_back(ipface.interface_address, port, device, ssl, flags);
|
||||
if (iface.device != ipface.name) 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 = 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
|
||||
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
|
||||
if (ssl == transport::ssl)
|
||||
if (iface.ssl)
|
||||
{
|
||||
#ifndef TORRENT_DISABLE_LOGGING
|
||||
session_log("attempted to listen ssl with no library support on device: \"%s\""
|
||||
, device.c_str());
|
||||
, iface.device.c_str());
|
||||
#endif
|
||||
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
|
||||
, boost::asio::error::operation_not_supported
|
||||
, 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
|
||||
// (potentially) end up binding a socket for each IP address associated
|
||||
// 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
|
||||
|
@ -1865,8 +1864,8 @@ namespace aux {
|
|||
, listen_socket_flags_t{});
|
||||
}
|
||||
|
||||
expand_unspecified_address(ifs, eps);
|
||||
expand_devices(ifs, routes, eps);
|
||||
expand_unspecified_address(ifs, routes, eps);
|
||||
expand_devices(ifs, eps);
|
||||
|
||||
auto remove_iter = partition_listen_sockets(eps, m_listen_sockets);
|
||||
|
||||
|
@ -1915,15 +1914,9 @@ namespace aux {
|
|||
m_listen_sockets.emplace_back(s);
|
||||
|
||||
#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
|
||||
&& s->ssl != transport::ssl
|
||||
&& ((s->flags & listen_socket_t::has_gateway)
|
||||
|| !(s->flags & listen_socket_t::was_expanded)))
|
||||
&& !(s->flags & listen_socket_t::local_network))
|
||||
{
|
||||
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 (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);
|
||||
|
||||
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()))
|
||||
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
|
||||
// into the session_impl.
|
||||
|
@ -5783,8 +5776,7 @@ namespace aux {
|
|||
for (auto& s : m_listen_sockets)
|
||||
{
|
||||
if (s->ssl != transport::ssl
|
||||
&& ((s->flags & listen_socket_t::has_gateway)
|
||||
|| !(s->flags & listen_socket_t::was_expanded)))
|
||||
&& !(s->flags & listen_socket_t::local_network))
|
||||
{
|
||||
m_dht->new_socket(s);
|
||||
}
|
||||
|
@ -6740,9 +6732,10 @@ namespace aux {
|
|||
if (is_v6(s.local_endpoint))
|
||||
return;
|
||||
|
||||
// there's no point in starting the UPnP mapper for a network that doesn't
|
||||
// have a gateway. The whole point is to forward ports through the gateway
|
||||
if (!(s.flags & listen_socket_t::has_gateway))
|
||||
// there's no point in starting the UPnP mapper for a network that isn't
|
||||
// connected to the internet. The whole point is to forward ports through
|
||||
// the gateway
|
||||
if (s.flags & listen_socket_t::local_network)
|
||||
return;
|
||||
|
||||
if (!s.upnp_mapper)
|
||||
|
|
|
@ -174,6 +174,7 @@ namespace libtorrent {
|
|||
ret += ':';
|
||||
ret += to_string(i.port).data();
|
||||
if (i.ssl) ret += 's';
|
||||
if (i.local) ret += 'l';
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
@ -211,6 +212,7 @@ namespace libtorrent {
|
|||
|
||||
listen_interface_t iface;
|
||||
iface.ssl = false;
|
||||
iface.local = false;
|
||||
|
||||
string_view port;
|
||||
if (element.front() == '[')
|
||||
|
@ -268,6 +270,7 @@ namespace libtorrent {
|
|||
switch (c)
|
||||
{
|
||||
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("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
|
||||
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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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", "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("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 "libtorrent/aux_/session_impl.hpp"
|
||||
#include "libtorrent/string_util.hpp"
|
||||
|
||||
using namespace lt;
|
||||
|
||||
|
@ -76,6 +77,13 @@ namespace
|
|||
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
|
||||
, tp ssl = tp::plaintext
|
||||
, std::string device = {})
|
||||
|
@ -92,6 +100,21 @@ namespace
|
|||
, 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
|
||||
, int const original_port, char const* device = "")
|
||||
{
|
||||
|
@ -242,13 +265,6 @@ TORRENT_TEST(partition_listen_sockets_op_ports)
|
|||
|
||||
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 = {
|
||||
ifc("127.0.0.1", "lo", "255.0.0.0")
|
||||
, ifc("192.168.1.2", "eth0", "255.255.255.0")
|
||||
|
@ -273,7 +289,7 @@ TORRENT_TEST(expand_devices)
|
|||
aux::listen_socket_flags_t{} }
|
||||
};
|
||||
|
||||
expand_devices(ifs, routes, eps);
|
||||
expand_devices(ifs, eps);
|
||||
|
||||
TEST_CHECK((eps == std::vector<aux::listen_endpoint_t>{
|
||||
{
|
||||
|
@ -295,6 +311,12 @@ TORRENT_TEST(expand_devices)
|
|||
|
||||
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 = {
|
||||
ifc("127.0.0.1", "lo")
|
||||
, ifc("192.168.1.2", "eth0")
|
||||
|
@ -304,28 +326,34 @@ TORRENT_TEST(expand_unspecified)
|
|||
, 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_ssl = ep("0.0.0.0", 6882, tp::ssl);
|
||||
auto v4_loopb_nossl= ep("127.0.0.1", 6881);
|
||||
auto v4_loopb_ssl = ep("127.0.0.1", 6882, tp::ssl);
|
||||
auto v4_g1_nossl = ep("192.168.1.2", 6881);
|
||||
auto v4_g1_ssl = ep("192.168.1.2", 6882, tp::ssl);
|
||||
auto v4_g2_nossl = ep("24.172.48.90", 6881);
|
||||
auto v4_g2_ssl = ep("24.172.48.90", 6882, tp::ssl);
|
||||
auto v6_unsp_nossl = ep("::", 6883);
|
||||
auto v6_unsp_ssl = ep("::", 6884, tp::ssl);
|
||||
auto v6_ll_nossl = ep("fe80::d250:99ff:fe0c:9b74", 6883);
|
||||
auto v6_ll_ssl = ep("fe80::d250:99ff:fe0c:9b74", 6884, tp::ssl);
|
||||
auto v6_g_nossl = ep("2601:646:c600:a3:d250:99ff:fe0c:9b74", 6883);
|
||||
auto v6_g_ssl = ep("2601:646:c600:a3:d250:99ff:fe0c:9b74", 6884, tp::ssl);
|
||||
auto v6_loopb_ssl = ep("::1", 6884, tp::ssl);
|
||||
auto v6_loopb_nossl= ep("::1", 6883);
|
||||
auto v4_loopb_nossl= ep("127.0.0.1", 6881, local);
|
||||
auto v4_loopb_ssl = ep("127.0.0.1", 6882, tp::ssl, local);
|
||||
auto v4_g1_nossl = ep("192.168.1.2", 6881, global);
|
||||
auto v4_g1_ssl = ep("192.168.1.2", 6882, tp::ssl, global);
|
||||
auto v4_g2_nossl = ep("24.172.48.90", 6881, global);
|
||||
auto v4_g2_ssl = ep("24.172.48.90", 6882, tp::ssl, global);
|
||||
auto v6_unsp_nossl = ep("::", 6883, global);
|
||||
auto v6_unsp_ssl = ep("::", 6884, tp::ssl, global);
|
||||
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, local);
|
||||
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, global);
|
||||
auto v6_loopb_ssl = ep("::1", 6884, tp::ssl, local);
|
||||
auto v6_loopb_nossl= ep("::1", 6883, local);
|
||||
|
||||
std::vector<aux::listen_endpoint_t> eps = {
|
||||
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_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_g_nossl_dev);
|
||||
|
||||
aux::expand_unspecified_address(ifs, eps);
|
||||
aux::expand_unspecified_address(ifs, routes, eps);
|
||||
|
||||
TEST_EQUAL(eps.size(), 3);
|
||||
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);
|
||||
}
|
||||
|
||||
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();
|
||||
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_natpmp, true);
|
||||
|
|
|
@ -377,26 +377,30 @@ TORRENT_TEST(parse_list)
|
|||
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"
|
||||
, {{"a", 4, false}, {"b", 35, false}, {"c", 1000, true}, {"d", 351, false}
|
||||
, {"e", 42, false}, {"foobar", 1337, true}, {"2001::1", 6881, false}}
|
||||
, {{"a", 4, false, false}, {"b", 35, false, 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");
|
||||
|
||||
// IPv6 address
|
||||
test_parse_interface("[2001:ffff::1]:6882s"
|
||||
, {{"2001:ffff::1", 6882, true}}
|
||||
, {{"2001:ffff::1", 6882, true, false}}
|
||||
, {}
|
||||
, "[2001:ffff::1]:6882s");
|
||||
|
||||
// IPv4 address
|
||||
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");
|
||||
|
||||
// maximum padding
|
||||
test_parse_interface(" nic\r\n:\t 12\r s "
|
||||
, {{"nic", 12, true}}
|
||||
, {{"nic", 12, true, false}}
|
||||
, {}
|
||||
, "nic:12s");
|
||||
|
||||
|
@ -409,7 +413,19 @@ TORRENT_TEST(parse_interface)
|
|||
test_parse_interface("nic s", {}, {"nic s"}, "");
|
||||
|
||||
// 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
|
||||
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]:s", {}, {"[aaaa::1]:s"}, "");
|
||||
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 [
|
||||
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
|
||||
test_parse_interface("foo:,bar", {}, {"foo:", "bar"}, "");
|
||||
|
@ -432,7 +448,7 @@ TORRENT_TEST(parse_interface)
|
|||
test_parse_interface("\"", {}, {"\""}, "");
|
||||
|
||||
// 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)
|
||||
|
|
Loading…
Reference in New Issue