/* -*- Mode: C; tab-width: 4 -*- * * Copyright (c) 2012-2015 Apple Inc. All rights reserved. * * xpc_services.c * mDNSResponder * * XPC as an IPC mechanism to communicate with Clients. Open only to Apple OSX/iOS clients */ #include "xpc_services.h" #include "dns_xpc.h" #ifndef UNICAST_DISABLED #include "dnsproxy.h" // DNSProxyInit/ProxyUDPCallback/ProxyTCPCallback #include "mDNSMacOSX.h" // KQueueLock/KQueueUnlock #include // xpc_connection_copy_entitlement_value // *************************************************************************** // Globals extern mDNS mDNSStorage; static int dps_client_pid; // To track current active client using DNS Proxy Service static dispatch_queue_t dps_queue = NULL; // *************************************************************************** // prints current XPC Server State mDNSexport void xpcserver_info(mDNS *const m) { LogMsg("----- Active XPC Clients -----"); if (dps_client_pid) LogMsg("DNSProxy->Client[%d]: InputIntfs[%d, %d, %d, %d, %d] Output[%d]", dps_client_pid, m->dp_ipintf[0], m->dp_ipintf[1], m->dp_ipintf[2], m->dp_ipintf[3], m->dp_ipintf[4], m->dp_opintf); } mDNSlocal void ActivateDNSProxy(mDNSu32 IpIfArr[MaxIp], mDNSu32 OpIf, mDNSBool proxy_off) { LogInfo("ActivateDNSProxy: InterfaceIndex List by Client: Input[%d, %d, %d, %d, %d] Output[%d] ", IpIfArr[0], IpIfArr[1], IpIfArr[2], IpIfArr[3], IpIfArr[4], OpIf); KQueueLock(); DNSProxyInit(IpIfArr, OpIf); if (proxy_off) // Open skts only if proxy was OFF else we may end up opening extra skts mDNSPlatformInitDNSProxySkts(ProxyUDPCallback, ProxyTCPCallback); KQueueUnlock("DNSProxy Activated"); } mDNSlocal void handle_dps_terminate() { LogInfo("handle_dps_terminate: Client PID[%d] terminated connection or crashed. Proceed to terminate DNSProxy", dps_client_pid); // Clear the Client's PID, so that we can now accept new DPS requests dps_client_pid = 0; KQueueLock(); mDNSPlatformCloseDNSProxySkts(&mDNSStorage); // TBD: Close TCP Sockets DNSProxyTerminate(); KQueueUnlock("DNSProxy Deactivated"); } mDNSlocal void handle_dps_request(xpc_object_t req) { int dps_tmp_client; mDNSBool proxy_off = mDNSfalse; xpc_connection_t remote_conn = xpc_dictionary_get_remote_connection(req); dps_tmp_client = (int) xpc_connection_get_pid(remote_conn); LogInfo("handle_dps_request: Handler for DNS Proxy Requests"); if (dps_client_pid <= 0) { LogInfo("handle_dps_request: DNSProxy is not engaged (New Client)"); // No Active Client, save new Client's PID (also indicates DNS Proxy was OFF) dps_client_pid = dps_tmp_client; proxy_off = mDNStrue; } else { // We already have an active DNS Proxy Client and until that client does not terminate the connection // or crashes, a new client cannot change/override the current DNS Proxy settings. if (dps_client_pid != dps_tmp_client) { LogMsg("handle_dps_request: A Client is already using DNS Proxy and your request cannot be handled at this time"); // Return Engaged Status to the client xpc_object_t reply = xpc_dictionary_create_reply(req); if (reply) { xpc_dictionary_set_uint64(reply, kDNSDaemonReply, kDNSMsg_Busy); xpc_connection_send_message(remote_conn, reply); xpc_release(reply); } else { LogMsg("handle_dps_request: Reply Dictionary could not be created"); return; } // We do not really need to terminate the connection with the client // as it may try again later which is fine return; } } xpc_object_t response = xpc_dictionary_create_reply(req); // Return Success Status to the client if (response) { xpc_dictionary_set_uint64(response, kDNSDaemonReply, kDNSMsg_NoError); xpc_connection_send_message(remote_conn, response); xpc_release(response); } else { LogMsg("handle_dps_request: Response Dictionary could not be created"); return; } // Proceed to get DNS Proxy Settings from the Client if (xpc_dictionary_get_uint64(req, kDNSProxyParameters)) { mDNSu32 inIf[MaxIp], outIf; inIf[0] = (mDNSu32)xpc_dictionary_get_uint64(req, kDNSInIfindex0); inIf[1] = (mDNSu32)xpc_dictionary_get_uint64(req, kDNSInIfindex1); inIf[2] = (mDNSu32)xpc_dictionary_get_uint64(req, kDNSInIfindex2); inIf[3] = (mDNSu32)xpc_dictionary_get_uint64(req, kDNSInIfindex3); inIf[4] = (mDNSu32)xpc_dictionary_get_uint64(req, kDNSInIfindex4); outIf = (mDNSu32)xpc_dictionary_get_uint64(req, kDNSOutIfindex); ActivateDNSProxy(inIf, outIf, proxy_off); } } // Verify Client's Entitlement mDNSexport mDNSBool IsEntitled(xpc_connection_t conn, const char *password) { mDNSBool entitled = mDNSfalse; xpc_object_t ent = xpc_connection_copy_entitlement_value(conn, password); if (ent) { if (xpc_get_type(ent) == XPC_TYPE_BOOL && xpc_bool_get_value(ent)) { entitled = mDNStrue; } xpc_release(ent); } else { LogMsg("IsEntitled: Client Entitlement is NULL"); } if (!entitled) LogMsg("IsEntitled: Client is missing Entitlement!"); return entitled; } mDNSlocal void accept_dps_client(xpc_connection_t conn) { uid_t c_euid; int c_pid; c_euid = xpc_connection_get_euid(conn); c_pid = xpc_connection_get_pid(conn); if (c_euid != 0 || !IsEntitled(conn, kDNSProxyService)) { LogMsg("accept_dps_client: DNSProxyService Client PID[%d] is missing Entitlement or is not running as root!", c_pid); xpc_connection_cancel(conn); return; } xpc_retain(conn); xpc_connection_set_target_queue(conn, dps_queue); xpc_connection_set_event_handler(conn, ^(xpc_object_t req_msg) { xpc_type_t type = xpc_get_type(req_msg); if (type == XPC_TYPE_DICTIONARY) { handle_dps_request(req_msg); } else // We hit this case ONLY if Client Terminated DPS Connection OR Crashed { LogInfo("accept_dps_client: DPS Client %p teared down the connection or Crashed", (void *) conn); // Only the Client that has activated DPS should be able to terminate it if (c_pid == dps_client_pid) handle_dps_terminate(); xpc_release(conn); } }); xpc_connection_resume(conn); } mDNSlocal void init_dnsproxy_service(void) { xpc_connection_t dps_listener = xpc_connection_create_mach_service(kDNSProxyService, NULL, XPC_CONNECTION_MACH_SERVICE_LISTENER); if (!dps_listener || xpc_get_type(dps_listener) != XPC_TYPE_CONNECTION) { LogMsg("init_dnsproxy_service: Error Creating XPC Listener for DNSProxyService !!"); return; } dps_queue = dispatch_queue_create("com.apple.mDNSResponder.dnsproxyservice_queue", NULL); xpc_connection_set_event_handler(dps_listener, ^(xpc_object_t eventmsg) { xpc_type_t type = xpc_get_type(eventmsg); if (type == XPC_TYPE_CONNECTION) { LogInfo("init_dnsproxy_service: New DNSProxyService Client %p", eventmsg); accept_dps_client(eventmsg); } else if (type == XPC_TYPE_ERROR) // Ideally, we would never hit these cases { LogMsg("init_dnsproxy_service: XPCError: %s", xpc_dictionary_get_string(eventmsg, XPC_ERROR_KEY_DESCRIPTION)); return; } else { LogMsg("init_dnsproxy_service: Unknown EventMsg type"); return; } }); xpc_connection_resume(dps_listener); } mDNSexport void xpc_server_init() { // Add XPC Services here init_dnsproxy_service(); init_dnsctl_service(); } #else // !UNICAST_DISABLED mDNSexport void xpc_server_init() { return; } mDNSexport void xpcserver_info(mDNS *const m) { (void) m; return; } #endif // !UNICAST_DISABLED