Skip to content

Commit

Permalink
fix IPv6 address change notification on Windows
Browse files Browse the repository at this point in the history
The old NotifyAddrChange only detects IPv4 address changes. Use the newer
NotifyUnicastIpAddressChange function instead which supports both v4 and v6.
  • Loading branch information
ssiloti authored and arvidn committed Apr 20, 2020
1 parent caf56ea commit a996891
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 25 deletions.
2 changes: 2 additions & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
* fix IPv6 address change detection on Windows

1.2.6 released

* fix peer timeout logic
Expand Down
70 changes: 45 additions & 25 deletions src/ip_notifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 <boost/asio/windows/object_handle.hpp>
#include <iphlpapi.h>
#include <mutex>
#include "libtorrent/aux_/disable_warnings_pop.hpp"
#endif

Expand Down Expand Up @@ -348,52 +348,72 @@ struct ip_change_notifier_impl final : ip_change_notifier
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<system_error>(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<void(error_code const&)> 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<std::mutex> l(m_cb_mutex);
m_cb.emplace_back(std::move(cb));
}

void cancel() override
{
CancelIPChangeNotify(&m_ovl);
m_hnd.cancel();
std::vector<std::function<void(error_code const&)>> cbs;
{
std::lock_guard<std::mutex> 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<ip_change_notifier_impl*>(ctx);
std::vector<std::function<void(error_code const&)>> cbs;
{
std::lock_guard<std::mutex> 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<std::function<void(error_code const&)>> m_cb;
};
#else
struct ip_change_notifier_impl final : ip_change_notifier
Expand Down

0 comments on commit a996891

Please sign in to comment.