| /* |
| * Copyright (c) 2007-2012 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 <mach/mach.h> |
| #include <mach/mach_error.h> |
| #include <mach/vm_map.h> |
| #include <servers/bootstrap.h> |
| #include <IOKit/IOReturn.h> |
| #include <CoreFoundation/CoreFoundation.h> |
| #include "mDNSDebug.h" |
| #include "helper.h" |
| #include "helpermsg.h" |
| |
| #define ERROR(x, y) y, |
| static const char *errorstring[] = |
| { |
| #include "helper-error.h" |
| NULL |
| }; |
| #undef ERROR |
| |
| static mach_port_t getHelperPort(int retry) |
| { |
| static mach_port_t port = MACH_PORT_NULL; |
| if (retry) port = MACH_PORT_NULL; |
| if (port == MACH_PORT_NULL && BOOTSTRAP_SUCCESS != bootstrap_look_up(bootstrap_port, kmDNSHelperServiceName, &port)) |
| LogMsg("%s: cannot contact helper", __func__); |
| return port; |
| } |
| |
| const char *mDNSHelperError(int err) |
| { |
| static const char *p = "<unknown error>"; |
| if (mDNSHelperErrorBase < err && mDNSHelperErrorEnd > err) |
| p = errorstring[err - mDNSHelperErrorBase - 1]; |
| return p; |
| } |
| |
| /* Ugly but handy. */ |
| // We don't bother reporting kIOReturnNotReady because that error code occurs in "normal" operation |
| // and doesn't indicate anything unexpected that needs to be investigated |
| |
| #define MACHRETRYLOOP_BEGIN(kr, retry, err, fin) \ |
| for (;;) \ |
| { |
| #define MACHRETRYLOOP_END(kr, retry, err, fin) \ |
| if (KERN_SUCCESS == (kr)) break; \ |
| else if (MACH_SEND_INVALID_DEST == (kr) && 0 == (retry)++) continue; \ |
| else \ |
| { \ |
| (err) = kmDNSHelperCommunicationFailed; \ |
| LogMsg("%s: Mach communication failed: %d %X %s", __func__, kr, kr, mach_error_string(kr)); \ |
| goto fin; \ |
| } \ |
| } \ |
| if (0 != (err) && kIOReturnNotReady != (err)) \ |
| { LogMsg("%s: %d 0x%X (%s)", __func__, (err), (err), mDNSHelperError(err)); goto fin; } |
| |
| void mDNSPreferencesSetName(int key, domainlabel *old, domainlabel *new) |
| { |
| kern_return_t kr = KERN_FAILURE; |
| int retry = 0; |
| int err = 0; |
| char oldname[MAX_DOMAIN_LABEL+1] = {0}; |
| char newname[MAX_DOMAIN_LABEL+1] = {0}; |
| ConvertDomainLabelToCString_unescaped(old, oldname); |
| if (new) ConvertDomainLabelToCString_unescaped(new, newname); |
| |
| MACHRETRYLOOP_BEGIN(kr, retry, err, fin); |
| kr = proxy_mDNSPreferencesSetName(getHelperPort(retry), key, oldname, newname); |
| MACHRETRYLOOP_END(kr, retry, err, fin); |
| |
| fin: |
| (void)err; |
| } |
| |
| void mDNSDynamicStoreSetConfig(int key, const char *subkey, CFPropertyListRef value) |
| { |
| CFWriteStreamRef stream = NULL; |
| CFDataRef bytes = NULL; |
| kern_return_t kr = KERN_FAILURE; |
| int retry = 0; |
| int err = 0; |
| |
| if (NULL == (stream = CFWriteStreamCreateWithAllocatedBuffers(NULL, NULL))) |
| { |
| err = kmDNSHelperCreationFailed; |
| LogMsg("%s: CFWriteStreamCreateWithAllocatedBuffers failed", __func__); |
| goto fin; |
| } |
| CFWriteStreamOpen(stream); |
| if (0 == CFPropertyListWriteToStream(value, stream, kCFPropertyListBinaryFormat_v1_0, NULL)) |
| { |
| err = kmDNSHelperPListWriteFailed; |
| LogMsg("%s: CFPropertyListWriteToStream failed", __func__); |
| goto fin; |
| } |
| if (NULL == (bytes = CFWriteStreamCopyProperty(stream, kCFStreamPropertyDataWritten))) |
| { |
| err = kmDNSHelperCreationFailed; |
| LogMsg("%s: CFWriteStreamCopyProperty failed", __func__); |
| goto fin; |
| } |
| CFWriteStreamClose(stream); |
| CFRelease(stream); |
| stream = NULL; |
| MACHRETRYLOOP_BEGIN(kr, retry, err, fin); |
| kr = proxy_mDNSDynamicStoreSetConfig(getHelperPort(retry), key, subkey ? subkey : "", (vm_offset_t)CFDataGetBytePtr(bytes), CFDataGetLength(bytes)); |
| MACHRETRYLOOP_END(kr, retry, err, fin); |
| |
| fin: |
| if (NULL != stream) { CFWriteStreamClose(stream); CFRelease(stream); } |
| if (NULL != bytes) CFRelease(bytes); |
| (void)err; |
| } |
| |
| void mDNSRequestBPF(void) |
| { |
| kern_return_t kr = KERN_FAILURE; |
| int retry = 0, err = 0; |
| MACHRETRYLOOP_BEGIN(kr, retry, err, fin); |
| kr = proxy_mDNSRequestBPF(getHelperPort(retry)); |
| MACHRETRYLOOP_END(kr, retry, err, fin); |
| fin: |
| (void)err; |
| } |
| |
| int mDNSPowerRequest(int key, int interval) |
| { |
| kern_return_t kr = KERN_FAILURE; |
| int retry = 0, err = 0; |
| MACHRETRYLOOP_BEGIN(kr, retry, err, fin); |
| kr = proxy_mDNSPowerRequest(getHelperPort(retry), key, interval, &err); |
| MACHRETRYLOOP_END(kr, retry, err, fin); |
| fin: |
| return err; |
| } |
| |
| int mDNSSetLocalAddressCacheEntry(int ifindex, int family, const v6addr_t ip, const ethaddr_t eth) |
| { |
| kern_return_t kr = KERN_FAILURE; |
| int retry = 0, err = 0; |
| MACHRETRYLOOP_BEGIN(kr, retry, err, fin); |
| kr = proxy_mDNSSetLocalAddressCacheEntry(getHelperPort(retry), ifindex, family, (uint8_t*)ip, (uint8_t*)eth, &err); |
| MACHRETRYLOOP_END(kr, retry, err, fin); |
| fin: |
| return err; |
| } |
| |
| void mDNSNotify(const char *title, const char *msg) // Both strings are UTF-8 text |
| { |
| kern_return_t kr = KERN_FAILURE; |
| int retry = 0, err = 0; |
| MACHRETRYLOOP_BEGIN(kr, retry, err, fin); |
| kr = proxy_mDNSNotify(getHelperPort(retry), title, msg); |
| MACHRETRYLOOP_END(kr, retry, err, fin); |
| fin: |
| (void)err; |
| } |
| |
| int mDNSKeychainGetSecrets(CFArrayRef *result) |
| { |
| CFPropertyListRef plist = NULL; |
| CFDataRef bytes = NULL; |
| kern_return_t kr = KERN_FAILURE; |
| unsigned int numsecrets = 0; |
| vm_offset_t secrets = 0; |
| mach_msg_type_number_t secretsCnt = 0; |
| int retry = 0, err = 0; |
| |
| MACHRETRYLOOP_BEGIN(kr, retry, err, fin); |
| kr = proxy_mDNSKeychainGetSecrets(getHelperPort(retry), &numsecrets, &secrets, &secretsCnt, &err); |
| MACHRETRYLOOP_END(kr, retry, err, fin); |
| |
| if (NULL == (bytes = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, (void*)secrets, secretsCnt, kCFAllocatorNull))) |
| { |
| err = kmDNSHelperCreationFailed; |
| LogMsg("%s: CFDataCreateWithBytesNoCopy failed", __func__); |
| goto fin; |
| } |
| if (NULL == (plist = CFPropertyListCreateFromXMLData(kCFAllocatorDefault, bytes, kCFPropertyListImmutable, NULL))) |
| { |
| err = kmDNSHelperInvalidPList; |
| LogMsg("%s: CFPropertyListCreateFromXMLData failed", __func__); |
| goto fin; |
| } |
| if (CFArrayGetTypeID() != CFGetTypeID(plist)) |
| { |
| err = kmDNSHelperTypeError; |
| LogMsg("%s: Unexpected result type", __func__); |
| CFRelease(plist); |
| plist = NULL; |
| goto fin; |
| } |
| *result = (CFArrayRef)plist; |
| |
| fin: |
| if (bytes) CFRelease(bytes); |
| if (secrets) vm_deallocate(mach_task_self(), secrets, secretsCnt); |
| return err; |
| } |
| |
| void mDNSConfigureServer(int updown, const char *const prefix, const domainname *const fqdn) |
| { |
| kern_return_t kr = KERN_SUCCESS; |
| int retry = 0, err = 0; |
| 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)); |
| } |
| MACHRETRYLOOP_BEGIN(kr, retry, err, fin); |
| kr = proxy_mDNSConfigureServer(getHelperPort(retry), updown, fqdnStr); |
| MACHRETRYLOOP_END(kr, retry, err, fin); |
| fin: |
| (void)err; |
| } |
| |
| 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) |
| { |
| kern_return_t kr = KERN_SUCCESS; |
| int retry = 0, err = 0; |
| 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)); |
| } |
| MACHRETRYLOOP_BEGIN(kr, retry, err, fin); |
| kr = proxy_mDNSAutoTunnelSetKeys(getHelperPort(retry), replacedelete, local_inner, local_outer, local_port, remote_inner, remote_outer, remote_port, fqdnStr, &err); |
| MACHRETRYLOOP_END(kr, retry, err, fin); |
| fin: |
| return err; |
| } |
| |
| void mDNSSendWakeupPacket(unsigned ifid, char *eth_addr, char *ip_addr, int iteration) |
| { |
| kern_return_t kr = KERN_SUCCESS; |
| int retry = 0, err = 0; |
| MACHRETRYLOOP_BEGIN(kr, retry, err, fin); |
| kr = proxy_mDNSSendWakeupPacket(getHelperPort(retry), ifid, eth_addr, ip_addr, iteration); |
| MACHRETRYLOOP_END(kr, retry, err, fin); |
| fin: |
| (void) err; |
| } |
| |
| void mDNSPacketFilterControl(uint32_t command, char * ifname, uint32_t count, pfArray_t portArray, pfArray_t protocolArray) |
| { |
| kern_return_t kr = KERN_SUCCESS; |
| int retry = 0, err = 0; |
| MACHRETRYLOOP_BEGIN(kr, retry, err, fin); |
| kr = proxy_mDNSPacketFilterControl(getHelperPort(retry), command, ifname, count, portArray, protocolArray); |
| MACHRETRYLOOP_END(kr, retry, err, fin); |
| fin: |
| (void) err; |
| } |
| |
| int mDNSSendKeepalive(v6addr_t sadd, v6addr_t dadd, uint16_t lport, uint16_t rport, unsigned seq, unsigned ack, uint16_t win) |
| { |
| kern_return_t kr = KERN_FAILURE; |
| int retry = 0, err = 0; |
| MACHRETRYLOOP_BEGIN(kr, retry, err, fin); |
| kr = proxy_mDNSSendKeepalive(getHelperPort(retry), sadd, dadd, lport, rport, seq, ack, win); |
| MACHRETRYLOOP_END(kr, retry, err, fin); |
| fin: |
| return err; |
| } |
| |
| |
| int mDNSInterfaceAdvtIoctl(const char *ifname, int op) |
| { |
| kern_return_t kr = KERN_FAILURE; |
| int retry = 0, err = 0; |
| |
| MACHRETRYLOOP_BEGIN(kr, retry, err, fin); |
| kr = proxy_mDNSInterfaceAdvtIoctl(getHelperPort(retry), ifname, op); |
| MACHRETRYLOOP_END(kr, retry, err, fin); |
| |
| fin: |
| return err; |
| } |