From a9968916ca82366f1c236af59aaecb9bc94ffe73 Mon Sep 17 00:00:00 2001 From: Steven Siloti Date: Sat, 18 Apr 2020 11:41:27 -0700 Subject: [PATCH] fix IPv6 address change notification on Windows The old NotifyAddrChange only detects IPv4 address changes. Use the newer NotifyUnicastIpAddressChange function instead which supports both v4 and v6. --- ChangeLog | 2 ++ src/ip_notifier.cpp | 70 +++++++++++++++++++++++++++++---------------- 2 files changed, 47 insertions(+), 25 deletions(-) diff --git a/ChangeLog b/ChangeLog index be796a1f8..1cd008b56 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,5 @@ + * fix IPv6 address change detection on Windows + 1.2.6 released * fix peer timeout logic diff --git a/src/ip_notifier.cpp b/src/ip_notifier.cpp index daa25f84b..11f7b4fc4 100644 --- a/src/ip_notifier.cpp +++ b/src/ip_notifier.cpp @@ -44,8 +44,8 @@ POSSIBILITY OF SUCH DAMAGE. #elif defined TORRENT_WINDOWS #include "libtorrent/aux_/throw.hpp" #include "libtorrent/aux_/disable_warnings_push.hpp" -#include #include +#include #include "libtorrent/aux_/disable_warnings_pop.hpp" #endif @@ -348,52 +348,72 @@ private: struct ip_change_notifier_impl final : ip_change_notifier { explicit ip_change_notifier_impl(io_service& ios) - : m_hnd(ios, WSACreateEvent()) + : m_ios(ios) { - if (!m_hnd.is_open()) aux::throw_ex(WSAGetLastError(), system_category()); - m_ovl.hEvent = m_hnd.native_handle(); + NotifyUnicastIpAddressChange(AF_UNSPEC, address_change_cb, this, false, &m_hnd); } // non-copyable ip_change_notifier_impl(ip_change_notifier_impl const&) = delete; ip_change_notifier_impl& operator=(ip_change_notifier_impl const&) = delete; + // non-moveable + ip_change_notifier_impl(ip_change_notifier_impl&&) = delete; + ip_change_notifier_impl& operator=(ip_change_notifier_impl&&) = delete; + ~ip_change_notifier_impl() override { - cancel(); - // the reason to call close() here is because the - // object_handle destructor doesn't close the handle - m_hnd.close(); + if (m_hnd != nullptr) + { + CancelMibChangeNotify2(m_hnd); + m_hnd = nullptr; + } } void async_wait(std::function cb) override { - HANDLE hnd; - DWORD err = NotifyAddrChange(&hnd, &m_ovl); - if (err == ERROR_IO_PENDING) + if (m_hnd == nullptr) { - m_hnd.async_wait([cb](error_code const& ec) - { - // call CancelIPChangeNotify here? - cb(ec); - }); - } - else - { - lt::get_io_service(m_hnd).post([cb, err]() - { cb(error_code(err, system_category())); }); + cb(make_error_code(boost::system::errc::not_supported)); + return; } + + std::lock_guard l(m_cb_mutex); + m_cb.emplace_back(std::move(cb)); } void cancel() override { - CancelIPChangeNotify(&m_ovl); - m_hnd.cancel(); + std::vector> cbs; + { + std::lock_guard l(m_cb_mutex); + cbs = std::move(m_cb); + } + for (auto& cb : cbs) cb(make_error_code(boost::asio::error::operation_aborted)); } private: - OVERLAPPED m_ovl = {}; - boost::asio::windows::object_handle m_hnd; + static void WINAPI address_change_cb(void* ctx, MIB_UNICASTIPADDRESS_ROW*, MIB_NOTIFICATION_TYPE) + { + ip_change_notifier_impl* impl = static_cast(ctx); + std::vector> cbs; + { + std::lock_guard l(impl->m_cb_mutex); + cbs = std::move(impl->m_cb); + } + // TODO move cbs into the lambda with C++14 + impl->m_ios.post([cbs]() + { + for (auto& cb : cbs) cb(error_code()); + }); + } + + io_service& m_ios; + HANDLE m_hnd = nullptr; + // address_change_cb gets invoked from a separate worker thread so the callbacks + // vector must be protected by a mutex + std::mutex m_cb_mutex; + std::vector> m_cb; }; #else struct ip_change_notifier_impl final : ip_change_notifier