/* -*- Mode: C; tab-width: 4 -*- * * Copyright (c) 2007-2018 Apple Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include "mDNSDebug.h" #include "helper.h" #include #include #include #include // // Implementation Notes about the HelperQueue: // // To prevent blocking the main queue, all communications with mDNSResponderHelper should happen on // HelperQueue. There are a few calls which are still synchronous and needs to be handled separately // case by case. // // When spawning off the work to the HelperQueue, any arguments that are pointers need to be copied // explicitly as they may cease to exist after the call returns. From within the block that is scheduled, // arrays defined on the stack can't be referenced and hence it is enclosed them in a struct. If the array is // an argument to the function, the blocks can reference them as they are passed in as pointers. But care should // be taken to copy them locally as they may cease to exist when the function returns. // //************************************************************************************************************* // Globals static dispatch_queue_t HelperQueue; static xpc_connection_t helper_xpc_conn = NULL; static int64_t maxwait_secs = 5LL; #define mDNSHELPER_DEBUG LogOperation //************************************************************************************************************* // Utility Functions static void HelperLog(const char *prefix, xpc_object_t o) { char *desc = xpc_copy_description(o); mDNSHELPER_DEBUG("HelperLog %s: %s", prefix, desc); free(desc); } //************************************************************************************************************* // XPC Funcs: //************************************************************************************************************* mDNSlocal void Init_Connection(const char *servname) { helper_xpc_conn = xpc_connection_create_mach_service(servname, HelperQueue, XPC_CONNECTION_MACH_SERVICE_PRIVILEGED); xpc_connection_set_event_handler(helper_xpc_conn, ^(xpc_object_t event) { mDNSHELPER_DEBUG("Init_Connection xpc: [%s] \n", xpc_dictionary_get_string(event, XPC_ERROR_KEY_DESCRIPTION)); }); xpc_connection_resume(helper_xpc_conn); } mDNSlocal int SendDict_ToServer(xpc_object_t msg) { __block int errorcode = kHelperErr_NoResponse; HelperLog("SendDict_ToServer Sending msg to Daemon", msg); dispatch_semaphore_t sem = dispatch_semaphore_create(0); dispatch_retain(sem); // for the block below xpc_connection_send_message_with_reply(helper_xpc_conn, msg, HelperQueue, ^(xpc_object_t recv_msg) { xpc_type_t type = xpc_get_type(recv_msg); if (type == XPC_TYPE_DICTIONARY) { HelperLog("SendDict_ToServer Received reply msg from Daemon", recv_msg); uint64_t reply_status = xpc_dictionary_get_uint64(recv_msg, kHelperReplyStatus); errorcode = xpc_dictionary_get_int64(recv_msg, kHelperErrCode); switch (reply_status) { case kHelperReply_ACK: mDNSHELPER_DEBUG("NoError: successful reply"); break; default: LogMsg("default: Unexpected reply from Helper"); break; } } else { LogMsg("SendDict_ToServer Received unexpected reply from daemon [%s]", xpc_dictionary_get_string(recv_msg, XPC_ERROR_KEY_DESCRIPTION)); HelperLog("SendDict_ToServer Unexpected Reply contents", recv_msg); } dispatch_semaphore_signal(sem); dispatch_release(sem); }); if (dispatch_semaphore_wait(sem, dispatch_time(DISPATCH_TIME_NOW, (maxwait_secs * NSEC_PER_SEC))) != 0) LogMsg("SendDict_ToServer: UNEXPECTED WAIT_TIME in dispatch_semaphore_wait"); dispatch_release(sem); mDNSHELPER_DEBUG("SendDict_ToServer returning with errorcode[%d]", errorcode); return errorcode; } mDNSlocal xpc_object_t SendDict_GetReply(xpc_object_t msg) { // Create empty dictionary __block xpc_object_t dict = xpc_dictionary_create(NULL, NULL, 0); if (!dict) return NULL; xpc_retain(dict); HelperLog("SendDict_GetReply Sending msg to Daemon", msg); dispatch_semaphore_t sem = dispatch_semaphore_create(0); dispatch_retain(sem); // for the block below xpc_connection_send_message_with_reply(helper_xpc_conn, msg, HelperQueue, ^(xpc_object_t recv_msg) { xpc_type_t type = xpc_get_type(recv_msg); if (type == XPC_TYPE_DICTIONARY) { HelperLog("SendDict_GetReply Received reply msg from Daemon", recv_msg); uint64_t reply_status = xpc_dictionary_get_uint64(recv_msg, kHelperReplyStatus); switch (reply_status) { case kHelperReply_ACK: mDNSHELPER_DEBUG("NoError: successful reply"); break; default: LogMsg("default: Unexpected reply from Helper"); break; } // Copy result into dict reply xpc_dictionary_apply(recv_msg, ^bool(const char *key, xpc_object_t value) { xpc_dictionary_set_value(dict, key, value); return true; }); } else { LogMsg("SendDict_GetReply Received unexpected reply from daemon [%s]", xpc_dictionary_get_string(recv_msg, XPC_ERROR_KEY_DESCRIPTION)); HelperLog("SendDict_GetReply Unexpected Reply contents", recv_msg); } dispatch_semaphore_signal(sem); dispatch_release(sem); xpc_release(dict); }); if (dispatch_semaphore_wait(sem, dispatch_time(DISPATCH_TIME_NOW, (maxwait_secs * NSEC_PER_SEC))) != 0) { LogMsg("SendDict_GetReply: UNEXPECTED WAIT_TIME in dispatch_semaphore_wait"); xpc_release(dict); dispatch_release(sem); return NULL; } dispatch_release(sem); return dict; } //************************************************************************************************************** mDNSexport mStatus mDNSHelperInit() { HelperQueue = dispatch_queue_create("com.apple.mDNSResponder.HelperQueue", NULL); if (HelperQueue == NULL) { LogMsg("dispatch_queue_create: Helper queue NULL"); return mStatus_NoMemoryErr; } return mStatus_NoError; } void mDNSPreferencesSetName(int key, domainlabel *old, domainlabel *new) { struct { char oldname[MAX_DOMAIN_LABEL+1]; char newname[MAX_DOMAIN_LABEL+1]; } names; mDNSPlatformMemZero(names.oldname, MAX_DOMAIN_LABEL + 1); mDNSPlatformMemZero(names.newname, MAX_DOMAIN_LABEL + 1); ConvertDomainLabelToCString_unescaped(old, names.oldname); if (new) ConvertDomainLabelToCString_unescaped(new, names.newname); mDNSHELPER_DEBUG("mDNSPreferencesSetName: XPC IPC Test oldname %s newname %s", names.oldname, names.newname); Init_Connection(kHelperService); // Create Dictionary To Send xpc_object_t dict = xpc_dictionary_create(NULL, NULL, 0); xpc_dictionary_set_uint64(dict, kHelperMode, set_name); xpc_dictionary_set_uint64(dict, kPrefsNameKey, key); xpc_dictionary_set_string(dict, kPrefsOldName, names.oldname); xpc_dictionary_set_string(dict, kPrefsNewName, names.newname); SendDict_ToServer(dict); xpc_release(dict); dict = NULL; } void mDNSRequestBPF() { mDNSHELPER_DEBUG("mDNSRequestBPF: Using XPC IPC"); Init_Connection(kHelperService); // Create Dictionary To Send xpc_object_t dict = xpc_dictionary_create(NULL, NULL, 0); xpc_dictionary_set_uint64(dict, kHelperMode, bpf_request); SendDict_ToServer(dict); xpc_release(dict); dict = NULL; } int mDNSPowerRequest(int key, int interval) { int err_code = kHelperErr_NotConnected; mDNSHELPER_DEBUG("mDNSPowerRequest: Using XPC IPC calling out to Helper key is [%d] interval is [%d]", key, interval); Init_Connection(kHelperService); // Create Dictionary To Send xpc_object_t dict = xpc_dictionary_create(NULL, NULL, 0); xpc_dictionary_set_uint64(dict, kHelperMode, power_req); xpc_dictionary_set_uint64(dict, "powerreq_key", key); xpc_dictionary_set_uint64(dict, "powerreq_interval", interval); err_code = SendDict_ToServer(dict); xpc_release(dict); dict = NULL; mDNSHELPER_DEBUG("mDNSPowerRequest: Using XPC IPC returning error_code %d", err_code); return err_code; } int mDNSSetLocalAddressCacheEntry(int ifindex, int family, const v6addr_t ip, const ethaddr_t eth) { int err_code = kHelperErr_NotConnected; mDNSHELPER_DEBUG("mDNSSetLocalAddressCacheEntry: Using XPC IPC calling out to Helper: ifindex is [%d] family is [%d]", ifindex, family); Init_Connection(kHelperService); // Create Dictionary To Send xpc_object_t dict = xpc_dictionary_create(NULL, NULL, 0); xpc_dictionary_set_uint64(dict, kHelperMode, set_localaddr_cacheentry); xpc_dictionary_set_uint64(dict, "slace_ifindex", ifindex); xpc_dictionary_set_uint64(dict, "slace_family", family); xpc_dictionary_set_data(dict, "slace_ip", (uint8_t*)ip, sizeof(v6addr_t)); xpc_dictionary_set_data(dict, "slace_eth", (uint8_t*)eth, sizeof(ethaddr_t)); err_code = SendDict_ToServer(dict); xpc_release(dict); dict = NULL; mDNSHELPER_DEBUG("mDNSSetLocalAddressCacheEntry: Using XPC IPC returning error_code %d", err_code); return err_code; } void mDNSNotify(const char *title, const char *msg) // Both strings are UTF-8 text { mDNSHELPER_DEBUG("mDNSNotify() calling out to Helper XPC IPC title[%s] msg[%s]", title, msg); Init_Connection(kHelperService); // Create Dictionary To Send xpc_object_t dict = xpc_dictionary_create(NULL, NULL, 0); xpc_dictionary_set_uint64(dict, kHelperMode, user_notify); xpc_dictionary_set_string(dict, "notify_title", title); xpc_dictionary_set_string(dict, "notify_msg", msg); SendDict_ToServer(dict); xpc_release(dict); dict = NULL; } int mDNSKeychainGetSecrets(CFArrayRef *result) { CFPropertyListRef plist = NULL; CFDataRef bytes = NULL; unsigned int numsecrets = 0; unsigned int secretsCnt = 0; int error_code = kHelperErr_NotConnected; xpc_object_t reply_dict = NULL; const void *sec = NULL; size_t secrets_size = 0; mDNSHELPER_DEBUG("mDNSKeychainGetSecrets: Using XPC IPC calling out to Helper"); Init_Connection(kHelperService); // Create Dictionary To Send xpc_object_t dict = xpc_dictionary_create(NULL, NULL, 0); xpc_dictionary_set_uint64(dict, kHelperMode, keychain_getsecrets); reply_dict = SendDict_GetReply(dict); if (reply_dict != NULL) { numsecrets = xpc_dictionary_get_uint64(reply_dict, "keychain_num_secrets"); sec = xpc_dictionary_get_data(reply_dict, "keychain_secrets", &secrets_size); secretsCnt = xpc_dictionary_get_uint64(reply_dict, "keychain_secrets_count"); error_code = xpc_dictionary_get_int64(reply_dict, kHelperErrCode); } mDNSHELPER_DEBUG("mDNSKeychainGetSecrets: Using XPC IPC calling out to Helper: numsecrets is %d, secretsCnt is %d error_code is %d secret_size is %d", numsecrets, secretsCnt, error_code, secrets_size); if (NULL == (bytes = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, (void*)sec, secretsCnt, kCFAllocatorNull))) { error_code = kHelperErr_ApiErr; LogMsg("mDNSKeychainGetSecrets: CFDataCreateWithBytesNoCopy failed"); goto fin; } if (NULL == (plist = CFPropertyListCreateWithData(kCFAllocatorDefault, bytes, kCFPropertyListImmutable, NULL, NULL))) { error_code = kHelperErr_ApiErr; LogMsg("mDNSKeychainGetSecrets: CFPropertyListCreateFromXMLData failed"); goto fin; } if (CFArrayGetTypeID() != CFGetTypeID(plist)) { error_code = kHelperErr_ApiErr; LogMsg("mDNSKeychainGetSecrets: Unexpected result type"); CFRelease(plist); plist = NULL; goto fin; } *result = (CFArrayRef)plist; fin: if (bytes) CFRelease(bytes); if (dict) xpc_release(dict); if (reply_dict) xpc_release(reply_dict); dict = NULL; reply_dict = NULL; return error_code; } int mDNSAutoTunnelSetKeys(int replacedelete, v6addr_t local_inner, v6addr_t local_outer, short local_port, v6addr_t remote_inner, v6addr_t remote_outer, short remote_port, const char* const prefix, const domainname *const fqdn) { int err_code = kHelperErr_NotConnected; mDNSHELPER_DEBUG("mDNSAutoTunnelSetKeys: Using XPC IPC calling out to Helper. Parameters are repdel[%d], lport[%d], rport[%d], prefix[%s], fqdn[%##s]", replacedelete, local_port, remote_port, prefix, fqdn->c); char buf1[INET6_ADDRSTRLEN]; char buf2[INET6_ADDRSTRLEN]; char buf3[INET6_ADDRSTRLEN]; char buf4[INET6_ADDRSTRLEN]; buf1[0] = 0; buf2[0] = 0; buf3[0] = 0; buf4[0] = 0; inet_ntop(AF_INET6, local_inner, buf1, sizeof(buf1)); inet_ntop(AF_INET6, local_outer, buf2, sizeof(buf2)); inet_ntop(AF_INET6, remote_inner, buf3, sizeof(buf3)); inet_ntop(AF_INET6, remote_outer, buf4, sizeof(buf4)); char fqdnStr[MAX_ESCAPED_DOMAIN_NAME + 10] = { 0 }; // Assume the prefix is no larger than 10 chars if (fqdn) { mDNSPlatformStrCopy(fqdnStr, prefix); ConvertDomainNameToCString(fqdn, fqdnStr + mDNSPlatformStrLen(prefix)); } mDNSHELPER_DEBUG("mDNSAutoTunnelSetKeys: Using XPC IPC calling out to Helper: Parameters are local_inner is %s, local_outeris %s, remote_inner is %s, remote_outer is %s", buf1, buf2, buf3, buf4); Init_Connection(kHelperService); // Create Dictionary To Send xpc_object_t dict = xpc_dictionary_create(NULL, NULL, 0); xpc_dictionary_set_uint64(dict, kHelperMode, autotunnel_setkeys); xpc_dictionary_set_data(dict, "autotunnelsetkeys_localinner", (uint8_t*)local_inner, sizeof(v6addr_t)); xpc_dictionary_set_data(dict, "autotunnelsetkeys_localouter", (uint8_t*)local_outer, sizeof(v6addr_t)); xpc_dictionary_set_data(dict, "autotunnelsetkeys_remoteinner", (uint8_t*)remote_inner, sizeof(v6addr_t)); xpc_dictionary_set_data(dict, "autotunnelsetkeys_remoteouter", (uint8_t*)remote_outer, sizeof(v6addr_t)); xpc_dictionary_set_uint64(dict, "autotunnelsetkeys_lport", local_port); xpc_dictionary_set_uint64(dict, "autotunnelsetkeys_rport", remote_port); xpc_dictionary_set_uint64(dict, "autotunnelsetkeys_repdel", replacedelete); // xpc_dictionary_set_string(dict, "autotunnelsetkeys_prefix", prefix); xpc_dictionary_set_string(dict, "autotunnelsetkeys_fqdnStr", fqdnStr); err_code = SendDict_ToServer(dict); xpc_release(dict); dict = NULL; mDNSHELPER_DEBUG("mDNSAutoTunnelSetKeys: Using XPC IPC returning error_code %d", err_code); mDNSHELPER_DEBUG("mDNSAutoTunnelSetKeys: this should NOT be done in mDNSResponder/Helper. For future we shall be using "); return err_code; } void mDNSSendWakeupPacket(unsigned int ifid, char *eth_addr, char *ip_addr, int iteration) { // (void) ip_addr; // unused // (void) iteration; // unused mDNSHELPER_DEBUG("mDNSSendWakeupPacket: Entered ethernet address[%s],ip_address[%s], interface_id[%d], iteration[%d]", eth_addr, ip_addr, ifid, iteration); Init_Connection(kHelperService); // Create Dictionary To Send xpc_object_t dict = xpc_dictionary_create(NULL, NULL, 0); xpc_dictionary_set_uint64(dict, kHelperMode, send_wakepkt); xpc_dictionary_set_uint64(dict, "interface_index", ifid); xpc_dictionary_set_string(dict, "ethernet_address", eth_addr); xpc_dictionary_set_string(dict, "ip_address", ip_addr); xpc_dictionary_set_uint64(dict, "swp_iteration", iteration); SendDict_ToServer(dict); xpc_release(dict); dict = NULL; } void mDNSPacketFilterControl(uint32_t command, char * ifname, uint32_t count, pfArray_t portArray, pfArray_t protocolArray) { struct { pfArray_t portArray; pfArray_t protocolArray; } pfa; mDNSPlatformMemCopy(pfa.portArray, portArray, sizeof(pfArray_t)); mDNSPlatformMemCopy(pfa.protocolArray, protocolArray, sizeof(pfArray_t)); mDNSHELPER_DEBUG("mDNSPacketFilterControl: XPC IPC, ifname %s", ifname); Init_Connection(kHelperService); // Create Dictionary To Send xpc_object_t dict = xpc_dictionary_create(NULL, NULL, 0); xpc_dictionary_set_uint64(dict, kHelperMode, p2p_packetfilter); xpc_dictionary_set_uint64(dict, "pf_opcode", command); if (ifname) xpc_dictionary_set_string(dict, "pf_ifname", ifname); xpc_dictionary_set_uint64(dict, "pf_count", count); xpc_dictionary_set_uint64(dict, "pf_port0", pfa.portArray[0]); xpc_dictionary_set_uint64(dict, "pf_port1", pfa.portArray[1]); xpc_dictionary_set_uint64(dict, "pf_port2", pfa.portArray[2]); xpc_dictionary_set_uint64(dict, "pf_port3", pfa.portArray[3]); xpc_dictionary_set_uint64(dict, "pf_protocol0", pfa.protocolArray[0]); xpc_dictionary_set_uint64(dict, "pf_protocol1", pfa.protocolArray[1]); xpc_dictionary_set_uint64(dict, "pf_protocol2", pfa.protocolArray[2]); xpc_dictionary_set_uint64(dict, "pf_protocol3", pfa.protocolArray[3]); SendDict_ToServer(dict); xpc_release(dict); dict = NULL; mDNSHELPER_DEBUG("mDNSPacketFilterControl: portArray0[%d] portArray1[%d] portArray2[%d] portArray3[%d] protocolArray0[%d] protocolArray1[%d] protocolArray2[%d] protocolArray3[%d]", pfa.portArray[0], pfa.portArray[1], pfa.portArray[2], pfa.portArray[3], pfa.protocolArray[0], pfa.protocolArray[1], pfa.protocolArray[2], pfa.protocolArray[3]); } void mDNSSendKeepalive(const v6addr_t sadd, const v6addr_t dadd, uint16_t lport, uint16_t rport, uint32_t seq, uint32_t ack, uint16_t win) { mDNSHELPER_DEBUG("mDNSSendKeepalive: Using XPC IPC calling out to Helper: lport is[%d] rport is[%d] seq is[%d] ack is[%d] win is[%d]", lport, rport, seq, ack, win); char buf1[INET6_ADDRSTRLEN]; char buf2[INET6_ADDRSTRLEN]; buf1[0] = 0; buf2[0] = 0; inet_ntop(AF_INET6, sadd, buf1, sizeof(buf1)); inet_ntop(AF_INET6, dadd, buf2, sizeof(buf2)); mDNSHELPER_DEBUG("mDNSSendKeepalive: Using XPC IPC calling out to Helper: sadd is %s, dadd is %s", buf1, buf2); Init_Connection(kHelperService); // Create Dictionary To Send xpc_object_t dict = xpc_dictionary_create(NULL, NULL, 0); xpc_dictionary_set_uint64(dict, kHelperMode, send_keepalive); xpc_dictionary_set_data(dict, "send_keepalive_sadd", (uint8_t*)sadd, sizeof(v6addr_t)); xpc_dictionary_set_data(dict, "send_keepalive_dadd", (uint8_t*)dadd, sizeof(v6addr_t)); xpc_dictionary_set_uint64(dict, "send_keepalive_lport", lport); xpc_dictionary_set_uint64(dict, "send_keepalive_rport", rport); xpc_dictionary_set_uint64(dict, "send_keepalive_seq", seq); xpc_dictionary_set_uint64(dict, "send_keepalive_ack", ack); xpc_dictionary_set_uint64(dict, "send_keepalive_win", win); SendDict_ToServer(dict); xpc_release(dict); dict = NULL; } int mDNSRetrieveTCPInfo(int family, v6addr_t laddr, uint16_t lport, v6addr_t raddr, uint16_t rport, uint32_t *seq, uint32_t *ack, uint16_t *win, int32_t *intfid) { int error_code = kHelperErr_NotConnected; xpc_object_t reply_dict = NULL; mDNSHELPER_DEBUG("mDNSRetrieveTCPInfo: Using XPC IPC calling out to Helper: lport is[%d] rport is[%d] family is[%d]", lport, rport, family); char buf1[INET6_ADDRSTRLEN]; char buf2[INET6_ADDRSTRLEN]; buf1[0] = 0; buf2[0] = 0; inet_ntop(AF_INET6, laddr, buf1, sizeof(buf1)); inet_ntop(AF_INET6, raddr, buf2, sizeof(buf2)); mDNSHELPER_DEBUG("mDNSRetrieveTCPInfo:: Using XPC IPC calling out to Helper: laddr is %s, raddr is %s", buf1, buf2); Init_Connection(kHelperService); // Create Dictionary To Send xpc_object_t dict = xpc_dictionary_create(NULL, NULL, 0); xpc_dictionary_set_uint64(dict, kHelperMode, retreive_tcpinfo); xpc_dictionary_set_data(dict, "retreive_tcpinfo_laddr", (uint8_t*)laddr, sizeof(v6addr_t)); xpc_dictionary_set_data(dict, "retreive_tcpinfo_raddr", (uint8_t*)raddr, sizeof(v6addr_t)); xpc_dictionary_set_uint64(dict, "retreive_tcpinfo_family", family); xpc_dictionary_set_uint64(dict, "retreive_tcpinfo_lport", lport); xpc_dictionary_set_uint64(dict, "retreive_tcpinfo_rport", rport); reply_dict = SendDict_GetReply(dict); if (reply_dict != NULL) { *seq = xpc_dictionary_get_uint64(reply_dict, "retreive_tcpinfo_seq"); *ack = xpc_dictionary_get_uint64(reply_dict, "retreive_tcpinfo_ack"); *win = xpc_dictionary_get_uint64(reply_dict, "retreive_tcpinfo_win"); *intfid = (int32_t)xpc_dictionary_get_uint64(reply_dict, "retreive_tcpinfo_ifid"); error_code = xpc_dictionary_get_int64(reply_dict, kHelperErrCode); } mDNSHELPER_DEBUG("mDNSRetrieveTCPInfo: Using XPC IPC calling out to Helper: seq is %d, ack is %d, win is %d, intfid is %d, error is %d", *seq, *ack, *win, *intfid, error_code); if (dict) xpc_release(dict); if (reply_dict) xpc_release(reply_dict); dict = NULL; reply_dict = NULL; return error_code; }