SNHelper.c   [plain text]


/*
 * Copyright (c) 2013-2015 Apple Inc.
 * All rights reserved.
 */
#include <errno.h>
#include <syslog.h>
#include <xpc/xpc.h>

#include "SNHelperPrivate.h"

static xpc_connection_t
create_connection(dispatch_queue_t queue)
{
	xpc_connection_t new_connection;

	new_connection = xpc_connection_create_mach_service(kSNHelperService, queue, XPC_CONNECTION_MACH_SERVICE_PRIVILEGED);
	if (isa_xpc_connection(new_connection)) {
		xpc_connection_set_event_handler(new_connection,
			^(xpc_object_t message) {
				if (isa_xpc_error(message)) {
					syslog(LOG_INFO, "Got an error on the SNHelper connection");
				} else if (isa_xpc_dictionary(message)) {
					syslog(LOG_INFO, "Got an unexpected message on the SNHelper connection");
				}
			});
		xpc_connection_resume(new_connection);
	}

	return new_connection;
}

static xpc_object_t
copy_response(xpc_object_t request)
{
	dispatch_queue_t conn_queue = dispatch_queue_create("snhelper request", NULL);
	xpc_connection_t connection = create_connection(conn_queue);
	xpc_object_t response = NULL;

	if (connection) {
		response = xpc_connection_send_message_with_reply_sync(connection, request);
		xpc_connection_cancel(connection);
		xpc_release(connection);
	}

	dispatch_release(conn_queue);

	return response;
}

static int
flow_divert_uuid_policy_operate(const uuid_t uuid, int operation)
{
	int result = 0;
	xpc_object_t request = xpc_dictionary_create(NULL, NULL, 0);
	xpc_object_t response;

	xpc_dictionary_set_uint64(request, kSNHelperMessageType, operation);
	xpc_dictionary_set_uuid(request, kSNHelperMessageUUID, uuid);

	response = copy_response(request);

	if (isa_xpc_dictionary(response)) {
		result = (int)xpc_dictionary_get_int64(response, kSNHelperMessageResult);
	} else {
		result = EINVAL;
	}

	xpc_release(response);
	xpc_release(request);

	return result;
}

int
snhelper_flow_divert_uuid_policy_add(const uuid_t uuid)
{
	return flow_divert_uuid_policy_operate(uuid, kSNHelperMessageTypeFlowDivertUUIDAdd);
}

int
snhelper_flow_divert_uuid_policy_remove(const uuid_t uuid)
{
	return flow_divert_uuid_policy_operate(uuid, kSNHelperMessageTypeFlowDivertUUIDRemove);
}

int
snhelper_flow_divert_uuid_policy_clear(void)
{
	int result = 0;
	xpc_object_t request = xpc_dictionary_create(NULL, NULL, 0);
	xpc_object_t response;

	xpc_dictionary_set_uint64(request, kSNHelperMessageType, kSNHelperMessageTypeFlowDivertUUIDClear);

	response = copy_response(request);

	if (isa_xpc_dictionary(response)) {
		result = (int)xpc_dictionary_get_int64(response, kSNHelperMessageResult);
	} else {
		result = EINVAL;
	}

	xpc_release(response);
	xpc_release(request);

	return result;
}

int
snhelper_get_uuid_for_app(const char *appID, uuid_t uuid)
{
	int result;
	xpc_object_t request = xpc_dictionary_create(NULL, NULL, 0);
	xpc_object_t response;

	xpc_dictionary_set_uint64(request, kSNHelperMessageType, kSNHelperMessageTypeGetUUIDForApp);
	xpc_dictionary_set_string(request, kSNHelperMessageAppID, appID);

	response = copy_response(request);

	if (isa_xpc_dictionary(response)) {
		result = (int)xpc_dictionary_get_int64(response, kSNHelperMessageResult);
		const uint8_t *uuidBytes = xpc_dictionary_get_uuid(response, kSNHelperMessageResultData);
		if (result == 0 && uuid != NULL && uuidBytes != NULL) {
			memcpy(uuid, uuidBytes, sizeof(uuid_t));
		}
	} else {
		result = EINVAL;
	}

	xpc_release(response);
	xpc_release(request);

	return result;
}

static bool
isa_xpc_object_of_type(xpc_object_t obj, xpc_type_t type)
{
	return (obj != NULL && xpc_get_type(obj) == type);
}

bool
isa_xpc_connection(xpc_object_t obj)
{
	return isa_xpc_object_of_type(obj, XPC_TYPE_CONNECTION);
}

bool
isa_xpc_bool(xpc_object_t obj)
{
	return isa_xpc_object_of_type(obj, XPC_TYPE_BOOL);
}

bool
isa_xpc_dictionary(xpc_object_t obj)
{
	return isa_xpc_object_of_type(obj, XPC_TYPE_DICTIONARY);
}

bool
isa_xpc_error(xpc_object_t obj)
{
	return isa_xpc_object_of_type(obj, XPC_TYPE_ERROR);
}