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;
dispatch_queue_t queue;
xpc_connection_t connection;
CFMutableDictionaryRef assertions;
CFMutableDictionaryRef advisories;
};
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->queue != NULL) {
dispatch_release(control->queue);
}
my_CFRelease(&control->advisories);
my_CFRelease(&control->assertions);
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 xpc_object_t
create_request_dictionary(void)
{
const char * progname;
xpc_object_t request;
request = xpc_dictionary_create(NULL, NULL, 0);
progname = getprogname();
if (progname != NULL) {
xpc_dictionary_set_string(request,
kIPMonitorControlRequestKeyProcessName,
progname);
}
return (request);
}
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
_IPMonitorControlSetInterfacePrimaryRank(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) {
my_CFRelease(&control->assertions);
}
}
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 = create_request_dictionary();
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;
}
STATIC void
_IPMonitorControlSetInterfaceAdvisory(IPMonitorControlRef control,
CFStringRef ifname_cf,
SCNetworkInterfaceAdvisory advisory)
{
if (control->advisories == NULL) {
if (advisory == kSCNetworkInterfaceAdvisoryNone) {
return;
}
control->advisories
= CFDictionaryCreateMutable(NULL, 0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
}
if (advisory == kSCNetworkInterfaceAdvisoryNone) {
CFDictionaryRemoveValue(control->advisories, ifname_cf);
if (CFDictionaryGetCount(control->advisories) == 0) {
my_CFRelease(&control->advisories);
}
}
else {
CFNumberRef advisory_cf;
advisory_cf = CFNumberCreate(NULL, kCFNumberSInt32Type, &advisory);
CFDictionarySetValue(control->advisories, ifname_cf, advisory_cf);
CFRelease(advisory_cf);
}
return;
}
STATIC void
ApplyInterfaceAdvisory(const void * key, const void * value, void * context)
{
xpc_connection_t connection = (xpc_connection_t)context;
char ifname[IF_NAMESIZE];
SCNetworkInterfaceAdvisory advisory;
xpc_object_t request;
if (!CFStringGetCString(key, ifname, sizeof(ifname),
kCFStringEncodingUTF8)) {
return;
}
if (!CFNumberGetValue(value, kCFNumberSInt32Type, &advisory)) {
return;
}
request = create_request_dictionary();
xpc_dictionary_set_uint64(request,
kIPMonitorControlRequestKeyType,
kIPMonitorControlRequestTypeSetInterfaceAdvisory);
xpc_dictionary_set_string(request,
kIPMonitorControlRequestKeyInterfaceName,
ifname);
xpc_dictionary_set_uint64(request,
kIPMonitorControlRequestKeyAdvisory,
advisory);
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) {
Boolean retry;
(void)IPMonitorControlHandleResponse(event, TRUE, &retry);
if (retry) {
if (control->assertions != NULL) {
CFDictionaryApplyFunction(control->assertions,
ApplyInterfaceRank,
control->connection);
}
if (control->advisories != NULL) {
CFDictionaryApplyFunction(control->advisories,
ApplyInterfaceAdvisory,
control->connection);
}
}
};
xpc_connection_set_event_handler(connection, handler);
control->connection = connection;
control->queue = queue;
xpc_connection_resume(connection);
return (control);
}
STATIC xpc_object_t
IPMonitorControlSendRequest(IPMonitorControlRef control,
xpc_object_t request)
{
xpc_object_t reply;
while (TRUE) {
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) {
break;
}
xpc_release(reply);
reply = NULL;
if (retry_on_error) {
continue;
}
my_log(LOG_NOTICE, "fatal error");
break;
}
return (reply);
}
PRIVATE_EXTERN Boolean
IPMonitorControlSetInterfacePrimaryRank(IPMonitorControlRef control,
CFStringRef ifname_cf,
SCNetworkServicePrimaryRank rank)
{
char ifname[IF_NAMESIZE];
xpc_object_t reply;
xpc_object_t request;
Boolean success = FALSE;
if (!CFStringGetCString(ifname_cf, ifname, sizeof(ifname),
kCFStringEncodingUTF8)) {
return (FALSE);
}
request = create_request_dictionary();
xpc_dictionary_set_uint64(request,
kIPMonitorControlRequestKeyType,
kIPMonitorControlRequestTypeSetInterfaceRank);
xpc_dictionary_set_string(request,
kIPMonitorControlRequestKeyInterfaceName,
ifname);
xpc_dictionary_set_uint64(request,
kIPMonitorControlRequestKeyPrimaryRank,
rank);
reply = IPMonitorControlSendRequest(control, request);
xpc_release(request);
if (reply != NULL) {
success = TRUE;
xpc_release(reply);
CFRetain(ifname_cf);
CFRetain(control);
dispatch_async(control->queue,
^{
_IPMonitorControlSetInterfacePrimaryRank(control,
ifname_cf,
rank);
CFRelease(ifname_cf);
CFRelease(control);
});
}
return (success);
}
PRIVATE_EXTERN SCNetworkServicePrimaryRank
IPMonitorControlGetInterfacePrimaryRank(IPMonitorControlRef control,
CFStringRef ifname_cf)
{
char ifname[IF_NAMESIZE];
SCNetworkServicePrimaryRank rank;
xpc_object_t reply;
xpc_object_t request;
rank = kSCNetworkServicePrimaryRankDefault;
if (!CFStringGetCString(ifname_cf, ifname, sizeof(ifname),
kCFStringEncodingUTF8)) {
return rank;
}
request = create_request_dictionary();
xpc_dictionary_set_uint64(request,
kIPMonitorControlRequestKeyType,
kIPMonitorControlRequestTypeGetInterfaceRank);
xpc_dictionary_set_string(request,
kIPMonitorControlRequestKeyInterfaceName,
ifname);
reply = IPMonitorControlSendRequest(control, request);
if (reply != NULL) {
rank = (SCNetworkServicePrimaryRank)
xpc_dictionary_get_uint64(reply,
kIPMonitorControlResponseKeyPrimaryRank);
xpc_release(reply);
}
xpc_release(request);
return (rank);
}
PRIVATE_EXTERN Boolean
IPMonitorControlSetInterfaceAdvisory(IPMonitorControlRef control,
CFStringRef ifname_cf,
SCNetworkInterfaceAdvisory advisory,
CFStringRef reason)
{
char ifname[IF_NAMESIZE];
char * reason_str = NULL;
xpc_object_t reply;
xpc_object_t request;
Boolean success = FALSE;
if (!CFStringGetCString(ifname_cf, ifname, sizeof(ifname),
kCFStringEncodingUTF8)) {
return (FALSE);
}
if (reason != NULL) {
reason_str
= _SC_cfstring_to_cstring(reason, NULL, 0, kCFStringEncodingUTF8);
}
request = create_request_dictionary();
xpc_dictionary_set_uint64(request,
kIPMonitorControlRequestKeyType,
kIPMonitorControlRequestTypeSetInterfaceAdvisory);
xpc_dictionary_set_string(request,
kIPMonitorControlRequestKeyInterfaceName,
ifname);
xpc_dictionary_set_uint64(request,
kIPMonitorControlRequestKeyAdvisory,
advisory);
if (reason_str != NULL) {
xpc_dictionary_set_string(request,
kIPMonitorControlRequestKeyReason,
reason_str);
CFAllocatorDeallocate(NULL, reason_str);
}
reply = IPMonitorControlSendRequest(control, request);
xpc_release(request);
if (reply != NULL) {
xpc_release(reply);
success = TRUE;
CFRetain(ifname_cf);
CFRetain(control);
dispatch_async(control->queue,
^{
_IPMonitorControlSetInterfaceAdvisory(control,
ifname_cf,
advisory);
CFRelease(ifname_cf);
CFRelease(control);
});
}
return (success);
}
PRIVATE_EXTERN Boolean
IPMonitorControlInterfaceAdvisoryIsSet(IPMonitorControlRef control,
CFStringRef ifname_cf)
{
char ifname[IF_NAMESIZE];
xpc_object_t reply;
xpc_object_t request;
Boolean is_set = FALSE;
if (!CFStringGetCString(ifname_cf, ifname, sizeof(ifname),
kCFStringEncodingUTF8)) {
return (FALSE);
}
request = create_request_dictionary();
xpc_dictionary_set_uint64(request,
kIPMonitorControlRequestKeyType,
kIPMonitorControlRequestTypeInterfaceAdvisoryIsSet);
xpc_dictionary_set_string(request,
kIPMonitorControlRequestKeyInterfaceName,
ifname);
reply = IPMonitorControlSendRequest(control, request);
xpc_release(request);
if (reply != NULL) {
if (xpc_dictionary_get_bool(reply,
kIPMonitorControlResponseKeyAdvisoryIsSet)) {
is_set = TRUE;
}
xpc_release(reply);
}
return (is_set);
}
PRIVATE_EXTERN Boolean
IPMonitorControlAnyInterfaceAdvisoryIsSet(IPMonitorControlRef control)
{
xpc_object_t reply;
xpc_object_t request;
Boolean is_set = FALSE;
request = create_request_dictionary();
xpc_dictionary_set_uint64(request,
kIPMonitorControlRequestKeyType,
kIPMonitorControlRequestTypeAnyInterfaceAdvisoryIsSet);
reply = IPMonitorControlSendRequest(control, request);
xpc_release(request);
if (reply != NULL) {
if (xpc_dictionary_get_bool(reply,
kIPMonitorControlResponseKeyAdvisoryIsSet)) {
is_set = TRUE;
}
xpc_release(reply);
}
return (is_set);
}
PRIVATE_EXTERN CFStringRef
IPMonitorControlCopyInterfaceAdvisoryNotificationKey(CFStringRef ifname)
{
return (_IPMonitorControlCopyInterfaceAdvisoryNotificationKey(ifname));
}