From cc2353da72086feabaeeb0dc936e697c43d00110 Mon Sep 17 00:00:00 2001 From: cheloveck3-0 <cheloveck2.0@gmail.com> Date: Tue, 26 Feb 2019 20:43:06 +0600 Subject: [PATCH] Native macos network monitor (#40) --- CMakeLists.txt | 4 + core/CMakeLists.txt | 5 + core/core.pri | 3 + core/darwin/CMakeLists.txt | 17 ++ core/darwin/darwin.pri | 5 + core/darwin/macos/dap_network_monitor.c | 329 ++++++++++++++++++++++++ core/darwin/macos/dap_network_monitor.h | 53 ++++ core/darwin/macos/macos.pri | 10 + core/darwin/macos/pthread_barrier.h | 65 +++++ 9 files changed, 491 insertions(+) create mode 100644 core/darwin/CMakeLists.txt create mode 100644 core/darwin/darwin.pri create mode 100644 core/darwin/macos/dap_network_monitor.c create mode 100644 core/darwin/macos/dap_network_monitor.h create mode 100644 core/darwin/macos/macos.pri create mode 100644 core/darwin/macos/pthread_barrier.h diff --git a/CMakeLists.txt b/CMakeLists.txt index fae4546..6c8d56f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,6 +10,10 @@ if(UNIX AND NOT APPLE) set(LINUX TRUE) endif() +if(APPLE) + set(DARWIN TRUE) +endif() + if(BUILD_DAP_TESTS) enable_testing() add_subdirectory(test) diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index c081f32..8e65cca 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -16,3 +16,8 @@ if(UNIX) add_subdirectory(unix) target_link_libraries(${PROJECT_NAME} dap_core_unix) endif() + +if(DARWIN) + add_subdirectory(darwin) + target_link_libraries(${PROJECT_NAME} dap_core_darwin) +endif() diff --git a/core/core.pri b/core/core.pri index f2486be..69a0688 100644 --- a/core/core.pri +++ b/core/core.pri @@ -1,6 +1,9 @@ unix { include(unix/unix.pri) } +darwin { + include(darwin/darwin.pri) +} HEADERS += $$PWD/dap_common.h \ $$PWD/dap_config.h \ diff --git a/core/darwin/CMakeLists.txt b/core/darwin/CMakeLists.txt new file mode 100644 index 0000000..8f981d5 --- /dev/null +++ b/core/darwin/CMakeLists.txt @@ -0,0 +1,17 @@ +cmake_minimum_required(VERSION 3.0) + +project (dap_core_darwin) + +file(GLOB CORE_DARWIN_SRCS *.c) +file(GLOB CORE_DARWIN_HEADERS *.h) + +if(DARWIN) + file(GLOB CORE_MACOS_SRCS macos/*.c) + file(GLOB CORE_MACOS_HEADERS macos/*.h) +endif() + +add_library(${PROJECT_NAME} STATIC ${CORE_DARWIN_SRCS} ${CORE_DARWIN_HEADERS} + ${CORE_MACOS_SRCS} ${CORE_MACOS_HEADERS}) + +target_link_libraries(${PROJECT_NAME} dap_core pthread) + diff --git a/core/darwin/darwin.pri b/core/darwin/darwin.pri new file mode 100644 index 0000000..d8afafc --- /dev/null +++ b/core/darwin/darwin.pri @@ -0,0 +1,5 @@ +macos { + include(macos/macos.pri) +} + +INCLUDEPATH += $$PWD diff --git a/core/darwin/macos/dap_network_monitor.c b/core/darwin/macos/dap_network_monitor.c new file mode 100644 index 0000000..f66c715 --- /dev/null +++ b/core/darwin/macos/dap_network_monitor.c @@ -0,0 +1,329 @@ +#include <SystemConfiguration/SystemConfiguration.h> +#include <CoreFoundation/CoreFoundation.h> +#include <CoreFoundation/CFDictionary.h> +#include <CoreFoundation/CFArray.h> +#include <CoreFoundation/CFString.h> +#include <pthread.h> +#include <string.h> +#include <unistd.h> + +#include "dap_network_monitor.h" +#include "dap_common.h" +#include "pthread_barrier.h" + + +#define LOG_TAG "dap_network_monitor" + +static SCDynamicStoreRef s_store = NULL; +static CFRunLoopSourceRef s_rls; +#define __bridge + +static void* network_monitor_worker(void *arg); + +static struct { + CFRunLoopRef rlref; + pthread_t thread; + dap_network_monitor_notification_callback_t callback; +} _net_notification; + + +void watch_for_network_changes() +{ + SCDynamicStoreContext context = { 0, (void *)s_store, NULL, NULL, NULL }; + + s_store = SCDynamicStoreCreate(NULL, CFSTR("watch_for_network_changes"), _net_notification.callback, &context); + if (!s_store) { + log_it(L_ERROR, "Could not open session with config.error = %s\n", SCErrorString(SCError())); + return; + } + +/* +* establish and register dynamic store keys to watch +* - global IPv4 configuration changes (e.g. new default route) +* - per-service IPv4 state changes (IP service added/removed/...) +*/ + CFStringRef key1 = SCDynamicStoreKeyCreateNetworkGlobalEntity (NULL, kSCDynamicStoreDomainState, kSCEntNetIPv4); + CFStringRef key2 = SCDynamicStoreKeyCreateNetworkInterfaceEntity (NULL, kSCDynamicStoreDomainState, kSCCompAnyRegex, kSCEntNetIPv4); + CFStringRef key3 = SCDynamicStoreKeyCreateNetworkServiceEntity (NULL, kSCDynamicStoreDomainState, kSCCompAnyRegex, kSCEntNetIPv4); + CFStringRef pattern1 = SCDynamicStoreKeyCreateNetworkInterfaceEntity (NULL, kSCDynamicStoreDomainState, kSCCompAnyRegex, kSCEntNetIPv4); + CFStringRef pattern2 = SCDynamicStoreKeyCreateNetworkInterfaceEntity (NULL, kSCDynamicStoreDomainState, kSCCompAnyRegex, kSCEntNetInterface); + CFStringRef pattern3 = SCDynamicStoreKeyCreateNetworkGlobalEntity (NULL, kSCDynamicStoreDomainState, kSCEntNetIPv4); + CFStringRef pattern4 = SCDynamicStoreKeyCreateNetworkGlobalEntity (NULL, kSCDynamicStoreDomainState, kSCEntNetInterface); + CFStringRef pattern5 = SCDynamicStoreKeyCreateNetworkServiceEntity (NULL, kSCDynamicStoreDomainState, kSCCompAnyRegex, kSCEntNetIPv4); + CFStringRef pattern6 = SCDynamicStoreKeyCreateNetworkServiceEntity (NULL, kSCDynamicStoreDomainState, kSCCompAnyRegex, kSCEntNetInterface); + CFMutableArrayRef keys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); + CFMutableArrayRef patterns = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); + + if (!key1 || !key2 || !key3 || !keys || !pattern1 || !pattern2 || !pattern3 || !pattern4 || !pattern5 || !pattern6 || !patterns) goto error; + + CFArrayAppendValue(keys, key1); + CFArrayAppendValue(keys, key2); + CFArrayAppendValue(keys, key3); + CFArrayAppendValue(patterns, pattern1); + CFArrayAppendValue(patterns, pattern2); + CFArrayAppendValue(patterns, pattern3); + CFArrayAppendValue(patterns, pattern4); + CFArrayAppendValue(patterns, pattern5); + CFArrayAppendValue(patterns, pattern6); + + if (SCDynamicStoreSetNotificationKeys(s_store, keys, patterns)){ + s_rls = SCDynamicStoreCreateRunLoopSource(NULL, s_store, 0); + if (s_rls) { + CFRunLoopAddSource(CFRunLoopGetCurrent(), s_rls, kCFRunLoopDefaultMode); + }else{ + log_it(L_ERROR, "SCDynamicStoreCreateRunLoopSource failed: %s\n", SCErrorString(SCError())); + CFRelease(s_store); + } + }else { + log_it(L_ERROR, "SCDynamicStoreSetNotificationKeys failed: %s\n", SCErrorString(SCError())); + CFRelease(s_store); + } + goto exit; + + error: + if (s_store) CFRelease(s_store); + + exit: + if (key1) CFRelease(key1); + if (key2) CFRelease(key2); + if (key3) CFRelease(key3); + if (pattern1) CFRelease(pattern1); + if (pattern2) CFRelease(pattern2); + if (pattern3) CFRelease(pattern3); + if (pattern4) CFRelease(pattern4); + if (pattern5) CFRelease(pattern5); + if (pattern6) CFRelease(pattern6); + if (keys) CFRelease(keys); + if (patterns) CFRelease(patterns); + return; +} + + +/** + * @brief dap_network_monitor_init + * @param callback + * @details starts network monitorting + * @return 0 if successful + */ +int dap_network_monitor_init(dap_network_monitor_notification_callback_t cbMonitorNatification) +{ + memset((void*)&_net_notification, 0, sizeof(_net_notification)); + _net_notification.callback = cbMonitorNatification; + + pthread_barrier_t barrier; + + pthread_barrier_init(&barrier, NULL, 2); + if(pthread_create(&_net_notification.thread, NULL, network_monitor_worker, &barrier) != 0) { + log_it(L_ERROR, "Error create notification thread"); + return -3; + } + + pthread_barrier_wait(&barrier); + + pthread_barrier_destroy(&barrier); + + log_it(L_INFO, "dap_network_monitor was initialized"); + return 0; +} + +/** + * @brief dap_network_monitor_deinit + */ +void dap_network_monitor_deinit(void) +{ + CFRunLoopStop(_net_notification.rlref); + //log_it(L_INFO, "After stopping run loop cycle"); + pthread_cancel(_net_notification.thread); + //log_it(L_INFO, "After cancelation monitor thread!"); + pthread_join(_net_notification.thread, NULL); + //log_it(L_INFO, "After deinit that wonderfull monitor!"); +} + + + +static void* network_monitor_worker(void *arg) +{ + pthread_barrier_t *barrier = (pthread_barrier_t *)arg; + watch_for_network_changes(); + pthread_barrier_wait(barrier); + _net_notification.rlref = CFRunLoopGetCurrent(); + CFRunLoopRun(); + log_it(L_WARNING, "We are in the loop activity and won't have to see this message!"); + return NULL; +} + + + + + +//////////////////////////////////////////////////////////////// +// Usefull functions for future processing changes to interfaces + +static OSStatus MoreSCErrorBoolean(Boolean success) +{ + OSStatus err; + int scErr; + + err = noErr; + if ( ! success ) { + scErr = SCError(); + if (scErr == kSCStatusOK) { + scErr = kSCStatusFailed; + } + // Return an SCF error directly as an OSStatus. + // That's a little cheesy. In a real program + // you might want to do some mapping from SCF + // errors to a range within the OSStatus range. + err = scErr; + } + return err; +} + +static OSStatus MoreSCError(const void *value) +{ + return MoreSCErrorBoolean(value != NULL); +} + +static OSStatus CFQError(CFTypeRef cf) + // Maps Core Foundation error indications (such as they + // are) to the OSStatus domain. +{ + OSStatus err; + + err = noErr; + if (cf == NULL) { + err = -4960; + } + return err; +} + +static void CFQRelease(CFTypeRef cf) + // A version of CFRelease that's tolerant of NULL. +{ + if (cf != NULL) { + CFRelease(cf); + } +} + + +static void GetIPAddressListFromValue(const void *key, + const void *value, + void *context) + // This function is a callback CopyIPAddressListSCF when + // it calls CFDictionaryApplyFunction. It extracts the + // IPv4 address list from the network service dictionary + // and appends it to the result dictionary (which is passed + // in via the context pointers). +{ + CFArrayRef intfAddrList; + + assert( key != NULL ); + assert( CFGetTypeID(key) == CFStringGetTypeID() ); + assert( value != NULL ); + assert( CFGetTypeID(value) == CFDictionaryGetTypeID() ); + assert( context != NULL ); + assert( CFGetTypeID(context) == CFArrayGetTypeID() ); + + //CFDictionaryRef _value = (CFDictionaryRef) value; + struct __CFDictionary * _value = (__bridge struct __CFDictionary *) value; + intfAddrList = (__bridge struct __CFArray *) CFDictionaryGetValue(_value, + kSCPropNetIPv4Addresses); + if (intfAddrList != NULL) { + assert( CFGetTypeID(intfAddrList) + == CFArrayGetTypeID() ); + struct __CFArray * _context = (__bridge struct __CFArray *) context; + CFArrayAppendArray(_context, + intfAddrList, + CFRangeMake(0, CFArrayGetCount(intfAddrList)) + ); + } + +} + +static OSStatus CopyIPAddressListSCF(CFArrayRef *addrList) + // Returns a CFArray that contains every IPv4 + // address on the system (as CFStrings) in no + // particular order. +{ + OSStatus err; + SCDynamicStoreRef ref; + CFStringRef pattern; + CFArrayRef patternList; + CFDictionaryRef valueDict; + CFMutableArrayRef result; + + assert( addrList != NULL); + assert(*addrList == NULL); + + ref = NULL; + pattern = NULL; + patternList = NULL; + valueDict = NULL; + result = NULL; + + // Create a connection to the dynamic store, then create + // a search pattern that finds all IPv4 entities. + // The pattern is "State:/Network/Service/[^/]+/IPv4". + ref = SCDynamicStoreCreate( NULL, + CFSTR("CopyIPAddressListSCF"), + NULL, + NULL); + err = MoreSCError(ref); + if (err == noErr) { + pattern = SCDynamicStoreKeyCreateNetworkInterfaceEntity( + NULL, + kSCDynamicStoreDomainState, + kSCCompAnyRegex, + kSCEntNetIPv4); + err = MoreSCError(pattern); + } + + // Now make a pattern list out of the pattern and then + // call SCDynamicStoreCopyMultiple. We use that call, + // rather than repeated calls to SCDynamicStoreCopyValue, + // because it gives us a snapshot of the state. + if (err == noErr) { + patternList = CFArrayCreate(NULL, + (const void **) &pattern, + 1, + &kCFTypeArrayCallBacks); + err = CFQError(patternList); + } + if (err == noErr) { + valueDict = SCDynamicStoreCopyMultiple(ref, + NULL, + patternList); + err = MoreSCError(valueDict); + } + + // For each IPv4 entity that we found, extract the list + // of IP addresses and append it to our results array. + if (err == noErr) { + result = CFArrayCreateMutable(NULL, 0, + &kCFTypeArrayCallBacks); + err = CFQError(result); + } + + // Iterate over the values, extracting the IP address + // arrays and appending them to the result. + if (err == noErr) { + CFDictionaryApplyFunction(valueDict, + GetIPAddressListFromValue, + result); + } + // Clean up. + + CFQRelease(ref); + CFQRelease(pattern); + CFQRelease(patternList); + if (err != noErr && result != NULL) { + CFQRelease(result); + result = NULL; + printf("10\n"); + } + *addrList = result; + + assert( (err == noErr) == (*addrList != NULL) ); + + return err; +} diff --git a/core/darwin/macos/dap_network_monitor.h b/core/darwin/macos/dap_network_monitor.h new file mode 100644 index 0000000..6d570f4 --- /dev/null +++ b/core/darwin/macos/dap_network_monitor.h @@ -0,0 +1,53 @@ +/* + * Authors: + * Anton Isaikin <anton.isaikin@demlabs.net> + * DeM Labs Inc. https://demlabs.net + * DeM Labs Open source community https://github.com/demlabsinc + * Copyright (c) 2017-2019 + * All rights reserved. + + This file is part of DAP (Deus Applications Prototypes) the open source project + + DAP (Deus Applicaions Prototypes) is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + DAP is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with any DAP based project. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +#pragma once + +#include <CoreFoundation/CoreFoundation.h> +#include <SystemConfiguration/SystemConfiguration.h> +#include <CoreFoundation/CFArray.h> + +typedef void (*dap_network_monitor_notification_callback_t) + (SCDynamicStoreRef store, CFArrayRef changedKeys, void *info); +/** + * @brief dap_network_monitor_init + * @param callback + * @details starts network monitorting + * @return 0 if successful + */ +int dap_network_monitor_init(dap_network_monitor_notification_callback_t callback); + +/** + * @brief dap_network_monitor_deinit + */ +void dap_network_monitor_deinit(void); + + +#ifdef __cplusplus +} +#endif diff --git a/core/darwin/macos/macos.pri b/core/darwin/macos/macos.pri new file mode 100644 index 0000000..1fb87f8 --- /dev/null +++ b/core/darwin/macos/macos.pri @@ -0,0 +1,10 @@ +HEADERS += $$PWD/dap_network_monitor.h \ +HEADERS += $$PWD/pthread_barrier.h + +SOURCES += $$PWD/dap_network_monitor.c + +INCLUDEPATH += $$PWD + +LIBS += -framework CoreFoundation +LIBS += -framework SystemConfiguration +#LIBS += -framework NetworkExtension diff --git a/core/darwin/macos/pthread_barrier.h b/core/darwin/macos/pthread_barrier.h new file mode 100644 index 0000000..e1f5a8d --- /dev/null +++ b/core/darwin/macos/pthread_barrier.h @@ -0,0 +1,65 @@ +#ifndef PTHREAD_BARRIER_H +#define PTHREAD_BARRIER_H + +#include <pthread.h> +#include <errno.h> + +typedef int pthread_barrierattr_t; +typedef struct +{ + pthread_mutex_t mutex; + pthread_cond_t cond; + int count; + int tripCount; +} pthread_barrier_t; + + +int pthread_barrier_init(pthread_barrier_t *barrier, const pthread_barrierattr_t *attr, unsigned int count) +{ + if(count == 0) + { + errno = EINVAL; + return -1; + } + if(pthread_mutex_init(&barrier->mutex, 0) < 0) + { + return -2; + } + if(pthread_cond_init(&barrier->cond, 0) < 0) + { + pthread_mutex_destroy(&barrier->mutex); + return -3; + } + barrier->tripCount = count; + barrier->count = 0; + + return 0; +} + +int pthread_barrier_destroy(pthread_barrier_t *barrier) +{ + pthread_cond_destroy(&barrier->cond); + pthread_mutex_destroy(&barrier->mutex); + return 0; +} + +int pthread_barrier_wait(pthread_barrier_t *barrier) +{ + pthread_mutex_lock(&barrier->mutex); + ++(barrier->count); + if(barrier->count >= barrier->tripCount) + { + barrier->count = 0; + pthread_cond_broadcast(&barrier->cond); + pthread_mutex_unlock(&barrier->mutex); + return 1; + } + else + { + pthread_cond_wait(&barrier->cond, &(barrier->mutex)); + pthread_mutex_unlock(&barrier->mutex); + return 0; + } +} + +#endif // PTHREAD_BARRIER_H -- GitLab