diff --git a/CMakeLists.txt b/CMakeLists.txt index c522d5a0d..201fe37c3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -300,6 +300,11 @@ if (WIN32) endif() endif() +if(APPLE) + # for ip_notifier + target_link_libraries(torrent-rasterbar "-framework CoreFoundation" "-framework SystemConfiguration") +endif (APPLE) + if (encryption) target_link_libraries(torrent-rasterbar ${OPENSSL_LIBRARIES}) endif() diff --git a/Jamfile b/Jamfile index 6fd871a53..4d6aee5ff 100644 --- a/Jamfile +++ b/Jamfile @@ -160,6 +160,12 @@ rule linking ( properties * ) result += libsocket libnsl ; } + if darwin in $(properties) + { + # for ip_notifier + result += CoreFoundation SystemConfiguration ; + } + if on in $(properties) { result += libiconv ; diff --git a/include/libtorrent/config.hpp b/include/libtorrent/config.hpp index 510513170..5b16cbaec 100644 --- a/include/libtorrent/config.hpp +++ b/include/libtorrent/config.hpp @@ -166,6 +166,8 @@ POSSIBILITY OF SUCH DAMAGE. #define TORRENT_USE_EXECINFO 1 #endif +#define TORRENT_USE_SYSTEMCONFIGURATION 1 + #else // __APPLE__ // FreeBSD has a reasonable iconv signature // unless we're on glibc @@ -436,6 +438,10 @@ POSSIBILITY OF SUCH DAMAGE. #define TORRENT_USE_COMMONCRYPTO 0 #endif +#ifndef TORRENT_USE_SYSTEMCONFIGURATION +#define TORRENT_USE_SYSTEMCONFIGURATION 0 +#endif + #ifndef TORRENT_USE_CRYPTOAPI #define TORRENT_USE_CRYPTOAPI 0 #endif diff --git a/src/ip_notifier.cpp b/src/ip_notifier.cpp index f3d450be8..24268e2b4 100644 --- a/src/ip_notifier.cpp +++ b/src/ip_notifier.cpp @@ -31,12 +31,15 @@ POSSIBILITY OF SUCH DAMAGE. */ #include "libtorrent/aux_/ip_notifier.hpp" +#include "libtorrent/assert.hpp" #if defined TORRENT_BUILD_SIMULATOR // TODO: simulator support #elif TORRENT_USE_NETLINK #include "libtorrent/netlink.hpp" #include +#elif TORRENT_USE_SYSTEMCONFIGURATION +#include #elif defined TORRENT_WINDOWS #include "libtorrent/aux_/throw.hpp" #include "libtorrent/aux_/disable_warnings_push.hpp" @@ -55,11 +58,139 @@ namespace // TODO: ip_change_notifier_sim #elif TORRENT_USE_NETLINK // TODO: ip_change_notifier_nl +#elif TORRENT_USE_SYSTEMCONFIGURATION +// see https://developer.apple.com/library/content/technotes/tn1145/_index.html + +template void CFRefRetain(T h) { CFRetain(h); } +template void CFRefRelease(T h) { CFRelease(h); } + +template , void (*Release)(T) = CFRefRelease> +struct CFRef +{ + CFRef() {} + explicit CFRef(T h) : m_h(h) {} // take ownership + ~CFRef() { release(); } + + CFRef(CFRef const& rhs) : m_h(rhs.m_h) { retain(); } + CFRef& operator=(CFRef const& rhs) + { + if (m_h == rhs.m_h) return *this; + release(); + m_h = rhs.m_h; + retain(); + return *this; + } + + CFRef& operator=(T h) { m_h = h; return *this;} + CFRef& operator=(std::nullptr_t) { release(); return *this;} + + T get() const { return m_h; } + explicit operator bool() const { return m_h != nullptr; } + +private: + T m_h = nullptr; // handle + + void retain() { if (m_h != nullptr) Retain(m_h); } + void release() { if (m_h != nullptr) Release(m_h); m_h = nullptr; } +}; + +void CFDispatchRetain(dispatch_queue_t q) { dispatch_retain(q); } +void CFDispatchRelease(dispatch_queue_t q) { dispatch_release(q); } +using CFDispatchRef = CFRef; + +CFRef create_keys_array() +{ + CFRef keys{CFArrayCreateMutable(nullptr + , 0, &kCFTypeArrayCallBacks)}; + + // "State:/Network/Interface/[^/]+/IPv4" + CFRef key{SCDynamicStoreKeyCreateNetworkInterfaceEntity(nullptr + , kSCDynamicStoreDomainState, kSCCompAnyRegex, kSCEntNetIPv4)}; + CFArrayAppendValue(keys.get(), key.get()); + + // NOTE: for IPv6, you can replicate the above setup with kSCEntNetIPv6 + // but due to the current state of most common configurations, where + // IPv4 is used alongside with IPv6, you will end up with twice the + // notifications for the same change + + return keys; +} + +CFRef create_dynamic_store(SCDynamicStoreCallBack callback, void* context_info) +{ + TORRENT_ASSERT(callback != nullptr); + + SCDynamicStoreContext context = {0, nullptr, nullptr, nullptr, nullptr}; + context.info = context_info; + CFRef store{SCDynamicStoreCreate(nullptr + , CFSTR("libtorrent.IPChangeNotifierStore"), callback, &context)}; + if (!store) + return CFRef(); + + CFRef keys = create_keys_array(); + return SCDynamicStoreSetNotificationKeys(store.get(), nullptr, keys.get()) + ? store : CFRef(); +} + +struct ip_change_notifier_macos final : ip_change_notifier +{ + explicit ip_change_notifier_macos(io_service& ios) + : m_ios(ios) + { + m_queue = dispatch_queue_create("libtorrent.IPChangeNotifierQueue", nullptr); + m_store = create_dynamic_store( + [](SCDynamicStoreRef /*store*/, CFArrayRef /*changedKeys*/, void *info) + { + auto obj = static_cast(info); + obj->m_ios.post([obj]() { if (obj->m_cb) obj->m_cb(error_code()); }); + }, this); + + if (!m_queue || !m_store + || !SCDynamicStoreSetDispatchQueue(m_store.get(), m_queue.get())) + cancel(); + } + + // noncopyable + ip_change_notifier_macos(ip_change_notifier_macos const&) = delete; + ip_change_notifier_macos& operator=(ip_change_notifier_macos const&) = delete; + + ~ip_change_notifier_macos() override + { cancel(); } + + void async_wait(std::function cb) override + { + if (m_queue) + m_cb = std::move(cb); + else + m_ios.post([cb]() + { cb(make_error_code(boost::system::errc::not_supported)); }); + } + + void cancel() override + { + if (m_store) + SCDynamicStoreSetDispatchQueue(m_store.get(), nullptr); + + m_cb = nullptr; + m_store = nullptr; + m_queue = nullptr; + } + +private: + io_service& m_ios; + CFDispatchRef m_queue; + CFRef m_store; + std::function m_cb = nullptr; +}; #elif defined TORRENT_WINDOWS // TODO: ip_change_notifier_win +#else + // TODO: ip_change_notifier_default #endif // TODO: to remove when separated per platform +#if defined TORRENT_BUILD_SIMULATOR || TORRENT_USE_NETLINK || defined TORRENT_WINDOWS struct ip_change_notifier_impl final : ip_change_notifier { explicit ip_change_notifier_impl(io_service& ios); @@ -170,11 +301,28 @@ namespace else cb(ec); } +#endif // defined TORRENT_BUILD_SIMULATOR || TORRENT_USE_NETLINK || defined TORRENT_WINDOWS } // anonymous namespace std::unique_ptr create_ip_notifier(io_service& ios) { - return std::unique_ptr(new ip_change_notifier_impl(ios)); + return std::unique_ptr( +#if defined TORRENT_BUILD_SIMULATOR + // ip_change_notifier_sim + new ip_change_notifier_impl(ios) +#elif TORRENT_USE_NETLINK + // ip_change_notifier_nl + new ip_change_notifier_impl(ios) +#elif TORRENT_USE_SYSTEMCONFIGURATION + new ip_change_notifier_macos(ios) +#elif defined TORRENT_WINDOWS + // ip_change_notifier_win + new ip_change_notifier_impl(ios) +#else + // ip_change_notifier_default + new ip_change_notifier_impl(ios) +#endif + ); } }} diff --git a/tools/Jamfile b/tools/Jamfile index 74c7bb5d5..adb79739b 100644 --- a/tools/Jamfile +++ b/tools/Jamfile @@ -38,4 +38,5 @@ project tools exe fuzz_torrent : fuzz_torrent.cpp ; exe parse_access_log : parse_access_log.cpp ; exe dht : dht_put.cpp : ../ed25519/src ; +exe session_log_alerts : session_log_alerts.cpp ; diff --git a/tools/Makefile.am b/tools/Makefile.am index 23f67cfb7..21df6663f 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -1,5 +1,6 @@ tool_programs = \ - fuzz_torrent + fuzz_torrent \ + session_log_alerts if ENABLE_EXAMPLES bin_PROGRAMS = $(tool_programs) @@ -20,6 +21,7 @@ EXTRA_DIST = Jamfile \ parse_utp_log.py fuzz_torrent_SOURCES = fuzz_torrent.cpp +session_log_alerts_SOURCES = session_log_alerts.cpp LDADD = $(top_builddir)/src/libtorrent-rasterbar.la diff --git a/tools/session_log_alerts.cpp b/tools/session_log_alerts.cpp new file mode 100644 index 000000000..5dc95673f --- /dev/null +++ b/tools/session_log_alerts.cpp @@ -0,0 +1,69 @@ +/* + +Copyright (c) 2017, Arvid Norberg, Alden Torres +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/session.hpp" +#include "libtorrent/alert_types.hpp" + +#include +#include +#include + +using namespace libtorrent; + +int main(int argc, char* argv[]) +{ + std::printf("press Ctrl+C, kill the process or wait for 1000 alerts\n"); + + settings_pack sett; + sett.set_int(settings_pack::alert_mask, 0xffffffff); + session s(sett); + + int count = 0; + while (count <= 1000) + { + s.wait_for_alert(seconds(5)); + + std::vector alerts; + s.pop_alerts(&alerts); + for (auto const a : alerts) + { + if (a->type() == log_alert::alert_type) + { + std::printf("log_alert - %s\n", a->message().c_str()); + count++; + } + } + } + std::printf("\n"); + + return 0; +}