Something went wrong. Try again.
Cider Isn't Darwin Emulation, Really
Something went wrong. Try again.
15 kB · 552 lines
C
at android
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553/* * Copyright (c) 2013-2014 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@ */
/* * IPMonitorControlServer.c * - IPC channel to IPMonitor * - used to create interface rank assertions */
/* * Modification History * * December 16, 2013 Dieter Siegmund (dieter@apple.com) * - initial revision */
#include <CoreFoundation/CoreFoundation.h>#include <xpc/xpc.h>#include <xpc/private.h>#include <sys/queue.h>#include <CoreFoundation/CFRunLoop.h>#include <SystemConfiguration/SCNetworkConfigurationPrivate.h>#include "IPMonitorControlServer.h"#include "symbol_scope.h"#include "IPMonitorControlPrivate.h"#include <SystemConfiguration/SCPrivate.h>
STATIC Boolean * S_verbose;
#ifdef TEST_IPMONITOR_CONTROL#define my_log(__level, fmt, ...) SCPrint(TRUE, stdout, CFSTR(fmt "\n"), ## __VA_ARGS__)
#else /* TEST_IPMONITOR_CONTROL */#include "ip_plugin.h"#endif /* TEST_IPMONITOR_CONTROL */
STATIC dispatch_queue_t S_IPMonitorControlServerQueue;
typedef struct ControlSession ControlSession, * ControlSessionRef;
#define LIST_HEAD_ControlSession LIST_HEAD(ControlSessionHead, ControlSession)#define LIST_ENTRY_ControlSession LIST_ENTRY(ControlSession)LIST_HEAD_ControlSession S_ControlSessions;
struct ControlSession { LIST_ENTRY_ControlSession link; xpc_connection_t connection; CFMutableDictionaryRef assertions; /* ifname<string> = rank<number> */};
/** ** Support Functions **/STATIC CFMutableArrayRef S_if_changes;STATIC CFRange S_if_changes_range;
STATIC voidInterfaceChangedListAddInterface(CFStringRef ifname){ if (S_if_changes == NULL) { S_if_changes = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); CFArrayAppendValue(S_if_changes, ifname); S_if_changes_range.length = 1; } else if (CFArrayContainsValue(S_if_changes, S_if_changes_range, ifname) == FALSE) { CFArrayAppendValue(S_if_changes, ifname); S_if_changes_range.length++; }}
STATIC CFArrayRefInterfaceChangedListCopy(void){ CFArrayRef current_list;
current_list = S_if_changes; S_if_changes = NULL; return (current_list);}
STATIC voidInterfaceRankAssertionAdd(const void * key, const void * value, void * context){ CFMutableDictionaryRef * assertions_p; CFNumberRef existing_rank; CFNumberRef rank = (CFNumberRef)value;
assertions_p = (CFMutableDictionaryRef *)context; if (*assertions_p == NULL) { *assertions_p = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); CFDictionarySetValue(*assertions_p, key, value); return; } existing_rank = CFDictionaryGetValue(*assertions_p, key); if (existing_rank == NULL || (CFNumberCompare(rank, existing_rank, NULL) == kCFCompareGreaterThan)) { CFDictionarySetValue(*assertions_p, key, value); } return;}
STATIC CFDictionaryRefInterfaceRankAssertionsCopy(void){ CFMutableDictionaryRef assertions = NULL; ControlSessionRef session;
LIST_FOREACH(session, &S_ControlSessions, link) { if (session->assertions == NULL) { continue; } CFDictionaryApplyFunction(session->assertions, InterfaceRankAssertionAdd, &assertions); } return (assertions);}
STATIC CFRunLoopRef S_runloop;STATIC CFRunLoopSourceRef S_signal_source;
STATIC voidSetNotificationInfo(CFRunLoopRef runloop, CFRunLoopSourceRef rls){ S_runloop = runloop; S_signal_source = rls; return;}
STATIC voidGenerateNotification(void){ if (S_signal_source != NULL) { CFRunLoopSourceSignal(S_signal_source); if (S_runloop != NULL) { CFRunLoopWakeUp(S_runloop); } } return;}
/** ** ControlSession **/STATIC voidAddChangedInterface(const void * key, const void * value, void * context){ InterfaceChangedListAddInterface((CFStringRef)key); return;}
STATIC voidControlSessionInvalidate(ControlSessionRef session){ if (*S_verbose) { my_log(LOG_NOTICE, "Invalidating %p", session); } LIST_REMOVE(session, link); if (session->assertions != NULL) { my_log(LOG_DEBUG, "IPMonitorControlServer: %p pid %d removing assertions %@", session->connection, xpc_connection_get_pid(session->connection), session->assertions); CFDictionaryApplyFunction(session->assertions, AddChangedInterface, NULL); CFRelease(session->assertions); session->assertions = NULL; GenerateNotification(); } return;}
STATIC voidControlSessionRelease(void * p){ if (*S_verbose) { my_log(LOG_NOTICE, "Releasing %p", p); } free(p); return;}
STATIC ControlSessionRefControlSessionLookup(xpc_connection_t connection){ return ((ControlSessionRef)xpc_connection_get_context(connection));}
STATIC ControlSessionRefControlSessionCreate(xpc_connection_t connection){ ControlSessionRef session;
session = (ControlSessionRef)malloc(sizeof(*session)); bzero(session, sizeof(*session)); session->connection = connection; xpc_connection_set_finalizer_f(connection, ControlSessionRelease); xpc_connection_set_context(connection, session); LIST_INSERT_HEAD(&S_ControlSessions, session, link); if (*S_verbose) { my_log(LOG_NOTICE, "Created %p (connection %p)", session, connection); } return (session);}
STATIC ControlSessionRefControlSessionGet(xpc_connection_t connection){ ControlSessionRef session;
session = ControlSessionLookup(connection); if (session != NULL) { return (session); } return (ControlSessionCreate(connection));}
STATIC voidControlSessionSetInterfaceRank(ControlSessionRef session, const char * ifname, SCNetworkServicePrimaryRank rank){ CFStringRef ifname_cf;
if (session->assertions == NULL) { if (rank == kSCNetworkServicePrimaryRankDefault) { /* no assertions, no need to store rank */ return; } session->assertions = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); } ifname_cf = CFStringCreateWithCString(NULL, ifname, kCFStringEncodingUTF8); if (rank == kSCNetworkServicePrimaryRankDefault) { CFDictionaryRemoveValue(session->assertions, ifname_cf); if (CFDictionaryGetCount(session->assertions) == 0) { CFRelease(session->assertions); session->assertions = NULL; } } else { CFNumberRef rank_cf;
rank_cf = CFNumberCreate(NULL, kCFNumberSInt32Type, &rank); CFDictionarySetValue(session->assertions, ifname_cf, rank_cf); CFRelease(rank_cf); } InterfaceChangedListAddInterface(ifname_cf); GenerateNotification(); CFRelease(ifname_cf); return;}
STATIC SCNetworkServicePrimaryRankControlSessionGetInterfaceRank(ControlSessionRef session, const char * ifname){ SCNetworkServicePrimaryRank rank = kSCNetworkServicePrimaryRankDefault;
if (session->assertions != NULL) { CFStringRef ifname_cf; CFNumberRef rank_cf;
ifname_cf = CFStringCreateWithCString(NULL, ifname, kCFStringEncodingUTF8); rank_cf = CFDictionaryGetValue(session->assertions, ifname_cf); CFRelease(ifname_cf); if (rank_cf != NULL) { (void)CFNumberGetValue(rank_cf, kCFNumberSInt32Type, &rank); } } return (rank);}
/** ** IPMonitorControlServer **/STATIC BooleanIPMonitorControlServerValidateConnection(xpc_connection_t connection){ uid_t uid;
uid = xpc_connection_get_euid(connection); return (uid == 0);}
STATIC intIPMonitorControlServerHandleSetInterfaceRank(xpc_connection_t connection, xpc_object_t request, xpc_object_t reply){ const char * ifname; SCNetworkServicePrimaryRank rank; ControlSessionRef session;
if (IPMonitorControlServerValidateConnection(connection) == FALSE) { my_log(LOG_DEBUG, "IPMonitorControlServer: %p pid %d permission denied", connection, xpc_connection_get_pid(connection)); return (EPERM); } ifname = xpc_dictionary_get_string(request, kIPMonitorControlRequestKeyInterfaceName); if (ifname == NULL) { return (EINVAL); } rank = (SCNetworkServicePrimaryRank) xpc_dictionary_get_uint64(request, kIPMonitorControlRequestKeyPrimaryRank); switch (rank) { case kSCNetworkServicePrimaryRankDefault: case kSCNetworkServicePrimaryRankFirst: case kSCNetworkServicePrimaryRankLast: case kSCNetworkServicePrimaryRankNever: case kSCNetworkServicePrimaryRankScoped: break; default: return (EINVAL); } session = ControlSessionGet(connection); ControlSessionSetInterfaceRank(session, ifname, rank); my_log(LOG_DEBUG, "IPMonitorControlServer: %p pid %d set %s %u", connection, xpc_connection_get_pid(connection), ifname, rank); return (0);}
STATIC intIPMonitorControlServerHandleGetInterfaceRank(xpc_connection_t connection, xpc_object_t request, xpc_object_t reply){ const char * ifname; SCNetworkServicePrimaryRank rank; ControlSessionRef session;
if (reply == NULL) { /* no point in processing the request if we can't provide an answer */ return (EINVAL); } session = ControlSessionLookup(connection); if (session == NULL) { /* no session, no rank assertion */ return (ENOENT); } ifname = xpc_dictionary_get_string(request, kIPMonitorControlRequestKeyInterfaceName); if (ifname == NULL) { return (EINVAL); } rank = ControlSessionGetInterfaceRank(session, ifname); xpc_dictionary_set_uint64(reply, kIPMonitorControlResponseKeyPrimaryRank, rank); return (0);}
STATIC voidIPMonitorControlServerHandleDisconnect(xpc_connection_t connection){ ControlSessionRef session;
if (*S_verbose) { my_log(LOG_NOTICE, "IPMonitorControlServer: client %p went away", connection); } session = ControlSessionLookup(connection); if (session == NULL) { /* never asserted anything */ return; } ControlSessionInvalidate(session); return;}
STATIC voidIPMonitorControlServerHandleRequest(xpc_connection_t connection, xpc_object_t request){ xpc_type_t type; type = xpc_get_type(request); if (type == XPC_TYPE_DICTIONARY) { int error = 0; uint64_t request_type; xpc_connection_t remote; xpc_object_t reply; request_type = xpc_dictionary_get_uint64(request, kIPMonitorControlRequestKeyType); reply = xpc_dictionary_create_reply(request); switch (request_type) { case kIPMonitorControlRequestTypeSetInterfaceRank: error = IPMonitorControlServerHandleSetInterfaceRank(connection, request, reply); break; case kIPMonitorControlRequestTypeGetInterfaceRank: error = IPMonitorControlServerHandleGetInterfaceRank(connection, request, reply); break; default: error = EINVAL; break; } if (reply == NULL) { /* client didn't want a reply */ return; } xpc_dictionary_set_int64(reply, kIPMonitorControlResponseKeyError, error); remote = xpc_dictionary_get_remote_connection(request); xpc_connection_send_message(remote, reply); xpc_release(reply); } else if (type == XPC_TYPE_ERROR) { if (request == XPC_ERROR_CONNECTION_INVALID) { IPMonitorControlServerHandleDisconnect(connection); } else if (request == XPC_ERROR_CONNECTION_INTERRUPTED) { my_log(LOG_NOTICE, "IPMonitorControlServer: connection interrupted"); } } else { my_log(LOG_NOTICE, "IPMonitorControlServer: unexpected event"); } return;}
STATIC voidIPMonitorControlServerHandleNewConnection(xpc_connection_t connection){ xpc_handler_t handler;
handler = ^(xpc_object_t event) { IPMonitorControlServerHandleRequest(connection, event); }; xpc_connection_set_event_handler(connection, handler); xpc_connection_resume(connection); return;}
STATIC xpc_connection_tIPMonitorControlServerCreate(dispatch_queue_t queue, const char * name){ uint64_t flags = XPC_CONNECTION_MACH_SERVICE_LISTENER; xpc_connection_t connection; xpc_handler_t handler;
connection = xpc_connection_create_mach_service(name, queue, flags); if (connection == NULL) { return (NULL); } handler = ^(xpc_object_t event) { xpc_type_t type; type = xpc_get_type(event); if (type == XPC_TYPE_CONNECTION) { IPMonitorControlServerHandleNewConnection(event); } else if (type == XPC_TYPE_ERROR) { const char * desc; desc = xpc_dictionary_get_string(event, XPC_ERROR_KEY_DESCRIPTION); if (event == XPC_ERROR_CONNECTION_INVALID) { my_log(LOG_NOTICE, "IPMonitorControlServer: %s", desc); xpc_release(connection); } else { my_log(LOG_NOTICE, "IPMonitorControlServer: %s", desc); } } else { my_log(LOG_NOTICE, "IPMonitorControlServer: unknown event %p", type); } }; S_IPMonitorControlServerQueue = queue; xpc_connection_set_event_handler(connection, handler); xpc_connection_resume(connection); return (connection);}
PRIVATE_EXTERN BooleanIPMonitorControlServerStart(CFRunLoopRef runloop, CFRunLoopSourceRef rls, Boolean * verbose){ dispatch_queue_t q; xpc_connection_t connection;
S_verbose = verbose; SetNotificationInfo(runloop, rls); q = dispatch_queue_create("IPMonitorControlServer", NULL); connection = IPMonitorControlServerCreate(q, kIPMonitorControlServerName); if (connection == NULL) { my_log(LOG_ERR, "IPMonitorControlServer: failed to create server"); dispatch_release(q); return (FALSE); } return (TRUE);}
PRIVATE_EXTERN CFArrayRefIPMonitorControlServerCopyInterfaceRankInformation(CFDictionaryRef * info){ __block CFArrayRef changed; __block CFDictionaryRef dict;
dispatch_sync(S_IPMonitorControlServerQueue, ^{ dict = InterfaceRankAssertionsCopy(); changed = InterfaceChangedListCopy(); }); *info = dict; return (changed);}