// This file Copyright © 2010-2022 Transmission authors and contributors. // It may be used under the MIT (SPDX: MIT) license. // License text can be found in the licenses/ folder. #include #include #include #include #include #include #include #include // std::back_inserter #include #include // std::pair #include #ifdef _WIN32 #include #else #include /* TCP_CONGESTION */ #endif #include #include #include #include "transmission.h" #include "log.h" #include "net.h" #include "peer-socket.h" #include "session.h" #include "tr-assert.h" #include "tr-macros.h" #include "tr-utp.h" #include "utils.h" #include "variant.h" #ifndef IN_MULTICAST #define IN_MULTICAST(a) (((a)&0xf0000000) == 0xe0000000) #endif std::string tr_net_strerror(int err) { #ifdef _WIN32 auto buf = std::array{}; auto const len = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, nullptr, err, 0, std::data(buf), std::size(buf), nullptr); return std::string{ tr_strvStrip(std::data(buf)) }; #else return std::string{ tr_strerror(err) }; #endif } /*********************************************************************** * TCP sockets **********************************************************************/ [[nodiscard]] std::optional tr_tos_t::from_string(std::string_view name) { auto const needle = tr_strlower(tr_strvStrip(name)); for (auto const& [value, key] : Names) { if (needle == key) { return tr_tos_t(value); } } if (auto value = tr_parseNum(needle); value) { return tr_tos_t(*value); } return {}; } std::string tr_tos_t::toString() const { for (auto const& [value, key] : Names) { if (value_ == value) { return std::string{ key }; } } return std::to_string(value_); } void tr_netSetTOS([[maybe_unused]] tr_socket_t s, [[maybe_unused]] int tos, tr_address_type type) { if (s == TR_BAD_SOCKET) { return; } if (type == TR_AF_INET) { #if defined(IP_TOS) && !defined(_WIN32) if (setsockopt(s, IPPROTO_IP, IP_TOS, (void const*)&tos, sizeof(tos)) == -1) { tr_logAddDebug(fmt::format("Can't set TOS '{}': {}", tos, tr_net_strerror(sockerrno))); } #endif } else if (type == TR_AF_INET6) { #if defined(IPV6_TCLASS) && !defined(_WIN32) if (setsockopt(s, IPPROTO_IPV6, IPV6_TCLASS, (void const*)&tos, sizeof(tos)) == -1) { tr_logAddDebug(fmt::format("Can't set IPv6 QoS '{}': {}", tos, tr_net_strerror(sockerrno))); } #endif } else { /* program should never reach here! */ tr_logAddDebug("Something goes wrong while setting TOS/Traffic-Class"); } } void tr_netSetCongestionControl([[maybe_unused]] tr_socket_t s, [[maybe_unused]] char const* algorithm) { #ifdef TCP_CONGESTION if (setsockopt(s, IPPROTO_TCP, TCP_CONGESTION, (void const*)algorithm, strlen(algorithm) + 1) == -1) { tr_logAddDebug(fmt::format("Can't set congestion control algorithm '{}': {}", algorithm, tr_net_strerror(sockerrno))); } #endif } static tr_socket_t createSocket(tr_session* session, int domain, int type) { TR_ASSERT(session != nullptr); auto const sockfd = socket(domain, type, 0); if (sockfd == TR_BAD_SOCKET) { if (sockerrno != EAFNOSUPPORT) { tr_logAddWarn(fmt::format( _("Couldn't create socket: {error} ({error_code})"), fmt::arg("error", tr_net_strerror(sockerrno)), fmt::arg("error_code", sockerrno))); } return TR_BAD_SOCKET; } if ((evutil_make_socket_nonblocking(sockfd) == -1) || !session->incPeerCount()) { tr_netClose(session, sockfd); return TR_BAD_SOCKET; } if (static bool buf_logged = false; !buf_logged) { int i = 0; socklen_t size = sizeof(i); if (getsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, reinterpret_cast(&i), &size) != -1) { tr_logAddTrace(fmt::format("SO_SNDBUF size is {}", i)); } i = 0; size = sizeof(i); if (getsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, reinterpret_cast(&i), &size) != -1) { tr_logAddTrace(fmt::format("SO_RCVBUF size is {}", i)); } buf_logged = true; } return sockfd; } tr_peer_socket tr_netOpenPeerSocket(tr_session* session, tr_address const& addr, tr_port port, bool client_is_seed) { TR_ASSERT(addr.is_valid()); if (!session->allowsTCP()) { return {}; } if (!addr.is_valid_for_peers(port)) { return {}; } static auto constexpr Domains = std::array{ AF_INET, AF_INET6 }; auto const s = createSocket(session, Domains[addr.type], SOCK_STREAM); if (s == TR_BAD_SOCKET) { return {}; } // seeds don't need a big read buffer, so make it smaller if (client_is_seed) { int n = 8192; if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, reinterpret_cast(&n), sizeof(n)) == -1) { tr_logAddDebug(fmt::format("Unable to set SO_RCVBUF on socket {}: {}", s, tr_net_strerror(sockerrno))); } } auto const [sock, addrlen] = addr.to_sockaddr(port); // set source address auto const [source_addr, is_default_addr] = session->publicAddress(addr.type); auto const [source_sock, sourcelen] = source_addr.to_sockaddr({}); if (bind(s, reinterpret_cast(&source_sock), sourcelen) == -1) { tr_logAddWarn(fmt::format( _("Couldn't set source address {address} on {socket}: {error} ({error_code})"), fmt::arg("address", source_addr.display_name()), fmt::arg("socket", s), fmt::arg("error", tr_net_strerror(sockerrno)), fmt::arg("error_code", sockerrno))); tr_netClose(session, s); return {}; } auto ret = tr_peer_socket{}; if (connect(s, reinterpret_cast(&sock), addrlen) == -1 && #ifdef _WIN32 sockerrno != WSAEWOULDBLOCK && #endif sockerrno != EINPROGRESS) { if (auto const tmperrno = sockerrno; (tmperrno != ENETUNREACH && tmperrno != EHOSTUNREACH) || addr.is_ipv4()) { tr_logAddWarn(fmt::format( _("Couldn't connect socket {socket} to {address}:{port}: {error} ({error_code})"), fmt::arg("socket", s), fmt::arg("address", addr.display_name()), fmt::arg("port", port.host()), fmt::arg("error", tr_net_strerror(tmperrno)), fmt::arg("error_code", tmperrno))); } tr_netClose(session, s); } else { ret = tr_peer_socket{ session, addr, port, s }; } tr_logAddTrace(fmt::format("New OUTGOING connection {} ({})", s, addr.display_name(port))); return ret; } static tr_socket_t tr_netBindTCPImpl(tr_address const& addr, tr_port port, bool suppress_msgs, int* err_out) { TR_ASSERT(addr.is_valid()); static auto constexpr Domains = std::array{ AF_INET, AF_INET6 }; auto const fd = socket(Domains[addr.type], SOCK_STREAM, 0); if (fd == TR_BAD_SOCKET) { *err_out = sockerrno; return TR_BAD_SOCKET; } if (evutil_make_socket_nonblocking(fd) == -1) { *err_out = sockerrno; tr_netCloseSocket(fd); return TR_BAD_SOCKET; } int optval = 1; (void)setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, reinterpret_cast(&optval), sizeof(optval)); (void)setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast(&optval), sizeof(optval)); #ifdef IPV6_V6ONLY if (addr.is_ipv6() && (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast(&optval), sizeof(optval)) == -1) && (sockerrno != ENOPROTOOPT)) // if the kernel doesn't support it, ignore it { *err_out = sockerrno; tr_netCloseSocket(fd); return TR_BAD_SOCKET; } #endif auto const [sock, addrlen] = addr.to_sockaddr(port); if (bind(fd, (struct sockaddr*)&sock, addrlen) == -1) { int const err = sockerrno; if (!suppress_msgs) { tr_logAddError(fmt::format( 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.display_name()), fmt::arg("port", port.host()), fmt::arg("error", tr_net_strerror(err)), fmt::arg("error_code", err))); } tr_netCloseSocket(fd); *err_out = err; return TR_BAD_SOCKET; } if (!suppress_msgs) { tr_logAddDebug(fmt::format(FMT_STRING("Bound socket {:d} to port {:d} on {:s}"), fd, port.host(), addr.display_name())); } #ifdef TCP_FASTOPEN #ifndef SOL_TCP #define SOL_TCP IPPROTO_TCP #endif optval = 5; (void)setsockopt(fd, SOL_TCP, TCP_FASTOPEN, reinterpret_cast(&optval), sizeof(optval)); #endif #ifdef _WIN32 if (listen(fd, SOMAXCONN) == -1) #else /* _WIN32 */ /* Listen queue backlog will be capped to the operating system's limit. */ if (listen(fd, INT_MAX) == -1) #endif /* _WIN32 */ { *err_out = sockerrno; tr_netCloseSocket(fd); return TR_BAD_SOCKET; } return fd; } tr_socket_t tr_netBindTCP(tr_address const& addr, tr_port port, bool suppress_msgs) { int unused = 0; return tr_netBindTCPImpl(addr, port, suppress_msgs, &unused); } bool tr_net_hasIPv6(tr_port port) { static bool result = false; static bool already_done = false; if (!already_done) { int err = 0; auto const fd = tr_netBindTCPImpl(tr_address::any_ipv4(), port, true, &err); if (fd != TR_BAD_SOCKET || err != EAFNOSUPPORT) /* we support ipv6 */ { result = true; } if (fd != TR_BAD_SOCKET) { tr_netCloseSocket(fd); } already_done = true; } return result; } std::optional> tr_netAccept(tr_session* session, tr_socket_t listening_sockfd) { TR_ASSERT(session != nullptr); // accept the incoming connection auto sock = sockaddr_storage{}; socklen_t len = sizeof(struct sockaddr_storage); auto const sockfd = accept(listening_sockfd, (struct sockaddr*)&sock, &len); if (sockfd == TR_BAD_SOCKET) { return {}; } // get the address and port, // make the socket unblocking, // and confirm we don't have too many peers auto const addrport = tr_address::from_sockaddr(reinterpret_cast(&sock)); if (!addrport || evutil_make_socket_nonblocking(sockfd) == -1 || !session->incPeerCount()) { tr_netCloseSocket(sockfd); return {}; } return std::make_tuple(addrport->first, addrport->second, sockfd); } void tr_netCloseSocket(tr_socket_t sockfd) { evutil_closesocket(sockfd); } void tr_netClose(tr_session* session, tr_socket_t sockfd) { tr_netCloseSocket(sockfd); session->decPeerCount(); } // code in global_ipv6_herlpers is written by Juliusz Chroboczek // and is covered under the same license as dht.cc. // Please feel free to copy them into your software if it can help // unbreaking the double-stack Internet. namespace global_ipv6_helpers { /* Get the source address used for a given destination address. Since there is no official interface to get this information, we create a connected UDP socket (connected UDP... hmm...) and check its source address. */ [[nodiscard]] int get_source_address(struct sockaddr const* dst, socklen_t dst_len, struct sockaddr* src, socklen_t* src_len) { tr_socket_t const s = socket(dst->sa_family, SOCK_DGRAM, 0); if (s == TR_BAD_SOCKET) { return -1; } // since it's a UDP socket, this doesn't actually send any packets if (connect(s, dst, dst_len) == 0 && getsockname(s, src, src_len) == 0) { evutil_closesocket(s); return 0; } auto const save = errno; evutil_closesocket(s); errno = save; return -1; } [[nodiscard]] int global_address(int af, void* addr, int* addr_len) { auto ss = sockaddr_storage{}; socklen_t sslen = sizeof(ss); auto sin = sockaddr_in{}; auto sin6 = sockaddr_in6{}; struct sockaddr const* sa = nullptr; socklen_t salen = 0; switch (af) { case AF_INET: memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; evutil_inet_pton(AF_INET, "91.121.74.28", &sin.sin_addr); sin.sin_port = htons(6969); sa = (struct sockaddr const*)&sin; salen = sizeof(sin); break; case AF_INET6: memset(&sin6, 0, sizeof(sin6)); sin6.sin6_family = AF_INET6; /* In order for address selection to work right, this should be a native IPv6 address, not Teredo or 6to4. */ evutil_inet_pton(AF_INET6, "2001:1890:1112:1::20", &sin6.sin6_addr); sin6.sin6_port = htons(6969); sa = (struct sockaddr const*)&sin6; salen = sizeof(sin6); break; default: return -1; } if (int const rc = get_source_address(sa, salen, (struct sockaddr*)&ss, &sslen); rc < 0) { return -1; } // We all hate NATs. if (auto const tmp = tr_address::from_sockaddr(reinterpret_cast(&ss)); !tmp || !tmp->first.is_global_unicast_address()) { return -1; } switch (af) { case AF_INET: if (*addr_len < 4) { return -1; } memcpy(addr, &((struct sockaddr_in*)&ss)->sin_addr, 4); *addr_len = 4; return 1; case AF_INET6: if (*addr_len < 16) { return -1; } memcpy(addr, &((struct sockaddr_in6*)&ss)->sin6_addr, 16); *addr_len = 16; return 1; default: return -1; } } } // namespace global_ipv6_helpers /* Return our global IPv6 address, with caching. */ std::optional tr_globalIPv6(tr_session const* session) { using namespace global_ipv6_helpers; static auto ipv6 = in6_addr{}; static time_t last_time = 0; static bool have_ipv6 = false; /* Re-check every half hour */ if (auto const now = tr_time(); last_time < now - 1800) { int addrlen = sizeof(ipv6); int const rc = global_address(AF_INET6, &ipv6, &addrlen); have_ipv6 = rc >= 0 && addrlen == sizeof(ipv6); last_time = now; } if (!have_ipv6) { return {}; // no IPv6 address at all } // Return the default address. // This is useful for checking for connectivity in general. if (session == nullptr) { return ipv6; } // We have some sort of address. // Now make sure that we return our bound address if non-default. auto const [ipv6_bindaddr, is_default] = session->publicAddress(TR_AF_INET6); if (!is_default) { // return this explicitly-bound address ipv6 = ipv6_bindaddr.addr.addr6; } return ipv6; } /*** **** **** ***/ namespace is_valid_for_peers_helpers { [[nodiscard]] constexpr auto is_ipv4_mapped_address(tr_address const* addr) { return addr->is_ipv6() && IN6_IS_ADDR_V4MAPPED(&addr->addr.addr6); } [[nodiscard]] constexpr auto is_ipv6_link_local_address(tr_address const* addr) { return addr->is_ipv6() && IN6_IS_ADDR_LINKLOCAL(&addr->addr.addr6); } /* isMartianAddr was written by Juliusz Chroboczek, and is covered under the same license as third-party/dht/dht.c. */ [[nodiscard]] auto is_martian_addr(tr_address const& addr) { static auto constexpr Zeroes = std::array{}; switch (addr.type) { case TR_AF_INET: { auto const* const address = (unsigned char const*)&addr.addr.addr4; return address[0] == 0 || address[0] == 127 || (address[0] & 0xE0) == 0xE0; } case TR_AF_INET6: { auto const* const address = (unsigned char const*)&addr.addr.addr6; return address[0] == 0xFF || (memcmp(address, std::data(Zeroes), 15) == 0 && (address[15] == 0 || address[15] == 1)); } default: return true; } } } // namespace is_valid_for_peers_helpers bool tr_address::is_valid_for_peers(tr_port port) const noexcept { using namespace is_valid_for_peers_helpers; return is_valid() && !std::empty(port) && !is_ipv6_link_local_address(this) && !is_ipv4_mapped_address(this) && !is_martian_addr(*this); } /// tr_port std::pair tr_port::fromCompact(std::byte const* compact) noexcept { static auto constexpr PortLen = size_t{ 2 }; static_assert(PortLen == sizeof(uint16_t)); auto nport = uint16_t{}; std::copy_n(compact, PortLen, reinterpret_cast(&nport)); compact += PortLen; return std::make_pair(tr_port::fromNetwork(nport), compact); } /// tr_address std::optional tr_address::from_string(std::string_view address_sv) { auto const address_sz = tr_strbuf{ address_sv }; auto addr = tr_address{}; if (evutil_inet_pton(AF_INET, address_sz, &addr.addr) == 1) { addr.type = TR_AF_INET; return addr; } if (evutil_inet_pton(AF_INET6, address_sz, &addr.addr) == 1) { addr.type = TR_AF_INET6; return addr; } return {}; } std::string_view tr_address::display_name(char* out, size_t outlen, tr_port port) const { if (std::empty(port)) { return is_ipv4() ? evutil_inet_ntop(AF_INET, &addr, out, outlen) : evutil_inet_ntop(AF_INET6, &addr, out, outlen); } auto buf = std::array{}; auto const addr_sv = display_name(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 OutputIt tr_address::display_name(OutputIt out, tr_port port) const { auto addrbuf = std::array{}; auto const addr_sv = display_name(std::data(addrbuf), std::size(addrbuf), port); return std::copy(std::begin(addr_sv), std::end(addr_sv), out); } template char* tr_address::display_name(char*, tr_port) const; [[nodiscard]] std::string tr_address::display_name(tr_port port) const { auto buf = std::string{}; buf.reserve(INET6_ADDRSTRLEN + 16); this->display_name(std::back_inserter(buf), port); return buf; } std::pair tr_address::from_compact_ipv4(std::byte const* compact) noexcept { static auto constexpr Addr4Len = size_t{ 4 }; auto address = tr_address{}; static_assert(sizeof(address.addr.addr4) == Addr4Len); address.type = TR_AF_INET; std::copy_n(compact, Addr4Len, reinterpret_cast(&address.addr)); compact += Addr4Len; return std::make_pair(address, compact); } std::pair tr_address::from_compact_ipv6(std::byte const* compact) noexcept { static auto constexpr Addr6Len = size_t{ 16 }; auto address = tr_address{}; address.type = TR_AF_INET6; std::copy_n(compact, Addr6Len, reinterpret_cast(&address.addr.addr6.s6_addr)); compact += Addr6Len; return std::make_pair(address, compact); } std::optional> tr_address::from_sockaddr(struct sockaddr const* from) { if (from == nullptr) { return {}; } if (from->sa_family == AF_INET) { auto const* const sin = reinterpret_cast(from); auto addr = tr_address{}; addr.type = TR_AF_INET; addr.addr.addr4 = sin->sin_addr; return std::make_pair(addr, tr_port::fromNetwork(sin->sin_port)); } if (from->sa_family == AF_INET6) { auto const* const sin6 = reinterpret_cast(from); auto addr = tr_address{}; addr.type = TR_AF_INET6; addr.addr.addr6 = sin6->sin6_addr; return std::make_pair(addr, tr_port::fromNetwork(sin6->sin6_port)); } return {}; } std::pair tr_address::to_sockaddr(tr_port port) const noexcept { auto ss = sockaddr_storage{}; if (is_ipv4()) { auto* const ss4 = reinterpret_cast(&ss); ss4->sin_addr = addr.addr4; ss4->sin_family = AF_INET; ss4->sin_port = port.network(); return { ss, sizeof(sockaddr_in) }; } auto* const ss6 = reinterpret_cast(&ss); ss6->sin6_addr = addr.addr6; ss6->sin6_family = AF_INET6; ss6->sin6_flowinfo = 0; ss6->sin6_port = port.network(); return { ss, sizeof(sockaddr_in6) }; } static 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->is_ipv4() ? 1 : -1; } return a->is_ipv4() ? 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)); } int tr_address::compare(tr_address const& that) const noexcept // <=> { return tr_address_compare(this, &that); } // https://en.wikipedia.org/wiki/Reserved_IP_addresses [[nodiscard]] bool tr_address::is_global_unicast_address() const noexcept { if (is_ipv4()) { auto const* const a = reinterpret_cast(&addr.addr4.s_addr); // [0.0.0.0–0.255.255.255] // Current network. if (a[0] == 0) { return false; } // [10.0.0.0 – 10.255.255.255] // Used for local communications within a private network. if (a[0] == 10) { return false; } // [100.64.0.0–100.127.255.255] // Shared address space for communications between a service provider // and its subscribers when using a carrier-grade NAT. if ((a[0] == 100) && (64 <= a[1] && a[1] <= 127)) { return false; } // [169.254.0.0–169.254.255.255] // Used for link-local addresses[5] between two hosts on a single link // when no IP address is otherwise specified, such as would have // normally been retrieved from a DHCP server. if (a[0] == 169 && a[1] == 254) { return false; } // [172.16.0.0–172.31.255.255] // Used for local communications within a private network. if ((a[0] == 172) && (16 <= a[1] && a[1] <= 31)) { return false; } // [192.0.0.0–192.0.0.255] // IETF Protocol Assignments. if (a[0] == 192 && a[1] == 0 && a[2] == 0) { return false; } // [192.0.2.0–192.0.2.255] // Assigned as TEST-NET-1, documentation and examples. if (a[0] == 192 && a[1] == 0 && a[2] == 2) { return false; } // [192.88.99.0–192.88.99.255] // Reserved. Formerly used for IPv6 to IPv4 relay. if (a[0] == 192 && a[1] == 88 && a[2] == 99) { return false; } // [192.168.0.0–192.168.255.255] // Used for local communications within a private network. if (a[0] == 192 && a[1] == 168) { return false; } // [198.18.0.0–198.19.255.255] // Used for benchmark testing of inter-network communications // between two separate subnets. if (a[0] == 198 && (18 <= a[1] && a[1] <= 19)) { return false; } // [198.51.100.0–198.51.100.255] // Assigned as TEST-NET-2, documentation and examples. if (a[0] == 198 && a[1] == 51 && a[2] == 100) { return false; } // [203.0.113.0–203.0.113.255] // Assigned as TEST-NET-3, documentation and examples. if (a[0] == 203 && a[1] == 0 && a[2] == 113) { return false; } // [224.0.0.0–239.255.255.255] // In use for IP multicast. (Former Class D network.) if (224 <= a[0] && a[0] <= 230) { return false; } // [233.252.0.0-233.252.0.255] // Assigned as MCAST-TEST-NET, documentation and examples. if (a[0] == 233 && a[1] == 252 && a[2] == 0) { return false; } // [240.0.0.0–255.255.255.254] // Reserved for future use. (Former Class E network.) // [255.255.255.255] // Reserved for the "limited broadcast" destination address. if (240 <= a[0]) { return false; } return true; } if (is_ipv6()) { auto const* const a = addr.addr6.s6_addr; // TODO: 2000::/3 is commonly used for global unicast but technically // other spaces would be allowable too, so we should test those here. // See RFC 4291 in the Section 2.4 lising global unicast as everything // that's not link-local, multicast, loopback, or unspecified. return (a[0] & 0xE0) == 0x20; } return false; }