X-Git-Url: https://git.saurik.com/apple/configd.git/blobdiff_plain/4f125ff561e6fa1bd12b93c65dc5fa8e3323fd9a..f715d9467d5e87b8d8c63f8bb206034f6823ebcd:/SystemConfiguration.fproj/SCNetworkInterfaceProvider.c diff --git a/SystemConfiguration.fproj/SCNetworkInterfaceProvider.c b/SystemConfiguration.fproj/SCNetworkInterfaceProvider.c new file mode 100644 index 0000000..65074b2 --- /dev/null +++ b/SystemConfiguration.fproj/SCNetworkInterfaceProvider.c @@ -0,0 +1,470 @@ +/* + * Copyright (c) 2018 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +/* + * Modification History + * + * January 17, 2018 Dieter Siegmund (dieter@apple.com) + * - initial revision + */ + +/* + * SCNetworkInterfaceProvider.c + */ + +#include +#include +#include +#include "SCNetworkConfigurationPrivate.h" +#include "SCNetworkConfigurationInternal.h" +#include "SCNetworkInterfaceProvider.h" + +static void +my_CFRelease(void * t) +{ + void * * obj = (void * *)t; + if (obj && *obj) { + CFRelease(*obj); + *obj = NULL; + } + return; +} + +/** + ** ObjectWrapper + **/ + +typedef struct { + const void * obj; + int32_t retain_count; +} ObjectWrapper, * ObjectWrapperRef; + +static const void * +ObjectWrapperRetain(const void * info) +{ + ObjectWrapperRef wrapper = (ObjectWrapperRef)info; + + (void)OSAtomicIncrement32(&wrapper->retain_count); + return (info); +} + +static ObjectWrapperRef +ObjectWrapperAllocate(const void * obj) +{ + ObjectWrapperRef wrapper; + + wrapper = (ObjectWrapperRef)malloc(sizeof(*wrapper)); + wrapper->obj = obj; + wrapper->retain_count = 1; + return (wrapper); +} + +static void +ObjectWrapperRelease(const void * info) +{ + int32_t new_val; + ObjectWrapperRef wrapper = (ObjectWrapperRef)info; + + new_val = OSAtomicDecrement32(&wrapper->retain_count); + if (new_val == 0) { + free(wrapper); + } + else { + assert(new_val > 0); + } + return; +} + +static void +ObjectWrapperSetObject(ObjectWrapperRef wrapper, const void * obj) +{ + wrapper->obj = obj; +} + +static const void * +ObjectWrapperGetObject(ObjectWrapperRef wrapper) +{ + return (wrapper->obj); +} + +static SCDynamicStoreRef +StoreObjectWrapperAllocate(const void * obj, + CFStringRef name, + SCDynamicStoreCallBack handler, + CFArrayRef keys, + CFArrayRef patterns, + dispatch_queue_t queue, + ObjectWrapperRef * ret_wrapper) +{ + SCDynamicStoreContext context = { + .version = 0, + .info = NULL, + .retain = ObjectWrapperRetain, + .release = ObjectWrapperRelease, + .copyDescription = NULL + }; + SCDynamicStoreRef store; + ObjectWrapperRef wrapper; + + wrapper = ObjectWrapperAllocate(obj); + context.info = wrapper; + store = SCDynamicStoreCreate(NULL, name, handler, &context); + if (store == NULL) { + SC_log(LOG_NOTICE, + "%@: SCDynamicStoreCreate failed", name); + } + else if (!SCDynamicStoreSetNotificationKeys(store, keys, patterns)) { + SC_log(LOG_NOTICE, + "%@: SCDynamicStoreSetNoticationKeys failed", name); + CFRelease(store); + store = NULL; + } + else if (queue != NULL + && !SCDynamicStoreSetDispatchQueue(store, queue)) { + SC_log(LOG_NOTICE, + "%@: SCDynamicStoreSetDispatchQueue failed", name); + CFRelease(store); + store = NULL; + } + if (store == NULL) { + ObjectWrapperRelease(wrapper); + wrapper = NULL; + } + *ret_wrapper = wrapper; + return (store); +} + +/** + ** CF object glue code + **/ +static CFStringRef __SCNetworkInterfaceProviderCopyDebugDesc(CFTypeRef cf); +static void __SCNetworkInterfaceProviderDeallocate(CFTypeRef cf); + +static CFTypeID __kSCNetworkInterfaceProviderTypeID = _kCFRuntimeNotATypeID; + +static const CFRuntimeClass __SCNetworkInterfaceProviderClass = { + 0, /* version */ + "SCNetworkInterfaceProvider", /* className */ + NULL, /* init */ + NULL, /* copy */ + __SCNetworkInterfaceProviderDeallocate, /* deallocate */ + NULL, /* equal */ + NULL, /* hash */ + NULL, /* copyFormattingDesc */ + __SCNetworkInterfaceProviderCopyDebugDesc /* copyDebugDesc */ +}; + +struct __SCNetworkInterfaceProvider { + CFRuntimeBase cf_base; + + IPMonitorControlRef control; + SCDynamicStoreRef store; + ObjectWrapperRef wrapper; + dispatch_queue_t queue; + + SCNetworkInterfaceProviderEventHandler handler; + CFStringRef if_name; + SCNetworkInterfaceRef if_type; + Boolean enabled; + Boolean needed; +}; + + +static CFStringRef +__SCNetworkInterfaceProviderCopyDebugDesc(CFTypeRef cf) +{ + CFAllocatorRef allocator = CFGetAllocator(cf); + SCNetworkInterfaceProviderRef provider = (SCNetworkInterfaceProviderRef)cf; + CFMutableStringRef result; + + result = CFStringCreateMutable(allocator, 0); + CFStringAppendFormat(result, NULL, + CFSTR(""), + provider->if_type, provider->if_name, cf); + return (result); +} + +static void +SCNetworkInterfaceProviderDeallocate(SCNetworkInterfaceProviderRef provider) +{ + provider->enabled = FALSE; + my_CFRelease(&provider->control); + if (provider->wrapper != NULL) { + ObjectWrapperSetObject(provider->wrapper, NULL); + ObjectWrapperRelease(provider->wrapper); + provider->wrapper = NULL; + } + if (provider->store != NULL) { + SCDynamicStoreSetDispatchQueue(provider->store, NULL); + my_CFRelease(&provider->store); + } + if (provider->queue != NULL) { + dispatch_release(provider->queue); + provider->queue = NULL; + } + if (provider->handler != NULL) { + Block_release(provider->handler); + provider->handler = NULL; + } + my_CFRelease(&provider->if_name); + my_CFRelease(&provider->if_type); +} + +static void +__SCNetworkInterfaceProviderDeallocate(CFTypeRef cf) +{ + SCNetworkInterfaceProviderRef provider = (SCNetworkInterfaceProviderRef)cf; + + if (provider->queue != NULL) { + dispatch_sync(provider->queue, ^{ + SCNetworkInterfaceProviderDeallocate(provider); + }); + } + else { + SCNetworkInterfaceProviderDeallocate(provider); + } + return; +} + +/** + ** Supporting Functions + **/ +static void +__SCNetworkInterfaceProviderRegisterClass(void) +{ + static dispatch_once_t once; + dispatch_block_t once_block; + + once_block = ^{ + __kSCNetworkInterfaceProviderTypeID + = _CFRuntimeRegisterClass(&__SCNetworkInterfaceProviderClass); + }; + dispatch_once(&once, once_block); + return; +} + +static SCNetworkInterfaceProviderRef +__SCNetworkInterfaceProviderAllocate(CFAllocatorRef allocator) +{ + SCNetworkInterfaceProviderRef provider; + int size; + + __SCNetworkInterfaceProviderRegisterClass(); + size = sizeof(*provider) - sizeof(CFRuntimeBase); + provider = (SCNetworkInterfaceProviderRef) + _CFRuntimeCreateInstance(allocator, + __kSCNetworkInterfaceProviderTypeID, + size, NULL); + memset(((void *)provider) + sizeof(CFRuntimeBase), 0, size); + return (provider); +} + +static void +SCNetworkInterfaceProviderCheck(SCNetworkInterfaceProviderRef provider) +{ + Boolean advisory_set; + + if (!provider->enabled || provider->handler == NULL) { + return; + } + advisory_set + = IPMonitorControlAnyInterfaceAdvisoryIsSet(provider->control); + if (provider->needed != advisory_set) { + SCNetworkInterfaceProviderEvent event; + + event = advisory_set + ? kSCNetworkInterfaceProviderEventActivationRequested + : kSCNetworkInterfaceProviderEventActivationNoLongerRequested; + (provider->handler)(event, NULL); + provider->needed = advisory_set; + } + return; +} + +static void +StoreHandleChanges(SCDynamicStoreRef store, CFArrayRef changes, void * info) +{ +#pragma unused(store) +#pragma unused(changes) + SCNetworkInterfaceProviderRef provider; + ObjectWrapperRef wrapper = (ObjectWrapperRef)info; + + provider = (SCNetworkInterfaceProviderRef)ObjectWrapperGetObject(wrapper); + if (provider == NULL) { + /* provider has been deallocated */ + return; + } + SCNetworkInterfaceProviderCheck(provider); + return; +} + + +/** + ** SCNetworkInterfaceProvider SPI + **/ +SCNetworkInterfaceProviderRef +SCNetworkInterfaceProviderCreate(CFStringRef type, + CFStringRef ifname, + CFDictionaryRef options) +{ + IPMonitorControlRef control; + CFStringRef pattern; + CFArrayRef patterns; + SCNetworkInterfaceProviderRef provider; + dispatch_queue_t queue; + SCDynamicStoreRef store = NULL; + ObjectWrapperRef wrapper = NULL; + + if (options != NULL || ifname == NULL || type == NULL) { + _SCErrorSet(kSCStatusInvalidArgument); + return (NULL); + } + control = IPMonitorControlCreate(); + if (control == NULL) { + _SCErrorSet(kSCStatusFailed); + return (NULL); + } + pattern + = IPMonitorControlCopyInterfaceAdvisoryNotificationKey(kSCCompAnyRegex); + patterns = CFArrayCreate(NULL, (const void * *)&pattern, 1, + &kCFTypeArrayCallBacks); + CFRelease(pattern); +#define OUR_NAME "SCNetworkInterfaceProvider" + queue = dispatch_queue_create(OUR_NAME, NULL); + provider = __SCNetworkInterfaceProviderAllocate(NULL); + store = StoreObjectWrapperAllocate(provider, + CFSTR(OUR_NAME), + StoreHandleChanges, + NULL, + patterns, + queue, + &wrapper); + CFRelease(patterns); + if (store == NULL) { + dispatch_release(queue); + CFRelease(provider); + provider = NULL; + CFRelease(control); + } + else { + provider->control = control; + provider->store = store; + provider->wrapper = wrapper; + provider->queue = queue; + provider->if_name = CFRetain(ifname); + provider->if_type = CFRetain(type); + } + return (provider); +} + +void +SCNetworkInterfaceProviderSetEventHandler(SCNetworkInterfaceProviderRef provider, + SCNetworkInterfaceProviderEventHandler handler) +{ + if (handler == NULL) { + /* can't clear handler once set */ + return; + } + dispatch_sync(provider->queue, ^{ + if (provider->enabled) { + /* enabling before setting the handler isn't allowed */ + SC_log(LOG_NOTICE, + "%s: call SCNetworkInterfaceSetEventHandler before " + " SCNetworkInterfaceProviderResume", __FUNCTION__); + return; + } + if (provider->handler != NULL) { + /* can't change the handler once set */ + SC_log(LOG_NOTICE, + "%s: ignoring second invocation of " + "SCNetworkInterfaceSetEventHandler", __FUNCTION__); + return; + } + provider->handler = Block_copy(handler); + }); + return; +} + +void +SCNetworkInterfaceProviderResume(SCNetworkInterfaceProviderRef provider) +{ + dispatch_async(provider->queue, ^{ + if (!provider->enabled) { + provider->enabled = TRUE; + SCNetworkInterfaceProviderCheck(provider); + } + }); + return; +} + +#if TEST_SCNetworkInterfaceProvider + +/* + xcrun -sdk iphoneos.internal cc -o scnip SCNetworkInterfaceProvider.c -DTEST_SCNetworkInterfaceProvider -framework CoreFoundation -framework SystemConfiguration -arch arm64 -I ../IPMonitorControl ../IPMonitorControl/IPMonitorControl.c -DSC_LOG_HANDLE=__log_SCNetworkInterfaceProvider +*/ + +__private_extern__ os_log_t +__log_SCNetworkInterfaceProvider(void) +{ + static os_log_t log = NULL; + + if (log == NULL) { + log = os_log_create("com.apple.SystemConfiguration", "SCNetworkConfiguration"); + } + + return log; +} + +static void +event_handler(SCNetworkInterfaceProviderRef provider, + SCNetworkInterfaceProviderEvent event, + CFDictionaryRef event_data) +{ + printf("<%p> event %d\n", provider, event); +} + +int +main(int argc, char * argv[]) +{ + SCNetworkInterfaceProviderEventHandler handler; + SCNetworkInterfaceProviderRef provider; + + provider + = SCNetworkInterfaceProviderCreate(kSCNetworkInterfaceTypeWWAN, + CFSTR("pdp_ip10"), + NULL); + if (provider == NULL) { + fprintf(stderr, "SCNetworkInterfaceProviderCreate failed\n"); + exit(1); + } + handler = ^(SCNetworkInterfaceProviderEvent event, + CFDictionaryRef event_data) { + event_handler(provider, event, event_data); + }; + SCNetworkInterfaceProviderSetEventHandler(provider, handler); + SCNetworkInterfaceProviderResume(provider); + dispatch_main(); + exit(0); + return (0); +} +#endif