IPMonitorControl.c [plain text]
#include "IPMonitorControl.h"
#include "IPMonitorControlPrivate.h"
#include "symbol_scope.h"
#include <CoreFoundation/CFRuntime.h>
#include <net/if.h>
#include <xpc/xpc.h>
#include <xpc/private.h>
#include <SystemConfiguration/SCPrivate.h>
#ifdef TEST_IPMONITOR_CONTROL
#define my_log(__level, __format, ...) SCPrint(TRUE, stdout, CFSTR(__format "\n"), ## __VA_ARGS__)
#else
#define my_log(__level, __format, ...) SC_log(__level, __format, ## __VA_ARGS__)
#endif
struct IPMonitorControl {
CFRuntimeBase cf_base;
os_activity_t activity;
dispatch_queue_t queue;
xpc_connection_t connection;
CFMutableDictionaryRef assertions;
};
STATIC CFStringRef __IPMonitorControlCopyDebugDesc(CFTypeRef cf);
STATIC void __IPMonitorControlDeallocate(CFTypeRef cf);
STATIC CFTypeID __kIPMonitorControlTypeID = _kCFRuntimeNotATypeID;
STATIC const CFRuntimeClass __IPMonitorControlClass = {
0,
"IPMonitorControl",
NULL,
NULL,
__IPMonitorControlDeallocate,
NULL,
NULL,
NULL,
__IPMonitorControlCopyDebugDesc
};
STATIC CFStringRef
__IPMonitorControlCopyDebugDesc(CFTypeRef cf)
{
CFAllocatorRef allocator = CFGetAllocator(cf);
IPMonitorControlRef control = (IPMonitorControlRef)cf;
return (CFStringCreateWithFormat(allocator, NULL,
CFSTR("<IPMonitorControl %p>"),
control));
}
STATIC void
__IPMonitorControlDeallocate(CFTypeRef cf)
{
IPMonitorControlRef control = (IPMonitorControlRef)cf;
if (control->connection != NULL) {
xpc_release(control->connection);
}
if (control->activity != NULL) {
os_release(control->activity);
}
if (control->queue != NULL) {
xpc_release(control->queue);
}
return;
}
STATIC void
__IPMonitorControlRegisterClass(void)
{
STATIC dispatch_once_t once;
dispatch_once(&once, ^{
__kIPMonitorControlTypeID
= _CFRuntimeRegisterClass(&__IPMonitorControlClass);
});
return;
}
STATIC IPMonitorControlRef
__IPMonitorControlAllocate(CFAllocatorRef allocator)
{
IPMonitorControlRef control;
int size;
__IPMonitorControlRegisterClass();
size = sizeof(*control) - sizeof(CFRuntimeBase);
control = (IPMonitorControlRef)
_CFRuntimeCreateInstance(allocator,
__kIPMonitorControlTypeID, size, NULL);
return (control);
}
STATIC Boolean
IPMonitorControlHandleResponse(xpc_object_t event, Boolean async,
Boolean * retry_p)
{
Boolean retry = FALSE;
Boolean success = FALSE;
xpc_type_t type;
type = xpc_get_type(event);
if (type == XPC_TYPE_DICTIONARY) {
if (async) {
my_log(LOG_NOTICE, "unexpected message");
}
else {
int64_t error;
error = xpc_dictionary_get_int64(event,
kIPMonitorControlResponseKeyError);
if (error != 0) {
success = FALSE;
#ifdef TEST_IPMONITOR_CONTROL
my_log(LOG_NOTICE, "failure code %lld", error);
#endif
}
else {
success = TRUE;
}
}
}
else if (type == XPC_TYPE_ERROR) {
if (event == XPC_ERROR_CONNECTION_INTERRUPTED) {
#ifdef TEST_IPMONITOR_CONTROL
my_log(LOG_NOTICE, "can retry");
#endif
retry = TRUE;
}
else {
const char * desc;
desc = xpc_dictionary_get_string(event, XPC_ERROR_KEY_DESCRIPTION);
my_log(LOG_NOTICE, "%s", desc);
}
}
else {
my_log(LOG_NOTICE, "unknown event type : %p", type);
}
if (retry_p != NULL) {
*retry_p = retry;
}
return (success);
}
STATIC void
IPMonitorControlSetInterfaceRank(IPMonitorControlRef control,
CFStringRef ifname_cf,
SCNetworkServicePrimaryRank rank)
{
if (control->assertions == NULL) {
if (rank == kSCNetworkServicePrimaryRankDefault) {
return;
}
control->assertions
= CFDictionaryCreateMutable(NULL, 0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
}
if (rank == kSCNetworkServicePrimaryRankDefault) {
CFDictionaryRemoveValue(control->assertions, ifname_cf);
if (CFDictionaryGetCount(control->assertions) == 0) {
CFRelease(control->assertions);
control->assertions = NULL;
}
}
else {
CFNumberRef rank_cf;
rank_cf = CFNumberCreate(NULL, kCFNumberSInt32Type, &rank);
CFDictionarySetValue(control->assertions, ifname_cf, rank_cf);
CFRelease(rank_cf);
}
return;
}
STATIC void
ApplyInterfaceRank(const void * key, const void * value, void * context)
{
xpc_connection_t connection = (xpc_connection_t)context;
char ifname[IF_NAMESIZE];
SCNetworkServicePrimaryRank rank;
xpc_object_t request;
if (!CFStringGetCString(key, ifname, sizeof(ifname),
kCFStringEncodingUTF8)) {
return;
}
if (!CFNumberGetValue(value, kCFNumberSInt32Type, &rank)) {
return;
}
request = xpc_dictionary_create(NULL, NULL, 0);
xpc_dictionary_set_uint64(request,
kIPMonitorControlRequestKeyType,
kIPMonitorControlRequestTypeSetInterfaceRank);
xpc_dictionary_set_string(request,
kIPMonitorControlRequestKeyInterfaceName,
ifname);
xpc_dictionary_set_uint64(request,
kIPMonitorControlRequestKeyPrimaryRank,
rank);
xpc_connection_send_message(connection, request);
xpc_release(request);
return;
}
PRIVATE_EXTERN IPMonitorControlRef
IPMonitorControlCreate(void)
{
xpc_connection_t connection;
IPMonitorControlRef control;
uint64_t flags = XPC_CONNECTION_MACH_SERVICE_PRIVILEGED;
xpc_handler_t handler;
dispatch_queue_t queue;
control = __IPMonitorControlAllocate(NULL);
queue = dispatch_queue_create("IPMonitorControl", NULL);
connection
= xpc_connection_create_mach_service(kIPMonitorControlServerName,
queue, flags);
handler = ^(xpc_object_t event) {
os_activity_t activity;
Boolean retry;
activity = os_activity_create("processing IPMonitor [rank] reply",
OS_ACTIVITY_CURRENT,
OS_ACTIVITY_FLAG_DEFAULT);
os_activity_scope(activity);
(void)IPMonitorControlHandleResponse(event, TRUE, &retry);
if (retry && control->assertions != NULL) {
CFDictionaryApplyFunction(control->assertions,
ApplyInterfaceRank,
control->connection);
}
os_release(activity);
};
xpc_connection_set_event_handler(connection, handler);
control->activity = os_activity_create("accessing IPMonitor [rank] controls",
OS_ACTIVITY_CURRENT,
OS_ACTIVITY_FLAG_DEFAULT);
control->connection = connection;
control->queue = queue;
xpc_connection_resume(connection);
return (control);
}
PRIVATE_EXTERN Boolean
IPMonitorControlSetInterfacePrimaryRank(IPMonitorControlRef control,
CFStringRef ifname_cf,
SCNetworkServicePrimaryRank rank)
{
char ifname[IF_NAMESIZE];
xpc_object_t request;
Boolean success = FALSE;
if (!CFStringGetCString(ifname_cf, ifname, sizeof(ifname),
kCFStringEncodingUTF8)) {
return (FALSE);
}
os_activity_scope(control->activity);
request = xpc_dictionary_create(NULL, NULL, 0);
xpc_dictionary_set_uint64(request,
kIPMonitorControlRequestKeyType,
kIPMonitorControlRequestTypeSetInterfaceRank);
xpc_dictionary_set_string(request,
kIPMonitorControlRequestKeyInterfaceName,
ifname);
xpc_dictionary_set_uint64(request,
kIPMonitorControlRequestKeyPrimaryRank,
rank);
while (TRUE) {
xpc_object_t reply;
Boolean retry_on_error = FALSE;
reply = xpc_connection_send_message_with_reply_sync(control->connection,
request);
if (reply == NULL) {
my_log(LOG_NOTICE, "failed to send message");
break;
}
success = IPMonitorControlHandleResponse(reply, FALSE,
&retry_on_error);
xpc_release(reply);
if (success) {
break;
}
if (retry_on_error) {
continue;
}
my_log(LOG_NOTICE, "fatal error");
break;
}
xpc_release(request);
if (success) {
CFRetain(ifname_cf);
CFRetain(control);
dispatch_async(control->queue,
^{
IPMonitorControlSetInterfaceRank(control,
ifname_cf,
rank);
CFRelease(ifname_cf);
CFRelease(control);
});
}
return (success);
}
SCNetworkServicePrimaryRank
IPMonitorControlGetInterfacePrimaryRank(IPMonitorControlRef control,
CFStringRef ifname_cf)
{
char ifname[IF_NAMESIZE];
SCNetworkServicePrimaryRank rank;
xpc_object_t request;
rank = kSCNetworkServicePrimaryRankDefault;
if (!CFStringGetCString(ifname_cf, ifname, sizeof(ifname),
kCFStringEncodingUTF8)) {
return rank;
}
os_activity_scope(control->activity);
request = xpc_dictionary_create(NULL, NULL, 0);
xpc_dictionary_set_uint64(request,
kIPMonitorControlRequestKeyType,
kIPMonitorControlRequestTypeGetInterfaceRank);
xpc_dictionary_set_string(request,
kIPMonitorControlRequestKeyInterfaceName,
ifname);
while (TRUE) {
xpc_object_t reply;
Boolean retry_on_error = FALSE;
Boolean success;
reply = xpc_connection_send_message_with_reply_sync(control->connection,
request);
if (reply == NULL) {
my_log(LOG_NOTICE, "failed to send message");
break;
}
success = IPMonitorControlHandleResponse(reply, FALSE, &retry_on_error);
if (success) {
rank = (SCNetworkServicePrimaryRank)
xpc_dictionary_get_uint64(reply,
kIPMonitorControlResponseKeyPrimaryRank);
}
xpc_release(reply);
if (success) {
break;
}
if (retry_on_error) {
continue;
}
break;
}
xpc_release(request);
return (rank);
}