refactor: tr_address cleanup (#3422)
* refactor: remove tr_address_compare() * refactor: remove tr_address_to_string() * refactor: remove NUM_TR_AF_INET_TYPES * refactor: replace tr_sessionGetPublicAddress with tr_session::getPublicAddress() * refactor: tr_peerIo() takes tr_address by value * refactor: replace tr_sessionIsAddressBlocked with tr_session::isAddressBlocked() * refactor: tr_peerMgrAddIncoming now takes tr_address by value * refactor: replace tr_address_is_valid_for_peers() with tr_address.isValidForPeers() * refactor: tr_netOpenPeerSocket takes tr_address by value * refactor: remove tr_generateAllowedSet() * refactor: setup_sockaddr takes a tr_address by value * refactor: tr_netBindTCP() takes a tr_address by value * refactor: tr_dhtAddNode() takes a tr_address by value * refactor: remove tr_address_from_string() * refactor: rename tr_address.isValidForPeers() to .isValidPeerAddress() * refactor: replace tr_address_from_sockaddr_storage() with tr_address::fromSockaddrStorage() * refactor: minor cleanup to tr_address::readable()
This commit is contained in:
parent
745adf8332
commit
9a44eeaa27
|
@ -184,7 +184,7 @@ void tr_announcerParseHttpAnnounceResponse(tr_announce_response& response, std::
|
|||
{
|
||||
BasicHandler::EndDict(context);
|
||||
|
||||
if (tr_address_is_valid_for_peers(&pex_.addr, pex_.port))
|
||||
if (pex_.addr.isValidPeerAddress(pex_.port))
|
||||
{
|
||||
response_.pex.push_back(pex_);
|
||||
pex_ = {};
|
||||
|
@ -251,7 +251,10 @@ void tr_announcerParseHttpAnnounceResponse(tr_announce_response& response, std::
|
|||
}
|
||||
else if (key == "ip")
|
||||
{
|
||||
tr_address_from_string(&pex_.addr, value);
|
||||
if (auto const addr = tr_address::fromString(value))
|
||||
{
|
||||
pex_.addr = *addr;
|
||||
}
|
||||
}
|
||||
else if (key == "peer id")
|
||||
{
|
||||
|
|
|
@ -155,9 +155,9 @@ bool BlocklistFile::parseLine1(std::string_view line, struct IPv4Range* range)
|
|||
{
|
||||
return false;
|
||||
}
|
||||
if (auto addr = tr_address{}; tr_address_from_string(&addr, line.substr(0, pos)))
|
||||
if (auto const addr = tr_address::fromString(line.substr(0, pos)); addr)
|
||||
{
|
||||
range->begin_ = ntohl(addr.addr.addr4.s_addr);
|
||||
range->begin_ = ntohl(addr->addr.addr4.s_addr);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -166,9 +166,9 @@ bool BlocklistFile::parseLine1(std::string_view line, struct IPv4Range* range)
|
|||
line = line.substr(pos + 1);
|
||||
|
||||
// parse the trailing 'y.y.y.y'
|
||||
if (auto addr = tr_address{}; tr_address_from_string(&addr, line))
|
||||
if (auto const addr = tr_address::fromString(line); addr)
|
||||
{
|
||||
range->end_ = ntohl(addr.addr.addr4.s_addr);
|
||||
range->end_ = ntohl(addr->addr.addr4.s_addr);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -193,9 +193,9 @@ bool BlocklistFile::parseLine2(std::string_view line, struct IPv4Range* range)
|
|||
return false;
|
||||
}
|
||||
|
||||
if (auto addr = tr_address{}; tr_address_from_string(&addr, line.substr(0, pos)))
|
||||
if (auto const addr = tr_address::fromString(line.substr(0, pos)); addr)
|
||||
{
|
||||
range->begin_ = ntohl(addr.addr.addr4.s_addr);
|
||||
range->begin_ = ntohl(addr->addr.addr4.s_addr);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -209,9 +209,9 @@ bool BlocklistFile::parseLine2(std::string_view line, struct IPv4Range* range)
|
|||
return false;
|
||||
}
|
||||
|
||||
if (auto addr = tr_address{}; tr_address_from_string(&addr, line.substr(0, pos)))
|
||||
if (auto const addr = tr_address::fromString(line.substr(0, pos)); addr)
|
||||
{
|
||||
range->end_ = ntohl(addr.addr.addr4.s_addr);
|
||||
range->end_ = ntohl(addr->addr.addr4.s_addr);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -61,86 +61,6 @@ std::string tr_net_strerror(int err)
|
|||
#endif
|
||||
}
|
||||
|
||||
char const* tr_address_and_port_to_string(char* buf, size_t buflen, tr_address const* addr, tr_port port)
|
||||
{
|
||||
char addr_buf[INET6_ADDRSTRLEN];
|
||||
tr_address_to_string_with_buf(addr, addr_buf, sizeof(addr_buf));
|
||||
*fmt::format_to_n(buf, buflen - 1, FMT_STRING("[{:s}]:{:d}"), addr_buf, port.host()).out = '\0';
|
||||
return buf;
|
||||
}
|
||||
|
||||
char const* tr_address_to_string_with_buf(tr_address const* addr, char* buf, size_t buflen)
|
||||
{
|
||||
TR_ASSERT(tr_address_is_valid(addr));
|
||||
|
||||
return addr->type == TR_AF_INET ? evutil_inet_ntop(AF_INET, &addr->addr, buf, buflen) :
|
||||
evutil_inet_ntop(AF_INET6, &addr->addr, buf, buflen);
|
||||
}
|
||||
|
||||
/*
|
||||
* Non-threadsafe version of tr_address_to_string_with_buf()
|
||||
* and uses a static memory area for a buffer.
|
||||
* This function is suitable to be called from libTransmission's networking code,
|
||||
* which is single-threaded.
|
||||
*/
|
||||
char const* tr_address_to_string(tr_address const* addr)
|
||||
{
|
||||
static char buf[INET6_ADDRSTRLEN];
|
||||
return tr_address_to_string_with_buf(addr, buf, sizeof(buf));
|
||||
}
|
||||
|
||||
bool tr_address_from_string(tr_address* dst, char const* src)
|
||||
{
|
||||
if (evutil_inet_pton(AF_INET, src, &dst->addr) == 1)
|
||||
{
|
||||
dst->type = TR_AF_INET;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (evutil_inet_pton(AF_INET6, src, &dst->addr) == 1)
|
||||
{
|
||||
dst->type = TR_AF_INET6;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool tr_address_from_string(tr_address* dst, std::string_view src)
|
||||
{
|
||||
// inet_pton() requires zero-terminated strings,
|
||||
// so make a zero-terminated copy here on the stack.
|
||||
auto buf = std::array<char, TR_ADDRSTRLEN>{};
|
||||
if (std::size(src) >= std::size(buf))
|
||||
{
|
||||
// shouldn't ever be that large; malformed address
|
||||
return false;
|
||||
}
|
||||
|
||||
*std::copy(std::begin(src), std::end(src), std::begin(buf)) = '\0';
|
||||
|
||||
return tr_address_from_string(dst, std::data(buf));
|
||||
}
|
||||
|
||||
/*
|
||||
* Compare two tr_address structures.
|
||||
* Returns:
|
||||
* <0 if a < b
|
||||
* >0 if a > b
|
||||
* 0 if a == b
|
||||
*/
|
||||
int tr_address_compare(tr_address const* a, tr_address const* b) noexcept
|
||||
{
|
||||
// IPv6 addresses are always "greater than" IPv4
|
||||
if (a->type != b->type)
|
||||
{
|
||||
return a->type == TR_AF_INET ? 1 : -1;
|
||||
}
|
||||
|
||||
return a->type == TR_AF_INET ? memcmp(&a->addr.addr4, &b->addr.addr4, sizeof(a->addr.addr4)) :
|
||||
memcmp(&a->addr.addr6.s6_addr, &b->addr.addr6.s6_addr, sizeof(a->addr.addr6.s6_addr));
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* TCP sockets
|
||||
**********************************************************************/
|
||||
|
@ -247,38 +167,36 @@ void tr_netSetCongestionControl([[maybe_unused]] tr_socket_t s, [[maybe_unused]]
|
|||
#endif
|
||||
}
|
||||
|
||||
bool tr_address_from_sockaddr_storage(tr_address* setme_addr, tr_port* setme_port, struct sockaddr_storage const* from)
|
||||
std::optional<std::pair<tr_address, tr_port>> tr_address::fromSockaddrStorage(sockaddr_storage from)
|
||||
{
|
||||
if (from->ss_family == AF_INET)
|
||||
if (from.ss_family == AF_INET)
|
||||
{
|
||||
auto const* const sin = (struct sockaddr_in const*)from;
|
||||
setme_addr->type = TR_AF_INET;
|
||||
setme_addr->addr.addr4.s_addr = sin->sin_addr.s_addr;
|
||||
*setme_port = tr_port::fromNetwork(sin->sin_port);
|
||||
return true;
|
||||
auto const* const sin = reinterpret_cast<sockaddr_in const*>(&from);
|
||||
auto tmp = tr_address{};
|
||||
tmp.type = TR_AF_INET;
|
||||
tmp.addr.addr4.s_addr = sin->sin_addr.s_addr;
|
||||
return std::make_pair(tmp, tr_port::fromNetwork(sin->sin_port));
|
||||
}
|
||||
|
||||
if (from->ss_family == AF_INET6)
|
||||
if (from.ss_family == AF_INET6)
|
||||
{
|
||||
auto const* const sin6 = (struct sockaddr_in6 const*)from;
|
||||
setme_addr->type = TR_AF_INET6;
|
||||
setme_addr->addr.addr6 = sin6->sin6_addr;
|
||||
*setme_port = tr_port::fromNetwork(sin6->sin6_port);
|
||||
return true;
|
||||
auto const* const sin6 = reinterpret_cast<sockaddr_in6 const*>(&from);
|
||||
auto tmp = tr_address{};
|
||||
tmp.type = TR_AF_INET6;
|
||||
tmp.addr.addr6 = sin6->sin6_addr;
|
||||
return std::make_pair(tmp, tr_port::fromNetwork(sin6->sin6_port));
|
||||
}
|
||||
|
||||
return false;
|
||||
return {};
|
||||
}
|
||||
|
||||
static socklen_t setup_sockaddr(tr_address const* addr, tr_port port, struct sockaddr_storage* sockaddr)
|
||||
static socklen_t setup_sockaddr(tr_address addr, tr_port port, struct sockaddr_storage* sockaddr)
|
||||
{
|
||||
TR_ASSERT(tr_address_is_valid(addr));
|
||||
|
||||
if (addr->type == TR_AF_INET)
|
||||
if (addr.type == TR_AF_INET)
|
||||
{
|
||||
sockaddr_in sock4 = {};
|
||||
sock4.sin_family = AF_INET;
|
||||
sock4.sin_addr.s_addr = addr->addr.addr4.s_addr;
|
||||
sock4.sin_addr.s_addr = addr.addr.addr4.s_addr;
|
||||
sock4.sin_port = port.network();
|
||||
memcpy(sockaddr, &sock4, sizeof(sock4));
|
||||
return sizeof(struct sockaddr_in);
|
||||
|
@ -288,7 +206,7 @@ static socklen_t setup_sockaddr(tr_address const* addr, tr_port port, struct soc
|
|||
sock6.sin6_family = AF_INET6;
|
||||
sock6.sin6_port = port.network();
|
||||
sock6.sin6_flowinfo = 0;
|
||||
sock6.sin6_addr = addr->addr.addr6;
|
||||
sock6.sin6_addr = addr.addr.addr6;
|
||||
memcpy(sockaddr, &sock6, sizeof(sock6));
|
||||
return sizeof(struct sockaddr_in6);
|
||||
}
|
||||
|
@ -341,17 +259,15 @@ static tr_socket_t createSocket(tr_session* session, int domain, int type)
|
|||
return sockfd;
|
||||
}
|
||||
|
||||
struct tr_peer_socket tr_netOpenPeerSocket(tr_session* session, tr_address const* addr, tr_port port, bool client_is_seed)
|
||||
struct tr_peer_socket tr_netOpenPeerSocket(tr_session* session, tr_address addr, tr_port port, bool client_is_seed)
|
||||
{
|
||||
TR_ASSERT(tr_address_is_valid(addr));
|
||||
|
||||
if (!tr_address_is_valid_for_peers(addr, port))
|
||||
if (!addr.isValidPeerAddress(port))
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
static auto constexpr Domains = std::array<int, NUM_TR_AF_INET_TYPES>{ AF_INET, AF_INET6 };
|
||||
auto const s = createSocket(session, Domains[addr->type], SOCK_STREAM);
|
||||
static auto constexpr Domains = std::array<int, 2>{ AF_INET, AF_INET6 };
|
||||
auto const s = createSocket(session, Domains[addr.type], SOCK_STREAM);
|
||||
if (s == TR_BAD_SOCKET)
|
||||
{
|
||||
return {};
|
||||
|
@ -372,8 +288,7 @@ struct tr_peer_socket tr_netOpenPeerSocket(tr_session* session, tr_address const
|
|||
socklen_t const addrlen = setup_sockaddr(addr, port, &sock);
|
||||
|
||||
// set source address
|
||||
tr_address const* const source_addr = tr_sessionGetPublicAddress(session, addr->type, nullptr);
|
||||
TR_ASSERT(source_addr != nullptr);
|
||||
auto const [source_addr, is_default_addr] = session->getPublicAddress(addr.type);
|
||||
struct sockaddr_storage source_sock;
|
||||
socklen_t const sourcelen = setup_sockaddr(source_addr, {}, &source_sock);
|
||||
|
||||
|
@ -381,7 +296,7 @@ struct tr_peer_socket tr_netOpenPeerSocket(tr_session* session, tr_address const
|
|||
{
|
||||
tr_logAddWarn(fmt::format(
|
||||
_("Couldn't set source address {address} on {socket}: {error} ({error_code})"),
|
||||
fmt::arg("address", source_addr->readable()),
|
||||
fmt::arg("address", source_addr.readable()),
|
||||
fmt::arg("socket", s),
|
||||
fmt::arg("error", tr_net_strerror(sockerrno)),
|
||||
fmt::arg("error_code", sockerrno)));
|
||||
|
@ -398,12 +313,12 @@ struct tr_peer_socket tr_netOpenPeerSocket(tr_session* session, tr_address const
|
|||
{
|
||||
int const tmperrno = sockerrno;
|
||||
|
||||
if ((tmperrno != ENETUNREACH && tmperrno != EHOSTUNREACH) || addr->type == TR_AF_INET)
|
||||
if ((tmperrno != ENETUNREACH && tmperrno != EHOSTUNREACH) || addr.type == TR_AF_INET)
|
||||
{
|
||||
tr_logAddWarn(fmt::format(
|
||||
_("Couldn't connect socket {socket} to {address}:{port}: {error} ({error_code})"),
|
||||
fmt::arg("socket", s),
|
||||
fmt::arg("address", addr->readable()),
|
||||
fmt::arg("address", addr.readable()),
|
||||
fmt::arg("port", port.host()),
|
||||
fmt::arg("error", tr_net_strerror(tmperrno)),
|
||||
fmt::arg("error_code", tmperrno)));
|
||||
|
@ -416,22 +331,16 @@ struct tr_peer_socket tr_netOpenPeerSocket(tr_session* session, tr_address const
|
|||
ret = tr_peer_socket_tcp_create(s);
|
||||
}
|
||||
|
||||
char addrstr[TR_ADDRSTRLEN];
|
||||
tr_address_and_port_to_string(addrstr, sizeof(addrstr), addr, port);
|
||||
tr_logAddTrace(fmt::format("New OUTGOING connection {} ({})", s, addrstr));
|
||||
tr_logAddTrace(fmt::format("New OUTGOING connection {} ({})", s, addr.readable(port)));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct tr_peer_socket tr_netOpenPeerUTPSocket(
|
||||
tr_session* session,
|
||||
tr_address const* addr,
|
||||
tr_port port,
|
||||
bool /*client_is_seed*/)
|
||||
struct tr_peer_socket tr_netOpenPeerUTPSocket(tr_session* session, tr_address addr, tr_port port, bool /*client_is_seed*/)
|
||||
{
|
||||
auto ret = tr_peer_socket{};
|
||||
|
||||
if (session->utp_context != nullptr && tr_address_is_valid_for_peers(addr, port))
|
||||
if (session->utp_context != nullptr && addr.isValidPeerAddress(port))
|
||||
{
|
||||
struct sockaddr_storage ss;
|
||||
socklen_t const sslen = setup_sockaddr(addr, port, &ss);
|
||||
|
@ -476,14 +385,12 @@ void tr_netClosePeerSocket(tr_session* session, tr_peer_socket socket)
|
|||
}
|
||||
}
|
||||
|
||||
static tr_socket_t tr_netBindTCPImpl(tr_address const* addr, tr_port port, bool suppressMsgs, int* errOut)
|
||||
static tr_socket_t tr_netBindTCPImpl(tr_address addr, tr_port port, bool suppressMsgs, int* errOut)
|
||||
{
|
||||
TR_ASSERT(tr_address_is_valid(addr));
|
||||
|
||||
static int const domains[NUM_TR_AF_INET_TYPES] = { AF_INET, AF_INET6 };
|
||||
static auto constexpr Domains = std::array<int, 2>{ AF_INET, AF_INET6 };
|
||||
struct sockaddr_storage sock;
|
||||
|
||||
auto const fd = socket(domains[addr->type], SOCK_STREAM, 0);
|
||||
auto const fd = socket(Domains[addr.type], SOCK_STREAM, 0);
|
||||
if (fd == TR_BAD_SOCKET)
|
||||
{
|
||||
*errOut = sockerrno;
|
||||
|
@ -503,7 +410,7 @@ static tr_socket_t tr_netBindTCPImpl(tr_address const* addr, tr_port port, bool
|
|||
|
||||
#ifdef IPV6_V6ONLY
|
||||
|
||||
if ((addr->type == TR_AF_INET6) &&
|
||||
if ((addr.type == TR_AF_INET6) &&
|
||||
(setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast<char const*>(&optval), sizeof(optval)) == -1) &&
|
||||
(sockerrno != ENOPROTOOPT)) // if the kernel doesn't support it, ignore it
|
||||
{
|
||||
|
@ -526,7 +433,7 @@ static tr_socket_t tr_netBindTCPImpl(tr_address const* addr, tr_port port, bool
|
|||
err == EADDRINUSE ?
|
||||
_("Couldn't bind port {port} on {address}: {error} ({error_code}) -- Is another copy of Transmission already running?") :
|
||||
_("Couldn't bind port {port} on {address}: {error} ({error_code})"),
|
||||
fmt::arg("address", addr->readable()),
|
||||
fmt::arg("address", addr.readable()),
|
||||
fmt::arg("port", port.host()),
|
||||
fmt::arg("error", tr_net_strerror(err)),
|
||||
fmt::arg("error_code", err)));
|
||||
|
@ -539,7 +446,7 @@ static tr_socket_t tr_netBindTCPImpl(tr_address const* addr, tr_port port, bool
|
|||
|
||||
if (!suppressMsgs)
|
||||
{
|
||||
tr_logAddDebug(fmt::format(FMT_STRING("Bound socket {:d} to port {:d} on {:s}"), fd, port.host(), addr->readable()));
|
||||
tr_logAddDebug(fmt::format(FMT_STRING("Bound socket {:d} to port {:d} on {:s}"), fd, port.host(), addr.readable()));
|
||||
}
|
||||
|
||||
#ifdef TCP_FASTOPEN
|
||||
|
@ -568,7 +475,7 @@ static tr_socket_t tr_netBindTCPImpl(tr_address const* addr, tr_port port, bool
|
|||
return fd;
|
||||
}
|
||||
|
||||
tr_socket_t tr_netBindTCP(tr_address const* addr, tr_port port, bool suppressMsgs)
|
||||
tr_socket_t tr_netBindTCP(tr_address addr, tr_port port, bool suppressMsgs)
|
||||
{
|
||||
int unused = 0;
|
||||
return tr_netBindTCPImpl(addr, port, suppressMsgs, &unused);
|
||||
|
@ -582,7 +489,7 @@ bool tr_net_hasIPv6(tr_port port)
|
|||
if (!alreadyDone)
|
||||
{
|
||||
int err = 0;
|
||||
auto const fd = tr_netBindTCPImpl(&tr_in6addr_any, port, true, &err);
|
||||
auto const fd = tr_netBindTCPImpl(tr_in6addr_any, port, true, &err);
|
||||
|
||||
if (fd != TR_BAD_SOCKET || err != EAFNOSUPPORT) /* we support ipv6 */
|
||||
{
|
||||
|
@ -600,11 +507,9 @@ bool tr_net_hasIPv6(tr_port port)
|
|||
return result;
|
||||
}
|
||||
|
||||
tr_socket_t tr_netAccept(tr_session* session, tr_socket_t listening_sockfd, tr_address* addr, tr_port* port)
|
||||
std::optional<std::tuple<tr_socket_t, tr_address, tr_port>> tr_netAccept(tr_session* session, tr_socket_t listening_sockfd)
|
||||
{
|
||||
TR_ASSERT(tr_isSession(session));
|
||||
TR_ASSERT(addr != nullptr);
|
||||
TR_ASSERT(port != nullptr);
|
||||
|
||||
// accept the incoming connection
|
||||
struct sockaddr_storage sock;
|
||||
|
@ -612,20 +517,21 @@ tr_socket_t tr_netAccept(tr_session* session, tr_socket_t listening_sockfd, tr_a
|
|||
auto const sockfd = accept(listening_sockfd, (struct sockaddr*)&sock, &len);
|
||||
if (sockfd == TR_BAD_SOCKET)
|
||||
{
|
||||
return TR_BAD_SOCKET;
|
||||
return {};
|
||||
}
|
||||
|
||||
// get the address and port,
|
||||
// make the socket unblocking,
|
||||
// and confirm we don't have too many peers
|
||||
if (!tr_address_from_sockaddr_storage(addr, port, &sock) || evutil_make_socket_nonblocking(sockfd) == -1 ||
|
||||
!session->incPeerCount())
|
||||
auto const addrport = tr_address::fromSockaddrStorage(sock);
|
||||
if (!addrport || evutil_make_socket_nonblocking(sockfd) == -1 || !session->incPeerCount())
|
||||
{
|
||||
tr_netCloseSocket(sockfd);
|
||||
return TR_BAD_SOCKET;
|
||||
return {};
|
||||
}
|
||||
|
||||
return sockfd;
|
||||
auto const& [addr, port] = *addrport;
|
||||
return std::make_tuple(sockfd, addr, port);
|
||||
}
|
||||
|
||||
void tr_netCloseSocket(tr_socket_t sockfd)
|
||||
|
@ -800,12 +706,11 @@ unsigned char const* tr_globalIPv6(tr_session const* session)
|
|||
/* We have some sort of address, now make sure that we return
|
||||
our bound address if non-default. */
|
||||
|
||||
bool is_default = false;
|
||||
auto const* ipv6_bindaddr = tr_sessionGetPublicAddress(session, TR_AF_INET6, &is_default);
|
||||
if (ipv6_bindaddr != nullptr && !is_default)
|
||||
auto const [ipv6_bindaddr, is_default] = session->getPublicAddress(TR_AF_INET6);
|
||||
if (!is_default)
|
||||
{
|
||||
/* Explicitly bound. Return that address. */
|
||||
memcpy(ipv6, ipv6_bindaddr->addr.addr6.s6_addr, 16);
|
||||
memcpy(ipv6, ipv6_bindaddr.addr.addr6.s6_addr, 16);
|
||||
}
|
||||
|
||||
return ipv6;
|
||||
|
@ -853,10 +758,10 @@ static bool isMartianAddr(struct tr_address const* a)
|
|||
}
|
||||
}
|
||||
|
||||
bool tr_address_is_valid_for_peers(tr_address const* addr, tr_port port)
|
||||
[[nodiscard]] bool tr_address::isValidPeerAddress(tr_port port) const noexcept
|
||||
{
|
||||
return !std::empty(port) && tr_address_is_valid(addr) && !isIPv6LinkLocalAddress(addr) && !isIPv4MappedAddress(addr) &&
|
||||
!isMartianAddr(addr);
|
||||
return !std::empty(port) && tr_address_is_valid(this) && !isIPv6LinkLocalAddress(this) && !isIPv4MappedAddress(this) &&
|
||||
!isMartianAddr(this);
|
||||
}
|
||||
|
||||
struct tr_peer_socket tr_peer_socket_tcp_create(tr_socket_t const handle)
|
||||
|
@ -891,50 +796,53 @@ std::pair<tr_port, uint8_t const*> tr_port::fromCompact(uint8_t const* compact)
|
|||
|
||||
/// tr_address
|
||||
|
||||
std::optional<tr_address> tr_address::fromString(std::string_view address_str)
|
||||
std::optional<tr_address> tr_address::fromString(std::string_view addrstr)
|
||||
{
|
||||
auto const addrstr_sz = tr_strbuf<char, 64>{ addrstr };
|
||||
auto addr = tr_address{};
|
||||
|
||||
if (!tr_address_from_string(&addr, address_str))
|
||||
if (evutil_inet_pton(AF_INET, addrstr_sz, &addr.addr) == 1)
|
||||
{
|
||||
return {};
|
||||
addr.type = TR_AF_INET;
|
||||
return addr;
|
||||
}
|
||||
|
||||
return addr;
|
||||
if (evutil_inet_pton(AF_INET6, addrstr_sz, &addr.addr) == 1)
|
||||
{
|
||||
addr.type = TR_AF_INET6;
|
||||
return addr;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename OutputIt>
|
||||
OutputIt tr_address::readable(OutputIt out) const
|
||||
std::string_view tr_address::readable(char* out, size_t outlen, tr_port port) const
|
||||
{
|
||||
if (std::empty(port))
|
||||
{
|
||||
return isIPv4() ? evutil_inet_ntop(AF_INET, &addr, out, outlen) : evutil_inet_ntop(AF_INET6, &addr, out, outlen);
|
||||
}
|
||||
|
||||
auto buf = std::array<char, INET6_ADDRSTRLEN>{};
|
||||
tr_address_to_string_with_buf(this, std::data(buf), std::size(buf));
|
||||
return fmt::format_to(out, FMT_STRING("{:s}"), std::data(buf));
|
||||
}
|
||||
|
||||
template char* tr_address::readable<char*>(char*) const;
|
||||
|
||||
std::string tr_address::readable() const
|
||||
{
|
||||
auto buf = std::string{};
|
||||
buf.reserve(INET6_ADDRSTRLEN);
|
||||
this->readable(std::back_inserter(buf));
|
||||
return buf;
|
||||
auto const addr_sv = readable(std::data(buf), std::size(buf));
|
||||
auto const [end, size] = fmt::format_to_n(out, outlen - 1, FMT_STRING("[{:s}]:{:d}"), addr_sv, port.host());
|
||||
return { out, size };
|
||||
}
|
||||
|
||||
template<typename OutputIt>
|
||||
OutputIt tr_address::readable(OutputIt out, tr_port port) const
|
||||
{
|
||||
auto buf = std::array<char, INET6_ADDRSTRLEN>{};
|
||||
tr_address_to_string_with_buf(this, std::data(buf), std::size(buf));
|
||||
return fmt::format_to(out, FMT_STRING("[{:s}]:{:d}"), std::data(buf), port.host());
|
||||
auto addrbuf = std::array<char, TR_ADDRSTRLEN + 16>{};
|
||||
auto const addr_sv = readable(std::data(addrbuf), std::size(addrbuf), port);
|
||||
return std::copy(std::begin(addr_sv), std::end(addr_sv), out);
|
||||
}
|
||||
|
||||
template char* tr_address::readable<char*>(char*, tr_port) const;
|
||||
|
||||
std::string tr_address::readable(tr_port port) const
|
||||
[[nodiscard]] std::string tr_address::readable(tr_port port) const
|
||||
{
|
||||
auto buf = std::string{};
|
||||
buf.reserve(INET6_ADDRSTRLEN + 9);
|
||||
buf.reserve(INET6_ADDRSTRLEN + 16);
|
||||
this->readable(std::back_inserter(buf), port);
|
||||
return buf;
|
||||
}
|
||||
|
@ -966,5 +874,12 @@ std::pair<tr_address, uint8_t const*> tr_address::fromCompact6(uint8_t const* co
|
|||
|
||||
int tr_address::compare(tr_address const& that) const noexcept // <=>
|
||||
{
|
||||
return tr_address_compare(this, &that);
|
||||
// IPv6 addresses are always "greater than" IPv4
|
||||
if (type != that.type)
|
||||
{
|
||||
return type == TR_AF_INET ? 1 : -1;
|
||||
}
|
||||
|
||||
return type == TR_AF_INET ? memcmp(&addr.addr4, &that.addr.addr4, sizeof(addr.addr4)) :
|
||||
memcmp(&addr.addr6.s6_addr, &that.addr.addr6.s6_addr, sizeof(addr.addr6.s6_addr));
|
||||
}
|
||||
|
|
|
@ -66,13 +66,10 @@ enum tr_address_type
|
|||
{
|
||||
TR_AF_INET,
|
||||
TR_AF_INET6,
|
||||
NUM_TR_AF_INET_TYPES
|
||||
};
|
||||
|
||||
struct tr_address;
|
||||
|
||||
[[nodiscard]] int tr_address_compare(tr_address const* a, tr_address const* b) noexcept;
|
||||
|
||||
/**
|
||||
* Literally just a port number.
|
||||
*
|
||||
|
@ -152,19 +149,27 @@ private:
|
|||
struct tr_address
|
||||
{
|
||||
[[nodiscard]] static std::optional<tr_address> fromString(std::string_view str);
|
||||
[[nodiscard]] static std::optional<std::pair<tr_address, tr_port>> fromSockaddrStorage(sockaddr_storage);
|
||||
[[nodiscard]] static std::pair<tr_address, uint8_t const*> fromCompact4(uint8_t const* compact) noexcept;
|
||||
[[nodiscard]] static std::pair<tr_address, uint8_t const*> fromCompact6(uint8_t const* compact) noexcept;
|
||||
|
||||
[[nodiscard]] bool isValidPeerAddress(tr_port) const noexcept;
|
||||
|
||||
// human-readable formatting
|
||||
|
||||
template<typename OutputIt>
|
||||
OutputIt readable(OutputIt out) const;
|
||||
OutputIt readable(OutputIt out, tr_port port = {}) const;
|
||||
std::string_view readable(char* out, size_t outlen, tr_port port = {}) const;
|
||||
[[nodiscard]] std::string readable(tr_port port = {}) const;
|
||||
|
||||
template<typename OutputIt>
|
||||
OutputIt readable(OutputIt out, tr_port) const;
|
||||
[[nodiscard]] constexpr auto isIPv4() const noexcept
|
||||
{
|
||||
return type == TR_AF_INET;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string readable() const;
|
||||
[[nodiscard]] std::string readable(tr_port) const;
|
||||
[[nodiscard]] constexpr auto isIPv6() const noexcept
|
||||
{
|
||||
return type == TR_AF_INET6;
|
||||
}
|
||||
|
||||
// comparisons
|
||||
|
||||
|
@ -196,20 +201,6 @@ struct tr_address
|
|||
extern tr_address const tr_inaddr_any;
|
||||
extern tr_address const tr_in6addr_any;
|
||||
|
||||
char const* tr_address_to_string(tr_address const* addr);
|
||||
|
||||
char const* tr_address_to_string_with_buf(tr_address const* addr, char* buf, size_t buflen);
|
||||
|
||||
char const* tr_address_and_port_to_string(char* buf, size_t buflen, tr_address const* addr, tr_port port);
|
||||
|
||||
bool tr_address_from_string(tr_address* setme, char const* string);
|
||||
|
||||
bool tr_address_from_string(tr_address* dst, std::string_view src);
|
||||
|
||||
bool tr_address_from_sockaddr_storage(tr_address* setme, tr_port* port, struct sockaddr_storage const* src);
|
||||
|
||||
bool tr_address_is_valid_for_peers(tr_address const* addr, tr_port port);
|
||||
|
||||
constexpr bool tr_address_is_valid(tr_address const* a)
|
||||
{
|
||||
return a != nullptr && (a->type == TR_AF_INET || a->type == TR_AF_INET6);
|
||||
|
@ -221,9 +212,9 @@ constexpr bool tr_address_is_valid(tr_address const* a)
|
|||
|
||||
struct tr_session;
|
||||
|
||||
tr_socket_t tr_netBindTCP(tr_address const* addr, tr_port port, bool suppressMsgs);
|
||||
tr_socket_t tr_netBindTCP(tr_address addr, tr_port port, bool suppressMsgs);
|
||||
|
||||
tr_socket_t tr_netAccept(tr_session* session, tr_socket_t bound, tr_address* setme_addr, tr_port* setme_port);
|
||||
std::optional<std::tuple<tr_socket_t, tr_address, tr_port>> tr_netAccept(tr_session*, tr_socket_t listening_sockfd);
|
||||
|
||||
void tr_netSetCongestionControl(tr_socket_t s, char const* algorithm);
|
||||
|
||||
|
|
|
@ -560,7 +560,7 @@ static uint64 utp_callback(utp_callback_arguments* args)
|
|||
static tr_peerIo* tr_peerIoNew(
|
||||
tr_session* session,
|
||||
tr_bandwidth* parent,
|
||||
tr_address const* addr,
|
||||
tr_address addr,
|
||||
tr_port port,
|
||||
time_t current_time,
|
||||
tr_sha1_digest_t const* torrent_hash,
|
||||
|
@ -580,11 +580,11 @@ static tr_peerIo* tr_peerIoNew(
|
|||
|
||||
if (socket.type == TR_PEER_SOCKET_TYPE_TCP)
|
||||
{
|
||||
session->setSocketTOS(socket.handle.tcp, addr->type);
|
||||
session->setSocketTOS(socket.handle.tcp, addr.type);
|
||||
maybeSetCongestionAlgorithm(socket.handle.tcp, session->peerCongestionAlgorithm());
|
||||
}
|
||||
|
||||
auto* io = new tr_peerIo{ session, torrent_hash, is_incoming, *addr, port, is_seed, current_time, parent };
|
||||
auto* io = new tr_peerIo{ session, torrent_hash, is_incoming, addr, port, is_seed, current_time, parent };
|
||||
io->socket = socket;
|
||||
io->bandwidth().setPeer(io);
|
||||
tr_logAddTraceIo(io, fmt::format("bandwidth is {}; its parent is {}", fmt::ptr(&io->bandwidth()), fmt::ptr(parent)));
|
||||
|
@ -631,13 +631,12 @@ void tr_peerIoUtpInit([[maybe_unused]] struct_utp_context* ctx)
|
|||
tr_peerIo* tr_peerIoNewIncoming(
|
||||
tr_session* session,
|
||||
tr_bandwidth* parent,
|
||||
tr_address const* addr,
|
||||
tr_address addr,
|
||||
tr_port port,
|
||||
time_t current_time,
|
||||
struct tr_peer_socket const socket)
|
||||
{
|
||||
TR_ASSERT(session != nullptr);
|
||||
TR_ASSERT(tr_address_is_valid(addr));
|
||||
|
||||
return tr_peerIoNew(session, parent, addr, port, current_time, nullptr, true, false, socket);
|
||||
}
|
||||
|
@ -645,7 +644,7 @@ tr_peerIo* tr_peerIoNewIncoming(
|
|||
tr_peerIo* tr_peerIoNewOutgoing(
|
||||
tr_session* session,
|
||||
tr_bandwidth* parent,
|
||||
tr_address const* addr,
|
||||
tr_address addr,
|
||||
tr_port port,
|
||||
time_t current_time,
|
||||
tr_sha1_digest_t const& torrent_hash,
|
||||
|
@ -653,7 +652,6 @@ tr_peerIo* tr_peerIoNewOutgoing(
|
|||
bool utp)
|
||||
{
|
||||
TR_ASSERT(session != nullptr);
|
||||
TR_ASSERT(tr_address_is_valid(addr));
|
||||
|
||||
auto socket = tr_peer_socket{};
|
||||
|
||||
|
@ -911,7 +909,7 @@ int tr_peerIoReconnect(tr_peerIo* io)
|
|||
io_close_socket(io);
|
||||
|
||||
auto const [addr, port] = io->socketAddress();
|
||||
io->socket = tr_netOpenPeerSocket(session, &addr, port, io->isSeed());
|
||||
io->socket = tr_netOpenPeerSocket(session, addr, port, io->isSeed());
|
||||
|
||||
if (io->socket.type != TR_PEER_SOCKET_TYPE_TCP)
|
||||
{
|
||||
|
|
|
@ -80,7 +80,7 @@ public:
|
|||
tr_session* session_in,
|
||||
tr_sha1_digest_t const* torrent_hash,
|
||||
bool is_incoming,
|
||||
tr_address const& addr,
|
||||
tr_address addr,
|
||||
tr_port port,
|
||||
bool is_seed,
|
||||
time_t current_time,
|
||||
|
@ -244,7 +244,7 @@ void tr_peerIoUtpInit(struct_utp_context* ctx);
|
|||
tr_peerIo* tr_peerIoNewOutgoing(
|
||||
tr_session* session,
|
||||
tr_bandwidth* parent,
|
||||
struct tr_address const* addr,
|
||||
tr_address addr,
|
||||
tr_port port,
|
||||
time_t current_time,
|
||||
tr_sha1_digest_t const& torrent_hash,
|
||||
|
@ -254,7 +254,7 @@ tr_peerIo* tr_peerIoNewOutgoing(
|
|||
tr_peerIo* tr_peerIoNewIncoming(
|
||||
tr_session* session,
|
||||
tr_bandwidth* parent,
|
||||
struct tr_address const* addr,
|
||||
tr_address addr,
|
||||
tr_port port,
|
||||
time_t current_time,
|
||||
struct tr_peer_socket const socket);
|
||||
|
|
|
@ -107,7 +107,7 @@ struct peer_atom
|
|||
return *blocklisted_;
|
||||
}
|
||||
|
||||
auto const value = tr_sessionIsAddressBlocked(session, &addr);
|
||||
auto const value = session->isAddressBlocked(addr);
|
||||
blocklisted_ = value;
|
||||
return value;
|
||||
}
|
||||
|
@ -1175,19 +1175,19 @@ static bool on_handshake_done(tr_handshake_result const& result)
|
|||
return success;
|
||||
}
|
||||
|
||||
void tr_peerMgrAddIncoming(tr_peerMgr* manager, tr_address const* addr, tr_port port, struct tr_peer_socket const socket)
|
||||
void tr_peerMgrAddIncoming(tr_peerMgr* manager, tr_address addr, tr_port port, struct tr_peer_socket const socket)
|
||||
{
|
||||
TR_ASSERT(tr_isSession(manager->session));
|
||||
auto const lock = manager->unique_lock();
|
||||
|
||||
tr_session* session = manager->session;
|
||||
|
||||
if (tr_sessionIsAddressBlocked(session, addr))
|
||||
if (session->isAddressBlocked(addr))
|
||||
{
|
||||
tr_logAddTrace(fmt::format("Banned IP address '{}' tried to connect to us", tr_address_to_string(addr)));
|
||||
tr_logAddTrace(fmt::format("Banned IP address '{}' tried to connect to us", addr.readable()));
|
||||
tr_netClosePeerSocket(session, socket);
|
||||
}
|
||||
else if (manager->incoming_handshakes.contains(*addr))
|
||||
else if (manager->incoming_handshakes.contains(addr))
|
||||
{
|
||||
tr_netClosePeerSocket(session, socket);
|
||||
}
|
||||
|
@ -1198,7 +1198,7 @@ void tr_peerMgrAddIncoming(tr_peerMgr* manager, tr_address const* addr, tr_port
|
|||
|
||||
tr_peerIoUnref(io); /* balanced by the implicit ref in tr_peerIoNewIncoming() */
|
||||
|
||||
manager->incoming_handshakes.add(*addr, handshake);
|
||||
manager->incoming_handshakes.add(addr, handshake);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1224,9 +1224,8 @@ size_t tr_peerMgrAddPex(tr_torrent* tor, uint8_t from, tr_pex const* pex, size_t
|
|||
|
||||
for (tr_pex const* const end = pex + n_pex; pex != end; ++pex)
|
||||
{
|
||||
if (tr_isPex(pex) && /* safeguard against corrupt data */
|
||||
!tr_sessionIsAddressBlocked(s->manager->session, &pex->addr) &&
|
||||
tr_address_is_valid_for_peers(&pex->addr, pex->port))
|
||||
if (tr_isPex(pex) && // safeguard against corrupt data
|
||||
!s->manager->session->isAddressBlocked(pex->addr) && pex->addr.isValidPeerAddress(pex->port))
|
||||
{
|
||||
ensureAtomExists(s, pex->addr, pex->port, pex->flags, from);
|
||||
++n_used;
|
||||
|
@ -1631,7 +1630,7 @@ namespace peer_stat_helpers
|
|||
|
||||
auto const [addr, port] = peer->socketAddress();
|
||||
|
||||
tr_address_to_string_with_buf(&addr, stats.addr, sizeof(stats.addr));
|
||||
addr.readable(stats.addr, sizeof(stats.addr));
|
||||
stats.client = peer->client.c_str();
|
||||
stats.port = port.host();
|
||||
stats.from = atom->fromFirst;
|
||||
|
@ -2754,7 +2753,7 @@ void initiateConnection(tr_peerMgr* mgr, tr_swarm* s, peer_atom& atom)
|
|||
tr_peerIo* const io = tr_peerIoNewOutgoing(
|
||||
mgr->session,
|
||||
&mgr->session->top_bandwidth_,
|
||||
&atom.addr,
|
||||
atom.addr,
|
||||
atom.port,
|
||||
tr_time(),
|
||||
s->tor->infoHash(),
|
||||
|
|
|
@ -124,7 +124,7 @@ void tr_peerMgrClientSentRequests(tr_torrent* torrent, tr_peer* peer, tr_block_s
|
|||
|
||||
size_t tr_peerMgrCountActiveRequestsToPeer(tr_torrent const* torrent, tr_peer const* peer);
|
||||
|
||||
void tr_peerMgrAddIncoming(tr_peerMgr* manager, tr_address const* addr, tr_port port, struct tr_peer_socket const socket);
|
||||
void tr_peerMgrAddIncoming(tr_peerMgr* manager, tr_address addr, tr_port port, struct tr_peer_socket const socket);
|
||||
|
||||
std::vector<tr_pex> tr_peerMgrCompactToPex(void const* compact, size_t compactLen, uint8_t const* added_f, size_t added_f_len);
|
||||
|
||||
|
|
|
@ -985,89 +985,6 @@ static void protocolSendHaveNone(tr_peerMsgsImpl* msgs)
|
|||
msgs->pokeBatchPeriod(ImmediatePriorityIntervalSecs);
|
||||
}
|
||||
|
||||
/**
|
||||
*** ALLOWED FAST SET
|
||||
*** For explanation, see http://www.bittorrent.org/beps/bep_0006.html
|
||||
**/
|
||||
|
||||
#if 0
|
||||
|
||||
size_t tr_generateAllowedSet(tr_piece_index_t* setmePieces, size_t desiredSetSize, size_t pieceCount, uint8_t const* infohash,
|
||||
tr_address const* addr)
|
||||
{
|
||||
TR_ASSERT(setmePieces != nullptr);
|
||||
TR_ASSERT(desiredSetSize <= pieceCount);
|
||||
TR_ASSERT(desiredSetSize != 0);
|
||||
TR_ASSERT(pieceCount != 0);
|
||||
TR_ASSERT(infohash != nullptr);
|
||||
TR_ASSERT(addr != nullptr);
|
||||
|
||||
size_t setSize = 0;
|
||||
|
||||
if (addr->type == TR_AF_INET)
|
||||
{
|
||||
uint8_t w[SHA_DIGEST_LENGTH + 4];
|
||||
uint8_t* walk = w;
|
||||
uint8_t x[SHA_DIGEST_LENGTH];
|
||||
|
||||
uint32_t ui32 = ntohl(htonl(addr->addr.addr4.s_addr) & 0xffffff00); /* (1) */
|
||||
memcpy(w, &ui32, sizeof(uint32_t));
|
||||
walk += sizeof(uint32_t);
|
||||
memcpy(walk, infohash, SHA_DIGEST_LENGTH); /* (2) */
|
||||
walk += SHA_DIGEST_LENGTH;
|
||||
tr_sha1(x, w, walk - w, nullptr); /* (3) */
|
||||
TR_ASSERT(sizeof(w) == walk - w);
|
||||
|
||||
while (setSize < desiredSetSize)
|
||||
{
|
||||
for (int i = 0; i < 5 && setSize < desiredSetSize; ++i) /* (4) */
|
||||
{
|
||||
uint32_t j = i * 4; /* (5) */
|
||||
uint32_t y = ntohl(*(uint32_t*)(x + j)); /* (6) */
|
||||
uint32_t index = y % pieceCount; /* (7) */
|
||||
bool found = false;
|
||||
|
||||
for (size_t k = 0; !found && k < setSize; ++k) /* (8) */
|
||||
{
|
||||
found = setmePieces[k] == index;
|
||||
}
|
||||
|
||||
if (!found)
|
||||
{
|
||||
setmePieces[setSize++] = index; /* (9) */
|
||||
}
|
||||
}
|
||||
|
||||
tr_sha1(x, x, sizeof(x), nullptr); /* (3) */
|
||||
}
|
||||
}
|
||||
|
||||
return setSize;
|
||||
}
|
||||
|
||||
static void updateFastSet(tr_peerMsgs*)
|
||||
{
|
||||
bool const fext = msgs->io->supportsFEXT();
|
||||
bool const peerIsNeedy = msgs->peer->progress < 0.10;
|
||||
|
||||
if (fext && peerIsNeedy && !msgs->haveFastSet)
|
||||
{
|
||||
tr_info const* inf = &msgs->torrent->info;
|
||||
size_t const numwant = std::min(MAX_FAST_SET_SIZE, inf->pieceCount);
|
||||
|
||||
/* build the fast set */
|
||||
msgs->fastsetSize = tr_generateAllowedSet(msgs->fastset, numwant, inf->pieceCount, inf->hash, msgs->io->address());
|
||||
msgs->haveFastSet = true;
|
||||
|
||||
/* send it to the peer */
|
||||
for (size_t i = 0; i < msgs->fastsetSize; ++i)
|
||||
{
|
||||
protocolSendAllowedFast(msgs, msgs->fastset[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
/**
|
||||
*** INTEREST
|
||||
**/
|
||||
|
@ -1902,7 +1819,7 @@ static ReadState readBtMessage(tr_peerMsgsImpl* msgs, struct evbuffer* inbuf, si
|
|||
if (auto const dht_port = tr_port::fromNetwork(nport); !std::empty(dht_port))
|
||||
{
|
||||
msgs->dht_port = dht_port;
|
||||
tr_dhtAddNode(msgs->session, &msgs->io->address(), msgs->dht_port, false);
|
||||
tr_dhtAddNode(msgs->session, msgs->io->address(), msgs->dht_port, false);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
|
|
@ -80,11 +80,4 @@ tr_peerMsgs* tr_peerMsgsNew(
|
|||
tr_peer_callback callback,
|
||||
void* callback_data);
|
||||
|
||||
size_t tr_generateAllowedSet(
|
||||
tr_piece_index_t* setmePieces,
|
||||
size_t desiredSetSize,
|
||||
size_t pieceCount,
|
||||
uint8_t const* infohash,
|
||||
struct tr_address const* addr);
|
||||
|
||||
/* @} */
|
||||
|
|
|
@ -35,10 +35,9 @@ struct tr_peer_socket tr_peer_socket_tcp_create(tr_socket_t const handle);
|
|||
struct tr_peer_socket tr_peer_socket_utp_create(struct UTPSocket* const handle);
|
||||
|
||||
struct tr_session;
|
||||
struct tr_address;
|
||||
|
||||
struct tr_peer_socket tr_netOpenPeerSocket(tr_session* session, tr_address const* addr, tr_port port, bool clientIsSeed);
|
||||
struct tr_peer_socket tr_netOpenPeerSocket(tr_session* session, tr_address addr, tr_port port, bool clientIsSeed);
|
||||
|
||||
struct tr_peer_socket tr_netOpenPeerUTPSocket(tr_session* session, tr_address const* addr, tr_port port, bool clientIsSeed);
|
||||
struct tr_peer_socket tr_netOpenPeerUTPSocket(tr_session* session, tr_address addr, tr_port port, bool clientIsSeed);
|
||||
|
||||
void tr_netClosePeerSocket(tr_session* session, tr_peer_socket socket);
|
||||
|
|
|
@ -96,12 +96,7 @@ static void natPulse(tr_shared* s, bool do_check)
|
|||
fmt::arg("private_port", session->private_peer_port.host())));
|
||||
}
|
||||
|
||||
s->upnpStatus = tr_upnpPulse(
|
||||
s->upnp,
|
||||
private_peer_port,
|
||||
is_enabled,
|
||||
do_check,
|
||||
tr_address_to_string(&session->bind_ipv4->addr));
|
||||
s->upnpStatus = tr_upnpPulse(s->upnp, private_peer_port, is_enabled, do_check, session->bind_ipv4->addr.readable());
|
||||
|
||||
auto const new_status = tr_sharedTraversalStatus(s);
|
||||
|
||||
|
|
|
@ -147,11 +147,11 @@ std::optional<std::string> tr_session::WebMediator::publicAddress() const
|
|||
{
|
||||
for (auto const type : { TR_AF_INET, TR_AF_INET6 })
|
||||
{
|
||||
auto is_default_value = bool{};
|
||||
tr_address const* addr = tr_sessionGetPublicAddress(session_, type, &is_default_value);
|
||||
if (addr != nullptr && !is_default_value)
|
||||
auto const [addr, is_default_value] = session_->getPublicAddress(type);
|
||||
|
||||
if (!is_default_value)
|
||||
{
|
||||
return addr->readable();
|
||||
return addr.readable();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -236,21 +236,15 @@ static void free_incoming_peer_port(tr_session* session)
|
|||
session->bind_ipv6 = nullptr;
|
||||
}
|
||||
|
||||
static void accept_incoming_peer(evutil_socket_t fd, short /*what*/, void* vsession)
|
||||
static void accept_incoming_peer(evutil_socket_t listening_sockfd, short /*what*/, void* vsession)
|
||||
{
|
||||
auto* session = static_cast<tr_session*>(vsession);
|
||||
|
||||
auto clientAddr = tr_address{};
|
||||
auto clientPort = tr_port{};
|
||||
auto const clientSocket = tr_netAccept(session, fd, &clientAddr, &clientPort);
|
||||
|
||||
if (clientSocket != TR_BAD_SOCKET)
|
||||
if (auto const accepted = tr_netAccept(session, listening_sockfd); accepted)
|
||||
{
|
||||
char addrstr[TR_ADDRSTRLEN];
|
||||
tr_address_and_port_to_string(addrstr, sizeof(addrstr), &clientAddr, clientPort);
|
||||
tr_logAddTrace(fmt::format("new incoming connection {} ({})", clientSocket, addrstr));
|
||||
|
||||
tr_peerMgrAddIncoming(session->peerMgr, &clientAddr, clientPort, tr_peer_socket_tcp_create(clientSocket));
|
||||
auto const& [sock, addr, port] = *accepted;
|
||||
tr_logAddTrace(fmt::format("new incoming connection {} ({})", sock, addr.readable(port)));
|
||||
tr_peerMgrAddIncoming(session->peerMgr, addr, port, tr_peer_socket_tcp_create(sock));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -258,7 +252,7 @@ static void open_incoming_peer_port(tr_session* session)
|
|||
{
|
||||
/* bind an ipv4 port to listen for incoming peers... */
|
||||
auto* b = session->bind_ipv4;
|
||||
b->socket = tr_netBindTCP(&b->addr, session->private_peer_port, false);
|
||||
b->socket = tr_netBindTCP(b->addr, session->private_peer_port, false);
|
||||
|
||||
if (b->socket != TR_BAD_SOCKET)
|
||||
{
|
||||
|
@ -270,7 +264,7 @@ static void open_incoming_peer_port(tr_session* session)
|
|||
if (tr_net_hasIPv6(session->private_peer_port))
|
||||
{
|
||||
b = session->bind_ipv6;
|
||||
b->socket = tr_netBindTCP(&b->addr, session->private_peer_port, false);
|
||||
b->socket = tr_netBindTCP(b->addr, session->private_peer_port, false);
|
||||
|
||||
if (b->socket != TR_BAD_SOCKET)
|
||||
{
|
||||
|
@ -280,33 +274,12 @@ static void open_incoming_peer_port(tr_session* session)
|
|||
}
|
||||
}
|
||||
|
||||
tr_address const* tr_sessionGetPublicAddress(tr_session const* session, int tr_af_type, bool* is_default_value)
|
||||
std::pair<tr_address, bool> tr_session::getPublicAddress(tr_address_type type) const
|
||||
{
|
||||
char const* default_value = "";
|
||||
tr_bindinfo const* bindinfo = nullptr;
|
||||
|
||||
switch (tr_af_type)
|
||||
{
|
||||
case TR_AF_INET:
|
||||
bindinfo = session->bind_ipv4;
|
||||
default_value = TR_DEFAULT_BIND_ADDRESS_IPV4;
|
||||
break;
|
||||
|
||||
case TR_AF_INET6:
|
||||
bindinfo = session->bind_ipv6;
|
||||
default_value = TR_DEFAULT_BIND_ADDRESS_IPV6;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (is_default_value != nullptr && bindinfo != nullptr)
|
||||
{
|
||||
*is_default_value = bindinfo->addr.readable() == default_value;
|
||||
}
|
||||
|
||||
return bindinfo != nullptr ? &bindinfo->addr : nullptr;
|
||||
auto const* const bindinfo = type == TR_AF_INET6 ? bind_ipv6 : bind_ipv4;
|
||||
char const* const default_value = type == TR_AF_INET6 ? TR_DEFAULT_BIND_ADDRESS_IPV6 : TR_DEFAULT_BIND_ADDRESS_IPV4;
|
||||
auto const is_default_value = bindinfo->addr.readable() == default_value;
|
||||
return std::make_pair(bindinfo->addr, is_default_value);
|
||||
}
|
||||
|
||||
/***
|
||||
|
@ -464,8 +437,8 @@ void tr_sessionGetSettings(tr_session const* s, tr_variant* d)
|
|||
tr_variantDictAddBool(d, TR_KEY_speed_limit_up_enabled, tr_sessionIsSpeedLimited(s, TR_UP));
|
||||
tr_variantDictAddStr(d, TR_KEY_umask, fmt::format("{:#o}", s->umask));
|
||||
tr_variantDictAddInt(d, TR_KEY_upload_slots_per_torrent, s->uploadSlotsPerTorrent);
|
||||
tr_variantDictAddStr(d, TR_KEY_bind_address_ipv4, tr_address_to_string(&s->bind_ipv4->addr));
|
||||
tr_variantDictAddStr(d, TR_KEY_bind_address_ipv6, tr_address_to_string(&s->bind_ipv6->addr));
|
||||
tr_variantDictAddStr(d, TR_KEY_bind_address_ipv4, s->bind_ipv4->addr.readable());
|
||||
tr_variantDictAddStr(d, TR_KEY_bind_address_ipv6, s->bind_ipv6->addr.readable());
|
||||
tr_variantDictAddBool(d, TR_KEY_start_added_torrents, !tr_sessionGetPaused(s));
|
||||
tr_variantDictAddBool(d, TR_KEY_trash_original_torrent_files, tr_sessionGetDeleteSource(s));
|
||||
tr_variantDictAddInt(d, TR_KEY_anti_brute_force_threshold, tr_sessionGetAntiBruteForceThreshold(s));
|
||||
|
@ -958,19 +931,25 @@ static void sessionSetImpl(struct init_data* const data)
|
|||
|
||||
free_incoming_peer_port(session);
|
||||
|
||||
if (!tr_variantDictFindStrView(settings, TR_KEY_bind_address_ipv4, &sv) || !tr_address_from_string(&b.addr, sv) ||
|
||||
b.addr.type != TR_AF_INET)
|
||||
b.addr = tr_inaddr_any;
|
||||
if (tr_variantDictFindStrView(settings, TR_KEY_bind_address_ipv4, &sv))
|
||||
{
|
||||
b.addr = tr_inaddr_any;
|
||||
if (auto const addr = tr_address::fromString(sv); addr && addr->isIPv4())
|
||||
{
|
||||
b.addr = *addr;
|
||||
}
|
||||
}
|
||||
|
||||
b.socket = TR_BAD_SOCKET;
|
||||
session->bind_ipv4 = static_cast<struct tr_bindinfo*>(tr_memdup(&b, sizeof(struct tr_bindinfo)));
|
||||
|
||||
if (!tr_variantDictFindStrView(settings, TR_KEY_bind_address_ipv6, &sv) || !tr_address_from_string(&b.addr, sv) ||
|
||||
b.addr.type != TR_AF_INET6)
|
||||
b.addr = tr_in6addr_any;
|
||||
if (tr_variantDictFindStrView(settings, TR_KEY_bind_address_ipv6, &sv))
|
||||
{
|
||||
b.addr = tr_in6addr_any;
|
||||
if (auto const addr = tr_address::fromString(sv); addr && addr->isIPv6())
|
||||
{
|
||||
b.addr = *addr;
|
||||
}
|
||||
}
|
||||
|
||||
b.socket = TR_BAD_SOCKET;
|
||||
|
@ -2491,10 +2470,10 @@ size_t tr_blocklistSetContent(tr_session* session, char const* contentFilename)
|
|||
return ruleCount;
|
||||
}
|
||||
|
||||
bool tr_sessionIsAddressBlocked(tr_session const* session, tr_address const* addr)
|
||||
[[nodiscard]] bool tr_session::isAddressBlocked(tr_address addr) const noexcept
|
||||
{
|
||||
auto const& src = session->blocklists;
|
||||
return std::any_of(std::begin(src), std::end(src), [&addr](auto& blocklist) { return blocklist->hasAddress(*addr); });
|
||||
auto const& src = blocklists;
|
||||
return std::any_of(std::begin(src), std::end(src), [&addr](auto& blocklist) { return blocklist->hasAddress(addr); });
|
||||
}
|
||||
|
||||
void tr_blocklistSetURL(tr_session* session, char const* url)
|
||||
|
|
|
@ -255,6 +255,10 @@ public:
|
|||
tr_netSetTOS(sock, peer_socket_tos_, type);
|
||||
}
|
||||
|
||||
[[nodiscard]] std::pair<tr_address, bool /*is default value*/> getPublicAddress(tr_address_type) const;
|
||||
|
||||
[[nodiscard]] bool isAddressBlocked(tr_address) const noexcept;
|
||||
|
||||
[[nodiscard]] constexpr bool incPeerCount() noexcept
|
||||
{
|
||||
if (this->peerCount >= this->peerLimit)
|
||||
|
@ -469,10 +473,6 @@ bool tr_sessionAllowsDHT(tr_session const* session);
|
|||
|
||||
bool tr_sessionAllowsLPD(tr_session const* session);
|
||||
|
||||
bool tr_sessionIsAddressBlocked(tr_session const* session, struct tr_address const* addr);
|
||||
|
||||
struct tr_address const* tr_sessionGetPublicAddress(tr_session const* session, int tr_af_type, bool* is_default_value);
|
||||
|
||||
struct tr_bindsockets* tr_sessionGetBindSockets(tr_session*);
|
||||
|
||||
int tr_sessionCountTorrents(tr_session const* session);
|
||||
|
|
|
@ -203,7 +203,7 @@ static void dht_bootstrap(void* closure)
|
|||
addr.type = TR_AF_INET;
|
||||
memcpy(&addr.addr.addr4, &cl->nodes[i * 6], 4);
|
||||
auto const [port, out] = tr_port::fromCompact(&cl->nodes[i * 6 + 4]);
|
||||
tr_dhtAddNode(cl->session, &addr, port, true);
|
||||
tr_dhtAddNode(cl->session, addr, port, true);
|
||||
}
|
||||
|
||||
if (i < num6 && !bootstrap_done(cl->session, AF_INET6))
|
||||
|
@ -213,7 +213,7 @@ static void dht_bootstrap(void* closure)
|
|||
addr.type = TR_AF_INET6;
|
||||
memcpy(&addr.addr.addr6, &cl->nodes6[i * 18], 16);
|
||||
auto const [port, out] = tr_port::fromCompact(&cl->nodes6[i * 18 + 16]);
|
||||
tr_dhtAddNode(cl->session, &addr, port, true);
|
||||
tr_dhtAddNode(cl->session, addr, port, true);
|
||||
}
|
||||
|
||||
/* Our DHT code is able to take up to 9 nodes in a row without
|
||||
|
@ -528,9 +528,9 @@ tr_port tr_dhtPort(tr_session* ss)
|
|||
return tr_dhtEnabled(ss) ? ss->udp_port : tr_port{};
|
||||
}
|
||||
|
||||
bool tr_dhtAddNode(tr_session* ss, tr_address const* address, tr_port port, bool bootstrap)
|
||||
bool tr_dhtAddNode(tr_session* ss, tr_address address, tr_port port, bool bootstrap)
|
||||
{
|
||||
int af = address->type == TR_AF_INET ? AF_INET : AF_INET6;
|
||||
int af = address.type == TR_AF_INET ? AF_INET : AF_INET6;
|
||||
|
||||
if (!tr_dhtEnabled(ss))
|
||||
{
|
||||
|
@ -545,23 +545,23 @@ bool tr_dhtAddNode(tr_session* ss, tr_address const* address, tr_port port, bool
|
|||
return false;
|
||||
}
|
||||
|
||||
if (address->type == TR_AF_INET)
|
||||
if (address.type == TR_AF_INET)
|
||||
{
|
||||
struct sockaddr_in sin;
|
||||
memset(&sin, 0, sizeof(sin));
|
||||
sin.sin_family = AF_INET;
|
||||
memcpy(&sin.sin_addr, &address->addr.addr4, 4);
|
||||
memcpy(&sin.sin_addr, &address.addr.addr4, 4);
|
||||
sin.sin_port = port.network();
|
||||
dht_ping_node((struct sockaddr*)&sin, sizeof(sin));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (address->type == TR_AF_INET6)
|
||||
if (address.type == TR_AF_INET6)
|
||||
{
|
||||
struct sockaddr_in6 sin6;
|
||||
memset(&sin6, 0, sizeof(sin6));
|
||||
sin6.sin6_family = AF_INET6;
|
||||
memcpy(&sin6.sin6_addr, &address->addr.addr6, 16);
|
||||
memcpy(&sin6.sin6_addr, &address.addr.addr6, 16);
|
||||
sin6.sin6_port = port.network();
|
||||
dht_ping_node((struct sockaddr*)&sin6, sizeof(sin6));
|
||||
return true;
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
#include "transmission.h"
|
||||
|
||||
#include "net.h" // tr_port
|
||||
#include "net.h" // tr_address, tr_port
|
||||
|
||||
enum
|
||||
{
|
||||
|
@ -27,6 +27,6 @@ bool tr_dhtEnabled(tr_session const*);
|
|||
tr_port tr_dhtPort(tr_session*);
|
||||
int tr_dhtStatus(tr_session*, int af, int* setme_nodeCount);
|
||||
char const* tr_dhtPrintableStatus(int status);
|
||||
bool tr_dhtAddNode(tr_session*, tr_address const*, tr_port, bool bootstrap);
|
||||
bool tr_dhtAddNode(tr_session*, tr_address, tr_port, bool bootstrap);
|
||||
void tr_dhtUpkeep(tr_session*);
|
||||
void tr_dhtCallback(unsigned char* buf, int buflen, struct sockaddr* from, socklen_t fromlen, void* sv);
|
||||
|
|
|
@ -290,14 +290,13 @@ void tr_udpInit(tr_session* ss)
|
|||
}
|
||||
else
|
||||
{
|
||||
auto is_default = bool{};
|
||||
tr_address const* public_addr = tr_sessionGetPublicAddress(ss, TR_AF_INET, &is_default);
|
||||
auto const [public_addr, is_default] = ss->getPublicAddress(TR_AF_INET);
|
||||
|
||||
auto sin = sockaddr_in{};
|
||||
sin.sin_family = AF_INET;
|
||||
if (public_addr != nullptr && !is_default)
|
||||
if (!is_default)
|
||||
{
|
||||
memcpy(&sin.sin_addr, &public_addr->addr.addr4, sizeof(struct in_addr));
|
||||
memcpy(&sin.sin_addr, &public_addr.addr.addr4, sizeof(struct in_addr));
|
||||
}
|
||||
|
||||
sin.sin_port = ss->udp_port.network();
|
||||
|
@ -308,7 +307,7 @@ void tr_udpInit(tr_session* ss)
|
|||
auto const error_code = errno;
|
||||
tr_logAddWarn(fmt::format(
|
||||
_("Couldn't bind IPv4 socket {address}: {error} ({error_code})"),
|
||||
fmt::arg("address", public_addr != nullptr ? public_addr->readable(ss->udp_port) : "?"),
|
||||
fmt::arg("address", public_addr.readable(ss->udp_port)),
|
||||
fmt::arg("error", tr_strerror(error_code)),
|
||||
fmt::arg("error_code", error_code)));
|
||||
tr_netCloseSocket(ss->udp_socket);
|
||||
|
|
|
@ -76,28 +76,28 @@ static auto constexpr UtpIntervalUs = int{ 500000 };
|
|||
|
||||
static void utp_on_accept(tr_session* const session, UTPSocket* const s)
|
||||
{
|
||||
struct sockaddr_storage from_storage;
|
||||
auto* const from = (struct sockaddr*)&from_storage;
|
||||
socklen_t fromlen = sizeof(from_storage);
|
||||
tr_address addr;
|
||||
tr_port port;
|
||||
|
||||
if (!tr_sessionIsUTPEnabled(session))
|
||||
{
|
||||
utp_close(s);
|
||||
return;
|
||||
}
|
||||
|
||||
struct sockaddr_storage from_storage;
|
||||
auto* const from = (struct sockaddr*)&from_storage;
|
||||
socklen_t fromlen = sizeof(from_storage);
|
||||
|
||||
utp_getpeername(s, from, &fromlen);
|
||||
|
||||
if (!tr_address_from_sockaddr_storage(&addr, &port, &from_storage))
|
||||
if (auto const addrport = tr_address::fromSockaddrStorage(from_storage); addrport)
|
||||
{
|
||||
auto const& [addr, port] = *addrport;
|
||||
tr_peerMgrAddIncoming(session->peerMgr, addr, port, tr_peer_socket_utp_create(s));
|
||||
}
|
||||
else
|
||||
{
|
||||
tr_logAddWarn(_("Unknown socket family"));
|
||||
utp_close(s);
|
||||
return;
|
||||
}
|
||||
|
||||
tr_peerMgrAddIncoming(session->peerMgr, &addr, port, tr_peer_socket_utp_create(s));
|
||||
}
|
||||
|
||||
static void utp_send_to(
|
||||
|
|
|
@ -240,11 +240,9 @@ enum
|
|||
UPNP_IGD_INVALID = 3
|
||||
};
|
||||
|
||||
static auto* discoverThreadfunc(char* bindaddr)
|
||||
static auto* discoverThreadfunc(std::string bindaddr)
|
||||
{
|
||||
auto* const ret = tr_upnpDiscover(2000, bindaddr);
|
||||
tr_free(bindaddr);
|
||||
return ret;
|
||||
return tr_upnpDiscover(2000, bindaddr.c_str());
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
|
@ -253,17 +251,17 @@ static bool isFutureReady(std::future<T> const& future)
|
|||
return future.wait_for(std::chrono::seconds(0)) == std::future_status::ready;
|
||||
}
|
||||
|
||||
tr_port_forwarding tr_upnpPulse(tr_upnp* handle, tr_port port, bool isEnabled, bool doPortCheck, char const* bindaddr)
|
||||
tr_port_forwarding tr_upnpPulse(tr_upnp* handle, tr_port port, bool isEnabled, bool doPortCheck, std::string bindaddr)
|
||||
{
|
||||
if (isEnabled && handle->state == UpnpState::WILL_DISCOVER)
|
||||
{
|
||||
TR_ASSERT(!handle->discover_future);
|
||||
|
||||
auto task = std::packaged_task<UPNPDev*(char*)>{ discoverThreadfunc };
|
||||
auto task = std::packaged_task<UPNPDev*(std::string)>{ discoverThreadfunc };
|
||||
handle->discover_future = task.get_future();
|
||||
handle->state = UpnpState::DISCOVERING;
|
||||
|
||||
std::thread(std::move(task), tr_strdup(bindaddr)).detach();
|
||||
std::thread(std::move(task), std::move(bindaddr)).detach();
|
||||
}
|
||||
|
||||
if (isEnabled && handle->state == UpnpState::DISCOVERING && handle->discover_future &&
|
||||
|
|
|
@ -18,12 +18,14 @@
|
|||
|
||||
#include "net.h" // tr_port
|
||||
|
||||
#include <string>
|
||||
|
||||
struct tr_upnp;
|
||||
|
||||
tr_upnp* tr_upnpInit(void);
|
||||
|
||||
void tr_upnpClose(tr_upnp*);
|
||||
|
||||
tr_port_forwarding tr_upnpPulse(tr_upnp*, tr_port port, bool isEnabled, bool doPortCheck, char const*);
|
||||
tr_port_forwarding tr_upnpPulse(tr_upnp*, tr_port port, bool isEnabled, bool doPortCheck, std::string addr);
|
||||
|
||||
/* @} */
|
||||
|
|
|
@ -33,8 +33,7 @@ using namespace std::literals;
|
|||
|
||||
bool tr_addressIsIP(char const* str)
|
||||
{
|
||||
tr_address tmp;
|
||||
return tr_address_from_string(&tmp, str);
|
||||
return tr_address::fromString(str).has_value();
|
||||
}
|
||||
|
||||
char const* tr_webGetResponseStr(long code)
|
||||
|
@ -271,16 +270,15 @@ std::string_view getSiteName(std::string_view host)
|
|||
return host;
|
||||
}
|
||||
|
||||
// psl needs a zero-terminated hostname
|
||||
auto const szhost = tr_urlbuf{ host };
|
||||
|
||||
// is it an IP?
|
||||
auto addr = tr_address{};
|
||||
if (tr_address_from_string(&addr, std::data(szhost)))
|
||||
if (auto const addr = tr_address::fromString(host); addr)
|
||||
{
|
||||
return host;
|
||||
}
|
||||
|
||||
// psl needs a zero-terminated hostname
|
||||
auto const szhost = tr_urlbuf{ host };
|
||||
|
||||
// is it a registered name?
|
||||
if (isAsciiNonUpperCase(host))
|
||||
{
|
||||
|
|
|
@ -57,10 +57,10 @@ protected:
|
|||
|
||||
#endif
|
||||
|
||||
bool addressIsBlocked(char const* address_str)
|
||||
bool addressIsBlocked(std::string_view address_str)
|
||||
{
|
||||
struct tr_address addr = {};
|
||||
return !tr_address_from_string(&addr, address_str) || tr_sessionIsAddressBlocked(session_, &addr);
|
||||
auto const addr = tr_address::fromString(address_str);
|
||||
return addr && session_->isAddressBlocked(*addr);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -14,7 +14,6 @@ TEST(PeerMsgs, placeholder)
|
|||
#if 0
|
||||
|
||||
auto infohash = tr_sha1_digest_t{};
|
||||
struct tr_address addr;
|
||||
tr_piece_index_t pieceCount = 1313;
|
||||
size_t numwant;
|
||||
size_t numgot;
|
||||
|
@ -23,15 +22,15 @@ TEST(PeerMsgs, placeholder)
|
|||
|
||||
memset(std::data(infohash), 0xaa, std::size(infohash));
|
||||
|
||||
tr_address_from_string(&addr, "80.4.4.200");
|
||||
auto const addr = tr_address::fromString("80.4.4.200");
|
||||
|
||||
numwant = 7;
|
||||
numgot = tr_generateAllowedSet(buf, numwant, pieceCount, infohash, &addr);
|
||||
numgot = tr_generateAllowedSet(buf, numwant, pieceCount, infohash, *addr);
|
||||
check_uint(numgot, ==, numwant);
|
||||
check_mem(buf, ==, pieces, numgot);
|
||||
|
||||
numwant = 9;
|
||||
numgot = tr_generateAllowedSet(buf, numwant, pieceCount, infohash, &addr);
|
||||
numgot = tr_generateAllowedSet(buf, numwant, pieceCount, infohash, *addr);
|
||||
check_uint(numgot, ==, numwant);
|
||||
check_mem(buf, ==, pieces, numgot);
|
||||
|
||||
|
|
Loading…
Reference in New Issue