diff --git a/libtransmission/blocklist.cc b/libtransmission/blocklist.cc index 53b25c35c..1fc51bc81 100644 --- a/libtransmission/blocklist.cc +++ b/libtransmission/blocklist.cc @@ -372,14 +372,16 @@ void Blocklist::ensureLoaded() const { // bad binary file; try to rebuild it in.close(); - auto src_file = std::string_view{ bin_file_ }; - src_file.remove_suffix(std::size(BinFileSuffix)); - rules_ = parseFile(src_file); - if (!std::empty(rules_)) + auto const sz_src_file = std::string{ std::data(bin_file_), std::size(bin_file_) - std::size(BinFileSuffix) }; + if (tr_sys_path_exists(sz_src_file)) { - tr_logAddInfo(_("Rewriting old blocklist file format to new format")); - tr_sys_path_remove(bin_file_); - save(bin_file_, std::data(rules_), std::size(rules_)); + rules_ = parseFile(sz_src_file); + if (!std::empty(rules_)) + { + tr_logAddInfo(_("Rewriting old blocklist file format to new format")); + tr_sys_path_remove(bin_file_); + save(bin_file_, std::data(rules_), std::size(rules_)); + } } return; } diff --git a/libtransmission/net.cc b/libtransmission/net.cc index 50a382e1c..a42524911 100644 --- a/libtransmission/net.cc +++ b/libtransmission/net.cc @@ -43,10 +43,6 @@ #define IN_MULTICAST(a) (((a)&0xf0000000) == 0xe0000000) #endif -tr_address const tr_in6addr_any = { TR_AF_INET6, { IN6ADDR_ANY_INIT } }; - -tr_address const tr_inaddr_any = { TR_AF_INET, { { { { INADDR_ANY } } } } }; - std::string tr_net_strerror(int err) { #ifdef _WIN32 @@ -392,13 +388,13 @@ void tr_netClosePeerSocket(tr_session* session, tr_peer_socket socket) } } -static tr_socket_t tr_netBindTCPImpl(tr_address const* addr, tr_port port, bool suppress_msgs, int* err_out) +static tr_socket_t tr_netBindTCPImpl(tr_address const& addr, tr_port port, bool suppress_msgs, int* err_out) { - TR_ASSERT(tr_address_is_valid(addr)); + TR_ASSERT(tr_address_is_valid(&addr)); static auto constexpr Domains = std::array{ AF_INET, AF_INET6 }; - 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) { *err_out = sockerrno; @@ -418,7 +414,7 @@ static tr_socket_t tr_netBindTCPImpl(tr_address const* addr, tr_port port, bool #ifdef IPV6_V6ONLY - if (addr->isIPv6() && + if (addr.isIPv6() && (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast(&optval), sizeof(optval)) == -1) && (sockerrno != ENOPROTOOPT)) // if the kernel doesn't support it, ignore it { @@ -429,7 +425,7 @@ static tr_socket_t tr_netBindTCPImpl(tr_address const* addr, tr_port port, bool #endif - auto const [sock, addrlen] = addr->toSockaddr(port); + auto const [sock, addrlen] = addr.toSockaddr(port); if (bind(fd, (struct sockaddr*)&sock, addrlen) == -1) { @@ -441,7 +437,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))); @@ -454,7 +450,7 @@ static tr_socket_t tr_netBindTCPImpl(tr_address const* addr, tr_port port, bool if (!suppress_msgs) { - 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 @@ -483,7 +479,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 suppress_msgs) +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); @@ -497,7 +493,7 @@ bool tr_net_hasIPv6(tr_port port) if (!already_done) { int err = 0; - auto const fd = tr_netBindTCPImpl(&tr_in6addr_any, port, true, &err); + auto const fd = tr_netBindTCPImpl(tr_address::AnyIPv4(), port, true, &err); if (fd != TR_BAD_SOCKET || err != EAFNOSUPPORT) /* we support ipv6 */ { diff --git a/libtransmission/net.h b/libtransmission/net.h index af7e1bb9a..d84546e70 100644 --- a/libtransmission/net.h +++ b/libtransmission/net.h @@ -263,10 +263,17 @@ struct tr_address struct in6_addr addr6; struct in_addr addr4; } addr; -}; -extern tr_address const tr_inaddr_any; -extern tr_address const tr_in6addr_any; + [[nodiscard]] static auto constexpr AnyIPv4() noexcept + { + return tr_address{ TR_AF_INET, { { { { INADDR_ANY } } } } }; + } + + [[nodiscard]] static auto constexpr AnyIPv6() noexcept + { + return tr_address{ TR_AF_INET6, { IN6ADDR_ANY_INIT } }; + } +}; bool tr_address_is_valid_for_peers(tr_address const* addr, tr_port port); @@ -281,7 +288,7 @@ 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 suppress_msgs); +tr_socket_t tr_netBindTCP(tr_address const& addr, tr_port port, bool suppress_msgs); [[nodiscard]] std::optional> tr_netAccept( tr_session* session, diff --git a/libtransmission/port-forwarding.cc b/libtransmission/port-forwarding.cc index 03e7df477..161a8bf75 100644 --- a/libtransmission/port-forwarding.cc +++ b/libtransmission/port-forwarding.cc @@ -44,7 +44,7 @@ public: tr_port_forwarding_impl& operator=(tr_port_forwarding_impl&&) = delete; tr_port_forwarding_impl& operator=(tr_port_forwarding_impl const&) = delete; - void portChanged() override + void localPortChanged() override { if (!is_enabled_) { diff --git a/libtransmission/port-forwarding.h b/libtransmission/port-forwarding.h index cc4f9ca69..035e8458c 100644 --- a/libtransmission/port-forwarding.h +++ b/libtransmission/port-forwarding.h @@ -40,6 +40,6 @@ public: [[nodiscard]] virtual bool isEnabled() const = 0; [[nodiscard]] virtual tr_port_forwarding_state state() const = 0; - virtual void portChanged() = 0; + virtual void localPortChanged() = 0; virtual void setEnabled(bool enabled) = 0; }; diff --git a/libtransmission/session.cc b/libtransmission/session.cc index f26b6852b..7096528bb 100644 --- a/libtransmission/session.cc +++ b/libtransmission/session.cc @@ -65,8 +65,6 @@ using namespace std::literals; std::recursive_mutex tr_session::session_mutex_; -static auto constexpr DefaultBindAddressIpv4 = "0.0.0.0"sv; -static auto constexpr DefaultBindAddressIpv6 = "::"sv; static auto constexpr SaveIntervalSecs = 360s; static void bandwidthGroupRead(tr_session* session, std::string_view config_dir); @@ -257,7 +255,50 @@ void tr_sessionSetEncryption(tr_session* session, tr_encryption_mode mode) **** ***/ -void tr_session::tr_bindinfo::close() +void tr_session::onIncomingPeerConnection(tr_socket_t fd, void* vsession) +{ + auto* session = static_cast(vsession); + + if (auto const incoming_info = tr_netAccept(session, fd); incoming_info) + { + auto const& [addr, port, sock] = *incoming_info; + tr_logAddTrace(fmt::format("new incoming connection {} ({})", sock, addr.readable(port))); + session->addIncoming(addr, port, tr_peer_socket_tcp_create(sock)); + } +} + +tr_session::BoundSocket::BoundSocket( + event_base* evbase, + tr_address const& addr, + tr_port port, + IncomingCallback cb, + void* cb_data) + : cb_{ cb } + , cb_data_{ cb_data } + , socket_{ tr_netBindTCP(addr, port, false) } +{ + if (socket_ == TR_BAD_SOCKET) + { + return; + } + + tr_logAddInfo( + fmt::format(_("Listening to incoming peer connections on {hostport}"), fmt::arg("hostport", addr.readable(port)))); + + ev_ = event_new( + evbase, + socket_, + EV_READ | EV_PERSIST, + [](evutil_socket_t fd, short /*evtype*/, void* vself) + { + auto* const self = static_cast(vself); + self->cb_(fd, self->cb_data_); + }, + this); + event_add(ev_, nullptr); +} + +tr_session::BoundSocket::~BoundSocket() { if (ev_ != nullptr) { @@ -272,49 +313,24 @@ void tr_session::tr_bindinfo::close() } } -static void acceptIncomingPeer(evutil_socket_t fd, short /*what*/, void* vsession) -{ - auto* session = static_cast(vsession); - - if (auto const incoming_info = tr_netAccept(session, fd); incoming_info) - { - auto const& [addr, port, sock] = *incoming_info; - tr_logAddTrace(fmt::format("new incoming connection {} ({})", sock, addr.readable(port))); - session->addIncoming(addr, port, tr_peer_socket_tcp_create(sock)); - } -} - -void tr_session::tr_bindinfo::bindAndListenForIncomingPeers(tr_session* session) -{ - TR_ASSERT(session->allowsTCP()); - - auto const& port = session->localPeerPort(); - - socket_ = tr_netBindTCP(&addr_, port, false); - - if (socket_ != TR_BAD_SOCKET) - { - tr_logAddInfo( - fmt::format(_("Listening to incoming peer connections on {hostport}"), fmt::arg("hostport", addr_.readable(port)))); - ev_ = event_new(session->eventBase(), socket_, EV_READ | EV_PERSIST, acceptIncomingPeer, session); - event_add(ev_, nullptr); - } -} - tr_session::PublicAddressResult tr_session::publicAddress(tr_address_type type) const noexcept { - switch (type) + if (type == TR_AF_INET) { - case TR_AF_INET: - return { bind_ipv4_.addr_, bind_ipv4_.addr_.readable() == DefaultBindAddressIpv4 }; - - case TR_AF_INET6: - return { bind_ipv6_.addr_, bind_ipv6_.addr_.readable() == DefaultBindAddressIpv6 }; - - default: - TR_ASSERT_MSG(false, "invalid type"); - return {}; + static auto constexpr DefaultAddr = tr_address::AnyIPv4(); + auto addr = tr_address::fromString(settings_.bind_address_ipv4).value_or(DefaultAddr); + return { addr, addr == DefaultAddr }; } + + if (type == TR_AF_INET6) + { + static auto constexpr DefaultAddr = tr_address::AnyIPv6(); + auto addr = tr_address::fromString(settings_.bind_address_ipv6).value_or(DefaultAddr); + return { addr, addr == DefaultAddr }; + } + + TR_ASSERT_MSG(false, "invalid type"); + return {}; } /*** @@ -529,8 +545,6 @@ void tr_session::initImpl(init_data& data) setSettings(client_settings, true); - this->udp_core_ = std::make_unique(*this, udpPort()); - if (this->allowsLPD()) { this->lpd_ = tr_lpd::create(lpd_mediator_, eventBase()); @@ -548,14 +562,25 @@ static void updateBandwidth(tr_session* session, tr_direction dir); void tr_session::setSettings(tr_variant* settings_dict, bool force) { TR_ASSERT(amInSessionThread()); + TR_ASSERT(tr_variantIsDict(settings_dict)); - auto* const settings = settings_dict; - TR_ASSERT(tr_variantIsDict(settings)); + // load the session settings + auto new_settings = tr_session_settings{}; + new_settings.load(settings_dict); + setSettings(std::move(new_settings), force); - // update the `settings_` field - auto const old_settings = settings_; - auto& new_settings = settings_; - new_settings.load(settings); + // delegate loading out the other settings + alt_speeds_.load(settings_dict); + rpc_server_->load(settings_dict); +} + +void tr_session::setSettings(tr_session_settings settings_in, bool force) +{ + auto const lock = unique_lock(); + + std::swap(settings_, settings_in); + auto const& new_settings = settings_; + auto const& old_settings = settings_in; // the rest of the func is session_ responding to settings changes @@ -581,51 +606,65 @@ void tr_session::setSettings(tr_variant* settings_dict, bool force) setDefaultTrackers(val); } - if (auto val = bool{}; tr_variantDictFindBool(settings, TR_KEY_dht_enabled, &val)) - { - tr_sessionSetDHTEnabled(this, val); - } - if (auto const& val = new_settings.utp_enabled; force || val != old_settings.utp_enabled) { tr_sessionSetUTPEnabled(this, val); } - if (auto const& val = new_settings.lpd_enabled; force || val != old_settings.lpd_enabled) - { - tr_sessionSetLPDEnabled(this, val); - } - useBlocklist(new_settings.blocklist_enabled); - /// bound addresses, peer port, port forwarding + auto local_peer_port = force && settings_.peer_port_random_on_start ? randomPort() : new_settings.peer_port; + bool port_changed = false; + if (force || local_peer_port_ != local_peer_port) { - auto port_needs_update = force; + local_peer_port_ = local_peer_port; + advertised_peer_port_ = local_peer_port; + port_changed = true; + } - if (auto const& val = new_settings.bind_address_ipv4; force || val != old_settings.bind_address_ipv4) + if (new_settings.tcp_enabled) + { + if (auto const& val = new_settings.bind_address_ipv4; force || port_changed || val != old_settings.bind_address_ipv4) { - if (auto const addr = tr_address::fromString(val); addr && addr->isIPv4()) - { - this->bind_ipv4_ = tr_bindinfo{ *addr }; - port_needs_update |= true; - } + auto const [addr, is_default] = publicAddress(TR_AF_INET); + bound_ipv4_.emplace(eventBase(), addr, local_peer_port_, &tr_session::onIncomingPeerConnection, this); } - if (auto const& val = new_settings.bind_address_ipv6; force || val != old_settings.bind_address_ipv6) + if (auto const& val = new_settings.bind_address_ipv6; force || port_changed || val != old_settings.bind_address_ipv6) { - if (auto const addr = tr_address::fromString(val); addr && addr->isIPv6()) - { - this->bind_ipv6_ = tr_bindinfo{ *addr }; - port_needs_update |= true; - } + auto const [addr, is_default] = publicAddress(TR_AF_INET6); + bound_ipv6_.emplace(eventBase(), addr, local_peer_port_, &tr_session::onIncomingPeerConnection, this); } + } + else + { + bound_ipv4_.reset(); + bound_ipv6_.reset(); + } - port_needs_update |= (new_settings.port_forwarding_enabled != old_settings.port_forwarding_enabled); + if (port_changed) + { + port_forwarding_->localPortChanged(); + } - if (port_needs_update) + bool const dht_changed = new_settings.dht_enabled != old_settings.dht_enabled; + + if (!udp_core_ || force || port_changed || dht_changed) + { + udp_core_ = std::make_unique(*this, udpPort()); + } + + // Sends out announce messages with advertisedPeerPort(), so this + // section neesd be happen here after the peer port settings changes + if (auto const& val = new_settings.lpd_enabled; force || val != old_settings.lpd_enabled) + { + if (val) { - setPeerPort(isPortRandom() ? randomPort() : new_settings.peer_port); - tr_sessionSetPortForwardingEnabled(this, new_settings.port_forwarding_enabled); + lpd_ = tr_lpd::create(lpd_mediator_, eventBase()); + } + else + { + lpd_.reset(); } } @@ -633,9 +672,6 @@ void tr_session::setSettings(tr_variant* settings_dict, bool force) // It's a harmless call, so just call it instead of checking for settings changes updateBandwidth(this, TR_UP); updateBandwidth(this, TR_DOWN); - - alt_speeds_.load(settings); - rpc_server_->load(settings); } void tr_sessionSet(tr_session* session, tr_variant* settings) @@ -740,44 +776,20 @@ bool tr_sessionIsIncompleteDirEnabled(tr_session const* session) **** Peer Port ***/ -void tr_session::setPeerPort(tr_port port_in) -{ - auto const in_session_thread = [this](tr_port port) - { - auto const lock = unique_lock(); - - auto& private_peer_port = settings_.peer_port; - private_peer_port = port; - advertised_peer_port_ = port; - - closePeerPort(); - - if (allowsTCP()) - { - bind_ipv4_.bindAndListenForIncomingPeers(this); - - if (tr_net_hasIPv6(private_peer_port)) - { - bind_ipv6_.bindAndListenForIncomingPeers(this); - } - } - - port_forwarding_->portChanged(); - - for (auto* const tor : torrents()) - { - tr_torrentChangeMyPort(tor); - } - }; - - runInSessionThread(in_session_thread, port_in); -} - void tr_sessionSetPeerPort(tr_session* session, uint16_t hport) { TR_ASSERT(session != nullptr); - session->setPeerPort(tr_port::fromHost(hport)); + if (auto const port = tr_port::fromHost(hport); port != session->localPeerPort()) + { + session->runInSessionThread( + [session, port]() + { + auto settings = session->settings_; + settings.peer_port = port; + session->setSettings(std::move(settings), false); + }); + } } uint16_t tr_sessionGetPeerPort(tr_session const* session) @@ -1175,7 +1187,8 @@ void tr_session::closeImplPart1() rpc_server_.reset(); lpd_.reset(); port_forwarding_.reset(); - closePeerPort(); + bound_ipv6_.reset(); + bound_ipv4_.reset(); // tell other items to start shutting down udp_core_->startShutdown(); @@ -1389,18 +1402,16 @@ void tr_sessionSetDHTEnabled(tr_session* session, bool enabled) { TR_ASSERT(session != nullptr); - if (enabled == session->allowsDHT()) + if (enabled != session->allowsDHT()) { - return; + session->runInSessionThread( + [session, enabled]() + { + auto settings = session->settings_; + settings.dht_enabled = enabled; + session->setSettings(std::move(settings), false); + }); } - - session->runInSessionThread( - [session, enabled]() - { - session->udp_core_.reset(); - session->settings_.dht_enabled = enabled; - session->udp_core_ = std::make_unique(*session, session->udpPort()); - }); } /*** @@ -1439,21 +1450,16 @@ void tr_sessionSetLPDEnabled(tr_session* session, bool enabled) { TR_ASSERT(session != nullptr); - if (enabled == session->allowsLPD()) + if (enabled != session->allowsLPD()) { - return; - } - - session->runInSessionThread( - [session, enabled]() - { - session->lpd_.reset(); - session->settings_.lpd_enabled = enabled; - if (enabled) + session->runInSessionThread( + [session, enabled]() { - session->lpd_ = tr_lpd::create(session->lpd_mediator_, session->eventBase()); - } - }); + auto settings = session->settings_; + settings.lpd_enabled = enabled; + session->setSettings(std::move(settings), false); + }); + } } bool tr_sessionIsLPDEnabled(tr_session const* session) diff --git a/libtransmission/session.h b/libtransmission/session.h index 55987ba36..9656189de 100644 --- a/libtransmission/session.h +++ b/libtransmission/session.h @@ -77,25 +77,22 @@ class SessionTest; struct tr_session { private: - struct tr_bindinfo + class BoundSocket { - explicit tr_bindinfo(tr_address addr) - : addr_{ std::move(addr) } - { - } + public: + using IncomingCallback = void (*)(tr_socket_t, void*); + BoundSocket(struct event_base*, tr_address const& addr, tr_port port, IncomingCallback cb, void* cb_data); + BoundSocket(BoundSocket&&) = delete; + BoundSocket(BoundSocket const&) = delete; + BoundSocket operator=(BoundSocket&&) = delete; + BoundSocket operator=(BoundSocket const&) = delete; + ~BoundSocket(); - void bindAndListenForIncomingPeers(tr_session* session); - - void close(); - - [[nodiscard]] auto readable() const - { - return addr_.readable(); - } - - tr_address addr_; - struct event* ev_ = nullptr; + private: + IncomingCallback cb_; + void* cb_data_; tr_socket_t socket_ = TR_BAD_SOCKET; + struct event* ev_ = nullptr; }; class AltSpeedMediator final : public tr_session_alt_speeds::Mediator @@ -155,7 +152,7 @@ private: [[nodiscard]] tr_address incomingPeerAddress() const override { - return session_.bind_ipv4_.addr_; + return session_.publicAddress(TR_AF_INET).address; } [[nodiscard]] tr_port localPeerPort() const override @@ -625,7 +622,15 @@ public: // that Transmission is running on. [[nodiscard]] constexpr tr_port localPeerPort() const noexcept { - return settings_.peer_port; + return local_peer_port_; + } + + [[nodiscard]] constexpr tr_port udpPort() const noexcept + { + // Always use the same port number that's used for incoming TCP connections. + // This simplifies port forwarding and reduces the chance of confusion, + // since incoming UDP and TCP connections will use the same port number + return localPeerPort(); } // The incoming peer port that's been opened on the public-facing @@ -637,14 +642,6 @@ public: return advertised_peer_port_; } - [[nodiscard]] constexpr tr_port udpPort() const noexcept - { - // Always use the same port number that's used for incoming TCP connections. - // This simplifies port forwarding and reduces the chance of confusion, - // since incoming UDP and TCP connections will use the same port number - return advertisedPeerPort(); - } - [[nodiscard]] constexpr auto queueEnabled(tr_direction dir) const noexcept { return dir == TR_DOWN ? settings_.download_queue_enabled : settings_.seed_queue_enabled; @@ -888,21 +885,18 @@ private: void setPeerPort(tr_port port); - void closePeerPort() - { - bind_ipv4_.close(); - bind_ipv6_.close(); - } - struct init_data; void initImpl(init_data&); void setSettings(tr_variant* settings_dict, bool force); + void setSettings(tr_session_settings settings, bool force); void closeImplPart1(); void closeImplPart2(); void onNowTimer(); + static void onIncomingPeerConnection(tr_socket_t fd, void* vsession); + friend class libtransmission::test::SessionTest; friend struct tr_bindinfo; @@ -1028,6 +1022,12 @@ private: tr_altSpeedFunc alt_speed_active_changed_func_ = nullptr; void* alt_speed_active_changed_func_user_data_ = nullptr; + // The local peer port that we bind a socket to for listening + // to incoming peer connections. Usually the same as + // `settings_.peer_port` but can differ if + // `settings_.peer_port_random_on_start` is enabled. + tr_port local_peer_port_; + // The incoming peer port that's been opened on the public-facing // device. This is usually the same as localPeerPort() but can differ, // e.g. if the public device is a router that chose to use a different @@ -1048,15 +1048,17 @@ private: tr_session_id session_id_; + tr_open_files open_files_; + std::vector blocklists_; /// other fields - // depends-on: session_thread_ - tr_bindinfo bind_ipv4_ = tr_bindinfo{ tr_inaddr_any }; + // depends-on: session_thread_, settings_.bind_address_ipv4, local_peer_port_ + std::optional bound_ipv4_; - // depends-on: session_thread_ - tr_bindinfo bind_ipv6_ = tr_bindinfo{ tr_in6addr_any }; + // depends-on: session_thread_, settings_.bind_address_ipv6, local_peer_port_ + std::optional bound_ipv6_; public: // depends-on: announcer_udp_ @@ -1070,7 +1072,7 @@ private: // depends-on: top_bandwidth_ std::vector>> bandwidth_groups_; - // depends-on: settings_, timer_maker_ + // depends-on: timer_maker_, settings_, local_peer_port_ PortForwardingMediator port_forwarding_mediator_{ *this }; std::unique_ptr port_forwarding_ = tr_port_forwarding::create(port_forwarding_mediator_); @@ -1078,8 +1080,6 @@ private: AltSpeedMediator alt_speed_mediator_{ *this }; tr_session_alt_speeds alt_speeds_{ alt_speed_mediator_ }; - tr_open_files open_files_; - // depends-on: open_files_ tr_torrents torrents_; @@ -1095,7 +1095,7 @@ private: // depends-on: timer_maker_, top_bandwidth_, torrents_, web_ std::unique_ptr peer_mgr_; - // depends-on: peer_mgr_, torrents_ + // depends-on: peer_mgr_, advertised_peer_port_, torrents_ LpdMediator lpd_mediator_{ *this }; // depends-on: lpd_mediator_ diff --git a/tests/libtransmission/net-test.cc b/tests/libtransmission/net-test.cc index 32d78e1e0..8a7108029 100644 --- a/tests/libtransmission/net-test.cc +++ b/tests/libtransmission/net-test.cc @@ -32,6 +32,12 @@ TEST_F(NetTest, conversionsIPv4) EXPECT_EQ(Port, addrport->second); } +TEST_F(NetTest, trAddress) +{ + EXPECT_EQ("0.0.0.0", tr_address::AnyIPv4().readable()); + EXPECT_EQ("::", tr_address::AnyIPv6().readable()); +} + TEST_F(NetTest, compact4) { static auto constexpr ExpectedReadable = "10.10.10.5"sv;