diff options
author | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2014-01-30 13:52:13 +0100 |
---|---|---|
committer | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2014-01-30 16:23:04 +0100 |
commit | 9449f151d0ccf3ac755d5f2bd9b4057ae2b03157 (patch) | |
tree | ada21dc6aa0b146c62a7561a08fb51fe4a8922ee /mDNSResponder/mDNSMacOSX | |
parent | DHCPCD(8): Add MASTER_ONLY option (diff) | |
download | rtems-libbsd-9449f151d0ccf3ac755d5f2bd9b4057ae2b03157.tar.bz2 |
mDNS: Import
The sources can be obtained via:
http://opensource.apple.com/tarballs/mDNSResponder/mDNSResponder-544.tar.gz
Diffstat (limited to 'mDNSResponder/mDNSMacOSX')
81 files changed, 35289 insertions, 0 deletions
diff --git a/mDNSResponder/mDNSMacOSX/BonjourEvents-Info.plist b/mDNSResponder/mDNSMacOSX/BonjourEvents-Info.plist new file mode 100644 index 00000000..473d4143 --- /dev/null +++ b/mDNSResponder/mDNSMacOSX/BonjourEvents-Info.plist @@ -0,0 +1,49 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>CFBundleDevelopmentRegion</key> + <string>English</string> + <key>CFBundleExecutable</key> + <string>${EXECUTABLE_NAME}</string> + <key>CFBundleName</key> + <string>${PRODUCT_NAME}</string> + <key>CFBundleIconFile</key> + <string></string> + <key>CFBundleIdentifier</key> + <string>com.apple.${PRODUCT_NAME:rfc1034identifier}</string> + <key>CFBundleInfoDictionaryVersion</key> + <string>6.0</string> + <key>CFBundlePackageType</key> + <string>BNDL</string> + <key>CFBundleSignature</key> + <string>????</string> + <key>CFBundleVersion</key> + <string>1</string> + <key>CFBundleShortVersionString</key> + <string>1.0</string> + <key>CFPlugInDynamicRegisterFunction</key> + <string></string> + <key>CFPlugInDynamicRegistration</key> + <string>NO</string> + <key>CFPlugInFactories</key> + <dict> + <key>FB86416D-6164-2070-726F-70735C216EC0</key> + <string>UserEventAgentFactory</string> + </dict> + <key>CFPlugInTypes</key> + <dict> + <key>FC86416D-6164-2070-726F-70735C216EC0</key> + <array> + <string>FB86416D-6164-2070-726F-70735C216EC0</string> + </array> + </dict> + <key>CFPlugInUnloadFunction</key> + <string></string> + <key>LimitLoadToSessionType</key> + <array> + <string>System</string> + <string>Aqua</string> + </array> +</dict> +</plist> diff --git a/mDNSResponder/mDNSMacOSX/BonjourEvents.c b/mDNSResponder/mDNSMacOSX/BonjourEvents.c new file mode 100644 index 00000000..b9308189 --- /dev/null +++ b/mDNSResponder/mDNSMacOSX/BonjourEvents.c @@ -0,0 +1,991 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2010 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 <CoreFoundation/CoreFoundation.h> +#include <CoreFoundation/CFXPCBridge.h> +#include "dns_sd.h" +#include <UserEventAgentInterface.h> +#include <stdio.h> +#include <stdlib.h> +#include <asl.h> +#include <xpc/xpc.h> + + +#pragma mark - +#pragma mark Types +#pragma mark - +static const char* sPluginIdentifier = "com.apple.bonjour.events"; + +// PLIST Keys +static const CFStringRef sServiceNameKey = CFSTR("ServiceName"); +static const CFStringRef sServiceTypeKey = CFSTR("ServiceType"); +static const CFStringRef sServiceDomainKey = CFSTR("ServiceDomain"); + +static const CFStringRef sOnServiceAddKey = CFSTR("OnServiceAdd"); +static const CFStringRef sOnServiceRemoveKey = CFSTR("OnServiceRemove"); + +static const CFStringRef sLaunchdTokenKey = CFSTR("LaunchdToken"); +static const CFStringRef sLaunchdDictKey = CFSTR("LaunchdDict"); + + +/************************************************ +* Launch Event Dictionary (input from launchd) +* Passed To: ManageEventsCallback +*----------------------------------------------- +* Typing in this dictionary is not enforced +* above us. So this may not be true. Type check +* all input before using it. +*----------------------------------------------- +* sServiceNameKey - CFString (Optional) +* sServiceTypeKey - CFString +* sServiceDomainKey - CFString +* +* One or more of the following. +*----------------------------------- +* sOnServiceAddKey - CFBoolean +* sOnServiceRemoveKey - CFBoolean +* sWhileServiceExistsKey - CFBoolean +************************************************/ + +/************************************************ +* Browser Dictionary +*----------------------------------------------- +* sServiceDomainKey - CFString +* sServiceTypeKey - CFString +************************************************/ + +/************************************************ +* Event Dictionary +*----------------------------------------------- +* sServiceNameKey - CFString (Optional) +* sLaunchdTokenKey - CFNumber +************************************************/ + +typedef struct { + UserEventAgentInterfaceStruct* _UserEventAgentInterface; + CFUUIDRef _factoryID; + UInt32 _refCount; + + void* _pluginContext; + + CFMutableDictionaryRef _tokenToBrowserMap; // Maps a token to a browser that can be used to scan the remaining dictionaries. + CFMutableDictionaryRef _browsers; // A Dictionary of Browser Dictionaries where the resposible browser is the key. + CFMutableDictionaryRef _onAddEvents; // A Dictionary of Event Dictionaries that describe events to trigger on a service appearing. + CFMutableDictionaryRef _onRemoveEvents; // A Dictionary of Event Dictionaries that describe events to trigger on a service disappearing. +} BonjourUserEventsPlugin; + +typedef struct { + CFIndex refCount; + DNSServiceRef browserRef; +} NetBrowserInfo; + +#pragma mark - +#pragma mark Prototypes +#pragma mark - +// COM Stuff +static HRESULT QueryInterface(void *myInstance, REFIID iid, LPVOID *ppv); +static ULONG AddRef(void* instance); +static ULONG Release(void* instance); + +static BonjourUserEventsPlugin* Alloc(CFUUIDRef factoryID); +static void Dealloc(BonjourUserEventsPlugin* plugin); + +void * UserEventAgentFactory(CFAllocatorRef allocator, CFUUIDRef typeID); + +// Plugin Management +static void Install(void* instance); +static void ManageEventsCallback( + UserEventAgentLaunchdAction action, + CFNumberRef token, + CFTypeRef eventMatchDict, + void * vContext); + + +// Plugin Guts +void AddEventToPlugin(BonjourUserEventsPlugin* plugin, CFNumberRef launchdToken, CFDictionaryRef eventParameters); +void RemoveEventFromPlugin(BonjourUserEventsPlugin* plugin, CFNumberRef launchToken); + +NetBrowserInfo* CreateBrowser(BonjourUserEventsPlugin* plugin, CFStringRef type, CFStringRef domain); +NetBrowserInfo* BrowserForSDRef(BonjourUserEventsPlugin* plugin, DNSServiceRef sdRef); +void AddEventDictionary(CFDictionaryRef eventDict, CFMutableDictionaryRef allEventsDictionary, NetBrowserInfo* key); +void RemoveEventFromArray(CFMutableArrayRef array, CFNumberRef launchdToken); + +// Net Service Browser Stuff +void ServiceBrowserCallback (DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, const char* serviceName, const char* regtype, const char* replyDomain, void* context); +void HandleTemporaryEventsForService(BonjourUserEventsPlugin* plugin, NetBrowserInfo* browser, CFStringRef serviceName, CFMutableDictionaryRef eventsDictionary); + +// Convence Stuff +const char* CStringFromCFString(CFStringRef string); + +// NetBrowserInfo "Object" +NetBrowserInfo* NetBrowserInfoCreate(CFStringRef serviceType, CFStringRef domain, void* context); +const void* NetBrowserInfoRetain(CFAllocatorRef allocator, const void* info); +void NetBrowserInfoRelease(CFAllocatorRef allocator, const void* info); +Boolean NetBrowserInfoEqual(const void *value1, const void *value2); +CFHashCode NetBrowserInfoHash(const void *value); +CFStringRef NetBrowserInfoCopyDescription(const void *value); + +static const CFDictionaryKeyCallBacks kNetBrowserInfoDictionaryKeyCallbacks = { + 0, + NetBrowserInfoRetain, + NetBrowserInfoRelease, + NetBrowserInfoCopyDescription, + NetBrowserInfoEqual, + NetBrowserInfoHash +}; + +static const CFDictionaryValueCallBacks kNetBrowserInfoDictionaryValueCallbacks = { + 0, + NetBrowserInfoRetain, + NetBrowserInfoRelease, + NetBrowserInfoCopyDescription, + NetBrowserInfoEqual +}; + +// COM type definition goop. +static UserEventAgentInterfaceStruct UserEventAgentInterfaceFtbl = { + NULL, // Required padding for COM + QueryInterface, // Query Interface + AddRef, // AddRef() + Release, // Release() + Install // Install +}; + +#pragma mark - +#pragma mark COM Management +#pragma mark - + +/***************************************************************************** +*****************************************************************************/ +static HRESULT QueryInterface(void *myInstance, REFIID iid, LPVOID *ppv) +{ + CFUUIDRef interfaceID = CFUUIDCreateFromUUIDBytes(NULL, iid); + + // Test the requested ID against the valid interfaces. + if(CFEqual(interfaceID, kUserEventAgentInterfaceID)) + { + ((BonjourUserEventsPlugin *) myInstance)->_UserEventAgentInterface->AddRef(myInstance); + *ppv = myInstance; + CFRelease(interfaceID); + return S_OK; + } + else if(CFEqual(interfaceID, IUnknownUUID)) + { + ((BonjourUserEventsPlugin *) myInstance)->_UserEventAgentInterface->AddRef(myInstance); + *ppv = myInstance; + CFRelease(interfaceID); + return S_OK; + } + else // Requested interface unknown, bail with error. + { + *ppv = NULL; + CFRelease(interfaceID); + return E_NOINTERFACE; + } +} + +/***************************************************************************** +*****************************************************************************/ +static ULONG AddRef(void* instance) +{ + BonjourUserEventsPlugin* plugin = (BonjourUserEventsPlugin*)instance; + return ++plugin->_refCount; +} + +/***************************************************************************** +*****************************************************************************/ +static ULONG Release(void* instance) +{ + BonjourUserEventsPlugin* plugin = (BonjourUserEventsPlugin*)instance; + + if (plugin->_refCount != 0) + --plugin->_refCount; + + if (plugin->_refCount == 0) + { + Dealloc(instance); + return 0; + } + + return plugin->_refCount; +} + +/***************************************************************************** +* Alloc +* - +* Functionas as both +[alloc] and -[init] for the plugin. Add any +* initalization of member variables here. +*****************************************************************************/ +static BonjourUserEventsPlugin* Alloc(CFUUIDRef factoryID) +{ + BonjourUserEventsPlugin* plugin = malloc(sizeof(BonjourUserEventsPlugin)); + + plugin->_UserEventAgentInterface = &UserEventAgentInterfaceFtbl; + plugin->_pluginContext = NULL; + + if (factoryID) + { + plugin->_factoryID = (CFUUIDRef)CFRetain(factoryID); + CFPlugInAddInstanceForFactory(factoryID); + } + + plugin->_refCount = 1; + plugin->_tokenToBrowserMap = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kNetBrowserInfoDictionaryValueCallbacks); + plugin->_browsers = CFDictionaryCreateMutable(NULL, 0, &kNetBrowserInfoDictionaryKeyCallbacks, &kCFTypeDictionaryValueCallBacks); + plugin->_onAddEvents = CFDictionaryCreateMutable(NULL, 0, &kNetBrowserInfoDictionaryKeyCallbacks, &kCFTypeDictionaryValueCallBacks); + plugin->_onRemoveEvents = CFDictionaryCreateMutable(NULL, 0, &kNetBrowserInfoDictionaryKeyCallbacks, &kCFTypeDictionaryValueCallBacks); + + return plugin; +} + +/***************************************************************************** +* Dealloc +* - +* Much like Obj-C dealloc this method is responsible for releasing any object +* this plugin is holding. Unlike ObjC, you call directly free() instead of +* [super dalloc]. +*****************************************************************************/ +static void Dealloc(BonjourUserEventsPlugin* plugin) +{ + CFUUIDRef factoryID = plugin->_factoryID; + + if (factoryID) + { + CFPlugInRemoveInstanceForFactory(factoryID); + CFRelease(factoryID); + } + + if (plugin->_tokenToBrowserMap) + CFRelease(plugin->_tokenToBrowserMap); + + if (plugin->_browsers) + CFRelease(plugin->_browsers); + + if (plugin->_onAddEvents) + CFRelease(plugin->_onAddEvents); + + if (plugin->_onRemoveEvents) + CFRelease(plugin->_onRemoveEvents); + + free(plugin); +} + +/******************************************************************************* +*******************************************************************************/ +void * UserEventAgentFactory(CFAllocatorRef allocator, CFUUIDRef typeID) +{ + (void)allocator; + BonjourUserEventsPlugin * result = NULL; + + if (typeID && CFEqual(typeID, kUserEventAgentTypeID)) { + result = Alloc(kUserEventAgentFactoryID); + } + + return (void *)result; +} + +#pragma mark - +#pragma mark Plugin Management +#pragma mark - +/***************************************************************************** +* Install +* - +* This is invoked once when the plugin is loaded to do initial setup and +* allow us to register with launchd. If UserEventAgent crashes, the plugin +* will need to be reloaded, and hence this will get invoked again. +*****************************************************************************/ +static void Install(void *instance) +{ + BonjourUserEventsPlugin* plugin = (BonjourUserEventsPlugin*)instance; + + plugin->_pluginContext = UserEventAgentRegisterForLaunchEvents(sPluginIdentifier, &ManageEventsCallback, plugin); + + if (!plugin->_pluginContext) + { + fprintf(stderr, "%s:%s failed to register for launch events.\n", sPluginIdentifier, __FUNCTION__); + return; + } + +} + +/***************************************************************************** +* ManageEventsCallback +* - +* This is invoked when launchd loads a event dictionary and needs to inform +* us what a daemon / agent is looking for. +*****************************************************************************/ +static void ManageEventsCallback(UserEventAgentLaunchdAction action, CFNumberRef token, CFTypeRef eventMatchDict, void* vContext) +{ + if (action == kUserEventAgentLaunchdAdd) + { + if (!eventMatchDict) + { + fprintf(stderr, "%s:%s empty dictionary\n", sPluginIdentifier, __FUNCTION__); + return; + } + if (CFGetTypeID(eventMatchDict) != CFDictionaryGetTypeID()) + { + fprintf(stderr, "%s:%s given non-dict for event dictionary, action %d\n", sPluginIdentifier, __FUNCTION__, action); + return; + } + // Launchd wants us to add a launch event for this token and matching dictionary. + asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s calling AddEventToPlugin", sPluginIdentifier, __FUNCTION__); + AddEventToPlugin((BonjourUserEventsPlugin*)vContext, token, (CFDictionaryRef)eventMatchDict); + } + else if (action == kUserEventAgentLaunchdRemove) + { + // Launchd wants us to remove the event hook we setup for this token / matching dictionary. + // Note: eventMatchDict can be NULL for Remove. + asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s calling RemoveEventToPlugin", sPluginIdentifier, __FUNCTION__); + RemoveEventFromPlugin((BonjourUserEventsPlugin*)vContext, token); + } + else + { + asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s unknown callback event\n", sPluginIdentifier, __FUNCTION__); + } +} + + +#pragma mark - +#pragma mark Plugin Guts +#pragma mark - + +/***************************************************************************** +* AddEventToPlugin +* - +* This method is invoked when launchd wishes the plugin to setup a launch +* event matching the parameters in the dictionary. +*****************************************************************************/ +void AddEventToPlugin(BonjourUserEventsPlugin* plugin, CFNumberRef launchdToken, CFDictionaryRef eventParameters) +{ + CFStringRef domain = CFDictionaryGetValue(eventParameters, sServiceDomainKey); + CFStringRef type = CFDictionaryGetValue(eventParameters, sServiceTypeKey); + CFStringRef name = CFDictionaryGetValue(eventParameters, sServiceNameKey); + CFBooleanRef cfOnAdd = CFDictionaryGetValue(eventParameters, sOnServiceAddKey); + CFBooleanRef cfOnRemove = CFDictionaryGetValue(eventParameters, sOnServiceRemoveKey); + + Boolean onAdd = false; + Boolean onRemove = false; + + if (cfOnAdd && CFGetTypeID(cfOnAdd) == CFBooleanGetTypeID() && CFBooleanGetValue(cfOnAdd)) + onAdd = true; + + if (cfOnRemove && CFGetTypeID(cfOnRemove) == CFBooleanGetTypeID() && CFBooleanGetValue(cfOnRemove)) + onRemove = true; + + // A type is required. If none is specified, BAIL + if (!type || CFGetTypeID(type) != CFStringGetTypeID()) + { + fprintf(stderr, "%s:%s: a LaunchEvent is missing a service type.\n", sPluginIdentifier, __FUNCTION__); + return; + } + + // If we aren't suppose to launch on services appearing or disappearing, this service does nothing. Ignore. + if (!onAdd && !onRemove) + { + fprintf(stderr, "%s:%s a LaunchEvent is missing both onAdd and onRemove events\n", sPluginIdentifier, __FUNCTION__); + return; + } + + // If no domain is specified, assume local. + if (!domain) + { + domain = CFSTR("local"); + } + else if (CFGetTypeID(domain) != CFStringGetTypeID() ) // If the domain is not a string, fail + { + fprintf(stderr, "%s:%s a LaunchEvent has a domain that is not a string.\n", sPluginIdentifier, __FUNCTION__); + return; + } + + // If we have a name filter, but it's not a string. This event is broken, bail. + if (name && CFGetTypeID(name) != CFStringGetTypeID()) + { + fprintf(stderr, "%s:%s a LaunchEvent has a domain that is not a string.\n", sPluginIdentifier, __FUNCTION__); + return; + } + + // Get us a browser + NetBrowserInfo* browser = CreateBrowser(plugin, type, domain); + + if (!browser) + { + fprintf(stderr, "%s:%s cannot create browser\n", sPluginIdentifier, __FUNCTION__); + return; + } + + // Create Event Dictionary + CFMutableDictionaryRef eventDictionary = CFDictionaryCreateMutable(NULL, 4, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + + // We store both the Token and the Dictionary. UserEventAgentSetLaunchEventState needs + // the token and UserEventAgentSetFireEvent needs both the token and the dictionary + CFDictionarySetValue(eventDictionary, sLaunchdTokenKey, launchdToken); + CFDictionarySetValue(eventDictionary, sLaunchdDictKey, eventParameters); + + if (name) + CFDictionarySetValue(eventDictionary, sServiceNameKey, name); + + // Add to the correct dictionary. + if (onAdd) + { + asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s: Adding browser to AddEvents", sPluginIdentifier, __FUNCTION__); + AddEventDictionary(eventDictionary, plugin->_onAddEvents, browser); + } + + if (onRemove) + { + asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s: Adding browser to RemoveEvents", sPluginIdentifier, __FUNCTION__); + AddEventDictionary(eventDictionary, plugin->_onRemoveEvents, browser); + } + + // Add Token Mapping + CFDictionarySetValue(plugin->_tokenToBrowserMap, launchdToken, browser); + + // Release Memory + CFRelease(eventDictionary); +} + +/***************************************************************************** +* RemoveEventFromPlugin +* - +* This method is invoked when launchd wishes the plugin to setup a launch +* event matching the parameters in the dictionary. +*****************************************************************************/ +void RemoveEventFromPlugin(BonjourUserEventsPlugin* plugin, CFNumberRef launchdToken) +{ + NetBrowserInfo* browser = (NetBrowserInfo*)CFDictionaryGetValue(plugin->_tokenToBrowserMap, launchdToken); + Boolean othersUsingBrowser = false; + + if (!browser) + { + long long value = 0; + CFNumberGetValue(launchdToken, kCFNumberLongLongType, &value); + fprintf(stderr, "%s:%s Launchd asked us to remove a token we did not register! ==Token:%lld== \n", sPluginIdentifier, __FUNCTION__, value); + return; + } + + CFMutableArrayRef onAddEvents = (CFMutableArrayRef)CFDictionaryGetValue(plugin->_onAddEvents, browser); + CFMutableArrayRef onRemoveEvents = (CFMutableArrayRef)CFDictionaryGetValue(plugin->_onRemoveEvents, browser); + + if (onAddEvents) + { + asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s: Calling RemoveEventFromArray for OnAddEvents", sPluginIdentifier, __FUNCTION__); + RemoveEventFromArray(onAddEvents, launchdToken); + + // Is the array now empty, clean up + if (CFArrayGetCount(onAddEvents) == 0) + { + asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s: Removing the browser from AddEvents", sPluginIdentifier, __FUNCTION__); + CFDictionaryRemoveValue(plugin->_onAddEvents, browser); + } + } + + if (onRemoveEvents) + { + asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s: Calling RemoveEventFromArray for OnRemoveEvents", sPluginIdentifier, __FUNCTION__); + RemoveEventFromArray(onRemoveEvents, launchdToken); + + // Is the array now empty, clean up + if (CFArrayGetCount(onRemoveEvents) == 0) + { + asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s: Removing the browser from RemoveEvents", sPluginIdentifier, __FUNCTION__); + CFDictionaryRemoveValue(plugin->_onRemoveEvents, browser); + } + } + + // Remove ourselves from the token dictionary. + CFDictionaryRemoveValue(plugin->_tokenToBrowserMap, launchdToken); + + // Check to see if anyone else is using this browser. + CFIndex i; + CFIndex count = CFDictionaryGetCount(plugin->_tokenToBrowserMap); + NetBrowserInfo** browsers = malloc(count * sizeof(NetBrowserInfo*)); + + // Fetch the values of the token dictionary + CFDictionaryGetKeysAndValues(plugin->_tokenToBrowserMap, NULL, (const void**)browsers); + + for (i = 0; i < count; ++i) + { + if (NetBrowserInfoEqual(browsers[i], browser)) + { + othersUsingBrowser = true; + break; + } + } + + // If no one else is useing our browser, clean up! + if (!othersUsingBrowser) + { + asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s: Removing browser %p from _browsers", sPluginIdentifier, __FUNCTION__, browser); + CFDictionaryRemoveValue(plugin->_browsers, browser); // This triggers release and dealloc of the browser + } + else + { + asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s: Decrementing browsers %p count", sPluginIdentifier, __FUNCTION__, browser); + // Decrement my reference count (it was incremented when it was added to _browsers in CreateBrowser) + NetBrowserInfoRelease(NULL, browser); + } + + free(browsers); +} + + +/***************************************************************************** +* CreateBrowser +* - +* This method returns a NetBrowserInfo that is looking for a type of +* service in a domain. If no browser exists, it will create one and return it. +*****************************************************************************/ +NetBrowserInfo* CreateBrowser(BonjourUserEventsPlugin* plugin, CFStringRef type, CFStringRef domain) +{ + CFIndex i; + CFIndex count = CFDictionaryGetCount(plugin->_browsers); + NetBrowserInfo* browser = NULL; + CFDictionaryRef* dicts = malloc(count * sizeof(CFDictionaryRef)); + NetBrowserInfo** browsers = malloc(count * sizeof(NetBrowserInfo*)); + + // Fetch the values of the browser dictionary + CFDictionaryGetKeysAndValues(plugin->_browsers, (const void**)browsers, (const void**)dicts); + + + // Loop thru the browsers list and see if we can find a matching one. + for (i = 0; i < count; ++i) + { + CFDictionaryRef browserDict = dicts[i]; + + CFStringRef browserType = CFDictionaryGetValue(browserDict, sServiceTypeKey); + CFStringRef browserDomain = CFDictionaryGetValue(browserDict, sServiceDomainKey); + + // If we have a matching browser, break + if ((CFStringCompare(browserType, type, kCFCompareCaseInsensitive) == kCFCompareEqualTo) && + (CFStringCompare(browserDomain, domain, kCFCompareCaseInsensitive) == kCFCompareEqualTo)) + { + asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s: found a duplicate browser\n", sPluginIdentifier, __FUNCTION__); + browser = browsers[i]; + NetBrowserInfoRetain(NULL, browser); + break; + } + } + + // No match found, lets create one! + if (!browser) + { + + browser = NetBrowserInfoCreate(type, domain, plugin); + + if (!browser) + { + fprintf(stderr, "%s:%s failed to search for %s.%s", sPluginIdentifier, __FUNCTION__, CStringFromCFString(type), CStringFromCFString(domain)); + free(dicts); + free(browsers); + return NULL; + } + + // Service browser created, lets add this to ourselves to the dictionary. + CFMutableDictionaryRef browserDict = CFDictionaryCreateMutable(NULL, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + + CFDictionarySetValue(browserDict, sServiceTypeKey, type); + CFDictionarySetValue(browserDict, sServiceDomainKey, domain); + + // Add the dictionary to the browsers dictionary. + CFDictionarySetValue(plugin->_browsers, browser, browserDict); + + NetBrowserInfoRelease(NULL, browser); + + // Release Memory + CFRelease(browserDict); + } + + free(dicts); + free(browsers); + + return browser; +} + +/***************************************************************************** +* BrowserForSDRef +* - +* This method returns a NetBrowserInfo that matches the calling SDRef passed +* in via the callback. +*****************************************************************************/ +NetBrowserInfo* BrowserForSDRef(BonjourUserEventsPlugin* plugin, DNSServiceRef sdRef) +{ + CFIndex i; + CFIndex count = CFDictionaryGetCount(plugin->_browsers); + NetBrowserInfo* browser = NULL; + NetBrowserInfo** browsers = malloc(count * sizeof(NetBrowserInfo*)); + + // Fetch the values of the browser dictionary + CFDictionaryGetKeysAndValues(plugin->_browsers, (const void**)browsers, NULL); + + // Loop thru the browsers list and see if we can find a matching one. + for (i = 0; i < count; ++i) + { + NetBrowserInfo* currentBrowser = browsers[i]; + + if (currentBrowser->browserRef == sdRef) + { + browser = currentBrowser; + break; + } + } + + + free(browsers); + + return browser; +} + +/***************************************************************************** +* AddEventDictionary +* - +* Adds a event to a browser's event dictionary +*****************************************************************************/ + +void AddEventDictionary(CFDictionaryRef eventDict, CFMutableDictionaryRef allEventsDictionary, NetBrowserInfo* key) +{ + CFMutableArrayRef eventsForBrowser = (CFMutableArrayRef)CFDictionaryGetValue(allEventsDictionary, key); + + if (!eventsForBrowser) // We have no events for this browser yet, lets add him. + { + eventsForBrowser = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); + CFDictionarySetValue(allEventsDictionary, key, eventsForBrowser); + asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s creating a new array", sPluginIdentifier, __FUNCTION__); + } + else + { + asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s Incrementing refcount", sPluginIdentifier, __FUNCTION__); + CFRetain(eventsForBrowser); + } + + CFArrayAppendValue(eventsForBrowser, eventDict); + CFRelease(eventsForBrowser); +} + +/***************************************************************************** +* RemoveEventFromArray +* - +* Searches a Array of Event Dictionaries to find one with a matching launchd +* token and remove it. +*****************************************************************************/ + +void RemoveEventFromArray(CFMutableArrayRef array, CFNumberRef launchdToken) +{ + CFIndex i; + CFIndex count = CFArrayGetCount(array); + + // Loop thru looking for us. + for (i = 0; i < count; ) + { + CFDictionaryRef eventDict = CFArrayGetValueAtIndex(array, i); + CFNumberRef token = CFDictionaryGetValue(eventDict, sLaunchdTokenKey); + + if (CFEqual(token, launchdToken)) // This is the same event? + { + asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s found token", sPluginIdentifier, __FUNCTION__); + CFArrayRemoveValueAtIndex(array, i); // Remove the event, + break; // The token should only exist once, so it makes no sense to continue. + } + else + { + ++i; // If it's not us, advance. + } + } + if (i == count) asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s did not find token", sPluginIdentifier, __FUNCTION__); +} + +#pragma mark - +#pragma mark Net Service Browser Stuff +#pragma mark - + +/***************************************************************************** +* ServiceBrowserCallback +* - +* This method is the heart of the plugin. It's the runloop callback annoucing +* the appearence and disappearance of network services. +*****************************************************************************/ + +void ServiceBrowserCallback (DNSServiceRef sdRef, + DNSServiceFlags flags, + uint32_t interfaceIndex, + DNSServiceErrorType errorCode, + const char* serviceName, + const char* regtype, + const char* replyDomain, + void* context ) +{ + (void)interfaceIndex; + (void)regtype; + (void)replyDomain; + BonjourUserEventsPlugin* plugin = (BonjourUserEventsPlugin*)context; + NetBrowserInfo* browser = BrowserForSDRef(plugin, sdRef); + + if (!browser) // Missing browser? + { + fprintf(stderr, "%s:%s ServiceBrowserCallback: missing browser\n", sPluginIdentifier, __FUNCTION__); + return; + } + + if (errorCode != kDNSServiceErr_NoError) + { + fprintf(stderr, "%s:%s ServiceBrowserCallback: errcode set %d\n", sPluginIdentifier, __FUNCTION__, errorCode); + return; + } + + CFStringRef cfServiceName = CFStringCreateWithCString(NULL, serviceName, kCFStringEncodingUTF8); + + if (flags & kDNSServiceFlagsAdd) + { + asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s calling HandleTemporaryEventsForService Add\n", sPluginIdentifier, __FUNCTION__); + HandleTemporaryEventsForService(plugin, browser, cfServiceName, plugin->_onAddEvents); + } + else + { + asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s calling HandleTemporaryEventsForService Remove\n", sPluginIdentifier, __FUNCTION__); + HandleTemporaryEventsForService(plugin, browser, cfServiceName, plugin->_onRemoveEvents); + } + + CFRelease(cfServiceName); +} + +/***************************************************************************** +* HandleTemporaryEventsForService +* - +* This method handles the firing of one shot events. Aka. Events that are +* signaled when a service appears / disappears. They have a temporarly +* signaled state. +*****************************************************************************/ +void HandleTemporaryEventsForService(BonjourUserEventsPlugin* plugin, NetBrowserInfo* browser, CFStringRef serviceName, CFMutableDictionaryRef eventsDictionary) +{ + CFArrayRef events = (CFArrayRef)CFDictionaryGetValue(eventsDictionary, browser); // Get events for the browser we passed in. + CFIndex i; + CFIndex count; + + if (!events) // Somehow we have a orphan browser... + return; + + count = CFArrayGetCount(events); + + // Go thru the events and run filters, notifity if they pass. + for (i = 0; i < count; ++i) + { + CFDictionaryRef eventDict = (CFDictionaryRef)CFArrayGetValueAtIndex(events, i); + CFStringRef eventServiceName = (CFStringRef)CFDictionaryGetValue(eventDict, sServiceNameKey); + CFNumberRef token = (CFNumberRef) CFDictionaryGetValue(eventDict, sLaunchdTokenKey); + CFDictionaryRef dict = (CFDictionaryRef) CFDictionaryGetValue(eventDict, sLaunchdDictKey); + + // Currently we only filter on service name, that makes this as simple as... + if (!eventServiceName || CFEqual(serviceName, eventServiceName)) + { + uint64_t tokenUint64; + // Signal Event: This is edge trigger. When the action has been taken, it will not + // be remembered anymore. + + asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s HandleTemporaryEventsForService signal\n", sPluginIdentifier, __FUNCTION__); + CFNumberGetValue(token, kCFNumberLongLongType, &tokenUint64); + + xpc_object_t jobRequest = _CFXPCCreateXPCObjectFromCFObject(dict); + + UserEventAgentFireEvent(plugin->_pluginContext, tokenUint64, jobRequest); + xpc_release(jobRequest); + } + } +} + +#pragma mark - +#pragma mark Convenience +#pragma mark - + +/***************************************************************************** +* CStringFromCFString +* - +* Silly convenence function for dealing with non-critical CFSTR -> cStr +* conversions. +*****************************************************************************/ + +const char* CStringFromCFString(CFStringRef string) +{ + const char* defaultString = "??????"; + const char* cstring; + + if (!string) + return defaultString; + + cstring = CFStringGetCStringPtr(string, kCFStringEncodingUTF8); + + return (cstring) ? cstring : defaultString; + +} + +#pragma mark - +#pragma mark NetBrowserInfo "Object" +#pragma mark - +/***************************************************************************** +* NetBrowserInfoCreate +* - +* The method creates a NetBrowserInfo Object and initalizes it. +*****************************************************************************/ +NetBrowserInfo* NetBrowserInfoCreate(CFStringRef serviceType, CFStringRef domain, void* context) +{ + NetBrowserInfo* outObj = NULL; + DNSServiceRef browserRef = NULL; + char* cServiceType = NULL; + char* cDomain = NULL; + Boolean success = true; + + CFIndex serviceSize = CFStringGetMaximumSizeForEncoding(CFStringGetLength(serviceType), kCFStringEncodingUTF8); + cServiceType = calloc(serviceSize, 1); + success = CFStringGetCString(serviceType, cServiceType, serviceSize, kCFStringEncodingUTF8); + + + if (domain) + { + CFIndex domainSize = CFStringGetMaximumSizeForEncoding(CFStringGetLength(domain), kCFStringEncodingUTF8); + if (domainSize) + { + cDomain = calloc(domainSize, 1); + success = success && CFStringGetCString(domain, cDomain, domainSize, kCFStringEncodingUTF8); + } + } + + if (!success) + { + fprintf(stderr, "%s:%s LaunchEvent has badly encoded service type or domain.\n", sPluginIdentifier, __FUNCTION__); + free(cServiceType); + + if (cDomain) + free(cDomain); + + return NULL; + } + + DNSServiceErrorType err = DNSServiceBrowse(&browserRef, 0, 0, cServiceType, cDomain, ServiceBrowserCallback, context); + + if (err != kDNSServiceErr_NoError) + { + fprintf(stderr, "%s:%s Failed to create browser for %s, %s\n", sPluginIdentifier, __FUNCTION__, cServiceType, cDomain); + free(cServiceType); + + if (cDomain) + free(cDomain); + + return NULL; + } + + DNSServiceSetDispatchQueue(browserRef, dispatch_get_main_queue()); + + + outObj = malloc(sizeof(NetBrowserInfo)); + + outObj->refCount = 1; + outObj->browserRef = browserRef; + + asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s: created new object %p", sPluginIdentifier, __FUNCTION__, outObj); + + free(cServiceType); + + if (cDomain) + free(cDomain); + + return outObj; +} + +/***************************************************************************** +* NetBrowserInfoRetain +* - +* The method retains a NetBrowserInfo object. +*****************************************************************************/ +const void* NetBrowserInfoRetain(CFAllocatorRef allocator, const void* info) +{ + (void)allocator; + NetBrowserInfo* obj = (NetBrowserInfo*)info; + + if (!obj) + return NULL; + + ++obj->refCount; + asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s: Incremented ref count on %p, count %d", sPluginIdentifier, __FUNCTION__, obj->browserRef, (int)obj->refCount); + + return obj; +} + +/***************************************************************************** +* NetBrowserInfoRelease +* - +* The method releases a NetBrowserInfo object. +*****************************************************************************/ +void NetBrowserInfoRelease(CFAllocatorRef allocator, const void* info) +{ + (void)allocator; + NetBrowserInfo* obj = (NetBrowserInfo*)info; + + if (!obj) + return; + + if (obj->refCount == 1) + { + asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s: DNSServiceRefDeallocate %p", sPluginIdentifier, __FUNCTION__, obj->browserRef); + DNSServiceRefDeallocate(obj->browserRef); + free(obj); + } + else + { + --obj->refCount; + asl_log(NULL, NULL, ASL_LEVEL_INFO, "%s:%s: Decremented ref count on %p, count %d", sPluginIdentifier, __FUNCTION__, obj->browserRef, (int)obj->refCount); + } + +} + +/***************************************************************************** +* NetBrowserInfoEqual +* - +* The method is used to compare two NetBrowserInfo objects for equality. +*****************************************************************************/ +Boolean NetBrowserInfoEqual(const void *value1, const void *value2) +{ + NetBrowserInfo* obj1 = (NetBrowserInfo*)value1; + NetBrowserInfo* obj2 = (NetBrowserInfo*)value2; + + if (obj1->browserRef == obj2->browserRef) + return true; + + return false; +} + +/***************************************************************************** +* NetBrowserInfoHash +* - +* The method is used to make a hash for the object. We can cheat and use the +* browser pointer. +*****************************************************************************/ +CFHashCode NetBrowserInfoHash(const void *value) +{ + return (CFHashCode)((NetBrowserInfo*)value)->browserRef; +} + + +/***************************************************************************** +* NetBrowserInfoCopyDescription +* - +* Make CF happy. +*****************************************************************************/ +CFStringRef NetBrowserInfoCopyDescription(const void *value) +{ + (void)value; + return CFStringCreateWithCString(NULL, "NetBrowserInfo: No useful description", kCFStringEncodingUTF8); +} + diff --git a/mDNSResponder/mDNSMacOSX/CUPolicy.c b/mDNSResponder/mDNSMacOSX/CUPolicy.c new file mode 100644 index 00000000..434e65cd --- /dev/null +++ b/mDNSResponder/mDNSMacOSX/CUPolicy.c @@ -0,0 +1,91 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2013 Apple Computer, 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 "mDNSMacOSX.h" +#include <network/config.h> + +#if TARGET_OS_IPHONE + +mDNSexport void CUPInit(mDNS *const m) +{ + + m->p->handle = cellular_usage_policy_create_client(); + if (!m->p->handle) + { + LogMsg("CUPInit: cellular_usage_policy_create_client failed"); + } +} + +mDNSexport mDNSBool mDNSPlatformAllowPID(mDNS *const m, DNSQuestion *q) +{ + // Currently the policy applies only for DNS requests sent over cellular interface + if (m->p->handle && q->qDNSServer && q->qDNSServer->cellIntf) + { + mDNSBool allowed; + if (q->pid) + { + allowed = (mDNSBool) cellular_usage_policy_is_data_allowed_for_pid(m->p->handle, q->pid); + if (!allowed) + { + xpc_object_t pidx = xpc_uint64_create(q->pid); + if (pidx) + { + network_config_cellular_blocked_notify(pidx, NULL, NULL); + LogInfo("mDNSPlaformAllowPID: Notified PID(%d) for %##s (%s)", q->pid, q->qname.c, DNSTypeName(q->qtype)); + xpc_release(pidx); + } + } + } + else + { + allowed = (mDNSBool) cellular_usage_policy_is_data_allowed_for_uuid(m->p->handle, q->uuid); + if (!allowed) + { + xpc_object_t uuidx = xpc_uuid_create(q->uuid); + if (uuidx) + { + network_config_cellular_blocked_notify(NULL, uuidx, NULL); + LogInfo("mDNSPlaformAllowPID: Notified UUID for %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); + xpc_release(uuidx); + } + } + } + return allowed; + } + else + { + return mDNStrue; + } +} + +#else // TARGET_OS_IPHONE + +mDNSexport void CUPInit(mDNS *const m) +{ + (void)m; //unused +} + +mDNSexport mDNSBool mDNSPlatformAllowPID(mDNS *const m, DNSQuestion *q) +{ + (void)m; //unused + (void)q; //unused + //LogMsg("mDNSPlatformAllowPID: %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); + return mDNStrue; +} + +#endif // TARGET_OS_IPHONE + diff --git a/mDNSResponder/mDNSMacOSX/CryptoSupport.c b/mDNSResponder/mDNSMacOSX/CryptoSupport.c new file mode 100644 index 00000000..408b3a22 --- /dev/null +++ b/mDNSResponder/mDNSMacOSX/CryptoSupport.c @@ -0,0 +1,738 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2011 Apple Computer, 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. + */ + +// *************************************************************************** +// CryptoSupport.c +// Supporting routines for DNSSEC crypto +// *************************************************************************** + +#include "mDNSEmbeddedAPI.h" +#include <CommonCrypto/CommonDigest.h> // For Hash algorithms SHA1 etc. +#include <dispatch/dispatch.h> // For Base32/Base64 encoding/decoding +#include <dispatch/private.h> // dispatch_data_create_with_transform +#include "CryptoAlg.h" +#include "CryptoSupport.h" +#include "dnssec.h" +#include "DNSSECSupport.h" + +#if TARGET_OS_IPHONE +#include "SecRSAKey.h" // For RSA_SHA1 etc. verification +#else +#include <Security/Security.h> +#endif + +#if !TARGET_OS_IPHONE +mDNSlocal SecKeyRef SecKeyCreateRSAPublicKey_OSX(unsigned char *asn1, int length); +#endif + +typedef struct +{ + dispatch_data_t encData; + dispatch_data_t encMap; + dispatch_data_t encNULL; +}encContext; + +mDNSlocal mStatus enc_create(AlgContext *ctx) +{ + encContext *ptr; + + switch (ctx->alg) + { + case ENC_BASE32: + case ENC_BASE64: + ptr = (encContext *)mDNSPlatformMemAllocate(sizeof(encContext)); + if (!ptr) return mStatus_NoMemoryErr; + break; + default: + LogMsg("enc_create: Unsupported algorithm %d", ctx->alg); + return mStatus_BadParamErr; + } + ptr->encData = NULL; + ptr->encMap = NULL; + // The encoded data is not NULL terminated. So, we concatenate a null byte later when we encode and map + // the real data. + ptr->encNULL = dispatch_data_create("", 1, dispatch_get_global_queue(0, 0), ^{}); + if (!ptr->encNULL) + { + mDNSPlatformMemFree(ptr); + return mStatus_NoMemoryErr; + } + ctx->context = ptr; + return mStatus_NoError; +} + +mDNSlocal mStatus enc_destroy(AlgContext *ctx) +{ + encContext *ptr = (encContext *)ctx->context; + if (ptr->encData) dispatch_release(ptr->encData); + if (ptr->encMap) dispatch_release(ptr->encMap); + if (ptr->encNULL) dispatch_release(ptr->encNULL); + mDNSPlatformMemFree(ptr); + return mStatus_NoError; +} + +mDNSlocal mStatus enc_add(AlgContext *ctx, const void *data, mDNSu32 len) +{ + switch (ctx->alg) + { + case ENC_BASE32: + case ENC_BASE64: + { + encContext *ptr = (encContext *)ctx->context; + dispatch_data_t src_data = dispatch_data_create(data, len, dispatch_get_global_queue(0, 0), ^{}); + if (!src_data) + { + LogMsg("enc_add: dispatch_data_create src failed"); + return mStatus_BadParamErr; + } + dispatch_data_t dest_data = dispatch_data_create_with_transform(src_data, DISPATCH_DATA_FORMAT_TYPE_NONE, + (ctx->alg == ENC_BASE32 ? DISPATCH_DATA_FORMAT_TYPE_BASE32HEX : DISPATCH_DATA_FORMAT_TYPE_BASE64)); + dispatch_release(src_data); + if (!dest_data) + { + LogMsg("enc_add: dispatch_data_create dst failed"); + return mStatus_BadParamErr; + } + ptr->encData = dest_data; + + return mStatus_NoError; + } + default: + LogMsg("enc_add: Unsupported algorithm %d", ctx->alg); + return mStatus_BadParamErr; + } +} + +mDNSlocal mDNSu8* enc_encode(AlgContext *ctx) +{ + const void *result = NULL; + + switch (ctx->alg) + { + case ENC_BASE32: + case ENC_BASE64: + { + encContext *ptr = (encContext *)ctx->context; + size_t size; + dispatch_data_t dest_data = ptr->encData; + dispatch_data_t data = dispatch_data_create_concat(dest_data, ptr->encNULL); + + if (!data) + { + LogMsg("enc_encode: cannot concatenate"); + return NULL; + } + + dispatch_data_t map = dispatch_data_create_map(data, &result, &size); + if (!map) + { + LogMsg("enc_encode: cannot create map %d", ctx->alg); + return NULL; + } + dispatch_release(dest_data); + ptr->encData = data; + ptr->encMap = map; + + return (mDNSu8 *)result; + } + default: + LogMsg("enc_encode: Unsupported algorithm %d", ctx->alg); + return mDNSNULL; + } +} + +mDNSlocal mStatus sha_create(AlgContext *ctx) +{ + mDNSu8 *ptr; + switch (ctx->alg) + { + case SHA1_DIGEST_TYPE: + ptr = mDNSPlatformMemAllocate(sizeof(CC_SHA1_CTX)); + if (!ptr) return mStatus_NoMemoryErr; + CC_SHA1_Init((CC_SHA1_CTX *)ptr); + break; + case SHA256_DIGEST_TYPE: + ptr = mDNSPlatformMemAllocate(sizeof(CC_SHA256_CTX)); + if (!ptr) return mStatus_NoMemoryErr; + CC_SHA256_Init((CC_SHA256_CTX *)ptr); + break; + default: + LogMsg("sha_create: Unsupported algorithm %d", ctx->alg); + return mStatus_BadParamErr; + } + ctx->context = ptr; + return mStatus_NoError; +} + +mDNSlocal mStatus sha_destroy(AlgContext *ctx) +{ + mDNSPlatformMemFree(ctx->context); + return mStatus_NoError; +} + +mDNSlocal mDNSu32 sha_len(AlgContext *ctx) +{ + switch (ctx->alg) + { + case SHA1_DIGEST_TYPE: + return CC_SHA1_DIGEST_LENGTH; + case SHA256_DIGEST_TYPE: + return CC_SHA256_DIGEST_LENGTH; + default: + LogMsg("sha_len: Unsupported algorithm %d", ctx->alg); + return mStatus_BadParamErr; + } +} + +mDNSlocal mStatus sha_add(AlgContext *ctx, const void *data, mDNSu32 len) +{ + switch (ctx->alg) + { + case SHA1_DIGEST_TYPE: + CC_SHA1_Update((CC_SHA1_CTX *)ctx->context, data, len); + break; + case SHA256_DIGEST_TYPE: + CC_SHA256_Update((CC_SHA256_CTX *)ctx->context, data, len); + break; + default: + LogMsg("sha_add: Unsupported algorithm %d", ctx->alg); + return mStatus_BadParamErr; + } + return mStatus_NoError; +} + +mDNSlocal mStatus sha_verify(AlgContext *ctx, mDNSu8 *key, mDNSu32 keylen, mDNSu8 *digestIn, mDNSu32 dlen) +{ + mDNSu8 digest[CC_SHA512_DIGEST_LENGTH]; + mDNSu32 digestLen; + + (void) key; //unused + (void)keylen; //unused + switch (ctx->alg) + { + case SHA1_DIGEST_TYPE: + digestLen = CC_SHA1_DIGEST_LENGTH; + CC_SHA1_Final(digest, (CC_SHA1_CTX *)ctx->context); + break; + case SHA256_DIGEST_TYPE: + digestLen = CC_SHA256_DIGEST_LENGTH; + CC_SHA256_Final(digest, (CC_SHA256_CTX *)ctx->context); + break; + default: + LogMsg("sha_verify: Unsupported algorithm %d", ctx->alg); + return mStatus_BadParamErr; + } + if (dlen != digestLen) + { + LogMsg("sha_verify(Alg %d): digest len mismatch len %u, expected %u", ctx->alg, (unsigned int)dlen, (unsigned int)digestLen); + return mStatus_BadParamErr; + } + if (!memcmp(digest, digestIn, digestLen)) + return mStatus_NoError; + else + return mStatus_NoAuth; +} + +mDNSlocal mStatus sha_final(AlgContext *ctx, void *digestOut, mDNSu32 dlen) +{ + mDNSu8 digest[CC_SHA512_DIGEST_LENGTH]; + mDNSu32 digestLen; + + switch (ctx->alg) + { + case SHA1_DIGEST_TYPE: + digestLen = CC_SHA1_DIGEST_LENGTH; + CC_SHA1_Final(digest, (CC_SHA1_CTX *)ctx->context); + break; + case SHA256_DIGEST_TYPE: + digestLen = CC_SHA256_DIGEST_LENGTH; + CC_SHA256_Final(digest, (CC_SHA256_CTX *)ctx->context); + break; + default: + LogMsg("sha_final: Unsupported algorithm %d", ctx->alg); + return mStatus_BadParamErr; + } + if (dlen != digestLen) + { + LogMsg("sha_final(Alg %d): digest len mismatch len %u, expected %u", ctx->alg, (unsigned int)dlen, (unsigned int)digestLen); + return mStatus_BadParamErr; + } + memcpy(digestOut, digest, digestLen); + return mStatus_NoError; +} + +mDNSlocal mStatus rsa_sha_create(AlgContext *ctx) +{ + mDNSu8 *ptr; + switch (ctx->alg) + { + case CRYPTO_RSA_NSEC3_SHA1: + case CRYPTO_RSA_SHA1: + ptr = mDNSPlatformMemAllocate(sizeof(CC_SHA1_CTX)); + if (!ptr) return mStatus_NoMemoryErr; + CC_SHA1_Init((CC_SHA1_CTX *)ptr); + break; + case CRYPTO_RSA_SHA256: + ptr = mDNSPlatformMemAllocate(sizeof(CC_SHA256_CTX)); + if (!ptr) return mStatus_NoMemoryErr; + CC_SHA256_Init((CC_SHA256_CTX *)ptr); + break; + case CRYPTO_RSA_SHA512: + ptr = mDNSPlatformMemAllocate(sizeof(CC_SHA512_CTX)); + if (!ptr) return mStatus_NoMemoryErr; + CC_SHA512_Init((CC_SHA512_CTX *)ptr); + break; + default: + LogMsg("rsa_sha_create: Unsupported algorithm %d", ctx->alg); + return mStatus_BadParamErr; + } + ctx->context = ptr; + return mStatus_NoError; +} + +mDNSlocal mStatus rsa_sha_destroy(AlgContext *ctx) +{ + mDNSPlatformMemFree(ctx->context); + return mStatus_NoError; +} + +mDNSlocal mDNSu32 rsa_sha_len(AlgContext *ctx) +{ + switch (ctx->alg) + { + case CRYPTO_RSA_NSEC3_SHA1: + case CRYPTO_RSA_SHA1: + return CC_SHA1_DIGEST_LENGTH; + case CRYPTO_RSA_SHA256: + return CC_SHA256_DIGEST_LENGTH; + case CRYPTO_RSA_SHA512: + return CC_SHA512_DIGEST_LENGTH; + default: + LogMsg("rsa_sha_len: Unsupported algorithm %d", ctx->alg); + return mStatus_BadParamErr; + } +} + +mDNSlocal mStatus rsa_sha_add(AlgContext *ctx, const void *data, mDNSu32 len) +{ + switch (ctx->alg) + { + case CRYPTO_RSA_NSEC3_SHA1: + case CRYPTO_RSA_SHA1: + CC_SHA1_Update((CC_SHA1_CTX *)ctx->context, data, len); + break; + case CRYPTO_RSA_SHA256: + CC_SHA256_Update((CC_SHA256_CTX *)ctx->context, data, len); + break; + case CRYPTO_RSA_SHA512: + CC_SHA512_Update((CC_SHA512_CTX *)ctx->context, data, len); + break; + default: + LogMsg("rsa_sha_add: Unsupported algorithm %d", ctx->alg); + return mStatus_BadParamErr; + } + return mStatus_NoError; +} + +mDNSlocal SecKeyRef rfc3110_import(const mDNSu8 *data, const mDNSu32 len) +{ + static const int max_key_bytes = 4096 / 8; // max DNSSEC supported modulus is 4096 bits + static const int max_exp_bytes = 3; // DNSSEC supports 1 or 3 bytes for exponent + static const int asn1_cmd_bytes = 3; // since there is an ASN1 SEQ and two INTs + //static const int asn1_max_len_bytes = asn1_cmd_bytes * 3; // capped at 3 due to max payload size + static const int asn1_max_len_bytes = 3 * 3; // capped at 3 due to max payload size + unsigned char asn1[max_key_bytes + 1 + max_exp_bytes + asn1_cmd_bytes + asn1_max_len_bytes]; // +1 is for leading 0 for non negative asn1 number + const mDNSu8 *modulus; + unsigned int modulus_length; + unsigned int exp_length; + mDNSu32 index = 0; + mDNSu32 asn1_length = 0; + unsigned int i; + + // Validate Input + if (!data) + return NULL; + + // we have to have at least 1 byte for the length + if (len < 1) + return NULL; + + // Parse Modulus and Exponent + exp_length = data[0]; + + // we have to have at least len byte + size of exponent + if (len < 1+exp_length) + return NULL; + + // -1 is for the exp_length byte + modulus_length = len - 1 - exp_length; + + // rfc3110 limits modulus to 4096 bits + if (modulus_length > 512) + return NULL; + + if (modulus_length < 1) + return NULL; + + // add 1 to modulus length for pre-ceding 0 t make ASN1 value non-negative + ++modulus_length; + + // 1 is to skip exp_length byte + modulus = &data[1+exp_length]; + + // 2 bytes for commands since first doesn't count + // 2 bytes for min 1 byte length field + asn1_length = modulus_length + exp_length + 2 + 2; + + // account for modulus length causing INT length field to grow + if (modulus_length > 0xFF) + asn1_length += 2; + else if (modulus_length >= 128) + ++asn1_length; + + // Construct ASN1 formatted public key + // Write ASN1 SEQ byte + asn1[index++] = 0x30; + + // Write ASN1 length for SEQ + if (asn1_length < 128) + { + asn1[index++] = asn1_length & 0xFF; + } + else + { + asn1[index++] = (0x80 | ((asn1_length & 0xFF00) ? 2 : 1)); + if (asn1_length & 0xFF00) + asn1[index++] = (asn1_length & 0xFF00) >> 8; + asn1[index++] = asn1_length & 0xFF; + } + + // Write ASN1 INT for modulus + asn1[index++] = 0x02; + // Write ASN1 length for INT + if (modulus_length < 128) + { + asn1[index++] = asn1_length & 0xFF; + } + else + { + asn1[index++] = 0x80 | ((modulus_length & 0xFF00) ? 2 : 1); + if (modulus_length & 0xFF00) + asn1[index++] = (modulus_length & 0xFF00) >> 8; + asn1[index++] = modulus_length & 0xFF; + } + + // Write preceding 0 so our integer isn't negative + asn1[index++] = 0x00; + // Write actual modulus (-1 for preceding 0) + memcpy(&asn1[index], (void *)modulus, modulus_length-1); + index += modulus_length-1; + + // Write ASN1 INT for exponent + asn1[index++] = 0x02; + // Write ASN1 length for INT + asn1[index++] = exp_length & 0xFF; + // Write exponent bytes + for (i = 1; i <= exp_length; i++) + asn1[index++] = data[i]; + +#if TARGET_OS_IPHONE + // index contains bytes written, use it for length + return (SecKeyCreateRSAPublicKey(NULL, asn1, index, kSecKeyEncodingPkcs1)); +#else + return (SecKeyCreateRSAPublicKey_OSX(asn1, index)); +#endif +} + +#if TARGET_OS_IPHONE +mDNSlocal mStatus rsa_sha_verify(AlgContext *ctx, mDNSu8 *key, mDNSu32 keylen, mDNSu8 *signature, mDNSu32 siglen) +{ + SecKeyRef keyref; + OSStatus result; + mDNSu8 digest[CC_SHA512_DIGEST_LENGTH]; + int digestlen; + int cryptoAlg; + + switch (ctx->alg) + { + case CRYPTO_RSA_NSEC3_SHA1: + case CRYPTO_RSA_SHA1: + cryptoAlg = kSecPaddingPKCS1SHA1; + digestlen = CC_SHA1_DIGEST_LENGTH; + CC_SHA1_Final(digest, (CC_SHA1_CTX *)ctx->context); + break; + case CRYPTO_RSA_SHA256: + cryptoAlg = kSecPaddingPKCS1SHA256; + digestlen = CC_SHA256_DIGEST_LENGTH; + CC_SHA256_Final(digest, (CC_SHA256_CTX *)ctx->context); + break; + case CRYPTO_RSA_SHA512: + cryptoAlg = kSecPaddingPKCS1SHA512; + digestlen = CC_SHA512_DIGEST_LENGTH; + CC_SHA512_Final(digest, (CC_SHA512_CTX *)ctx->context); + break; + default: + LogMsg("rsa_sha_verify: Unsupported algorithm %d", ctx->alg); + return mStatus_BadParamErr; + } + + keyref = rfc3110_import(key, keylen); + if (!keyref) + { + LogMsg("rsa_sha_verify: Error decoding rfc3110 key data"); + return mStatus_NoMemoryErr; + } + result = SecKeyRawVerify(keyref, cryptoAlg, digest, digestlen, signature, siglen); + CFRelease(keyref); + if (result != noErr) + { + LogMsg("rsa_sha_verify: Failed for alg %d", ctx->alg); + return mStatus_BadParamErr; + } + else + { + LogInfo("rsa_sha_verify: Passed for alg %d", ctx->alg); + return mStatus_NoError; + } +} +#else // TARGET_OS_IPHONE + +mDNSlocal SecKeyRef SecKeyCreateRSAPublicKey_OSX(unsigned char *asn1, int length) +{ + SecKeyRef result = NULL; + + SecExternalFormat extFormat = kSecFormatBSAFE; + SecExternalItemType itemType = kSecItemTypePublicKey; + CFArrayRef outArray = NULL; + + CFDataRef keyData = CFDataCreate(NULL, asn1, length); + if (!keyData) + return NULL; + + OSStatus err = SecItemImport(keyData, NULL, &extFormat, &itemType, 0, NULL, NULL, &outArray); + + CFRelease(keyData); + if (noErr != err || outArray == NULL) + { + if (outArray) + CFRelease(outArray); + return NULL; + } + + result = (SecKeyRef)CFArrayGetValueAtIndex(outArray, 0); + if (result == NULL) + { + CFRelease(outArray); + return NULL; + } + + CFRetain(result); + CFRelease(outArray); + return result; +} + +mDNSlocal Boolean VerifyData(SecKeyRef key, CFStringRef digestStr, mDNSu8 *digest, int dlen, int digestlenAttr, mDNSu8 *sig, int siglen, CFStringRef digest_type) +{ + CFErrorRef error; + Boolean ret; + + CFDataRef signature = CFDataCreate(NULL, sig, siglen); + if (!signature) + return false; + + SecTransformRef verifyXForm = SecVerifyTransformCreate(key, signature, &error); + CFRelease(signature); + if (verifyXForm == NULL) + { + return false; + } + + // tell the transform what type of data it is geting + if (!SecTransformSetAttribute(verifyXForm, kSecInputIsAttributeName, digest_type, &error)) + { + LogMsg("VerifyData: SecTransformSetAttribute digest_type"); + goto err; + } + + if (!SecTransformSetAttribute(verifyXForm, kSecDigestTypeAttribute, digestStr, &error)) + { + LogMsg("VerifyData: SecTransformSetAttribute digestStr"); + goto err; + } + + CFNumberRef digestLengthRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberCFIndexType, &digestlenAttr); + if (digestLengthRef == NULL) + { + LogMsg("VerifyData: CFNumberCreate failed"); + goto err; + } + + ret = SecTransformSetAttribute(verifyXForm, kSecDigestLengthAttribute, digestLengthRef, &error); + CFRelease(digestLengthRef); + if (!ret) + { + LogMsg("VerifyData: SecTransformSetAttribute digestLengthRef"); + goto err; + } + + CFDataRef dataToSign = CFDataCreate(NULL, digest, dlen); + if (dataToSign == NULL) + { + LogMsg("VerifyData: CFDataCreate failed"); + goto err; + } + + ret = SecTransformSetAttribute(verifyXForm, kSecTransformInputAttributeName, dataToSign, &error); + CFRelease(dataToSign); + if (!ret) + { + LogMsg("VerifyData: SecTransformSetAttribute TransformAttributeName"); + goto err; + } + + CFBooleanRef boolRef = SecTransformExecute(verifyXForm, &error); + CFRelease(verifyXForm); + + if (error != NULL) + { + CFStringRef errStr = CFErrorCopyDescription(error); + char errorbuf[128]; + errorbuf[0] = 0; + if (errStr != NULL) + { + if (!CFStringGetCString(errStr, errorbuf, sizeof(errorbuf), kCFStringEncodingUTF8)) + { + LogMsg("VerifyData: CFStringGetCString failed"); + } + } + LogMsg("VerifyData: SecTransformExecute failed with %s", errorbuf); + return false; + } + return CFEqual(boolRef, kCFBooleanTrue); +err: + CFRelease(verifyXForm); + return false; +} + +mDNSlocal mStatus rsa_sha_verify(AlgContext *ctx, mDNSu8 *key, mDNSu32 keylen, mDNSu8 *signature, mDNSu32 siglen) +{ + SecKeyRef keyref; + mDNSu8 digest[CC_SHA512_DIGEST_LENGTH]; + int digestlen; + int digestlenAttr; + CFStringRef digestStr; + mDNSBool ret; + + switch (ctx->alg) + { + case CRYPTO_RSA_NSEC3_SHA1: + case CRYPTO_RSA_SHA1: + digestStr = kSecDigestSHA1; + digestlen = CC_SHA1_DIGEST_LENGTH; + digestlenAttr = 0; + CC_SHA1_Final(digest, (CC_SHA1_CTX *)ctx->context); + break; + case CRYPTO_RSA_SHA256: + digestStr = kSecDigestSHA2; + digestlen = CC_SHA256_DIGEST_LENGTH; + digestlenAttr = 256; + CC_SHA256_Final(digest, (CC_SHA256_CTX *)ctx->context); + break; + case CRYPTO_RSA_SHA512: + digestStr = kSecDigestSHA2; + digestlen = CC_SHA512_DIGEST_LENGTH; + digestlenAttr = 512; + CC_SHA512_Final(digest, (CC_SHA512_CTX *)ctx->context); + break; + default: + LogMsg("rsa_sha_verify: Unsupported algorithm %d", ctx->alg); + return mStatus_BadParamErr; + } + + keyref = rfc3110_import(key, keylen); + if (!keyref) + { + LogMsg("rsa_sha_verify: Error decoding rfc3110 key data"); + return mStatus_NoMemoryErr; + } + ret = VerifyData(keyref, digestStr, digest, digestlen, digestlenAttr, signature, siglen, kSecInputIsDigest); + CFRelease(keyref); + if (!ret) + { + LogMsg("rsa_sha_verify: Failed for alg %d", ctx->alg); + return mStatus_BadParamErr; + } + else + { + LogInfo("rsa_sha_verify: Passed for alg %d", ctx->alg); + return mStatus_NoError; + } +} +#endif // TARGET_OS_IPHONE + +AlgFuncs sha_funcs = {sha_create, sha_destroy, sha_len, sha_add, sha_verify, mDNSNULL, sha_final}; +AlgFuncs rsa_sha_funcs = {rsa_sha_create, rsa_sha_destroy, rsa_sha_len, rsa_sha_add, rsa_sha_verify, mDNSNULL, mDNSNULL}; +AlgFuncs enc_funcs = {enc_create, enc_destroy, mDNSNULL, enc_add, mDNSNULL, enc_encode, mDNSNULL}; + +#ifndef DNSSEC_DISABLED + +mDNSexport mStatus DNSSECCryptoInit(mDNS *const m) +{ + mStatus result; + + result = DigestAlgInit(SHA1_DIGEST_TYPE, &sha_funcs); + if (result != mStatus_NoError) + return result; + result = DigestAlgInit(SHA256_DIGEST_TYPE, &sha_funcs); + if (result != mStatus_NoError) + return result; + result = CryptoAlgInit(CRYPTO_RSA_SHA1, &rsa_sha_funcs); + if (result != mStatus_NoError) + return result; + result = CryptoAlgInit(CRYPTO_RSA_NSEC3_SHA1, &rsa_sha_funcs); + if (result != mStatus_NoError) + return result; + result = CryptoAlgInit(CRYPTO_RSA_SHA256, &rsa_sha_funcs); + if (result != mStatus_NoError) + return result; + result = CryptoAlgInit(CRYPTO_RSA_SHA512, &rsa_sha_funcs); + if (result != mStatus_NoError) + return result; + result = EncAlgInit(ENC_BASE32, &enc_funcs); + if (result != mStatus_NoError) + return result; + result = EncAlgInit(ENC_BASE64, &enc_funcs); + if (result != mStatus_NoError) + return result; + + result = DNSSECPlatformInit(m); + + return result; +} + +#else // !DNSSEC_DISABLED + +mDNSexport mStatus DNSSECCryptoInit(mDNS *const m) +{ + (void) m; + + return mStatus_NoError; +} + +#endif // !DNSSEC_DISABLED + + diff --git a/mDNSResponder/mDNSMacOSX/CryptoSupport.h b/mDNSResponder/mDNSMacOSX/CryptoSupport.h new file mode 100644 index 00000000..9c0fd079 --- /dev/null +++ b/mDNSResponder/mDNSMacOSX/CryptoSupport.h @@ -0,0 +1,22 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2011 Apple Computer, 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. + */ +#ifndef __CRYPTO_SUPPORT_H +#define __CRYPTO_SUPPORT_H + +extern mStatus DNSSECCryptoInit(mDNS *const m); + +#endif // __CRYPTO_SUPPORT_H diff --git a/mDNSResponder/mDNSMacOSX/DNSProxySupport.c b/mDNSResponder/mDNSMacOSX/DNSProxySupport.c new file mode 100644 index 00000000..042bbc80 --- /dev/null +++ b/mDNSResponder/mDNSMacOSX/DNSProxySupport.c @@ -0,0 +1,543 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2011 Apple Computer, 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 "mDNSEmbeddedAPI.h" +#include "mDNSMacOSX.h" + +#include <sys/types.h> +#include <sys/time.h> +#include <sys/event.h> + +#define ValidSocket(s) ((s) >= 0) + +// Global to store the 4 DNS Proxy Listeners (UDPv4/6, TCPv4/6) +static int dp_listener[4]; + +#define NUM_PROXY_TCP_CONNS 100 + +typedef struct +{ + TCPSocket sock; + DNSMessage *reply; + mDNSu16 replyLen; + mDNSu32 nread; +} ProxyTCPInfo_t; + +// returns -1 for failures including the other end closing the socket +// returns 0 if successful in reading data, but still not read the data fully +// returns 1 if successful in reading all the data +mDNSlocal int ProxyTCPRead(ProxyTCPInfo_t *tcpInfo) +{ + long n; + mDNSBool closed; + + if (tcpInfo->nread < 2) // First read the two-byte length preceeding the DNS message + { + mDNSu8 *lenptr = (mDNSu8 *)&tcpInfo->replyLen; + n = mDNSPlatformReadTCP(&tcpInfo->sock, lenptr + tcpInfo->nread, 2 - tcpInfo->nread, &closed); + if (n < 0 || closed) + { + LogMsg("ProxyTCPRead: attempt to read message length failed"); + return -1; + } + + tcpInfo->nread += n; + if (tcpInfo->nread < 2) + { + LogMsg("ProxyTCPRead: nread %d, n %d", tcpInfo->nread, n); + return 0; + } + + tcpInfo->replyLen = (mDNSu16)((mDNSu16)lenptr[0] << 8 | lenptr[1]); + if (tcpInfo->replyLen < sizeof(DNSMessageHeader)) + { + LogMsg("ProxyTCPRead: Message length too short (%d bytes)", tcpInfo->replyLen); + return -1; + } + + tcpInfo->reply = mallocL("ProxyTCPInfo", tcpInfo->replyLen); + if (!tcpInfo->reply) + { + LogMsg("ProxyTCPRead: Memory failure"); + return -1; + } + } + + n = mDNSPlatformReadTCP(&tcpInfo->sock, ((char *)tcpInfo->reply) + (tcpInfo->nread - 2), tcpInfo->replyLen - (tcpInfo->nread - 2), &closed); + + if (n < 0 || closed) + { + LogMsg("ProxyTCPRead: read failure n %d, closed %d", n, closed); + return -1; + } + tcpInfo->nread += n; + if ((tcpInfo->nread - 2) != tcpInfo->replyLen) + return 0; + else + return 1; +} + +mDNSlocal void ProxyTCPSocketCallBack(int s1, short filter, void *context) +{ + int ret; + struct sockaddr_storage from; + struct sockaddr_storage to; + mDNSAddr senderAddr, destAddr; + mDNSIPPort senderPort; + ProxyTCPInfo_t *ti = (ProxyTCPInfo_t *)context; + TCPSocket *sock = &ti->sock; + KQSocketSet *kq = &sock->ss; + + (void) filter; + + ret = ProxyTCPRead(ti); + if (ret == -1) + { + mDNSPlatformDisposeProxyContext(ti); + return; + } + else if (!ret) + { + debugf("ProxyTCPReceive: Not yet read completely Actual length %d, Read length %d", ti->replyLen, ti->nread); + return; + } + // We read all the data and hence not interested in read events anymore + KQueueSet(s1, EV_DELETE, EVFILT_READ, sock->kqEntry); + + mDNSPlatformMemZero(&to, sizeof(to)); + mDNSPlatformMemZero(&from, sizeof(from)); + socklen_t len = sizeof(to); + ret = getsockname(s1, (struct sockaddr*) &to, &len); + if (ret < 0) + { + LogMsg("ProxyTCPReceive: getsockname(fd=%d) errno %d", s1, errno); + mDNSPlatformDisposeProxyContext(ti); + return; + } + ret = getpeername(s1, (struct sockaddr*) &from, &len); + if (ret < 0) + { + LogMsg("ProxyTCPReceive: getpeername(fd=%d) errno %d", s1, errno); + mDNSPlatformDisposeProxyContext(ti); + return; + } + + if (from.ss_family == AF_INET) + { + struct sockaddr_in *s = (struct sockaddr_in*)&from; + + senderAddr.type = mDNSAddrType_IPv4; + senderAddr.ip.v4.NotAnInteger = s->sin_addr.s_addr; + senderPort.NotAnInteger = s->sin_port; + + s = (struct sockaddr_in *)&to; + destAddr.type = mDNSAddrType_IPv4; + destAddr.ip.v4.NotAnInteger = s->sin_addr.s_addr; + + LogInfo("ProxyTCPReceive received IPv4 packet(len %d) from %#-15a to %#-15a on skt %d %s", ti->replyLen, &senderAddr, &destAddr, s1, NULL); + } + else if (from.ss_family == AF_INET6) + { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)&from; + senderAddr.type = mDNSAddrType_IPv6; + senderAddr.ip.v6 = *(mDNSv6Addr*)&sin6->sin6_addr; + senderPort.NotAnInteger = sin6->sin6_port; + + sin6 = (struct sockaddr_in6 *)&to; + destAddr.type = mDNSAddrType_IPv6; + destAddr.ip.v6 = *(mDNSv6Addr*)&sin6->sin6_addr; + + LogInfo("ProxyTCPReceive received IPv6 packet(len %d) from %#-15a to %#-15a on skt %d %s", ti->replyLen, &senderAddr, &destAddr, s1, NULL); + } + else + { + LogMsg("ProxyTCPReceive from is unknown address family %d", from.ss_family); + mDNSPlatformDisposeProxyContext(ti); + return; + } + + // We pass sock for the TCPSocket and the "ti" for context as that's what we want to free at the end. + // In the UDP case, there is just a single socket and nothing to free. Hence, the context (last argument) + // would be NULL. + kq->m->p->TCPProxyCallback(kq->m, sock, ti->reply, (mDNSu8 *)ti->reply + ti->replyLen, &senderAddr, senderPort, &destAddr, + UnicastDNSPort, 0, ti); +} + +mDNSlocal void ProxyTCPAccept(int s1, short filter, void *context) +{ + int newfd; + struct sockaddr_storage ss; + socklen_t sslen = sizeof(ss); + const int on = 1; + KQSocketSet *listenSet = (KQSocketSet *)context; + + (void) filter; + + while ((newfd = accept(s1, (struct sockaddr *)&ss, &sslen)) != -1) + { + int err; + int *s; + KQueueEntry *k; + KQSocketSet *kq; + + // Even though we just need a single KQueueEntry, for simplicity we re-use + // the KQSocketSet + ProxyTCPInfo_t *ti = mallocL("ProxyTCPContext", sizeof(ProxyTCPInfo_t)); + if (!ti) + { + LogMsg("ProxyTCPAccept: cannot allocate TCPSocket"); + close(newfd); + return; + } + mDNSPlatformMemZero(ti, sizeof(ProxyTCPInfo_t)); + TCPSocket *sock = &ti->sock; + + kq = &sock->ss; + kq->sktv4 = -1; + kq->sktv6 = -1; + kq->m = listenSet->m; + + fcntl(newfd, F_SETFL, fcntl(newfd, F_GETFL, 0) | O_NONBLOCK); // set non-blocking + if (ss.ss_family == AF_INET) + { + s = &kq->sktv4; + k = &kq->kqsv4; + // Receive interface identifiers + err = setsockopt(newfd, IPPROTO_IP, IP_RECVIF, &on, sizeof(on)); + if (err) + { + LogMsg("ProxyTCPAccept: IP_RECVIF %d errno %d (%s)", newfd, errno, strerror(errno)); + mDNSPlatformDisposeProxyContext(ti); + return; + } + } + else + { + s = &kq->sktv6; + k = &kq->kqsv6; + // We want to receive destination addresses and receive interface identifiers + err = setsockopt(newfd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, sizeof(on)); + if (err) + { + LogMsg("ProxyTCPAccept: IP_RECVPKTINFO %d errno %d (%s)", newfd, errno, strerror(errno)); + mDNSPlatformDisposeProxyContext(ti); + return; + } + } + *s = newfd; + // mDNSPlatformReadTCP/WriteTCP (unlike the UDP counterpart) does not provide the destination address + // from which we can infer the destination address family. Hence we need to remember that here. + // Instead of remembering the address family, we remember the right fd. + sock->fd = newfd; + sock->kqEntry = k; + + k->KQcallback = ProxyTCPSocketCallBack; + k->KQcontext = ti; + k->KQtask = "TCP Proxy packet reception"; +#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM + k->readSource = mDNSNULL; + k->writeSource = mDNSNULL; + k->fdClosed = mDNSfalse; +#endif + KQueueSet(*s, EV_ADD, EVFILT_READ, k); + } +} + +mDNSlocal mStatus SetupUDPProxySocket(mDNS *const m, int skt, KQSocketSet *cp, u_short sa_family, mDNSBool useBackgroundTrafficClass) +{ + int *s = (sa_family == AF_INET) ? &cp->sktv4 : &cp->sktv6; + KQueueEntry *k = (sa_family == AF_INET) ? &cp->kqsv4 : &cp->kqsv6; + const int on = 1; + mDNSIPPort port; + mStatus err = mStatus_NoError; + + cp->m = m; + port = cp->port; + cp->closeFlag = mDNSNULL; + + // set default traffic class + // setTrafficClass(skt, mDNSfalse); + (void) useBackgroundTrafficClass; + + if (sa_family == AF_INET) + { + err = setsockopt(skt, IPPROTO_IP, IP_RECVDSTADDR, &on, sizeof(on)); + if (err < 0) + { + LogMsg("SetupUDPProxySocket: IP_RECVDSTADDR %d errno %d (%s)", skt, errno, strerror(errno)); + return err; + } + + // We want to receive interface identifiers + err = setsockopt(skt, IPPROTO_IP, IP_RECVIF, &on, sizeof(on)); + if (err < 0) + { + LogMsg("SetupUDPProxySocket: IP_RECVIF %d errno %d (%s)", skt, errno, strerror(errno)); + return err; + } + } + else if (sa_family == AF_INET6) + { + // We want to receive destination addresses and receive interface identifiers + err = setsockopt(skt, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, sizeof(on)); + if (err < 0) + { + LogMsg("SetupUDPProxySocket: IPV6_RECVPKTINFO %d errno %d (%s)", skt, errno, strerror(errno)); + return err; + } + + // We want to receive packet hop count value so we can check it + err = setsockopt(skt, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &on, sizeof(on)); + if (err < 0) + { + LogMsg("SetupUDPProxySocket: IPV6_RECVHOPLIMIT %d errno %d (%s)", skt, errno, strerror(errno)); + return err; + } + } + else + { + LogMsg("SetupUDPProxySocket: wrong family %d", sa_family); + return -1; + } + + if (fcntl(skt, F_SETFL, fcntl(skt, F_GETFL, 0) | O_NONBLOCK) < 0) + { + LogMsg("SetupUDPProxySocket: fnctl failed %d", errno); + return -1; + } + + *s = skt; + //k->KQcallback = ProxyUDPSocketCallBack; + k->KQcallback = myKQSocketCallBack; + k->KQcontext = cp; + k->KQtask = "UDP Proxy packet reception"; +#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM + k->readSource = mDNSNULL; + k->writeSource = mDNSNULL; + k->fdClosed = mDNSfalse; +#endif + + KQueueSet(*s, EV_ADD, EVFILT_READ, k); + + return(err); +} + +mDNSlocal mStatus SetupTCPProxySocket(mDNS *const m, int skt, KQSocketSet *cp, u_short sa_family, mDNSBool useBackgroundTrafficClass) +{ + int *s = (sa_family == AF_INET) ? &cp->sktv4 : &cp->sktv6; + KQueueEntry *k = (sa_family == AF_INET) ? &cp->kqsv4 : &cp->kqsv6; + mDNSIPPort port; + mStatus err; + + cp->m = m; + port = cp->port; + // XXX may not be used by the TCP codepath + cp->closeFlag = mDNSNULL; + + // for TCP sockets, the traffic class is set once and not changed + // setTrafficClass(skt, useBackgroundTrafficClass); + (void) useBackgroundTrafficClass; + + // All the socket setup has already been done + err = listen(skt, NUM_PROXY_TCP_CONNS); + if (err) + { + LogMsg("SetupTCPProxySocket: listen %d errno %d (%s)", skt, errno, strerror(errno)); + return err; + } + fcntl(skt, F_SETFL, fcntl(skt, F_GETFL, 0) | O_NONBLOCK); // set non-blocking + + *s = skt; + k->KQcallback = ProxyTCPAccept; + k->KQcontext = cp; + k->KQtask = "TCP Accept"; +#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM + k->readSource = mDNSNULL; + k->writeSource = mDNSNULL; + k->fdClosed = mDNSfalse; +#endif + KQueueSet(*s, EV_ADD, EVFILT_READ, k); + return mStatus_NoError; +} + +mDNSlocal void BindDPSocket(int fd, int sa_family) +{ + int err; + const int on = 1; + + if (sa_family == AF_INET) + { + struct sockaddr_in addr; + + err = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(on)); + if (err < 0) + LogMsg("BindDPSocket: setsockopt SO_REUSEPORT failed for V4 %d errno %d (%s)", fd, errno, strerror(errno)); + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(53); + + err = bind(fd, (struct sockaddr*) &addr, sizeof(addr)); + if (err) + { + LogMsg("BindDPSocket: bind %d errno %d (%s)", fd, errno, strerror(errno)); + return; + } + } + else + { + struct sockaddr_in6 addr6; + + // We want to receive only IPv6 packets. Without this option we get IPv4 packets too, + // with mapped addresses of the form 0:0:0:0:0:FFFF:xxxx:xxxx, where xxxx:xxxx is the IPv4 address + err = setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)); + if (err < 0) + { + LogMsg("DPFBindSocket: setsockopt IPV6_V6ONLY %d errno %d (%s)", fd, errno, strerror(errno)); + return; + } + err = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(on)); + if (err < 0) + LogMsg("BindDPSocket: setsockopt SO_REUSEPORT failed for V6 %d errno %d (%s)", fd, errno, strerror(errno)); + + memset(&addr6, 0, sizeof(addr6)); + addr6.sin6_family = AF_INET6; + addr6.sin6_port = htons(53); + + err = bind(fd, (struct sockaddr*) &addr6, sizeof(addr6)); + if (err) + { + LogMsg("BindDPSocket: bind6 %d errno %d (%s)", fd, errno, strerror(errno)); + return; + } + } +} + +// Setup DNS Proxy Skts in main kevent loop and set the skt options +mDNSlocal void SetupDNSProxySkts(mDNS *const m, int fd[4]) +{ + int i; + mStatus err; + KQSocketSet *udpSS; + KQSocketSet *tcpSS; + + udpSS = &m->p->UDPProxy.ss; + tcpSS = &m->p->TCPProxy.ss; + udpSS->port = UnicastDNSPort; + tcpSS->port = UnicastDNSPort; + + LogMsg("SetupDNSProxySkts: %d, %d, %d, %d", fd[0], fd[1], fd[2], fd[3]); + + // myKQSocketCallBack checks for proxy and calls the m->p->ProxyCallback instead of mDNSCoreReceive + udpSS->proxy = mDNStrue; + err = SetupUDPProxySocket(m, fd[0], udpSS, AF_INET, mDNSfalse); + if (err) + LogMsg("SetupDNSProxySkts: ERROR!! UDPv4 Socket"); + + err = SetupUDPProxySocket(m, fd[1], udpSS, AF_INET6, mDNSfalse); + if (err) + LogMsg("SetupDNSProxySkts: ERROR!! UDPv6 Socket"); + + err = SetupTCPProxySocket(m, fd[2], tcpSS, AF_INET, mDNSfalse); + if (err) + LogMsg("SetupDNSProxySkts: ERROR!! TCPv4 Socket"); + + err = SetupTCPProxySocket(m, fd[3], tcpSS, AF_INET6, mDNSfalse); + if (err) + LogMsg("SetupDNSProxySkts: ERROR!! TCPv6 Socket"); + + for (i = 0; i < 4; i++) + dp_listener[i] = fd[i]; +} + +// Create and bind the DNS Proxy Skts for use +mDNSexport void mDNSPlatformInitDNSProxySkts(mDNS *const m, ProxyCallback UDPCallback, ProxyCallback TCPCallback) +{ + int dpskt[4]; + + dpskt[0] = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + dpskt[1] = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); + dpskt[2] = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + dpskt[3] = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP); + + // Close all DNS Proxy skts in case any of them are invalid + if (!ValidSocket(dpskt[0]) || !ValidSocket(dpskt[1]) || + !ValidSocket(dpskt[2]) || !ValidSocket(dpskt[3])) + { + if (ValidSocket(dpskt[0])) + close(dpskt[0]); + if (ValidSocket(dpskt[1])) + close(dpskt[1]); + if (ValidSocket(dpskt[2])) + close(dpskt[2]); + if (ValidSocket(dpskt[3])) + close(dpskt[3]); + } + + BindDPSocket(dpskt[0], AF_INET); + BindDPSocket(dpskt[1], AF_INET6); + BindDPSocket(dpskt[2], AF_INET); + BindDPSocket(dpskt[3], AF_INET6); + + LogInfo("mDNSPlatformInitDNSProxySkts: Opened Listener Sockets for DNS Proxy : %d, %d, %d, %d", + dpskt[0], dpskt[1], dpskt[2], dpskt[3]); + + m->p->UDPProxyCallback = UDPCallback; + m->p->TCPProxyCallback = TCPCallback; + + SetupDNSProxySkts(m, dpskt); +} + +mDNSexport void mDNSPlatformCloseDNSProxySkts(mDNS *const m) +{ + (void) m; + int i; + for (i = 0; i < 4; i++) + close(dp_listener[i]); + LogInfo("mDNSPlatformCloseDNSProxySkts: Closing DNS Proxy Listener Sockets"); +} + +mDNSexport void mDNSPlatformDisposeProxyContext(void *context) +{ + ProxyTCPInfo_t *ti; + TCPSocket *sock; + KQSocketSet *kq; + + if (!context) + return; + + ti = (ProxyTCPInfo_t *)context; + sock = &ti->sock; + + kq = &sock->ss; + if (kq->sktv4 != -1) + { + shutdown(kq->sktv4, 2); + mDNSPlatformCloseFD(&kq->kqsv4, kq->sktv4); + } + if (kq->sktv6 != -1) + { + shutdown(kq->sktv6, 2); + mDNSPlatformCloseFD(&kq->kqsv6, kq->sktv6); + } + if (kq->closeFlag) + *kq->closeFlag = 1; + + if (ti->reply) + freeL("ProxyTCPInfoLen", ti->reply); + freeL("ProxyTCPContext", ti); +} + diff --git a/mDNSResponder/mDNSMacOSX/DNSSECSupport.c b/mDNSResponder/mDNSMacOSX/DNSSECSupport.c new file mode 100644 index 00000000..e1255c4d --- /dev/null +++ b/mDNSResponder/mDNSMacOSX/DNSSECSupport.c @@ -0,0 +1,645 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2012 Apple Computer, 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. + */ + +// *************************************************************************** +// DNSSECSupport.c: Platform specific support for DNSSEC like fetching root +// trust anchor and dnssec probes etc. +// *************************************************************************** + +#include "mDNSEmbeddedAPI.h" +#include "DNSCommon.h" // For mDNS_Lock, mDNS_Random +#include "dnssec.h" +#include "DNSSECSupport.h" + +#include <CommonCrypto/CommonDigest.h> // For Hash algorithms SHA1 etc. + +// Following are needed for fetching the root trust anchor dynamically +#include <CoreFoundation/CoreFoundation.h> +#include <libxml2/libxml/parser.h> +#include <libxml2/libxml/tree.h> +#include <libxml2/libxml/xmlmemory.h> +#include <notify.h> + +// 30 days +#define ROOT_TA_UPDATE_INTERVAL (30 * 24 * 3600) // seconds + +// After 100 days, the test anchors are not valid. Just an arbitrary number +// to configure validUntil. +#define TEST_TA_EXPIRE_INTERVAL (100 * 24 * 4600) + +// When we can't fetch the root TA due to network errors etc., we start off a timer +// to fire at 60 seconds and then keep doubling it till we fetch it +#define InitialTAFetchInterval 60 + +#if !TARGET_OS_IPHONE +DNSQuestion DNSSECProbeQuestion; +#endif + +mDNSlocal int RegisterNotification(mDNS *const m, unsigned int interval); + +mDNSlocal void LinkTrustAnchor(mDNS *const m, TrustAnchor *ta) +{ + int length = 0; + int i; + mDNSu8 *p; + TrustAnchor **t = &m->TrustAnchors; + char buffer[256]; + + while (*t) + t = &((*t)->next); + *t = ta; + + buffer[0] = 0; + p = ta->rds.digest; + for (i = 0; i < ta->digestLen; i++) + { + length += mDNS_snprintf(buffer+length, sizeof(buffer)-1-length, "%x", p[i]); + } + LogInfo("LinkTrustAnchor: Zone %##s, keytag %d, alg %d, digestType %d, digestLen %d, digest %s", ta->zone.c, ta->rds.keyTag, + ta->rds.alg, ta->rds.digestType, ta->digestLen, buffer); +} + +mDNSlocal void DelTrustAnchor(mDNS *const m, const domainname *zone) +{ + TrustAnchor **ta = &m->TrustAnchors; + TrustAnchor *tmp; + + while (*ta && !SameDomainName(&(*ta)->zone, zone)) + ta = &(*ta)->next; + + // First time, we won't find the TrustAnchor in the list as it has + // not been added. + if (!(*ta)) + return; + + tmp = *ta; + *ta = (*ta)->next; // Cut this record from the list + tmp->next = mDNSNULL; + if (tmp->rds.digest) + mDNSPlatformMemFree(tmp->rds.digest); + mDNSPlatformMemFree(tmp); +} + +mDNSlocal void AddTrustAnchor(mDNS *const m, const domainname *zone, mDNSu16 keytag, mDNSu8 alg, mDNSu8 digestType, int diglen, + mDNSu8 *digest) +{ + TrustAnchor *ta, *tmp; + mDNSu32 t = (mDNSu32) time(NULL); + + // Check for duplicates + tmp = m->TrustAnchors; + while (tmp) + { + if (SameDomainName(zone, &tmp->zone) && tmp->rds.keyTag == keytag && tmp->rds.alg == alg && tmp->rds.digestType == digestType && + !memcmp(tmp->rds.digest, digest, diglen)) + { + LogMsg("AddTrustAnchors: Found a duplicate"); + return; + } + tmp = tmp->next; + } + + ta = (TrustAnchor *)mDNSPlatformMemAllocate(sizeof(TrustAnchor)); + if (!ta) + { + LogMsg("AddTrustAnchor: malloc failure ta"); + return; + } + ta->rds.keyTag = keytag; + ta->rds.alg = alg; + ta->rds.digestType = digestType; + ta->rds.digest = digest; + ta->digestLen = diglen; + ta->validFrom = t; + ta->validUntil = t + TEST_TA_EXPIRE_INTERVAL; + AssignDomainName(&ta->zone, zone); + ta->next = mDNSNULL; + + LinkTrustAnchor(m, ta); +} + +#define HexVal(X) ( ((X) >= '0' && (X) <= '9') ? ((X) - '0' ) : \ + ((X) >= 'A' && (X) <= 'F') ? ((X) - 'A' + 10) : \ + ((X) >= 'a' && (X) <= 'f') ? ((X) - 'a' + 10) : -1) + +mDNSlocal mDNSu8 *ConvertDigest(char *digest, int digestType, int *diglen) +{ + int i, j; + mDNSu8 *dig; + + switch (digestType) + { + case SHA1_DIGEST_TYPE: + *diglen = CC_SHA1_DIGEST_LENGTH; + break; + case SHA256_DIGEST_TYPE: + *diglen = CC_SHA256_DIGEST_LENGTH; + break; + default: + LogMsg("ConvertDigest: digest type %d not supported", digestType); + return mDNSNULL; + } + dig = mDNSPlatformMemAllocate(*diglen); + if (!dig) + { + LogMsg("ConvertDigest: malloc failure"); + return mDNSNULL; + } + + for (j=0,i=0; i<*diglen*2; i+=2) + { + int l, h; + l = HexVal(digest[i]); + h = HexVal(digest[i+1]); + if (l<0 || h<0) { LogMsg("ConvertDigest: Cannot convert digest"); return NULL;} + dig[j++] = (mDNSu8)((l << 4) | h); + } + return dig; +} + +// All the children are in a linked list +// +// <TrustAnchor> has two children: <Zone> and <KeyDigest> +// <KeyDigest> has four children <KeyTag> <Algorithm> <DigestType> <Digest> +// +// Returns false if failed to parse the element i.e., malformed xml document. +// Validity of the actual values itself is done outside the function. +mDNSlocal mDNSBool ParseElementChildren(xmlDocPtr tadoc, xmlNode *node, TrustAnchor *ta) +{ + xmlNode *cur_node; + xmlChar *val1, *val2, *val; + char *invalid = NULL; + + val = val1 = val2 = NULL; + + for (cur_node = node; cur_node; cur_node = cur_node->next) + { + invalid = NULL; + val1 = val2 = NULL; + + val = xmlNodeListGetString(tadoc, cur_node->xmlChildrenNode, 1); + if (!val) + { + LogInfo("ParseElementChildren: NULL value for %s", cur_node->name); + continue; + } + if (!xmlStrcmp(cur_node->name, (const xmlChar *)"Zone")) + { + // MaeDomainNameFromDNSNameString does not work for "." + if (!xmlStrcmp(val, (const xmlChar *)".")) + { + ta->zone.c[0] = 0; + } + else if (!MakeDomainNameFromDNSNameString(&ta->zone, (char *)val)) + { + LogMsg("ParseElementChildren: Cannot parse Zone %s", val); + goto error; + } + else + { + LogInfo("ParseElementChildren: Element %s, value %##s", cur_node->name, ta->zone.c); + } + } + else if (!xmlStrcmp(cur_node->name, (const xmlChar *)"KeyTag")) + { + ta->rds.keyTag = strtol((const char *)val, &invalid, 10); + if (*invalid != '\0') + { + LogMsg("ParseElementChildren: KeyTag invalid character %d", *invalid); + goto error; + } + else + { + LogInfo("ParseElementChildren: Element %s, value %d", cur_node->name, ta->rds.keyTag); + } + } + else if (!xmlStrcmp(cur_node->name, (const xmlChar *)"Algorithm")) + { + ta->rds.alg = strtol((const char *)val, &invalid, 10); + if (*invalid != '\0') + { + LogMsg("ParseElementChildren: Algorithm invalid character %c", *invalid); + goto error; + } + else + { + LogInfo("ParseElementChildren: Element %s, value %d", cur_node->name, ta->rds.alg); + } + } + else if (!xmlStrcmp(cur_node->name, (const xmlChar *)"DigestType")) + { + ta->rds.digestType = strtol((const char *)val, &invalid, 10); + if (*invalid != '\0') + { + LogMsg("ParseElementChildren: Algorithm invalid character %c", *invalid); + goto error; + } + else + { + LogInfo("ParseElementChildren: Element %s, value %d", cur_node->name, ta->rds.digestType); + } + } + else if (!xmlStrcmp(cur_node->name, (const xmlChar *)"Digest")) + { + int diglen; + mDNSu8 *dig = ConvertDigest((char *)val, ta->rds.digestType, &diglen); + if (dig) + { + LogInfo("ParseElementChildren: Element %s, digest %s", cur_node->name, val); + ta->digestLen = diglen; + ta->rds.digest = dig; + } + else + { + LogMsg("ParseElementChildren: Element %s, NULL digest", cur_node->name); + goto error; + } + } + else if (!xmlStrcmp(cur_node->name, (const xmlChar *)"KeyDigest")) + { + struct tm tm; + val1 = xmlGetProp(cur_node, (const xmlChar *)"validFrom"); + if (val1) + { + char *s = strptime((const char *)val1, "%Y-%m-%dT%H:%M:%S", &tm); + if (!s) + { + LogMsg("ParseElementChildren: Parsing ValidFrom failed %s", val1); + goto error; + } + else + { + ta->validFrom = (mDNSu32)timegm(&tm); + } + } + val2 = xmlGetProp(cur_node, (const xmlChar *)"validUntil"); + if (val2) + { + char *s = strptime((const char *)val2, "%Y-%m-%dT%H:%M:%S", &tm); + if (!s) + { + LogMsg("ParseElementChildren: Parsing ValidFrom failed %s", val2); + goto error; + } + else + { + ta->validUntil = (mDNSu32)timegm(&tm); + } + } + else + { + // If there is no validUntil, set it to the next probing interval + mDNSu32 t = (mDNSu32) time(NULL); + ta->validUntil = t + ROOT_TA_UPDATE_INTERVAL; + } + LogInfo("ParseElementChildren: ValidFrom time %u, validUntil %u", (unsigned)ta->validFrom, (unsigned)ta->validUntil); + } + if (val1) + xmlFree(val1); + if (val2) + xmlFree(val2); + if (val) + xmlFree(val); + } + return mDNStrue; +error: + if (val1) + xmlFree(val1); + if (val2) + xmlFree(val2); + if (val) + xmlFree(val); + return mDNSfalse; +} + +mDNSlocal mDNSBool ValidateTrustAnchor(TrustAnchor *ta) +{ + time_t currTime = time(NULL); + + // Currently only support trust anchor for root. + if (!SameDomainName(&ta->zone, (const domainname *)"\000")) + { + LogInfo("ParseElementChildren: Zone %##s not root", ta->zone.c); + return mDNSfalse; + } + + switch (ta->rds.digestType) + { + case SHA1_DIGEST_TYPE: + if (ta->digestLen != CC_SHA1_DIGEST_LENGTH) + { + LogMsg("ValidateTrustAnchor: Invalid digest len %d for SHA1", ta->digestLen); + return mDNSfalse; + } + break; + case SHA256_DIGEST_TYPE: + if (ta->digestLen != CC_SHA256_DIGEST_LENGTH) + { + LogMsg("ValidateTrustAnchor: Invalid digest len %d for SHA256", ta->digestLen); + return mDNSfalse; + } + break; + default: + LogMsg("ValidateTrustAnchor: digest type %d not supported", ta->rds.digestType); + return mDNSfalse; + } + if (!ta->rds.digest) + { + LogMsg("ValidateTrustAnchor: digest NULL for %d", ta->rds.digestType); + return mDNSfalse; + } + switch (ta->rds.alg) + { + case CRYPTO_RSA_SHA512: + case CRYPTO_RSA_SHA256: + case CRYPTO_RSA_NSEC3_SHA1: + case CRYPTO_RSA_SHA1: + break; + default: + LogMsg("ValidateTrustAnchor: Algorithm %d not supported", ta->rds.alg); + return mDNSfalse; + } + + if (DNS_SERIAL_LT(currTime, ta->validFrom)) + { + LogMsg("ValidateTrustAnchor: Invalid ValidFrom time %u, currtime %u", (unsigned)ta->validFrom, (unsigned)currTime); + return mDNSfalse; + } + if (DNS_SERIAL_LT(ta->validUntil, currTime)) + { + LogMsg("ValidateTrustAnchor: Invalid ValidUntil time %u, currtime %u", (unsigned)ta->validUntil, (unsigned)currTime); + return mDNSfalse; + } + return mDNStrue; +} + +mDNSlocal mDNSBool ParseElement(xmlDocPtr tadoc, xmlNode * a_node, TrustAnchor *ta) +{ + xmlNode *cur_node = NULL; + + for (cur_node = a_node; cur_node; cur_node = cur_node->next) + { + if (cur_node->type == XML_ELEMENT_NODE) + { + // There could be multiple KeyDigests per TrustAnchor. We keep parsing till we + // reach the last one or we encounter an error in parsing the document. + if (!xmlStrcmp(cur_node->name, (const xmlChar *)"KeyDigest")) + { + if (ta->rds.digest) + mDNSPlatformMemFree(ta->rds.digest); + ta->rds.digestType = 0; + ta->digestLen = 0; + } + if (!ParseElementChildren(tadoc, cur_node->children, ta)) + return mDNSfalse; + if (!ParseElement(tadoc, cur_node->children, ta)) + return mDNSfalse; + } + } + return mDNStrue; +} + +mDNSlocal void TAComplete(mDNS *const m, void *context) +{ + TrustAnchor *ta = (TrustAnchor *)context; + + DelTrustAnchor(m, &ta->zone); + LinkTrustAnchor(m, ta); +} + +mDNSlocal void FetchRootTA(mDNS *const m) +{ + CFStringRef urlString = CFSTR("https://data.iana.org/root-anchors/root-anchors.xml"); + CFDataRef xmlData; + CFStringRef fileRef = NULL; + const char *xmlFileName = NULL; + char buf[512]; + CFURLRef url = NULL; + static unsigned int RootTAFetchInterval = InitialTAFetchInterval; + + (void) m; + + TrustAnchor *ta = (TrustAnchor *)mDNSPlatformMemAllocate(sizeof(TrustAnchor)); + if (!ta) + { + LogMsg("FetchRootTA: TrustAnchor alloc failed"); + return; + } + memset(ta, 0, sizeof(TrustAnchor)); + + url = CFURLCreateWithString(NULL, urlString, NULL); + if (!url) + { + LogMsg("FetchRootTA: CFURLCreateWithString error"); + mDNSPlatformMemFree(ta); + return; + } + + // If we can't fetch the XML file e.g., network problems, trigger a timer. All other failures + // should hardly happen in practice for which schedule the normal interval to refetch the TA. + if (!CFURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault, url, &xmlData, NULL, NULL, NULL)) + { + LogInfo("FetchRootTA: CFURLCreateDataAndPropertiesFromResource error"); + CFRelease(url); + mDNSPlatformMemFree(ta); + RegisterNotification(m, RootTAFetchInterval); + RootTAFetchInterval *= 2 + 1; + return; + } + + // get the name of the last component from the url, libxml will use it if + // it has to report an error + fileRef = CFURLCopyLastPathComponent(url); + if (fileRef) + { + xmlFileName = CFStringGetCStringPtr(fileRef, kCFStringEncodingUTF8); + if (!xmlFileName) + { + if (!CFStringGetCString(fileRef, buf, sizeof(buf), kCFStringEncodingUTF8) ) + strlcpy(buf, "nofile.xml", sizeof(buf)); + xmlFileName = (const char *)buf; + } + } + + // Parse the XML and get the CFXMLTree. + xmlDocPtr tadoc = xmlReadMemory((const char*)CFDataGetBytePtr(xmlData), + (int)CFDataGetLength(xmlData), xmlFileName, NULL, 0); + + CFRelease(fileRef); + CFRelease(url); + CFRelease(xmlData); + + if (!tadoc) + { + LogMsg("FetchRootTA: xmlReadMemory failed"); + goto done; + } + + xmlNodePtr root = xmlDocGetRootElement(tadoc); + if (!root) + { + LogMsg("FetchRootTA: Cannot get root element"); + goto done; + } + + if (ParseElement(tadoc, root, ta) && ValidateTrustAnchor(ta)) + { + // Do the actual addition of TA on the main queue. + mDNSPlatformDispatchAsync(m, ta, TAComplete); + } + else + { + if (ta->rds.digest) + mDNSPlatformMemFree(ta->rds.digest); + mDNSPlatformMemFree(ta); + } +done: + if (tadoc) + xmlFreeDoc(tadoc); + RegisterNotification(m, ROOT_TA_UPDATE_INTERVAL); + RootTAFetchInterval = InitialTAFetchInterval; + return; +} + + +#if APPLE_OSX_mDNSResponder && !TARGET_OS_IPHONE +mDNSlocal void DNSSECProbeCallback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord) +{ + if (!AddRecord) + return; + + mDNS_Lock(m); + if ((m->timenow - question->StopTime) >= 0) + { + mDNS_Unlock(m); + LogDNSSEC("DNSSECProbeCallback: Question %##s (%s) timed out", question->qname.c, DNSTypeName(question->qtype)); + mDNS_StopQuery(m, question); + return; + } + mDNS_Unlock(m); + + // Wait till we get the DNSSEC results. If we get a negative response e.g., no DNS servers, the + // question will be restarted by the core and we should have the DNSSEC results eventually. + if (AddRecord != QC_dnssec) + { + LogDNSSEC("DNSSECProbeCallback: Question %##s (%s)", question->qname.c, DNSTypeName(question->qtype), RRDisplayString(m, answer)); + return; + } + + LogDNSSEC("DNSSECProbeCallback: Question %##s (%s), DNSSEC status %s", question->qname.c, DNSTypeName(question->qtype), + DNSSECStatusName(question->ValidationStatus)); + + mDNS_StopQuery(m, question); +} + +// Send a DNSSEC probe just for the sake of collecting DNSSEC statistics. +mDNSexport void DNSSECProbe(mDNS *const m) +{ + mDNSu32 rand; + + if (DNSSECProbeQuestion.ThisQInterval != -1) + return; + + rand = mDNSRandom(0x3FFFFFFF) % 100; + // Probe 5% of the time + if (rand > 5) + return; + + mDNS_DropLockBeforeCallback(); + InitializeQuestion(m, &DNSSECProbeQuestion, mDNSInterface_Any, (const domainname *)"\003com", kDNSType_DS, DNSSECProbeCallback, mDNSNULL); + DNSSECProbeQuestion.ValidatingResponse = 0; + DNSSECProbeQuestion.ValidationRequired = DNSSEC_VALIDATION_SECURE; + + BumpDNSSECStats(m, kStatsActionIncrement, kStatsTypeProbe, 1); + mDNS_StartQuery(m, &DNSSECProbeQuestion); + mDNS_ReclaimLockAfterCallback(); +} +#endif // APPLE_OSX_mDNSResponder && !TARGET_OS_IPHONE + +// For now we fetch the root trust anchor and update the local copy +mDNSexport void UpdateTrustAnchors(mDNS *const m) +{ + // Register for a notification to fire immediately which in turn will update + // the trust anchor + if (RegisterNotification(m, 1)) + { + LogMsg("UpdateTrustAnchors: ERROR!! failed to register for notification"); + } +} + +mDNSlocal int RegisterNotification(mDNS *const m, unsigned int interval) +{ + int len = strlen("com.apple.system.notify.service.timer:+") + 21; // 21 bytes to accomodate the interval + char buffer[len]; + unsigned int blen; + int status; + + // Starting "interval" second from now (+ below indicates relative) register for a notification + blen = mDNS_snprintf(buffer, sizeof(buffer), "com.apple.system.notify.service.timer:+%us", interval); + if (blen >= sizeof(buffer)) + { + LogMsg("RegisterNotification: Buffer too small blen %d, buffer size %d", blen, sizeof(buffer)); + return -1; + } + LogInfo("RegisterNotification: buffer %s", buffer); + if (m->notifyToken) + { + notify_cancel(m->notifyToken); + m->notifyToken = 0; + } + status = notify_register_dispatch(buffer, &m->notifyToken, + dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), + ^(int t) { (void) t; FetchRootTA(m); }); + + if (status != NOTIFY_STATUS_OK) + { + LogMsg("RegisterNotification: notify_register_dispatch failed"); + return -1; + } + return 0; +} + +mDNSexport mStatus DNSSECPlatformInit(mDNS *const m) +{ + int diglen; + + m->TrustAnchors = mDNSNULL; + m->notifyToken = 0; + + // Add a couple of trust anchors for testing purposes. + mDNSlocal const domainname *testZone = (const domainname*)"\007example"; + + char *digest = "F122E47B5B7D2B6A5CC0A21EADA11D96BB9CC927"; + mDNSu8 *dig = ConvertDigest(digest, 1, &diglen); + AddTrustAnchor(m, testZone, 23044, 5, 1, diglen, dig); + + char *digest1 = "D795AE5E1AFB200C6139474199B70EAD3F3484553FD97BE5A43704B8A4791F21"; + dig = ConvertDigest(digest1, 2, &diglen); + AddTrustAnchor(m, testZone, 23044, 5, 2, diglen, dig); + + // Add the TA for root zone manually here. We will dynamically fetch the root TA and + // update it shortly. If that fails e.g., disconnected from the network, we still + // have something to work with. + char *digest2 = "49AAC11D7B6F6446702E54A1607371607A1A41855200FD2CE1CDDE32F24E8FB5"; + dig = ConvertDigest(digest2, 2, &diglen); + AddTrustAnchor(m, (const domainname *)"\000", 19036, 8, 2, diglen, dig); + +#if !TARGET_OS_IPHONE + DNSSECProbeQuestion.ThisQInterval = -1; +#endif + return mStatus_NoError; +} diff --git a/mDNSResponder/mDNSMacOSX/DNSSECSupport.h b/mDNSResponder/mDNSMacOSX/DNSSECSupport.h new file mode 100644 index 00000000..2310fc2a --- /dev/null +++ b/mDNSResponder/mDNSMacOSX/DNSSECSupport.h @@ -0,0 +1,24 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2012 Apple Computer, 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. + */ + +#ifndef __DNSSEC_SUPPORT_H +#define __DNSSEC_SUPPORT_H + +extern mStatus DNSSECPlatformInit(mDNS *const m); +extern void UpdateTrustAnchors(mDNS *const m); + +#endif // __DNSSEC_SUPPORT_H diff --git a/mDNSResponder/mDNSMacOSX/DNSServiceDiscovery.c b/mDNSResponder/mDNSMacOSX/DNSServiceDiscovery.c new file mode 100644 index 00000000..717efca2 --- /dev/null +++ b/mDNSResponder/mDNSMacOSX/DNSServiceDiscovery.c @@ -0,0 +1,674 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2002 Apple Computer, 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. + */ + +// Suppress "warning: 'DNSServiceDiscoveryMachPort' is deprecated" messages -- we already know this code is building the deprecated API +// Since we compile with all warnings treated as errors, we have to turn off the warnings here or the project won't compile +#include <AvailabilityMacros.h> +#undef AVAILABLE_MAC_OS_X_VERSION_10_2_AND_LATER_BUT_DEPRECATED +#define AVAILABLE_MAC_OS_X_VERSION_10_2_AND_LATER_BUT_DEPRECATED +#undef AVAILABLE_MAC_OS_X_VERSION_10_2_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_3 +#define AVAILABLE_MAC_OS_X_VERSION_10_2_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_3 + +#include "../mDNSMacOSX/DNSServiceDiscovery.h" +#include "DNSServiceDiscoveryDefines.h" +#include "DNSServiceDiscoveryReplyServer.h" + +#include <stdlib.h> +#include <stdio.h> +#include <servers/bootstrap.h> +#include <mach/mach.h> +#include <mach/mach_error.h> +#include <pthread.h> + +#include <netinet/in.h> + +extern boolean_t DNSServiceDiscoveryReply_server( + mach_msg_header_t *InHeadP, + mach_msg_header_t *OutHeadP); + +extern +kern_return_t DNSServiceBrowserCreate_rpc +( + mach_port_t server, + mach_port_t client, + DNSCString regtype, + DNSCString domain +); + +extern +kern_return_t DNSServiceDomainEnumerationCreate_rpc +( + mach_port_t server, + mach_port_t client, + int registrationDomains +); + +extern +kern_return_t DNSServiceRegistrationCreate_rpc +( + mach_port_t server, + mach_port_t client, + DNSCString name, + DNSCString regtype, + DNSCString domain, + IPPort port, + DNSCString txtRecord +); + +extern +kern_return_t DNSServiceResolverResolve_rpc +( + mach_port_t server, + mach_port_t client, + DNSCString name, + DNSCString regtype, + DNSCString domain +); + +extern +kern_return_t DNSServiceRegistrationAddRecord_rpc +( + mach_port_t server, + mach_port_t client, + int type, + record_data_t data, + mach_msg_type_number_t record_dataCnt, + uint32_t ttl, + natural_t *reference +); + +extern +int DNSServiceRegistrationUpdateRecord_rpc +( + mach_port_t server, + mach_port_t client, + natural_t reference, + record_data_t data, + mach_msg_type_number_t record_dataCnt, + uint32_t ttl +); + +extern +kern_return_t DNSServiceRegistrationRemoveRecord_rpc +( + mach_port_t server, + mach_port_t client, + natural_t reference +); + +struct a_requests { + struct a_requests *next; + mach_port_t client_port; + union { + DNSServiceBrowserReply browserCallback; + DNSServiceDomainEnumerationReply enumCallback; + DNSServiceRegistrationReply regCallback; + DNSServiceResolverReply resolveCallback; + } callout; + void *context; +}; + +static struct a_requests *a_requests = NULL; +static pthread_mutex_t a_requests_lock = PTHREAD_MUTEX_INITIALIZER; + +typedef struct _dns_service_discovery_t { + mach_port_t port; +} dns_service_discovery_t; + +static mach_port_t DNSServiceDiscoveryLookupServer(void) +{ + static mach_port_t sndPort = MACH_PORT_NULL; + kern_return_t result; + + if (sndPort != MACH_PORT_NULL) { + return sndPort; + } + + result = bootstrap_look_up(bootstrap_port, DNS_SERVICE_DISCOVERY_SERVER, &sndPort); + if (result != KERN_SUCCESS) { + printf("%s(): {%s:%d} bootstrap_look_up() failed: $%x\n", __FUNCTION__, __FILE__, __LINE__, (int) result); + sndPort = MACH_PORT_NULL; + } + + + return sndPort; +} + +static void _increaseQueueLengthOnPort(mach_port_t port) +{ + mach_port_limits_t qlimits; + kern_return_t result; + + qlimits.mpl_qlimit = 16; + result = mach_port_set_attributes(mach_task_self(), port, MACH_PORT_LIMITS_INFO, (mach_port_info_t)&qlimits, MACH_PORT_LIMITS_INFO_COUNT); + + if (result != KERN_SUCCESS) { + printf("%s(): {%s:%d} mach_port_set_attributes() failed: $%x %s\n", __FUNCTION__, __FILE__, __LINE__, (int) result, mach_error_string(result)); + } +} + +dns_service_discovery_ref DNSServiceBrowserCreate (const char *regtype, const char *domain, DNSServiceBrowserReply callBack,void *context) +{ + mach_port_t serverPort = DNSServiceDiscoveryLookupServer(); + mach_port_t clientPort; + kern_return_t result; + dns_service_discovery_ref return_t; + struct a_requests *request; + + if (!serverPort) { + return NULL; + } + + result = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &clientPort); + if (result != KERN_SUCCESS) { + printf("Mach port receive creation failed, %s\n", mach_error_string(result)); + return NULL; + } + result = mach_port_insert_right(mach_task_self(), clientPort, clientPort, MACH_MSG_TYPE_MAKE_SEND); + if (result != KERN_SUCCESS) { + printf("Mach port send creation failed, %s\n", mach_error_string(result)); + mach_port_destroy(mach_task_self(), clientPort); + return NULL; + } + _increaseQueueLengthOnPort(clientPort); + + return_t = malloc(sizeof(dns_service_discovery_t)); + return_t->port = clientPort; + + request = malloc(sizeof(struct a_requests)); + request->client_port = clientPort; + request->context = context; + request->callout.browserCallback = callBack; + + result = DNSServiceBrowserCreate_rpc(serverPort, clientPort, (char *)regtype, (char *)domain); + + if (result != KERN_SUCCESS) { + printf("There was an error creating a browser, %s\n", mach_error_string(result)); + free(request); + return NULL; + } + + pthread_mutex_lock(&a_requests_lock); + request->next = a_requests; + a_requests = request; + pthread_mutex_unlock(&a_requests_lock); + + return return_t; +} + +/* Service Enumeration */ + +dns_service_discovery_ref DNSServiceDomainEnumerationCreate (int registrationDomains, DNSServiceDomainEnumerationReply callBack, void *context) +{ + mach_port_t serverPort = DNSServiceDiscoveryLookupServer(); + mach_port_t clientPort; + kern_return_t result; + dns_service_discovery_ref return_t; + struct a_requests *request; + + if (!serverPort) { + return NULL; + } + + result = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &clientPort); + if (result != KERN_SUCCESS) { + printf("Mach port receive creation failed, %s\n", mach_error_string(result)); + return NULL; + } + result = mach_port_insert_right(mach_task_self(), clientPort, clientPort, MACH_MSG_TYPE_MAKE_SEND); + if (result != KERN_SUCCESS) { + printf("Mach port send creation failed, %s\n", mach_error_string(result)); + mach_port_destroy(mach_task_self(), clientPort); + return NULL; + } + _increaseQueueLengthOnPort(clientPort); + + return_t = malloc(sizeof(dns_service_discovery_t)); + return_t->port = clientPort; + + request = malloc(sizeof(struct a_requests)); + request->client_port = clientPort; + request->context = context; + request->callout.enumCallback = callBack; + + result = DNSServiceDomainEnumerationCreate_rpc(serverPort, clientPort, registrationDomains); + + if (result != KERN_SUCCESS) { + printf("There was an error creating an enumerator, %s\n", mach_error_string(result)); + free(request); + return NULL; + } + + pthread_mutex_lock(&a_requests_lock); + request->next = a_requests; + a_requests = request; + pthread_mutex_unlock(&a_requests_lock); + + return return_t; +} + + +/* Service Registration */ + +dns_service_discovery_ref DNSServiceRegistrationCreate + (const char *name, const char *regtype, const char *domain, uint16_t port, const char *txtRecord, DNSServiceRegistrationReply callBack, void *context) +{ + mach_port_t serverPort = DNSServiceDiscoveryLookupServer(); + mach_port_t clientPort; + kern_return_t result; + dns_service_discovery_ref return_t; + struct a_requests *request; + IPPort IpPort; + char *portptr = (char *)&port; + + if (!serverPort) { + return NULL; + } + + if (!txtRecord) { + txtRecord = ""; + } + + result = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &clientPort); + if (result != KERN_SUCCESS) { + printf("Mach port receive creation failed, %s\n", mach_error_string(result)); + return NULL; + } + result = mach_port_insert_right(mach_task_self(), clientPort, clientPort, MACH_MSG_TYPE_MAKE_SEND); + if (result != KERN_SUCCESS) { + printf("Mach port send creation failed, %s\n", mach_error_string(result)); + mach_port_destroy(mach_task_self(), clientPort); + return NULL; + } + _increaseQueueLengthOnPort(clientPort); + + return_t = malloc(sizeof(dns_service_discovery_t)); + return_t->port = clientPort; + + request = malloc(sizeof(struct a_requests)); + request->client_port = clientPort; + request->context = context; + request->callout.regCallback = callBack; + + // older versions of this code passed the port via mach IPC as an int. + // we continue to pass it as 4 bytes to maintain binary compatibility, + // but now ensure that the network byte order is preserved by using a struct + IpPort.bytes[0] = 0; + IpPort.bytes[1] = 0; + IpPort.bytes[2] = portptr[0]; + IpPort.bytes[3] = portptr[1]; + + result = DNSServiceRegistrationCreate_rpc(serverPort, clientPort, (char *)name, (char *)regtype, (char *)domain, IpPort, (char *)txtRecord); + + if (result != KERN_SUCCESS) { + printf("There was an error creating a resolve, %s\n", mach_error_string(result)); + free(request); + return NULL; + } + + pthread_mutex_lock(&a_requests_lock); + request->next = a_requests; + a_requests = request; + pthread_mutex_unlock(&a_requests_lock); + + return return_t; +} + +/* Resolver requests */ + +dns_service_discovery_ref DNSServiceResolverResolve(const char *name, const char *regtype, const char *domain, DNSServiceResolverReply callBack, void *context) +{ + mach_port_t serverPort = DNSServiceDiscoveryLookupServer(); + mach_port_t clientPort; + kern_return_t result; + dns_service_discovery_ref return_t; + struct a_requests *request; + + if (!serverPort) { + return NULL; + } + + result = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &clientPort); + if (result != KERN_SUCCESS) { + printf("Mach port receive creation failed, %s\n", mach_error_string(result)); + return NULL; + } + result = mach_port_insert_right(mach_task_self(), clientPort, clientPort, MACH_MSG_TYPE_MAKE_SEND); + if (result != KERN_SUCCESS) { + printf("Mach port send creation failed, %s\n", mach_error_string(result)); + mach_port_destroy(mach_task_self(), clientPort); + return NULL; + } + _increaseQueueLengthOnPort(clientPort); + + return_t = malloc(sizeof(dns_service_discovery_t)); + return_t->port = clientPort; + + request = malloc(sizeof(struct a_requests)); + request->client_port = clientPort; + request->context = context; + request->callout.resolveCallback = callBack; + + DNSServiceResolverResolve_rpc(serverPort, clientPort, (char *)name, (char *)regtype, (char *)domain); + + pthread_mutex_lock(&a_requests_lock); + request->next = a_requests; + a_requests = request; + pthread_mutex_unlock(&a_requests_lock); + + return return_t; +} + +DNSRecordReference DNSServiceRegistrationAddRecord(dns_service_discovery_ref ref, uint16_t rrtype, uint16_t rdlen, const char *rdata, uint32_t ttl) +{ + mach_port_t serverPort = DNSServiceDiscoveryLookupServer(); + mach_port_t clientPort; + natural_t reference = 0; + kern_return_t result = KERN_SUCCESS; + + if (!serverPort) { + return kDNSServiceDiscoveryUnknownErr; + } + + clientPort = DNSServiceDiscoveryMachPort(ref); + + if (!clientPort) { + return kDNSServiceDiscoveryUnknownErr; + } + + result = DNSServiceRegistrationAddRecord_rpc(serverPort, clientPort, rrtype, (record_data_t)rdata, rdlen, ttl, &reference); + + if (result != KERN_SUCCESS) { + printf("The result of the registration was not successful. Error %d, result %s\n", result, mach_error_string(result)); + } + + return reference; +} + +DNSServiceRegistrationReplyErrorType DNSServiceRegistrationUpdateRecord(dns_service_discovery_ref ref, DNSRecordReference reference, uint16_t rdlen, const char *rdata, uint32_t ttl) +{ + mach_port_t serverPort = DNSServiceDiscoveryLookupServer(); + mach_port_t clientPort; + kern_return_t result = KERN_SUCCESS; + + if (!serverPort) { + return kDNSServiceDiscoveryUnknownErr; + } + + clientPort = DNSServiceDiscoveryMachPort(ref); + + if (!clientPort) { + return kDNSServiceDiscoveryUnknownErr; + } + + result = DNSServiceRegistrationUpdateRecord_rpc(serverPort, clientPort, (natural_t)reference, (record_data_t)rdata, rdlen, ttl); + if (result != KERN_SUCCESS) { + printf("The result of the registration was not successful. Error %d, result %s\n", result, mach_error_string(result)); + return result; + } + + return kDNSServiceDiscoveryNoError; +} + + +DNSServiceRegistrationReplyErrorType DNSServiceRegistrationRemoveRecord(dns_service_discovery_ref ref, DNSRecordReference reference) +{ + mach_port_t serverPort = DNSServiceDiscoveryLookupServer(); + mach_port_t clientPort; + kern_return_t result = KERN_SUCCESS; + + if (!serverPort) { + return kDNSServiceDiscoveryUnknownErr; + } + + clientPort = DNSServiceDiscoveryMachPort(ref); + + if (!clientPort) { + return kDNSServiceDiscoveryUnknownErr; + } + + result = DNSServiceRegistrationRemoveRecord_rpc(serverPort, clientPort, (natural_t)reference); + + if (result != KERN_SUCCESS) { + printf("The result of the registration was not successful. Error %d, result %s\n", result, mach_error_string(result)); + return result; + } + + return kDNSServiceDiscoveryNoError; +} + +void DNSServiceDiscovery_handleReply(void *replyMsg) +{ + unsigned long result = 0xFFFFFFFF; + mach_msg_header_t * msgSendBufPtr; + mach_msg_header_t * receivedMessage; + unsigned msgSendBufLength; + + msgSendBufLength = internal_DNSServiceDiscoveryReply_subsystem.maxsize; + msgSendBufPtr = (mach_msg_header_t *) malloc(msgSendBufLength); + + + receivedMessage = ( mach_msg_header_t * ) replyMsg; + + // Call DNSServiceDiscoveryReply_server to change mig-generated message into a + // genuine mach message. It will then cause the callback to get called. + result = DNSServiceDiscoveryReply_server ( receivedMessage, msgSendBufPtr ); + ( void ) mach_msg_send ( msgSendBufPtr ); + free(msgSendBufPtr); +} + +mach_port_t DNSServiceDiscoveryMachPort(dns_service_discovery_ref dnsServiceDiscovery) +{ + return dnsServiceDiscovery->port; +} + +void DNSServiceDiscoveryDeallocate(dns_service_discovery_ref dnsServiceDiscovery) +{ + struct a_requests *request0, *request; + mach_port_t reply = dnsServiceDiscovery->port; + + if (dnsServiceDiscovery->port) { + pthread_mutex_lock(&a_requests_lock); + request0 = NULL; + request = a_requests; + while (request) { + if (request->client_port == reply) { + /* request info found, remove from list */ + if (request0) { + request0->next = request->next; + } else { + a_requests = request->next; + } + break; + } else { + /* not info for this request, skip to next */ + request0 = request; + request = request->next; + } + + } + pthread_mutex_unlock(&a_requests_lock); + + free(request); + + mach_port_destroy(mach_task_self(), dnsServiceDiscovery->port); + + free(dnsServiceDiscovery); + } + return; +} + +// reply functions, calls the users setup callbacks with function pointers + +kern_return_t internal_DNSServiceDomainEnumerationReply_rpc +( + mach_port_t reply, + int resultType, + DNSCString replyDomain, + int flags +) +{ + struct a_requests *request; + void *requestContext = NULL; + DNSServiceDomainEnumerationReply callback = NULL; + + pthread_mutex_lock(&a_requests_lock); + request = a_requests; + while (request) { + if (request->client_port == reply) { + break; + } + request = request->next; + } + + if (request != NULL) { + callback = (*request->callout.enumCallback); + requestContext = request->context; + } + pthread_mutex_unlock(&a_requests_lock); + + if (request != NULL) { + (callback)(resultType, replyDomain, flags, requestContext); + } + + return KERN_SUCCESS; + +} + +kern_return_t internal_DNSServiceBrowserReply_rpc +( + mach_port_t reply, + int resultType, + DNSCString replyName, + DNSCString replyType, + DNSCString replyDomain, + int flags +) +{ + struct a_requests *request; + void *requestContext = NULL; + DNSServiceBrowserReply callback = NULL; + + pthread_mutex_lock(&a_requests_lock); + request = a_requests; + while (request) { + if (request->client_port == reply) { + break; + } + request = request->next; + } + if (request != NULL) { + callback = (*request->callout.browserCallback); + requestContext = request->context; + } + + pthread_mutex_unlock(&a_requests_lock); + + if (request != NULL) { + (callback)(resultType, replyName, replyType, replyDomain, flags, requestContext); + } + + return KERN_SUCCESS; +} + + +kern_return_t internal_DNSServiceRegistrationReply_rpc +( + mach_port_t reply, + int resultType +) +{ + struct a_requests *request; + void *requestContext = NULL; + DNSServiceRegistrationReply callback = NULL; + + pthread_mutex_lock(&a_requests_lock); + request = a_requests; + while (request) { + if (request->client_port == reply) { + break; + } + request = request->next; + } + if (request != NULL) { + callback = (*request->callout.regCallback); + requestContext = request->context; + } + + pthread_mutex_unlock(&a_requests_lock); + if (request != NULL) { + (callback)(resultType, requestContext); + } + return KERN_SUCCESS; +} + + +kern_return_t internal_DNSServiceResolverReply_rpc +( + mach_port_t reply, + sockaddr_t interface, + sockaddr_t address, + DNSCString txtRecord, + int flags +) +{ + struct sockaddr *interface_storage = NULL; + struct sockaddr *address_storage = NULL; + struct a_requests *request; + void *requestContext = NULL; + DNSServiceResolverReply callback = NULL; + + if (interface) { + int len = ((struct sockaddr *)interface)->sa_len; + interface_storage = (struct sockaddr *)malloc(len); + memcpy(interface_storage, interface, len); + } + + if (address) { + int len = ((struct sockaddr *)address)->sa_len; + address_storage = (struct sockaddr *)malloc(len); + memcpy(address_storage, address, len); + } + + pthread_mutex_lock(&a_requests_lock); + request = a_requests; + while (request) { + if (request->client_port == reply) { + break; + } + request = request->next; + } + + if (request != NULL) { + callback = (*request->callout.resolveCallback); + requestContext = request->context; + } + pthread_mutex_unlock(&a_requests_lock); + + if (request != NULL) { + (callback)(interface_storage, address_storage, txtRecord, flags, requestContext); + } + + if (interface) { + free(interface_storage); + } + if (address) { + free(address_storage); + } + + return KERN_SUCCESS; +} diff --git a/mDNSResponder/mDNSMacOSX/DNSServiceDiscovery.h b/mDNSResponder/mDNSMacOSX/DNSServiceDiscovery.h new file mode 100644 index 00000000..ae736477 --- /dev/null +++ b/mDNSResponder/mDNSMacOSX/DNSServiceDiscovery.h @@ -0,0 +1,314 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2002 Apple Computer, 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. + */ + +/*! @header DNS Service Discovery (Deprecated Mach-based API) + * + * @discussion This section describes the functions, callbacks, and data structures that + * make up the DNS Service Discovery API. + * + * The DNS Service Discovery API is part of Bonjour, Apple's implementation of + * zero-configuration networking (ZEROCONF). + * + * Bonjour allows you to register a network service, such as a + * printer or file server, so that it can be found by name or browsed + * for by service type and domain. Using Bonjour, applications can + * discover what services are available on the network, along with + * all necessary access information-such as name, IP address, and port + * number-for a given service. + * + * In effect, Bonjour combines the functions of a local DNS server + * and AppleTalk. Bonjour allows applications to provide user-friendly printer + * and server browsing, among other things, over standard IP networks. + * This behavior is a result of combining protocols such as multicast and DNS + * to add new functionality to the network (such as multicast DNS). + * + * Bonjour gives applications easy access to services over local IP + * networks without requiring the service or the application to support + * an AppleTalk or a Netbeui stack, and without requiring a DNS server + * for the local network. + * + * Note that this API was deprecated in Mac OS X 10.3, and replaced + * by the portable cross-platform /usr/include/dns_sd.h API. + */ + +#ifndef __DNS_SERVICE_DISCOVERY_H +#define __DNS_SERVICE_DISCOVERY_H + +#include <mach/mach_types.h> + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/cdefs.h> + +#include <netinet/in.h> + +#include <AvailabilityMacros.h> + +__BEGIN_DECLS + +/* Opaque internal data type */ +typedef struct _dns_service_discovery_t * dns_service_discovery_ref; + +/* possible reply flags values */ + +enum { + kDNSServiceDiscoveryNoFlags = 0, + kDNSServiceDiscoveryMoreRepliesImmediately = 1 << 0, +}; + + +/* possible error code values */ +typedef enum +{ + kDNSServiceDiscoveryWaiting = 1, + kDNSServiceDiscoveryNoError = 0, + // mDNS Error codes are in the range + // FFFE FF00 (-65792) to FFFE FFFF (-65537) + kDNSServiceDiscoveryUnknownErr = -65537, // 0xFFFE FFFF + kDNSServiceDiscoveryNoSuchNameErr = -65538, + kDNSServiceDiscoveryNoMemoryErr = -65539, + kDNSServiceDiscoveryBadParamErr = -65540, + kDNSServiceDiscoveryBadReferenceErr = -65541, + kDNSServiceDiscoveryBadStateErr = -65542, + kDNSServiceDiscoveryBadFlagsErr = -65543, + kDNSServiceDiscoveryUnsupportedErr = -65544, + kDNSServiceDiscoveryNotInitializedErr = -65545, + kDNSServiceDiscoveryNoCache = -65546, + kDNSServiceDiscoveryAlreadyRegistered = -65547, + kDNSServiceDiscoveryNameConflict = -65548, + kDNSServiceDiscoveryInvalid = -65549, + kDNSServiceDiscoveryMemFree = -65792 // 0xFFFE FF00 +} DNSServiceRegistrationReplyErrorType; + +typedef uint32_t DNSRecordReference; + + +/*! + @function DNSServiceResolver_handleReply + @discussion This function should be called with the Mach message sent + to the port returned by the call to DNSServiceResolverResolve. + The reply message will be interpreted and will result in a + call to the specified callout function. + @param replyMsg The Mach message. + */ +void DNSServiceDiscovery_handleReply(void *replyMsg); + +/* Service Registration */ + +typedef void (*DNSServiceRegistrationReply)( + DNSServiceRegistrationReplyErrorType errorCode, + void *context + ); + +/*! + @function DNSServiceRegistrationCreate + @discussion Register a named service with DNS Service Discovery + @param name The name of this service instance (e.g. "Steve's Printer") + @param regtype The service type (e.g. "_printer._tcp." -- see + RFC 2782 (DNS SRV) and <http://www.iana.org/assignments/port-numbers>) + @param domain The domain in which to register the service (e.g. "apple.com.") + @param port The local port on which this service is being offered (in network byte order) + @param txtRecord Optional protocol-specific additional information + @param callBack The DNSServiceRegistrationReply function to be called + @param context A user specified context which will be passed to the callout function. + @result A dns_registration_t + */ +dns_service_discovery_ref DNSServiceRegistrationCreate +( + const char *name, + const char *regtype, + const char *domain, + uint16_t port, + const char *txtRecord, + DNSServiceRegistrationReply callBack, + void *context +) AVAILABLE_MAC_OS_X_VERSION_10_2_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_3; + +/***************************************************************************/ +/* DNS Domain Enumeration */ + +typedef enum +{ + DNSServiceDomainEnumerationReplyAddDomain, // Domain found + DNSServiceDomainEnumerationReplyAddDomainDefault, // Domain found (and should be selected by default) + DNSServiceDomainEnumerationReplyRemoveDomain, // Domain has been removed from network +} DNSServiceDomainEnumerationReplyResultType; + +typedef enum +{ + DNSServiceDiscoverReplyFlagsFinished, + DNSServiceDiscoverReplyFlagsMoreComing, +} DNSServiceDiscoveryReplyFlags; + +typedef void (*DNSServiceDomainEnumerationReply)( + DNSServiceDomainEnumerationReplyResultType resultType, // One of DNSServiceDomainEnumerationReplyResultType + const char *replyDomain, + DNSServiceDiscoveryReplyFlags flags, // DNS Service Discovery reply flags information + void *context + ); + +/*! + @function DNSServiceDomainEnumerationCreate + @discussion Asynchronously create a DNS Domain Enumerator + @param registrationDomains A boolean indicating whether you are looking + for recommended registration domains + (e.g. equivalent to the AppleTalk zone list in the AppleTalk Control Panel) + or recommended browsing domains + (e.g. equivalent to the AppleTalk zone list in the Chooser). + @param callBack The function to be called when domains are found or removed + @param context A user specified context which will be passed to the callout function. + @result A dns_registration_t + */ +dns_service_discovery_ref DNSServiceDomainEnumerationCreate +( + int registrationDomains, + DNSServiceDomainEnumerationReply callBack, + void *context +) AVAILABLE_MAC_OS_X_VERSION_10_2_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_3; + +/***************************************************************************/ +/* DNS Service Browser */ + +typedef enum +{ + DNSServiceBrowserReplyAddInstance, // Instance of service found + DNSServiceBrowserReplyRemoveInstance // Instance has been removed from network +} DNSServiceBrowserReplyResultType; + +typedef void (*DNSServiceBrowserReply)( + DNSServiceBrowserReplyResultType resultType, // One of DNSServiceBrowserReplyResultType + const char *replyName, + const char *replyType, + const char *replyDomain, + DNSServiceDiscoveryReplyFlags flags, // DNS Service Discovery reply flags information + void *context + ); + +/*! + @function DNSServiceBrowserCreate + @discussion Asynchronously create a DNS Service browser + @param regtype The type of service + @param domain The domain in which to find the service + @param callBack The function to be called when service instances are found or removed + @param context A user specified context which will be passed to the callout function. + @result A dns_registration_t + */ +dns_service_discovery_ref DNSServiceBrowserCreate +( + const char *regtype, + const char *domain, + DNSServiceBrowserReply callBack, + void *context +) AVAILABLE_MAC_OS_X_VERSION_10_2_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_3; + +/***************************************************************************/ +/* Resolver requests */ + +typedef void (*DNSServiceResolverReply)( + struct sockaddr *interface, // Needed for scoped addresses like link-local + struct sockaddr *address, + const char *txtRecord, + DNSServiceDiscoveryReplyFlags flags, // DNS Service Discovery reply flags information + void *context + ); + +/*! + @function DNSServiceResolverResolve + @discussion Resolved a named instance of a service to its address, port, and + (optionally) other demultiplexing information contained in the TXT record. + @param name The name of the service instance + @param regtype The type of service + @param domain The domain in which to find the service + @param callBack The DNSServiceResolverReply function to be called when the specified + address has been resolved. + @param context A user specified context which will be passed to the callout function. + @result A dns_registration_t + */ + +dns_service_discovery_ref DNSServiceResolverResolve +( + const char *name, + const char *regtype, + const char *domain, + DNSServiceResolverReply callBack, + void *context +) AVAILABLE_MAC_OS_X_VERSION_10_2_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_3; + +/***************************************************************************/ +/* Mach port accessor and deallocation */ + +/*! + @function DNSServiceDiscoveryMachPort + @discussion Returns the mach port for a dns_service_discovery_ref + @param registration A dns_service_discovery_ref as returned from DNSServiceRegistrationCreate + @result A mach reply port which will be sent messages as appropriate. + These messages should be passed to the DNSServiceDiscovery_handleReply + function. A NULL value indicates that no address was + specified or some other error occurred which prevented the + resolution from being started. + */ +mach_port_t DNSServiceDiscoveryMachPort(dns_service_discovery_ref dnsServiceDiscovery) AVAILABLE_MAC_OS_X_VERSION_10_2_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_3; + +/*! + @function DNSServiceDiscoveryDeallocate + @discussion Deallocates the DNS Service Discovery type / closes the connection to the server + @param dnsServiceDiscovery A dns_service_discovery_ref as returned from a creation or enumeration call + @result void + */ +void DNSServiceDiscoveryDeallocate(dns_service_discovery_ref dnsServiceDiscovery) AVAILABLE_MAC_OS_X_VERSION_10_2_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_3; + +/***************************************************************************/ +/* Registration updating */ + + +/*! + @function DNSServiceRegistrationAddRecord + @discussion Request that the mDNS Responder add the DNS Record of a specific type + @param dnsServiceDiscovery A dns_service_discovery_ref as returned from a DNSServiceRegistrationCreate call + @param rrtype A standard DNS Resource Record Type, from http://www.iana.org/assignments/dns-parameters + @param rdlen Length of the data + @param rdata Opaque binary Resource Record data, up to 64 kB. + @param ttl time to live for the added record. + @result DNSRecordReference An opaque reference that can be passed to the update and remove record calls. If an error occurs, this value will be zero or negative + */ +DNSRecordReference DNSServiceRegistrationAddRecord(dns_service_discovery_ref dnsServiceDiscovery, uint16_t rrtype, uint16_t rdlen, const char *rdata, uint32_t ttl) AVAILABLE_MAC_OS_X_VERSION_10_2_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_3; + +/*! + @function DNSServiceRegistrationUpdateRecord + @discussion Request that the mDNS Responder add the DNS Record of a specific type + @param dnsServiceDiscovery A dns_service_discovery_ref as returned from a DNSServiceRegistrationCreate call + @param dnsRecordReference A dnsRecordReference as returned from a DNSServiceRegistrationAddRecord call + @param rdlen Length of the data + @param rdata Opaque binary Resource Record data, up to 64 kB. + @param ttl time to live for the updated record. + @result DNSServiceRegistrationReplyErrorType If an error occurs, this value will be non zero + */ +DNSServiceRegistrationReplyErrorType DNSServiceRegistrationUpdateRecord(dns_service_discovery_ref ref, DNSRecordReference reference, uint16_t rdlen, const char *rdata, uint32_t ttl) AVAILABLE_MAC_OS_X_VERSION_10_2_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_3; + +/*! + @function DNSServiceRegistrationRemoveRecord + @discussion Request that the mDNS Responder remove the DNS Record(s) of a specific type + @param dnsServiceDiscovery A dns_service_discovery_ref as returned from a DNSServiceRegistrationCreate call + @param dnsRecordReference A dnsRecordReference as returned from a DNSServiceRegistrationAddRecord call + @result DNSServiceRegistrationReplyErrorType If an error occurs, this value will be non zero + */ +DNSServiceRegistrationReplyErrorType DNSServiceRegistrationRemoveRecord(dns_service_discovery_ref ref, DNSRecordReference reference) AVAILABLE_MAC_OS_X_VERSION_10_2_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_3; + + +__END_DECLS + +#endif /* __DNS_SERVICE_DISCOVERY_H */ diff --git a/mDNSResponder/mDNSMacOSX/DNSServiceDiscoveryDefines.h b/mDNSResponder/mDNSMacOSX/DNSServiceDiscoveryDefines.h new file mode 100644 index 00000000..cfad0a41 --- /dev/null +++ b/mDNSResponder/mDNSMacOSX/DNSServiceDiscoveryDefines.h @@ -0,0 +1,31 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2002-2003 Apple Computer, 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. + */ + +#ifndef __DNS_SERVICE_DISCOVERY_DEFINES_H +#define __DNS_SERVICE_DISCOVERY_DEFINES_H + +#include <mach/mach_types.h> + +#define DNS_SERVICE_DISCOVERY_SERVER "com.apple.mDNSResponder" + +typedef char DNSCString[1024]; +typedef char sockaddr_t[128]; + +typedef const char * record_data_t; +typedef struct { char bytes[4]; } IPPort; + +#endif /* __DNS_SERVICE_DISCOVERY_DEFINES_H */ diff --git a/mDNSResponder/mDNSMacOSX/DNSServiceDiscoveryReply.defs b/mDNSResponder/mDNSMacOSX/DNSServiceDiscoveryReply.defs new file mode 100644 index 00000000..b918bee4 --- /dev/null +++ b/mDNSResponder/mDNSMacOSX/DNSServiceDiscoveryReply.defs @@ -0,0 +1,60 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2002 Apple Computer, 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. + */ + +subsystem + DNSServiceDiscoveryReply 7250; + +ServerPrefix internal_; + +#include <mach/std_types.defs> +#include <mach/mach_types.defs> + +import "DNSServiceDiscoveryDefines.h"; + +type DNSCString = c_string[*:1024]; +type sockaddr_t = array[128] of char; + +simpleroutine DNSServiceDomainEnumerationReply_rpc( + reply: mach_port_t; + in resultType: int; + in replyDomain: DNSCString; + in flags: int; + SendTime to: natural_t); + +simpleroutine DNSServiceBrowserReply_rpc( + reply: mach_port_t; + in resultType: int; + in replyName: DNSCString; + in replyType: DNSCString; + in replyDomain: DNSCString; + in flags: int; + SendTime to: natural_t); + + +simpleroutine DNSServiceRegistrationReply_rpc( + reply: mach_port_t; + in resultType: int; + SendTime to: natural_t); + + +simpleroutine DNSServiceResolverReply_rpc( + reply: mach_port_t; + in interface: sockaddr_t; + in address: sockaddr_t; + in txtRecord: DNSCString; + in flags: int; + SendTime to: natural_t); diff --git a/mDNSResponder/mDNSMacOSX/DNSServiceDiscoveryRequest.defs b/mDNSResponder/mDNSMacOSX/DNSServiceDiscoveryRequest.defs new file mode 100644 index 00000000..ad06bdb5 --- /dev/null +++ b/mDNSResponder/mDNSMacOSX/DNSServiceDiscoveryRequest.defs @@ -0,0 +1,80 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2002 Apple Computer, 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. + */ + +subsystem + DNSServiceDiscoveryRequest 7200; + +ServerPrefix provide_; + +#include <mach/std_types.defs> +#include <mach/mach_types.defs> + +import "DNSServiceDiscoveryDefines.h"; + +type DNSCString = c_string[*:1024]; +type record_data = ^ array [] of MACH_MSG_TYPE_BYTE + ctype: record_data_t; +type IPPort = struct[4] of char ctype:IPPort; + +simpleroutine DNSServiceBrowserCreate_rpc( + server: mach_port_t; + in client: mach_port_t; + in regtype: DNSCString; + in domain: DNSCString); + + +simpleroutine DNSServiceDomainEnumerationCreate_rpc( + server: mach_port_t; + in client: mach_port_t; + in registrationDomains: int); + +simpleroutine DNSServiceRegistrationCreate_rpc( + server: mach_port_t; + in client: mach_port_t; + in name: DNSCString; + in regtype: DNSCString; + in domain: DNSCString; + in port: IPPort; + in txtRecord: DNSCString); + + +simpleroutine DNSServiceResolverResolve_rpc( + server: mach_port_t; + in client: mach_port_t; + in name: DNSCString; + in regtype: DNSCString; + in domain: DNSCString); + +routine DNSServiceRegistrationAddRecord_rpc( + server: mach_port_t; + in client: mach_port_t; + in record_type: int; + in record_data: record_data; + in ttl: uint32_t; + out record_reference: natural_t); + +simpleroutine DNSServiceRegistrationUpdateRecord_rpc( + server: mach_port_t; + in client: mach_port_t; + in record_reference: natural_t; + in record_data: record_data; + in ttl: uint32_t); + +simpleroutine DNSServiceRegistrationRemoveRecord_rpc( + server: mach_port_t; + in client: mach_port_t; + in record_reference: natural_t); diff --git a/mDNSResponder/mDNSMacOSX/LaunchDaemonInfo-Tiger.helper.plist b/mDNSResponder/mDNSMacOSX/LaunchDaemonInfo-Tiger.helper.plist new file mode 100644 index 00000000..895287c7 --- /dev/null +++ b/mDNSResponder/mDNSMacOSX/LaunchDaemonInfo-Tiger.helper.plist @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>Label</key> + <string>com.apple.mDNSResponderHelper</string> + <key>OnDemand</key> + <false/> + <key>ProgramArguments</key> + <array> + <string>/usr/sbin/mDNSResponderHelper</string> + <string>-t</string> + <string>0</string> + </array> + <key>ServiceIPC</key> + <false/> +</dict> +</plist> diff --git a/mDNSResponder/mDNSMacOSX/LaunchDaemonInfo-Tiger.plist b/mDNSResponder/mDNSMacOSX/LaunchDaemonInfo-Tiger.plist new file mode 100644 index 00000000..e91b7751 --- /dev/null +++ b/mDNSResponder/mDNSMacOSX/LaunchDaemonInfo-Tiger.plist @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>Label</key> + <string>com.apple.mDNSResponder</string> + <key>OnDemand</key> + <false/> + <key>ProgramArguments</key> + <array> + <string>/usr/sbin/mDNSResponder</string> + <string>-launchdaemon</string> + </array> + <key>ServiceIPC</key> + <false/> +</dict> +</plist> diff --git a/mDNSResponder/mDNSMacOSX/LaunchDaemonInfo.dnsextd.plist b/mDNSResponder/mDNSMacOSX/LaunchDaemonInfo.dnsextd.plist new file mode 100644 index 00000000..e31b391f --- /dev/null +++ b/mDNSResponder/mDNSMacOSX/LaunchDaemonInfo.dnsextd.plist @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>Disabled</key> + <true/> + <key>Label</key> + <string>com.apple.dnsextd</string> + <key>OnDemand</key> + <false/> + <key>ProgramArguments</key> + <array> + <string>/usr/sbin/dnsextd</string> + <string>-launchd</string> + </array> +</dict> +</plist> diff --git a/mDNSResponder/mDNSMacOSX/LaunchDaemonInfo.helper.plist b/mDNSResponder/mDNSMacOSX/LaunchDaemonInfo.helper.plist new file mode 100644 index 00000000..a21868c7 --- /dev/null +++ b/mDNSResponder/mDNSMacOSX/LaunchDaemonInfo.helper.plist @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>Label</key> + <string>com.apple.mDNSResponderHelper</string> + <key>OnDemand</key> + <true/> + <key>ProgramArguments</key> + <array> + <string>/usr/sbin/mDNSResponderHelper</string> + </array> + <key>MachServices</key> + <dict> + <key>com.apple.mDNSResponderHelper</key> + <true/> + </dict> + <key>EnableTransactions</key> + <true/> + <key>BeginTransactionAtShutdown</key> + <true/> + <key>POSIXSpawnType</key> + <string>Interactive</string> +</dict> +</plist> diff --git a/mDNSResponder/mDNSMacOSX/LaunchDaemonInfo.plist b/mDNSResponder/mDNSMacOSX/LaunchDaemonInfo.plist new file mode 100644 index 00000000..ba99d15d --- /dev/null +++ b/mDNSResponder/mDNSMacOSX/LaunchDaemonInfo.plist @@ -0,0 +1,45 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>Label</key> + <string>com.apple.mDNSResponder</string> + <key>OnDemand</key> + <false/> + <key>InitGroups</key> + <false/> + <key>UserName</key> + <string>_mdnsresponder</string> + <key>GroupName</key> + <string>_mdnsresponder</string> + <key>ProgramArguments</key> + <array> + <string>/usr/sbin/mDNSResponder</string> + </array> + <key>MachServices</key> + <dict> + <key>com.apple.mDNSResponder</key> + <true/> + <key>com.apple.mDNSResponder.dnsproxy</key> + <true/> + </dict> + <key>Sockets</key> + <dict> + <key>Listeners</key> + <dict> + <key>SockFamily</key> + <string>Unix</string> + <key>SockPathName</key> + <string>/var/run/mDNSResponder</string> + <key>SockPathMode</key> + <integer>438</integer> + </dict> + </dict> + <key>EnableTransactions</key> + <true/> + <key>BeginTransactionAtShutdown</key> + <true/> + <key>POSIXSpawnType</key> + <string>Interactive</string> +</dict> +</plist> diff --git a/mDNSResponder/mDNSMacOSX/LegacyNATTraversal.c b/mDNSResponder/mDNSMacOSX/LegacyNATTraversal.c new file mode 100644 index 00000000..e7cd65f0 --- /dev/null +++ b/mDNSResponder/mDNSMacOSX/LegacyNATTraversal.c @@ -0,0 +1,933 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2004-2013 Apple Computer, 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. + */ + +#ifdef _LEGACY_NAT_TRAVERSAL_ + +#include "stdlib.h" // For strtol() +#include "string.h" // For strlcpy(), For strncpy(), strncasecmp() +#include "assert.h" // For assert() + +#if defined( WIN32 ) +# include <winsock2.h> +# include <ws2tcpip.h> +# define strcasecmp _stricmp +# define strncasecmp _strnicmp +# define mDNSASLLog( UUID, SUBDOMAIN, RESULT, SIGNATURE, FORMAT, ... ) ; + +static int +inet_pton( int family, const char * addr, void * dst ) +{ + struct sockaddr_storage ss; + int sslen = sizeof( ss ); + + ZeroMemory( &ss, sizeof( ss ) ); + ss.ss_family = (ADDRESS_FAMILY)family; + + if ( WSAStringToAddressA( (LPSTR)addr, family, NULL, ( struct sockaddr* ) &ss, &sslen ) == 0 ) + { + if ( family == AF_INET ) { memcpy( dst, &( ( struct sockaddr_in* ) &ss)->sin_addr, sizeof( IN_ADDR ) ); return 1; } + else if ( family == AF_INET6 ) { memcpy( dst, &( ( struct sockaddr_in6* ) &ss)->sin6_addr, sizeof( IN6_ADDR ) ); return 1; } + else return 0; + } + else return 0; +} +#else +# include <arpa/inet.h> // For inet_pton() +#endif + +#include "mDNSEmbeddedAPI.h" +#include "uDNS.h" // For natTraversalHandleAddressReply() etc. + +// used to format SOAP port mapping arguments +typedef struct Property_struct +{ + char *name; + char *type; + char *value; +} Property; + +// All of the text parsing in this file is intentionally transparent so that we know exactly +// what's being done to the text, with an eye towards preventing security problems. + +// This is an evolving list of useful acronyms to know. Please add to it at will. +// ST Service Type +// NT Notification Type +// USN Unique Service Name +// UDN Unique Device Name +// UUID Universally Unique Identifier +// URN/urn Universal Resource Name + +// Forward declaration because of circular reference: +// SendPortMapRequest -> SendSOAPMsgControlAction -> MakeTCPConnection -> tcpConnectionCallback -> handleLNTPortMappingResponse +// In the event of a port conflict, handleLNTPortMappingResponse then increments tcpInfo->retries and calls back to SendPortMapRequest to try again +mDNSlocal mStatus SendPortMapRequest(mDNS *m, NATTraversalInfo *n); + +#define RequestedPortNum(n) (mDNSVal16(mDNSIPPortIsZero((n)->RequestedPort) ? (n)->IntPort : (n)->RequestedPort) + (mDNSu16)(n)->tcpInfo.retries) + +// Note that this function assumes src is already NULL terminated +mDNSlocal void AllocAndCopy(mDNSu8 **const dst, const mDNSu8 *const src) +{ + if (src == mDNSNULL) return; + if ((*dst = mDNSPlatformMemAllocate((mDNSu32)strlen((char*)src) + 1)) == mDNSNULL) + { LogMsg("AllocAndCopy: can't allocate string"); return; } + strcpy((char*)*dst, (char*)src); +} + +// This function does a simple parse of an HTTP URL that may include a hostname, port, and path +// If found in the URL, addressAndPort and path out params will point to newly allocated space (and will leak if they were previously pointing at allocated space) +mDNSlocal mStatus ParseHttpUrl(const mDNSu8 *ptr, const mDNSu8 *const end, mDNSu8 **const addressAndPort, mDNSIPPort *const port, mDNSu8 **const path) +{ + // if the data begins with "http://", we assume there is a hostname and possibly a port number + if (end - ptr >= 7 && strncasecmp((char*)ptr, "http://", 7) == 0) + { + int i; + const mDNSu8 *stop = end; + const mDNSu8 *addrPtr = mDNSNULL; + + ptr += 7; //skip over "http://" + if (ptr >= end) { LogInfo("ParseHttpUrl: past end of buffer parsing host:port"); return mStatus_BadParamErr; } + + // find the end of the host:port + addrPtr = ptr; + for (i = 0; addrPtr && addrPtr != end; i++, addrPtr++) if (*addrPtr == '/') break; + + // allocate the buffer (len i+1 so we have space to terminate the string) + if ((*addressAndPort = mDNSPlatformMemAllocate(i+1)) == mDNSNULL) + { LogMsg("ParseHttpUrl: can't allocate address string"); return mStatus_NoMemoryErr; } + strncpy((char*)*addressAndPort, (char*)ptr, i); + (*addressAndPort)[i] = '\0'; + + // find the port number in the string, by looking backwards for the ':' + stop = ptr; // can't go back farther than the original start + ptr = addrPtr; // move ptr to the path part + + for (addrPtr--; addrPtr>stop; addrPtr--) + { + if (*addrPtr == ':') + { + addrPtr++; // skip over ':' + *port = mDNSOpaque16fromIntVal((mDNSu16)strtol((char*)addrPtr, mDNSNULL, 10)); // store it properly converted + break; + } + } + } + + // ptr should now point to the first character we haven't yet processed + // everything that remains is the path + if (path && ptr < end) + { + if ((*path = mDNSPlatformMemAllocate((mDNSu32)(end - ptr) + 1)) == mDNSNULL) + { LogMsg("ParseHttpUrl: can't mDNSPlatformMemAllocate path"); return mStatus_NoMemoryErr; } + strncpy((char*)*path, (char*)ptr, end - ptr); + (*path)[end - ptr] = '\0'; + } + + return mStatus_NoError; +} + +enum +{ + HTTPCode_NeedMoreData = -1, // No code found in stream + HTTPCode_Other = -2, // Valid code other than those below found in stream + HTTPCode_Bad = -3, + HTTPCode_200 = 200, + HTTPCode_404 = 404, + HTTPCode_500 = 500, +}; + +mDNSlocal mDNSs16 ParseHTTPResponseCode(const mDNSu8 **const data, const mDNSu8 *const end) +{ + const mDNSu8 *ptr = *data; + const mDNSu8 *code; + + if (end - ptr < 5) return HTTPCode_NeedMoreData; + if (strncasecmp((char*)ptr, "HTTP/", 5) != 0) return HTTPCode_Bad; + ptr += 5; + // should we care about the HTTP protocol version? + + // look for first space, which must come before first LF + while (ptr && ptr != end) + { + if (*ptr == '\n') return HTTPCode_Bad; + if (*ptr == ' ') break; + ptr++; + } + if (ptr == end) return HTTPCode_NeedMoreData; + ptr++; + + if (end - ptr < 3) return HTTPCode_NeedMoreData; + + code = ptr; + ptr += 3; + while (ptr && ptr != end) + { + if (*ptr == '\n') break; + ptr++; + } + if (ptr == end) return HTTPCode_NeedMoreData; + *data = ++ptr; + + if (memcmp((char*)code, "200", 3) == 0) return HTTPCode_200; + if (memcmp((char*)code, "404", 3) == 0) return HTTPCode_404; + if (memcmp((char*)code, "500", 3) == 0) return HTTPCode_500; + + LogInfo("ParseHTTPResponseCode found unexpected result code: %c%c%c", code[0], code[1], code[2]); + return HTTPCode_Other; +} + +// This function parses the xml body of the device description response from the router. Basically, we look to +// make sure this is a response referencing a service we care about (WANIPConnection or WANPPPConnection), +// look for the "controlURL" header immediately following, and copy the addressing and URL info we need +mDNSlocal void handleLNTDeviceDescriptionResponse(tcpLNTInfo *tcpInfo) +{ + mDNS *m = tcpInfo->m; + const mDNSu8 *ptr = tcpInfo->Reply; + const mDNSu8 *end = tcpInfo->Reply + tcpInfo->nread; + const mDNSu8 *stop; + mDNSs16 http_result; + + if (!mDNSIPPortIsZero(m->UPnPSOAPPort)) return; // already have the info we need + + http_result = ParseHTTPResponseCode(&ptr, end); // Note: modifies ptr + if (http_result == HTTPCode_404) LNT_ClearState(m); + if (http_result != HTTPCode_200) + { + mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.DeviceDescription", "noop", "HTTP Result", "HTTP code: %d", http_result); + return; + } + + // Always reset our flag to use WANIPConnection. We'll use WANPPPConnection if we find it and don't find WANIPConnection. + m->UPnPWANPPPConnection = mDNSfalse; + + // find either service we care about + while (ptr && ptr < end) + { + if ((*ptr & 0xDF) == 'W' && (strncasecmp((char*)ptr, "WANIPConnection:1", 17) == 0)) break; + ptr++; + } + if (ptr == end) + { + ptr = tcpInfo->Reply; + while (ptr && ptr < end) + { + if ((*ptr & 0xDF) == 'W' && (strncasecmp((char*)ptr, "WANPPPConnection:1", 18) == 0)) + { + m->UPnPWANPPPConnection = mDNStrue; + break; + } + ptr++; + } + } + if (ptr == mDNSNULL || ptr == end) { LogInfo("handleLNTDeviceDescriptionResponse: didn't find WANIPConnection:1 or WANPPPConnection:1 string"); return; } + + // find "controlURL", starting from where we left off + while (ptr && ptr < end) + { + if ((*ptr & 0xDF) == 'C' && (strncasecmp((char*)ptr, "controlURL", 10) == 0)) break; // find the first 'c'; is this controlURL? if not, keep looking + ptr++; + } + if (ptr == mDNSNULL || ptr == end) { LogInfo("handleLNTDeviceDescriptionResponse: didn't find controlURL string"); return; } + ptr += 11; // skip over "controlURL>" + if (ptr >= end) { LogInfo("handleLNTDeviceDescriptionResponse: past end of buffer and no body!"); return; } // check ptr again in case we skipped over the end of the buffer + + // find the end of the controlURL element + for (stop = ptr; stop < end; stop++) { if (*stop == '<') { end = stop; break; } } + + // fill in default port + m->UPnPSOAPPort = m->UPnPRouterPort; + + // free string pointers and set to NULL + if (m->UPnPSOAPAddressString != mDNSNULL) + { + mDNSPlatformMemFree(m->UPnPSOAPAddressString); + m->UPnPSOAPAddressString = mDNSNULL; + } + if (m->UPnPSOAPURL != mDNSNULL) + { + mDNSPlatformMemFree(m->UPnPSOAPURL); + m->UPnPSOAPURL = mDNSNULL; + } + + if (ParseHttpUrl(ptr, end, &m->UPnPSOAPAddressString, &m->UPnPSOAPPort, &m->UPnPSOAPURL) != mStatus_NoError) return; + // the SOAPURL should look something like "/uuid:0013-108c-4b3f0000f3dc" + + if (m->UPnPSOAPAddressString == mDNSNULL) + { + ptr = tcpInfo->Reply; + while (ptr && ptr < end) + { + if ((*ptr & 0xDF) == 'U' && (strncasecmp((char*)ptr, "URLBase", 7) == 0)) break; + ptr++; + } + + if (ptr < end) // found URLBase + { + LogInfo("handleLNTDeviceDescriptionResponse: found URLBase"); + ptr += 8; // skip over "URLBase>" + // find the end of the URLBase element + for (stop = ptr; stop < end; stop++) { if (*stop == '<') { end = stop; break; } } + if (ParseHttpUrl(ptr, end, &m->UPnPSOAPAddressString, &m->UPnPSOAPPort, mDNSNULL) != mStatus_NoError) + { + LogInfo("handleLNTDeviceDescriptionResponse: failed to parse URLBase"); + } + } + + // if all else fails, use the router address string + if (m->UPnPSOAPAddressString == mDNSNULL) AllocAndCopy(&m->UPnPSOAPAddressString, m->UPnPRouterAddressString); + } + if (m->UPnPSOAPAddressString == mDNSNULL) LogMsg("handleLNTDeviceDescriptionResponse: UPnPSOAPAddressString is NULL"); + else LogInfo("handleLNTDeviceDescriptionResponse: SOAP address string [%s]", m->UPnPSOAPAddressString); + + if (m->UPnPSOAPURL == mDNSNULL) AllocAndCopy(&m->UPnPSOAPURL, m->UPnPRouterURL); + if (m->UPnPSOAPURL == mDNSNULL) LogMsg("handleLNTDeviceDescriptionResponse: UPnPSOAPURL is NULL"); + else LogInfo("handleLNTDeviceDescriptionResponse: SOAP URL [%s]", m->UPnPSOAPURL); +} + +mDNSlocal void handleLNTGetExternalAddressResponse(tcpLNTInfo *tcpInfo) +{ + mDNS *m = tcpInfo->m; + mDNSu16 err = NATErr_None; + mDNSv4Addr ExtAddr; + const mDNSu8 *ptr = tcpInfo->Reply; + const mDNSu8 *end = tcpInfo->Reply + tcpInfo->nread; + mDNSu8 *addrend; + static char tagname[20] = { 'N','e','w','E','x','t','e','r','n','a','l','I','P','A','d','d','r','e','s','s' }; + // Array NOT including a terminating nul + +// LogInfo("handleLNTGetExternalAddressResponse: %s", ptr); + + mDNSs16 http_result = ParseHTTPResponseCode(&ptr, end); // Note: modifies ptr + if (http_result == HTTPCode_404) LNT_ClearState(m); + if (http_result != HTTPCode_200) + { + mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.AddressRequest", "noop", "HTTP Result", "HTTP code: %d", http_result); + return; + } + + while (ptr < end && strncasecmp((char*)ptr, tagname, sizeof(tagname))) ptr++; + ptr += sizeof(tagname); // Skip over "NewExternalIPAddress" + while (ptr < end && *ptr != '>') ptr++; + ptr += 1; // Skip over ">" + + // Find the end of the address and terminate the string so inet_pton() can convert it + // (Might be better to copy this to a local string here -- this is overwriting tcpInfo->Reply in-place + addrend = (mDNSu8*)ptr; + while (addrend < end && (mDNSIsDigit(*addrend) || *addrend == '.')) addrend++; + if (addrend >= end) return; + *addrend = 0; + + if (inet_pton(AF_INET, (char*)ptr, &ExtAddr) <= 0) + { + mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.AddressRequest", "noop", "inet_pton", ""); + LogMsg("handleLNTGetExternalAddressResponse: Router returned bad address %s", ptr); + err = NATErr_NetFail; + ExtAddr = zerov4Addr; + } + if (!err) LogInfo("handleLNTGetExternalAddressResponse: External IP address is %.4a", &ExtAddr); + + natTraversalHandleAddressReply(m, err, ExtAddr); +} + +mDNSlocal void handleLNTPortMappingResponse(tcpLNTInfo *tcpInfo) +{ + mDNS *m = tcpInfo->m; + mDNSIPPort extport = zeroIPPort; + const mDNSu8 *ptr = tcpInfo->Reply; + const mDNSu8 *const end = tcpInfo->Reply + tcpInfo->nread; + NATTraversalInfo *natInfo; + mDNSs16 http_result; + + for (natInfo = m->NATTraversals; natInfo; natInfo=natInfo->next) { if (natInfo == tcpInfo->parentNATInfo) break;} + + if (!natInfo) { LogInfo("handleLNTPortMappingResponse: can't find matching tcpInfo in NATTraversals!"); return; } + + http_result = ParseHTTPResponseCode(&ptr, end); // Note: modifies ptr + if (http_result == HTTPCode_200) + { + LogInfo("handleLNTPortMappingResponse: got a valid response, sending reply to natTraversalHandlePortMapReply(internal %d external %d retries %d)", + mDNSVal16(natInfo->IntPort), RequestedPortNum(natInfo), tcpInfo->retries); + + // Make sure to compute extport *before* we zero tcpInfo->retries + extport = mDNSOpaque16fromIntVal(RequestedPortNum(natInfo)); + tcpInfo->retries = 0; + natTraversalHandlePortMapReply(m, natInfo, m->UPnPInterfaceID, mStatus_NoError, extport, NATMAP_DEFAULT_LEASE, NATTProtocolUPNPIGD); + } + else if (http_result == HTTPCode_500) + { + while (ptr && ptr != end) + { + if (((*ptr & 0xDF) == 'C' && end - ptr >= 8 && strncasecmp((char*)ptr, "Conflict", 8) == 0) || + (*ptr == '>' && end - ptr >= 15 && strncasecmp((char*)ptr, ">718</errorCode", 15) == 0)) + { + if (tcpInfo->retries < 100) + { + tcpInfo->retries++; SendPortMapRequest(tcpInfo->m, natInfo); + mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.PortMapRequest", "noop", "Conflict", "Retry %d", tcpInfo->retries); + } + else + { + LogMsg("handleLNTPortMappingResponse too many conflict retries %d %d", mDNSVal16(natInfo->IntPort), mDNSVal16(natInfo->RequestedPort)); + mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.PortMapRequest", "noop", "Conflict - too many retries", "Retries: %d", tcpInfo->retries); + natTraversalHandlePortMapReply(m, natInfo, m->UPnPInterfaceID, NATErr_Res, zeroIPPort, 0, NATTProtocolUPNPIGD); + } + return; + } + ptr++; + } + } + else if (http_result == HTTPCode_Bad) LogMsg("handleLNTPortMappingResponse got data that was not a valid HTTP response"); + else if (http_result == HTTPCode_Other) LogMsg("handleLNTPortMappingResponse got unexpected response code"); + else if (http_result == HTTPCode_404) LNT_ClearState(m); + if (http_result != HTTPCode_200 && http_result != HTTPCode_500) + mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.PortMapRequest", "noop", "HTTP Result", "HTTP code: %d", http_result); +} + +mDNSlocal void DisposeInfoFromUnmapList(mDNS *m, tcpLNTInfo *tcpInfo) +{ + tcpLNTInfo **ptr = &m->tcpInfoUnmapList; + while (*ptr && *ptr != tcpInfo) ptr = &(*ptr)->next; + if (*ptr) { *ptr = (*ptr)->next; mDNSPlatformMemFree(tcpInfo); } // If we found it, cut it from our list and free the memory +} + +mDNSlocal void tcpConnectionCallback(TCPSocket *sock, void *context, mDNSBool ConnectionEstablished, mStatus err) +{ + mStatus status = mStatus_NoError; + tcpLNTInfo *tcpInfo = (tcpLNTInfo *)context; + mDNSBool closed = mDNSfalse; + long n = 0; + long nsent = 0; + static int LNTERRORcount = 0; + + if (tcpInfo == mDNSNULL) { LogInfo("tcpConnectionCallback: no tcpInfo context"); status = mStatus_Invalid; goto exit; } + + if (tcpInfo->sock != sock) + { + LogMsg("tcpConnectionCallback: WARNING- tcpInfo->sock(%p) != sock(%p) !!! Printing tcpInfo struct", tcpInfo->sock, sock); + LogMsg("tcpConnectionCallback: tcpInfo->Address:Port [%#a:%d] tcpInfo->op[%d] tcpInfo->retries[%d] tcpInfo->Request[%s] tcpInfo->Reply[%s]", + &tcpInfo->Address, mDNSVal16(tcpInfo->Port), tcpInfo->op, tcpInfo->retries, tcpInfo->Request, tcpInfo->Reply); + } + + // The handlers below expect to be called with the lock held + mDNS_Lock(tcpInfo->m); + + if (err) { LogInfo("tcpConnectionCallback: received error"); goto exit; } + + if (ConnectionEstablished) // connection is established - send the message + { + LogInfo("tcpConnectionCallback: connection established, sending message"); + nsent = mDNSPlatformWriteTCP(sock, (char*)tcpInfo->Request, tcpInfo->requestLen); + if (nsent != (long)tcpInfo->requestLen) { LogMsg("tcpConnectionCallback: error writing"); status = mStatus_UnknownErr; goto exit; } + } + else + { + n = mDNSPlatformReadTCP(sock, (char*)tcpInfo->Reply + tcpInfo->nread, tcpInfo->replyLen - tcpInfo->nread, &closed); + LogInfo("tcpConnectionCallback: mDNSPlatformReadTCP read %d bytes", n); + + if (n < 0) { LogInfo("tcpConnectionCallback - read returned %d", n); status = mStatus_ConnFailed; goto exit; } + else if (closed) { LogInfo("tcpConnectionCallback: socket closed by remote end %d", tcpInfo->nread); status = mStatus_ConnFailed; goto exit; } + + tcpInfo->nread += n; + LogInfo("tcpConnectionCallback tcpInfo->nread %d", tcpInfo->nread); + if (tcpInfo->nread > LNT_MAXBUFSIZE) + { + LogInfo("result truncated..."); + tcpInfo->nread = LNT_MAXBUFSIZE; + } + + switch (tcpInfo->op) + { + case LNTDiscoveryOp: handleLNTDeviceDescriptionResponse (tcpInfo); break; + case LNTExternalAddrOp: handleLNTGetExternalAddressResponse(tcpInfo); break; + case LNTPortMapOp: handleLNTPortMappingResponse (tcpInfo); break; + case LNTPortMapDeleteOp: status = mStatus_ConfigChanged; break; + default: LogMsg("tcpConnectionCallback: bad tcp operation! %d", tcpInfo->op); status = mStatus_Invalid; break; + } + } +exit: + if (err || status) + { + mDNS *m = tcpInfo->m; + if ((++LNTERRORcount % 1000) == 0) + { + LogMsg("ERROR: tcpconnectioncallback -> got error status %d times", LNTERRORcount); + assert(LNTERRORcount < 1000); + // Recovery Mechanism to bail mDNSResponder out of trouble: It has been seen that we can get into + // this loop: [tcpKQSocketCallback()--> doTcpSocketCallback()-->tcpconnectionCallback()-->mDNSASLLog()], + // if mDNSPlatformTCPCloseConnection() does not close the TCPSocket. Instead of calling mDNSASLLog() + // repeatedly and logging the same error msg causing 100% CPU usage, we + // crash mDNSResponder using assert() and restart fresh. See advantages below: + // 1.Better User Experience + // 2.CrashLogs frequency can be monitored + // 3.StackTrace can be used for more info + } + + switch (tcpInfo->op) + { + case LNTDiscoveryOp: if (m->UPnPSOAPAddressString == mDNSNULL) + mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.DeviceDescription", "failure", "SOAP Address", ""); + if (m->UPnPSOAPURL == mDNSNULL) + mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.DeviceDescription", "failure", "SOAP path", ""); + if (m->UPnPSOAPAddressString && m->UPnPSOAPURL) + mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.DeviceDescription", "success", "success", ""); + break; + case LNTExternalAddrOp: mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.AddressRequest", + mDNSIPv4AddressIsZero(m->ExtAddress) ? "failure" : "success", + mDNSIPv4AddressIsZero(m->ExtAddress) ? "failure" : "success", ""); + break; + case LNTPortMapOp: if (tcpInfo->parentNATInfo) + mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.PortMapRequest", (tcpInfo->parentNATInfo->Result) ? "failure" : "success", + (tcpInfo->parentNATInfo->Result) ? "failure" : "success", "Result: %d", tcpInfo->parentNATInfo->Result); + break; + case LNTPortMapDeleteOp: break; + default: break; + } + + mDNSPlatformTCPCloseConnection(sock); + tcpInfo->sock = mDNSNULL; + if (tcpInfo->Request) { mDNSPlatformMemFree(tcpInfo->Request); tcpInfo->Request = mDNSNULL; } + if (tcpInfo->Reply ) { mDNSPlatformMemFree(tcpInfo->Reply); tcpInfo->Reply = mDNSNULL; } + } + else + { + LNTERRORcount = 0; // clear LNTERRORcount + } + + if (tcpInfo) mDNS_Unlock(tcpInfo->m); + + if (status == mStatus_ConfigChanged) DisposeInfoFromUnmapList(tcpInfo->m, tcpInfo); +} + +mDNSlocal mStatus MakeTCPConnection(mDNS *const m, tcpLNTInfo *info, const mDNSAddr *const Addr, const mDNSIPPort Port, LNTOp_t op) +{ + mStatus err = mStatus_NoError; + mDNSIPPort srcport = zeroIPPort; + + if (mDNSIPv4AddressIsZero(Addr->ip.v4) || mDNSIPPortIsZero(Port)) + { LogMsg("LNT MakeTCPConnection: bad address/port %#a:%d", Addr, mDNSVal16(Port)); return(mStatus_Invalid); } + info->m = m; + info->Address = *Addr; + info->Port = Port; + info->op = op; + info->nread = 0; + info->replyLen = LNT_MAXBUFSIZE; + if (info->Reply != mDNSNULL) mDNSPlatformMemZero(info->Reply, LNT_MAXBUFSIZE); // reuse previously allocated buffer + else if ((info->Reply = mDNSPlatformMemAllocate(LNT_MAXBUFSIZE)) == mDNSNULL) { LogInfo("can't allocate reply buffer"); return (mStatus_NoMemoryErr); } + + if (info->sock) { LogInfo("MakeTCPConnection: closing previous open connection"); mDNSPlatformTCPCloseConnection(info->sock); info->sock = mDNSNULL; } + info->sock = mDNSPlatformTCPSocket(m, kTCPSocketFlags_Zero, &srcport, mDNSfalse); + if (!info->sock) { LogMsg("LNT MakeTCPConnection: unable to create TCP socket"); mDNSPlatformMemFree(info->Reply); info->Reply = mDNSNULL; return(mStatus_NoMemoryErr); } + LogInfo("MakeTCPConnection: connecting to %#a:%d", &info->Address, mDNSVal16(info->Port)); + err = mDNSPlatformTCPConnect(info->sock, Addr, Port, mDNSNULL, 0, tcpConnectionCallback, info); + + if (err == mStatus_ConnPending) err = mStatus_NoError; + else if (err == mStatus_ConnEstablished) + { + mDNS_DropLockBeforeCallback(); + tcpConnectionCallback(info->sock, info, mDNStrue, mStatus_NoError); + mDNS_ReclaimLockAfterCallback(); + err = mStatus_NoError; + } + else + { + // Don't need to log this in customer builds -- it happens quite often during sleep, wake, configuration changes, etc. + LogInfo("LNT MakeTCPConnection: connection failed"); + mDNSPlatformTCPCloseConnection(info->sock); // Dispose the socket we created with mDNSPlatformTCPSocket() above + info->sock = mDNSNULL; + mDNSPlatformMemFree(info->Reply); + info->Reply = mDNSNULL; + } + return(err); +} + +mDNSlocal unsigned int AddSOAPArguments(char *const buf, const unsigned int maxlen, const int numArgs, const Property *const a) +{ + static const char f1[] = "<%s>%s</%s>"; + static const char f2[] = "<%s xmlns:dt=\"urn:schemas-microsoft-com:datatypes\" dt:dt=\"%s\">%s</%s>"; + int i, len = 0; + *buf = 0; + for (i = 0; i < numArgs; i++) + { + if (a[i].type) len += mDNS_snprintf(buf + len, maxlen - len, f2, a[i].name, a[i].type, a[i].value, a[i].name); + else len += mDNS_snprintf(buf + len, maxlen - len, f1, a[i].name, a[i].value, a[i].name); + } + return(len); +} + +mDNSlocal mStatus SendSOAPMsgControlAction(mDNS *m, tcpLNTInfo *info, const char *const Action, const int numArgs, const Property *const Arguments, const LNTOp_t op) +{ + // SOAP message header format - + // - control URL + // - action (string) + // - router's host/port ("host:port") + // - content-length + static const char header[] = + "POST %s HTTP/1.1\r\n" + "Content-Type: text/xml; charset=\"utf-8\"\r\n" + "SOAPAction: \"urn:schemas-upnp-org:service:WAN%sConnection:1#%s\"\r\n" + "User-Agent: Mozilla/4.0 (compatible; UPnP/1.0; Windows 9x)\r\n" + "Host: %s\r\n" + "Content-Length: %d\r\n" + "Connection: close\r\n" + "Pragma: no-cache\r\n" + "\r\n" + "%s\r\n"; + + static const char body1[] = + "<?xml version=\"1.0\"?>\r\n" + "<SOAP-ENV:Envelope" + " xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\"" + " SOAP-ENV:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">" + "<SOAP-ENV:Body>" + "<m:%s xmlns:m=\"urn:schemas-upnp-org:service:WAN%sConnection:1\">"; + + static const char body2[] = + "</m:%s>" + "</SOAP-ENV:Body>" + "</SOAP-ENV:Envelope>\r\n"; + + mStatus err; + char *body = (char*)&m->omsg; // Typically requires 1110-1122 bytes; m->omsg is 8952 bytes, which is plenty + int bodyLen; + + if (mDNSIPPortIsZero(m->UPnPSOAPPort) || m->UPnPSOAPURL == mDNSNULL || m->UPnPSOAPAddressString == mDNSNULL) // if no SOAP URL or address exists get out here + { LogInfo("SendSOAPMsgControlAction: no SOAP port, URL or address string"); return mStatus_Invalid; } + + // Create body + bodyLen = mDNS_snprintf (body, sizeof(m->omsg), body1, Action, m->UPnPWANPPPConnection ? "PPP" : "IP"); + bodyLen += AddSOAPArguments(body + bodyLen, sizeof(m->omsg) - bodyLen, numArgs, Arguments); + bodyLen += mDNS_snprintf (body + bodyLen, sizeof(m->omsg) - bodyLen, body2, Action); + + // Create info->Request; the header needs to contain the bodyLen in the "Content-Length" field + if (!info->Request) info->Request = mDNSPlatformMemAllocate(LNT_MAXBUFSIZE); + if (!info->Request) { LogMsg("SendSOAPMsgControlAction: Can't allocate info->Request"); return mStatus_NoMemoryErr; } + info->requestLen = mDNS_snprintf((char*)info->Request, LNT_MAXBUFSIZE, header, m->UPnPSOAPURL, m->UPnPWANPPPConnection ? "PPP" : "IP", Action, m->UPnPSOAPAddressString, bodyLen, body); + + err = MakeTCPConnection(m, info, &m->Router, m->UPnPSOAPPort, op); + if (err) { mDNSPlatformMemFree(info->Request); info->Request = mDNSNULL; } + return err; +} + +// Build port mapping request with new port (up to max) and send it +mDNSlocal mStatus SendPortMapRequest(mDNS *m, NATTraversalInfo *n) +{ + char externalPort[6]; + char internalPort[6]; + char localIPAddrString[30]; + char publicPortString[40]; + Property propArgs[8]; + mDNSu16 ReqPortNum = RequestedPortNum(n); + NATTraversalInfo *n2 = m->NATTraversals; + + // Scan our m->NATTraversals list to make sure the external port we're requesting is locally unique. + // UPnP gateways will report conflicts if different devices request the same external port, but if two + // clients on the same device request the same external port the second one just stomps over the first. + // One way this can happen is like this: + // 1. Client A binds local port 80 + // 2. Client A requests external port 80 -> internal port 80 + // 3. UPnP NAT gateway refuses external port 80 (some other client already has it) + // 4. Client A tries again, and successfully gets external port 80 -> internal port 81 + // 5. Client B on same machine tries to bind local port 80, and fails + // 6. Client B tries again, and successfully binds local port 81 + // 7. Client B now requests external port 81 -> internal port 81 + // 8. UPnP NAT gateway allows this, stomping over Client A's existing mapping + + while (n2) + { + if (n2 == n || RequestedPortNum(n2) != ReqPortNum) n2=n2->next; + else + { + if (n->tcpInfo.retries < 100) + { + n->tcpInfo.retries++; + ReqPortNum = RequestedPortNum(n); // Pick a new port number + n2 = m->NATTraversals; // And re-scan the list looking for conflicts + } + else + { + natTraversalHandlePortMapReply(m, n, m->UPnPInterfaceID, NATErr_Res, zeroIPPort, 0, NATTProtocolUPNPIGD); + return mStatus_NoError; + } + } + } + + // create strings to use in the message + mDNS_snprintf(externalPort, sizeof(externalPort), "%u", ReqPortNum); + mDNS_snprintf(internalPort, sizeof(internalPort), "%u", mDNSVal16(n->IntPort)); + mDNS_snprintf(publicPortString, sizeof(publicPortString), "iC%u", ReqPortNum); + mDNS_snprintf(localIPAddrString, sizeof(localIPAddrString), "%u.%u.%u.%u", + m->AdvertisedV4.ip.v4.b[0], m->AdvertisedV4.ip.v4.b[1], m->AdvertisedV4.ip.v4.b[2], m->AdvertisedV4.ip.v4.b[3]); + + // build the message + mDNSPlatformMemZero(propArgs, sizeof(propArgs)); + propArgs[0].name = "NewRemoteHost"; + propArgs[0].type = "string"; + propArgs[0].value = ""; + propArgs[1].name = "NewExternalPort"; + propArgs[1].type = "ui2"; + propArgs[1].value = externalPort; + propArgs[2].name = "NewProtocol"; + propArgs[2].type = "string"; + propArgs[2].value = (n->Protocol == NATOp_MapUDP) ? "UDP" : "TCP"; + propArgs[3].name = "NewInternalPort"; + propArgs[3].type = "ui2"; + propArgs[3].value = internalPort; + propArgs[4].name = "NewInternalClient"; + propArgs[4].type = "string"; + propArgs[4].value = localIPAddrString; + propArgs[5].name = "NewEnabled"; + propArgs[5].type = "boolean"; + propArgs[5].value = "1"; + propArgs[6].name = "NewPortMappingDescription"; + propArgs[6].type = "string"; + propArgs[6].value = publicPortString; + propArgs[7].name = "NewLeaseDuration"; + propArgs[7].type = "ui4"; + propArgs[7].value = "0"; + + LogInfo("SendPortMapRequest: internal %u external %u", mDNSVal16(n->IntPort), ReqPortNum); + return SendSOAPMsgControlAction(m, &n->tcpInfo, "AddPortMapping", 8, propArgs, LNTPortMapOp); +} + +mDNSexport mStatus LNT_MapPort(mDNS *m, NATTraversalInfo *const n) +{ + LogInfo("LNT_MapPort"); + if (n->tcpInfo.sock) return(mStatus_NoError); // If we already have a connection up don't make another request for the same thing + n->tcpInfo.parentNATInfo = n; + n->tcpInfo.retries = 0; + return SendPortMapRequest(m, n); +} + +mDNSexport mStatus LNT_UnmapPort(mDNS *m, NATTraversalInfo *const n) +{ + char externalPort[10]; + Property propArgs[3]; + tcpLNTInfo *info; + tcpLNTInfo **infoPtr = &m->tcpInfoUnmapList; + mStatus err; + + // If no NAT gateway to talk to, no need to do all this work for nothing + if (mDNSIPPortIsZero(m->UPnPSOAPPort) || !m->UPnPSOAPURL || !m->UPnPSOAPAddressString) return mStatus_NoError; + + mDNS_snprintf(externalPort, sizeof(externalPort), "%u", mDNSVal16(mDNSIPPortIsZero(n->RequestedPort) ? n->IntPort : n->RequestedPort)); + + mDNSPlatformMemZero(propArgs, sizeof(propArgs)); + propArgs[0].name = "NewRemoteHost"; + propArgs[0].type = "string"; + propArgs[0].value = ""; + propArgs[1].name = "NewExternalPort"; + propArgs[1].type = "ui2"; + propArgs[1].value = externalPort; + propArgs[2].name = "NewProtocol"; + propArgs[2].type = "string"; + propArgs[2].value = (n->Protocol == NATOp_MapUDP) ? "UDP" : "TCP"; + + n->tcpInfo.parentNATInfo = n; + + // clean up previous port mapping requests and allocations + if (n->tcpInfo.sock) LogInfo("LNT_UnmapPort: closing previous open connection"); + if (n->tcpInfo.sock ) { mDNSPlatformTCPCloseConnection(n->tcpInfo.sock); n->tcpInfo.sock = mDNSNULL; } + if (n->tcpInfo.Request) { mDNSPlatformMemFree(n->tcpInfo.Request); n->tcpInfo.Request = mDNSNULL; } + if (n->tcpInfo.Reply ) { mDNSPlatformMemFree(n->tcpInfo.Reply); n->tcpInfo.Reply = mDNSNULL; } + + // make a copy of the tcpInfo that we can clean up later (the one passed in will be destroyed by the client as soon as this returns) + if ((info = mDNSPlatformMemAllocate(sizeof(tcpLNTInfo))) == mDNSNULL) + { LogInfo("LNT_UnmapPort: can't allocate tcpInfo"); return(mStatus_NoMemoryErr); } + *info = n->tcpInfo; + + while (*infoPtr) infoPtr = &(*infoPtr)->next; // find the end of the list + *infoPtr = info; // append + + err = SendSOAPMsgControlAction(m, info, "DeletePortMapping", 3, propArgs, LNTPortMapDeleteOp); + if (err) DisposeInfoFromUnmapList(m, info); + return err; +} + +mDNSexport mStatus LNT_GetExternalAddress(mDNS *m) +{ + return SendSOAPMsgControlAction(m, &m->tcpAddrInfo, "GetExternalIPAddress", 0, mDNSNULL, LNTExternalAddrOp); +} + +mDNSlocal mStatus GetDeviceDescription(mDNS *m, tcpLNTInfo *info) +{ + // Device description format - + // - device description URL + // - host/port + static const char szSSDPMsgDescribeDeviceFMT[] = + "GET %s HTTP/1.1\r\n" + "Accept: text/xml, application/xml\r\n" + "User-Agent: Mozilla/4.0 (compatible; UPnP/1.0; Windows NT/5.1)\r\n" + "Host: %s\r\n" + "Connection: close\r\n" + "\r\n"; + + if (!mDNSIPPortIsZero(m->UPnPSOAPPort)) return mStatus_NoError; // already have the info we need + + if (m->UPnPRouterURL == mDNSNULL || m->UPnPRouterAddressString == mDNSNULL) { LogInfo("GetDeviceDescription: no router URL or address string!"); return (mStatus_Invalid); } + + // build message + if (info->Request != mDNSNULL) mDNSPlatformMemZero(info->Request, LNT_MAXBUFSIZE); // reuse previously allocated buffer + else if ((info->Request = mDNSPlatformMemAllocate(LNT_MAXBUFSIZE)) == mDNSNULL) { LogInfo("can't allocate send buffer for discovery"); return (mStatus_NoMemoryErr); } + info->requestLen = mDNS_snprintf((char*)info->Request, LNT_MAXBUFSIZE, szSSDPMsgDescribeDeviceFMT, m->UPnPRouterURL, m->UPnPRouterAddressString); + LogInfo("Describe Device: [%s]", info->Request); + return MakeTCPConnection(m, info, &m->Router, m->UPnPRouterPort, LNTDiscoveryOp); +} + +// This function parses the response to our SSDP discovery message. Basically, we look to make sure this is a response +// referencing a service we care about (WANIPConnection or WANPPPConnection), then look for the "Location:" header and copy the addressing and +// URL info we need. +mDNSexport void LNT_ConfigureRouterInfo(mDNS *m, const mDNSInterfaceID InterfaceID, const mDNSu8 *const data, const mDNSu16 len) +{ + const mDNSu8 *ptr = data; + const mDNSu8 *end = data + len; + const mDNSu8 *stop = ptr; + + if (!mDNSIPPortIsZero(m->UPnPRouterPort)) return; // already have the info we need + + // The formatting of the HTTP header is not always the same when it comes to the placement of + // the service and location strings, so we just look for each of them from the beginning for every response + + // figure out if this is a message from a service we care about + while (ptr && ptr != end) + { + if ((*ptr & 0xDF) == 'W' && (strncasecmp((char*)ptr, "WANIPConnection:1", 17) == 0)) break; + ptr++; + } + if (ptr == end) + { + ptr = data; + while (ptr && ptr != end) + { + if ((*ptr & 0xDF) == 'W' && (strncasecmp((char*)ptr, "WANPPPConnection:1", 18) == 0)) break; + ptr++; + } + } + if (ptr == mDNSNULL || ptr == end) return; // not a message we care about + + // find "Location:", starting from the beginning + ptr = data; + while (ptr && ptr != end) + { + if ((*ptr & 0xDF) == 'L' && (strncasecmp((char*)ptr, "Location:", 9) == 0)) break; // find the first 'L'; is this Location? if not, keep looking + ptr++; + } + if (ptr == mDNSNULL || ptr == end) + { + mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.ssdp", "failure", "Location", ""); + return; // not a message we care about + } + ptr += 9; //Skip over 'Location:' + while (*ptr == ' ' && ptr < end) ptr++; // skip over spaces + if (ptr >= end) return; + + // find the end of the line + for (stop = ptr; stop != end; stop++) { if (*stop == '\r') { end = stop; break; } } + + // fill in default port + m->UPnPRouterPort = mDNSOpaque16fromIntVal(80); + + // free string pointers and set to NULL + if (m->UPnPRouterAddressString != mDNSNULL) + { + mDNSPlatformMemFree(m->UPnPRouterAddressString); + m->UPnPRouterAddressString = mDNSNULL; + } + if (m->UPnPRouterURL != mDNSNULL) + { + mDNSPlatformMemFree(m->UPnPRouterURL); + m->UPnPRouterURL = mDNSNULL; + } + + // the Router URL should look something like "/dyndev/uuid:0013-108c-4b3f0000f3dc" + if (ParseHttpUrl(ptr, end, &m->UPnPRouterAddressString, &m->UPnPRouterPort, &m->UPnPRouterURL) != mStatus_NoError) + { + mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.ssdp", "failure", "Parse URL", ""); + return; + } + + m->UPnPInterfaceID = InterfaceID; + + if (m->UPnPRouterAddressString == mDNSNULL) + { + mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.ssdp", "failure", "Router address", ""); + LogMsg("LNT_ConfigureRouterInfo: UPnPRouterAddressString is NULL"); + } + else LogInfo("LNT_ConfigureRouterInfo: Router address string [%s]", m->UPnPRouterAddressString); + + if (m->UPnPRouterURL == mDNSNULL) + { + mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.ssdp", "failure", "Router path", ""); + LogMsg("LNT_ConfigureRouterInfo: UPnPRouterURL is NULL"); + } + else LogInfo("LNT_ConfigureRouterInfo: Router URL [%s]", m->UPnPRouterURL); + + LogInfo("LNT_ConfigureRouterInfo: Router port %d", mDNSVal16(m->UPnPRouterPort)); + LogInfo("LNT_ConfigureRouterInfo: Router interface %d", m->UPnPInterfaceID); + + // Don't need the SSDP socket anymore + if (m->SSDPSocket) { debugf("LNT_ConfigureRouterInfo destroying SSDPSocket %p", &m->SSDPSocket); mDNSPlatformUDPClose(m->SSDPSocket); m->SSDPSocket = mDNSNULL; } + + mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.ssdp", "success", "success", ""); + // now send message to get the device description + GetDeviceDescription(m, &m->tcpDeviceInfo); +} + +mDNSexport void LNT_SendDiscoveryMsg(mDNS *m) +{ + static const char msg[] = + "M-SEARCH * HTTP/1.1\r\n" + "Host:239.255.255.250:1900\r\n" + "ST:urn:schemas-upnp-org:service:WAN%sConnection:1\r\n" + "Man:\"ssdp:discover\"\r\n" + "MX:3\r\n\r\n"; + static const mDNSAddr multicastDest = { mDNSAddrType_IPv4, { { { 239, 255, 255, 250 } } } }; + + mDNSu8 *buf = (mDNSu8*)&m->omsg; //m->omsg is 8952 bytes, which is plenty + unsigned int bufLen; + + if (!mDNSIPPortIsZero(m->UPnPRouterPort)) + { + if (m->SSDPSocket) { debugf("LNT_SendDiscoveryMsg destroying SSDPSocket %p", &m->SSDPSocket); mDNSPlatformUDPClose(m->SSDPSocket); m->SSDPSocket = mDNSNULL; } + if (mDNSIPPortIsZero(m->UPnPSOAPPort) && !m->tcpDeviceInfo.sock) GetDeviceDescription(m, &m->tcpDeviceInfo); + return; + } + + // Always query for WANIPConnection in the first SSDP packet + if (m->retryIntervalGetAddr <= NATMAP_INIT_RETRY) m->SSDPWANPPPConnection = mDNSfalse; + + // Create message + bufLen = mDNS_snprintf((char*)buf, sizeof(m->omsg), msg, m->SSDPWANPPPConnection ? "PPP" : "IP"); + + debugf("LNT_SendDiscoveryMsg Router %.4a Current External Address %.4a", &m->Router.ip.v4, &m->ExtAddress); + + if (!mDNSIPv4AddressIsZero(m->Router.ip.v4)) + { + if (!m->SSDPSocket) { m->SSDPSocket = mDNSPlatformUDPSocket(m, zeroIPPort); debugf("LNT_SendDiscoveryMsg created SSDPSocket %p", &m->SSDPSocket); } + mDNSPlatformSendUDP(m, buf, buf + bufLen, 0, m->SSDPSocket, &m->Router, SSDPPort, mDNSfalse); + mDNSPlatformSendUDP(m, buf, buf + bufLen, 0, m->SSDPSocket, &multicastDest, SSDPPort, mDNSfalse); + } + + m->SSDPWANPPPConnection = !m->SSDPWANPPPConnection; +} + +mDNSexport void LNT_ClearState(mDNS *const m) +{ + if (m->tcpAddrInfo.sock) { mDNSPlatformTCPCloseConnection(m->tcpAddrInfo.sock); m->tcpAddrInfo.sock = mDNSNULL; } + if (m->tcpDeviceInfo.sock) { mDNSPlatformTCPCloseConnection(m->tcpDeviceInfo.sock); m->tcpDeviceInfo.sock = mDNSNULL; } + m->UPnPSOAPPort = m->UPnPRouterPort = zeroIPPort; // Reset UPnP ports +} + +#endif /* _LEGACY_NAT_TRAVERSAL_ */ diff --git a/mDNSResponder/mDNSMacOSX/P2PPacketFilter.c b/mDNSResponder/mDNSMacOSX/P2PPacketFilter.c new file mode 100644 index 00000000..1ab8a02f --- /dev/null +++ b/mDNSResponder/mDNSMacOSX/P2PPacketFilter.c @@ -0,0 +1,298 @@ +/* + * + * Copyright (c) 2011 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 <net/if.h> +#include <System/net/pfvar.h> +#include <string.h> +#include <fcntl.h> +#include <errno.h> +#include <sys/ioctl.h> +#include <unistd.h> +#include <AssertMacros.h> +#include "P2PPacketFilter.h" + +#define AIRDROP_ANCHOR_PATH "com.apple/200.AirDrop" +#define MDNS_ANCHOR_NAME "Bonjour" +#define MDNS_ANCHOR_PATH AIRDROP_ANCHOR_PATH "/" MDNS_ANCHOR_NAME + +#define PF_DEV_PATH "/dev/pf" +#define BONJOUR_PORT 5353 + +static int openPFDevice( int * outFD ) +{ + int err; + int fd = open( PF_DEV_PATH, O_RDWR ); + + if( fd >= 0 ) + { + err = 0; + *outFD = fd; + } + else + { + err = errno; + } + + return err; +} + +static int getTicket( int devFD, u_int32_t * outTicket, char * anchorPath ) +{ + struct pfioc_trans_e trans_e; + + trans_e.rs_num = PF_RULESET_FILTER; + strlcpy( trans_e.anchor, anchorPath, sizeof( trans_e.anchor ) ); + + struct pfioc_trans trans; + + trans.size = 1; + trans.esize = sizeof( trans_e ); + trans.array = &trans_e; + + int result, ioctlError; + + ioctlError = ioctl( devFD, DIOCXBEGIN, &trans ); + if( ioctlError ) + { + result = errno; + } + else + { + result = 0; + *outTicket = trans_e.ticket; + } + + return result; +} + +static int commitChange( int devFD, u_int32_t ticket, char * anchorPath ) +{ + struct pfioc_trans_e trans_e; + + trans_e.rs_num = PF_RULESET_FILTER; + strlcpy( trans_e.anchor, anchorPath, sizeof( trans_e.anchor ) ); + trans_e.ticket = ticket; + + struct pfioc_trans trans; + + trans.size = 1; + trans.esize = sizeof( trans_e ); + trans.array = &trans_e; + + int result, ioctlError; + + ioctlError = ioctl( devFD, DIOCXCOMMIT, &trans ); + if( ioctlError ) + result = errno; + else + result = 0; + + return result; +} + +static int getPoolTicket( int devFD, u_int32_t * outPoolTicket ) +{ + struct pfioc_pooladdr pp; + + int result, ioctlError; + + ioctlError = ioctl( devFD, DIOCBEGINADDRS, &pp ); + if( ioctlError ) + { + result = errno; + } + else + { + result = 0; + *outPoolTicket = pp.ticket; + } + + return result; +} + +static int addRule( int devFD, struct pfioc_rule * pr ) +{ + int result, ioctlResult; + + ioctlResult = ioctl( devFD, DIOCADDRULE, pr ); + if( ioctlResult ) + result = errno; + else + result = 0; + + return result; +} + +static void initRuleHeader( struct pfioc_rule * pr, + u_int32_t ticket, + u_int32_t poolTicket, + char * anchorPath ) +{ + pr->action = PF_CHANGE_NONE; + pr->ticket = ticket; + pr->pool_ticket = poolTicket; + strlcpy( pr->anchor, anchorPath, sizeof( pr->anchor ) ); +} + +// allow inbound traffice on the Bonjour port (5353) +static void initBonjourRule( struct pfioc_rule * pr, + const char * interfaceName, + u_int32_t ticket, + u_int32_t poolTicket, + char * anchorPath ) +{ + memset( pr, 0, sizeof( *pr ) ); + + // Header + initRuleHeader( pr, ticket, poolTicket, anchorPath ); + + // Rule + pr->rule.dst.xport.range.port[0] = htons( BONJOUR_PORT ); + pr->rule.dst.xport.range.op = PF_OP_EQ; + + strlcpy( pr->rule.ifname, interfaceName, sizeof( pr->rule.ifname ) ); + + pr->rule.action = PF_PASS; + pr->rule.direction = PF_IN; + pr->rule.keep_state = 1; + pr->rule.af = AF_INET6; + pr->rule.proto = IPPROTO_UDP; + pr->rule.extfilter = PF_EXTFILTER_APD; +} + +// allow outbound TCP connections and return traffic for those connections +static void initOutboundTCPRule( struct pfioc_rule * pr, + const char * interfaceName, + u_int32_t ticket, + u_int32_t poolTicket, + char * anchorPath ) +{ + memset( pr, 0, sizeof( *pr ) ); + + // Header + initRuleHeader( pr, ticket, poolTicket, anchorPath ); + + // Rule + strlcpy( pr->rule.ifname, interfaceName, sizeof( pr->rule.ifname ) ); + + pr->rule.action = PF_PASS; + pr->rule.direction = PF_OUT; + pr->rule.keep_state = 1; + pr->rule.proto = IPPROTO_TCP; +} + +// allow inbound traffic on the specified port and protocol +static void initPortRule( struct pfioc_rule * pr, + const char * interfaceName, + u_int32_t ticket, + u_int32_t poolTicket, + char * anchorPath, + u_int16_t port, + u_int16_t protocol ) +{ + memset( pr, 0, sizeof( *pr ) ); + + // Header + initRuleHeader( pr, ticket, poolTicket, anchorPath ); + + // Rule + // mDNSResponder passes the port in Network Byte Order, so htons(port) is not required + pr->rule.dst.xport.range.port[0] = port; + pr->rule.dst.xport.range.op = PF_OP_EQ; + + strlcpy( pr->rule.ifname, interfaceName, sizeof( pr->rule.ifname ) ); + + pr->rule.action = PF_PASS; + pr->rule.direction = PF_IN; + pr->rule.keep_state = 1; + pr->rule.af = AF_INET6; + pr->rule.proto = protocol; + pr->rule.extfilter = PF_EXTFILTER_APD; +} + +// allow inbound traffic on the Bonjour port (5353) and the specified port and protocol sets +int P2PPacketFilterAddBonjourRuleSet(const char * interfaceName, u_int32_t count, pfArray_t portArray, pfArray_t protocolArray ) +{ + int result; + u_int32_t i, ticket, poolTicket; + int devFD = -1; + char * anchorPath = MDNS_ANCHOR_PATH; + + result = openPFDevice( &devFD ); + require( result == 0, exit ); + + result = getTicket( devFD, &ticket, anchorPath ); + require( result == 0, exit ); + + result = getPoolTicket( devFD, &poolTicket ); + require( result == 0, exit ); + + struct pfioc_rule pr; + + // allow inbound Bonjour traffice to port 5353 + initBonjourRule( &pr, interfaceName, ticket, poolTicket, anchorPath); + + result = addRule( devFD, &pr ); + require( result == 0, exit ); + + // open inbound port for each service + for (i = 0; i < count; i++) { + initPortRule( &pr, interfaceName, ticket, poolTicket, anchorPath, portArray[i], protocolArray[i] ); + result = addRule( devFD, &pr ); + require( result == 0, exit ); + } + + // allow outbound TCP connections and return traffic for those connections + initOutboundTCPRule( &pr, interfaceName, ticket, poolTicket, anchorPath); + + result = addRule( devFD, &pr ); + require( result == 0, exit ); + + result = commitChange( devFD, ticket, anchorPath ); + require( result == 0, exit ); + +exit: + + if( devFD >= 0 ) + close( devFD ); + + return result; +} + +int P2PPacketFilterClearBonjourRules() +{ + int result; + int pfDev = -1; + u_int32_t ticket; + char * anchorPath = MDNS_ANCHOR_PATH; + + result = openPFDevice( &pfDev ); + require( result == 0, exit ); + + result = getTicket( pfDev, &ticket, anchorPath ); + require( result == 0, exit ); + + result = commitChange( pfDev, ticket, anchorPath ); + +exit: + + if( pfDev >= 0 ) + close( pfDev ); + + return result; +} + diff --git a/mDNSResponder/mDNSMacOSX/P2PPacketFilter.h b/mDNSResponder/mDNSMacOSX/P2PPacketFilter.h new file mode 100644 index 00000000..9c196577 --- /dev/null +++ b/mDNSResponder/mDNSMacOSX/P2PPacketFilter.h @@ -0,0 +1,31 @@ +/* + * + * Copyright (c) 2011 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. + */ + +#ifndef _P2P_PACKET_FILTER_H_ +#define _P2P_PACKET_FILTER_H_ + +#include "helpermsg-types.h" + +enum { + PF_SET_RULES, + PF_CLEAR_RULES +}; + +int P2PPacketFilterAddBonjourRuleSet(const char * interfaceName, u_int32_t count, pfArray_t portArray, pfArray_t protocolArray ); +int P2PPacketFilterClearBonjourRules(void); + +#endif /* _P2P_PACKET_FILTER_H_ */ diff --git a/mDNSResponder/mDNSMacOSX/PreferencePane/Artwork/add_idle.tiff b/mDNSResponder/mDNSMacOSX/PreferencePane/Artwork/add_idle.tiff Binary files differnew file mode 100644 index 00000000..b78b0c2a --- /dev/null +++ b/mDNSResponder/mDNSMacOSX/PreferencePane/Artwork/add_idle.tiff diff --git a/mDNSResponder/mDNSMacOSX/PreferencePane/Artwork/add_pressed.tiff b/mDNSResponder/mDNSMacOSX/PreferencePane/Artwork/add_pressed.tiff Binary files differnew file mode 100644 index 00000000..b842e20c --- /dev/null +++ b/mDNSResponder/mDNSMacOSX/PreferencePane/Artwork/add_pressed.tiff diff --git a/mDNSResponder/mDNSMacOSX/PreferencePane/Artwork/failure.tiff b/mDNSResponder/mDNSMacOSX/PreferencePane/Artwork/failure.tiff Binary files differnew file mode 100644 index 00000000..89a10dfe --- /dev/null +++ b/mDNSResponder/mDNSMacOSX/PreferencePane/Artwork/failure.tiff diff --git a/mDNSResponder/mDNSMacOSX/PreferencePane/Artwork/inprogress.tiff b/mDNSResponder/mDNSMacOSX/PreferencePane/Artwork/inprogress.tiff Binary files differnew file mode 100644 index 00000000..340bb335 --- /dev/null +++ b/mDNSResponder/mDNSMacOSX/PreferencePane/Artwork/inprogress.tiff diff --git a/mDNSResponder/mDNSMacOSX/PreferencePane/Artwork/remove_disabled.tiff b/mDNSResponder/mDNSMacOSX/PreferencePane/Artwork/remove_disabled.tiff Binary files differnew file mode 100644 index 00000000..70d3dd9c --- /dev/null +++ b/mDNSResponder/mDNSMacOSX/PreferencePane/Artwork/remove_disabled.tiff diff --git a/mDNSResponder/mDNSMacOSX/PreferencePane/Artwork/remove_idle.tiff b/mDNSResponder/mDNSMacOSX/PreferencePane/Artwork/remove_idle.tiff Binary files differnew file mode 100644 index 00000000..dacc97c3 --- /dev/null +++ b/mDNSResponder/mDNSMacOSX/PreferencePane/Artwork/remove_idle.tiff diff --git a/mDNSResponder/mDNSMacOSX/PreferencePane/Artwork/remove_pressed.tiff b/mDNSResponder/mDNSMacOSX/PreferencePane/Artwork/remove_pressed.tiff Binary files differnew file mode 100644 index 00000000..de3f8779 --- /dev/null +++ b/mDNSResponder/mDNSMacOSX/PreferencePane/Artwork/remove_pressed.tiff diff --git a/mDNSResponder/mDNSMacOSX/PreferencePane/Artwork/success.tiff b/mDNSResponder/mDNSMacOSX/PreferencePane/Artwork/success.tiff Binary files differnew file mode 100644 index 00000000..21702a9a --- /dev/null +++ b/mDNSResponder/mDNSMacOSX/PreferencePane/Artwork/success.tiff diff --git a/mDNSResponder/mDNSMacOSX/PreferencePane/BonjourPref.icns b/mDNSResponder/mDNSMacOSX/PreferencePane/BonjourPref.icns Binary files differnew file mode 100644 index 00000000..5ba4674e --- /dev/null +++ b/mDNSResponder/mDNSMacOSX/PreferencePane/BonjourPref.icns diff --git a/mDNSResponder/mDNSMacOSX/PreferencePane/BonjourPref.tiff b/mDNSResponder/mDNSMacOSX/PreferencePane/BonjourPref.tiff Binary files differnew file mode 100644 index 00000000..55cb212c --- /dev/null +++ b/mDNSResponder/mDNSMacOSX/PreferencePane/BonjourPref.tiff diff --git a/mDNSResponder/mDNSMacOSX/PreferencePane/ConfigurationAuthority.c b/mDNSResponder/mDNSMacOSX/PreferencePane/ConfigurationAuthority.c new file mode 100644 index 00000000..a2ab5464 --- /dev/null +++ b/mDNSResponder/mDNSMacOSX/PreferencePane/ConfigurationAuthority.c @@ -0,0 +1,180 @@ +/* + File: ConfigurationAuthority.c + + Abstract: Interface to system security framework that manages access + to protected resources like system configuration preferences. + + Copyright: (c) Copyright 2005 Apple Computer, Inc. All rights reserved. + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. + ("Apple") in consideration of your agreement to the following terms, and your + use, installation, modification or redistribution of this Apple software + constitutes acceptance of these terms. If you do not agree with these terms, + please do not use, install, modify or redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and subject + to these terms, Apple grants you a personal, non-exclusive license, under Apple's + copyrights in this original Apple software (the "Apple Software"), to use, + reproduce, modify and redistribute the Apple Software, with or without + modifications, in source and/or binary forms; provided that if you redistribute + the Apple Software in its entirety and without modifications, you must retain + this notice and the following text and disclaimers in all such redistributions of + the Apple Software. Neither the name, trademarks, service marks or logos of + Apple Computer, Inc. may be used to endorse or promote products derived from the + Apple Software without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or implied, + are granted by Apple herein, including but not limited to any patent rights that + may be infringed by your derivative works or by other works in which the Apple + Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO + WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN + COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION + OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT + (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "ConfigurationAuthority.h" +#include "ConfigurationRights.h" + +#include <AssertMacros.h> + + +static AuthorizationRef gAuthRef = 0; + +static AuthorizationItem gAuthorizations[] = { { UPDATE_SC_RIGHT, 0, NULL, 0 }, + { EDIT_SYS_KEYCHAIN_RIGHT, 0, NULL, 0 }}; +static AuthorizationRights gAuthSet = { sizeof gAuthorizations / sizeof gAuthorizations[0], gAuthorizations }; + +static CFDictionaryRef CreateRightsDict( CFStringRef prompt) +/* Create a CFDictionary decribing an auth right. See /etc/authorization for examples. */ +/* Specifies that the right requires admin authentication, which persists for 5 minutes. */ +{ + CFMutableDictionaryRef dict = NULL, tmpDict; + CFMutableArrayRef mechanisms; + CFNumberRef timeout; + int val; + + tmpDict = CFDictionaryCreateMutable( (CFAllocatorRef) NULL, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + require( tmpDict != NULL, MakeDictFailed); + + CFDictionaryAddValue(tmpDict, CFSTR("class"), CFSTR("user")); + CFDictionaryAddValue(tmpDict, CFSTR("comment"), prompt); + CFDictionaryAddValue(tmpDict, CFSTR("group"), CFSTR("admin")); + + mechanisms = CFArrayCreateMutable((CFAllocatorRef) NULL, 1, &kCFTypeArrayCallBacks); + require( mechanisms != NULL, MakeArrayFailed); + CFArrayAppendValue( mechanisms, CFSTR("builtin:authenticate")); + CFDictionaryAddValue( tmpDict, CFSTR("mechanisms"), mechanisms); + + val = 300; // seconds + timeout = CFNumberCreate((CFAllocatorRef) NULL, kCFNumberIntType, &val); + require( timeout != NULL, MakeIntFailed); + CFDictionaryAddValue( tmpDict, CFSTR("timeout"), timeout); + CFDictionaryAddValue( tmpDict, CFSTR("shared"), kCFBooleanTrue); + + dict = tmpDict; + tmpDict = NULL; + + CFRelease( timeout); +MakeIntFailed: + CFRelease( mechanisms); +MakeArrayFailed: + if ( tmpDict) + CFRelease( tmpDict); +MakeDictFailed: + return dict; +} + +OSStatus InitConfigAuthority(void) +/* Initialize the authorization record-keeping */ +{ + OSStatus err; + CFDictionaryRef dict; + CFStringRef rightInfo; + + err = AuthorizationCreate((AuthorizationRights*) NULL, (AuthorizationEnvironment*) NULL, + (AuthorizationFlags) 0, &gAuthRef); + require_noerr( err, NewAuthFailed); + + err = AuthorizationRightGet( UPDATE_SC_RIGHT, (CFDictionaryRef*) NULL); + if (err == errAuthorizationDenied) + { + rightInfo = CFCopyLocalizedString(CFSTR("Authentication required to set Dynamic DNS preferences."), + CFSTR("Describes operation that requires user authorization")); + require_action( rightInfo != NULL, GetStrFailed, err=coreFoundationUnknownErr;); + dict = CreateRightsDict(rightInfo); + require_action( dict != NULL, GetStrFailed, err=coreFoundationUnknownErr;); + + err = AuthorizationRightSet(gAuthRef, UPDATE_SC_RIGHT, dict, (CFStringRef) NULL, + (CFBundleRef) NULL, (CFStringRef) NULL); + CFRelease(rightInfo); + CFRelease(dict); + } + require_noerr( err, AuthSetFailed); + + err = AuthorizationRightGet( EDIT_SYS_KEYCHAIN_RIGHT, (CFDictionaryRef*) NULL); + if (err == errAuthorizationDenied) + { + rightInfo = CFCopyLocalizedString( CFSTR("Authentication required to edit System Keychain."), + CFSTR("Describes operation that requires user authorization")); + require_action( rightInfo != NULL, GetStrFailed, err=coreFoundationUnknownErr;); + dict = CreateRightsDict( rightInfo); + require_action( dict != NULL, GetStrFailed, err=coreFoundationUnknownErr;); + + err = AuthorizationRightSet(gAuthRef, EDIT_SYS_KEYCHAIN_RIGHT, dict, (CFStringRef) NULL, + (CFBundleRef) NULL, (CFStringRef) NULL); + CFRelease( rightInfo); + CFRelease( dict); + } + require_noerr( err, AuthSetFailed); + +AuthSetFailed: +GetStrFailed: +NewAuthFailed: + return err; +} + +OSStatus AttemptAcquireAuthority( Boolean allowUI) +/* Try to get permission for privileged ops, either implicitly or by asking the user for */ +/* authority to perform operations (if necessary) */ +{ + AuthorizationFlags allowFlag = allowUI ? kAuthorizationFlagInteractionAllowed : 0; + OSStatus err; + + err = AuthorizationCopyRights( gAuthRef, &gAuthSet, (AuthorizationEnvironment*) NULL, + kAuthorizationFlagExtendRights | kAuthorizationFlagPreAuthorize | + allowFlag, + (AuthorizationRights**) NULL); + return err; +} + +OSStatus ReleaseAuthority(void) +/* Discard authority to perform operations */ +{ + (void) AuthorizationFree( gAuthRef, kAuthorizationFlagDefaults); + gAuthRef = 0; + return AuthorizationCreate( (AuthorizationRights*) NULL, (AuthorizationEnvironment*) NULL, + (AuthorizationFlags) 0, &gAuthRef); +} + +Boolean CurrentlyAuthorized(void) +{ + OSStatus err = AttemptAcquireAuthority(true); + return err == noErr; +} + + +OSStatus ExternalizeAuthority(AuthorizationExternalForm *pAuth) +/* Package up current authorizations for transfer to another process */ +{ + return AuthorizationMakeExternalForm(gAuthRef, pAuth); +} diff --git a/mDNSResponder/mDNSMacOSX/PreferencePane/ConfigurationAuthority.h b/mDNSResponder/mDNSMacOSX/PreferencePane/ConfigurationAuthority.h new file mode 100644 index 00000000..49da93d0 --- /dev/null +++ b/mDNSResponder/mDNSMacOSX/PreferencePane/ConfigurationAuthority.h @@ -0,0 +1,52 @@ +/* + File: ConfigurationAuthority.h + + Abstract: Interface to system security framework that manages access + to protected resources like system configuration preferences. + + Copyright: (c) Copyright 2005 Apple Computer, Inc. All rights reserved. + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. + ("Apple") in consideration of your agreement to the following terms, and your + use, installation, modification or redistribution of this Apple software + constitutes acceptance of these terms. If you do not agree with these terms, + please do not use, install, modify or redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and subject + to these terms, Apple grants you a personal, non-exclusive license, under Apple's + copyrights in this original Apple software (the "Apple Software"), to use, + reproduce, modify and redistribute the Apple Software, with or without + modifications, in source and/or binary forms; provided that if you redistribute + the Apple Software in its entirety and without modifications, you must retain + this notice and the following text and disclaimers in all such redistributions of + the Apple Software. Neither the name, trademarks, service marks or logos of + Apple Computer, Inc. may be used to endorse or promote products derived from the + Apple Software without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or implied, + are granted by Apple herein, including but not limited to any patent rights that + may be infringed by your derivative works or by other works in which the Apple + Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO + WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN + COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION + OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT + (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <CoreServices/CoreServices.h> +#include <Security/Security.h> + +OSStatus InitConfigAuthority(void); +Boolean CurrentlyAuthorized(void); +OSStatus AttemptAcquireAuthority(Boolean allowUI); +OSStatus ReleaseAuthority(void); +OSStatus ExternalizeAuthority(AuthorizationExternalForm *pAuth); diff --git a/mDNSResponder/mDNSMacOSX/PreferencePane/ConfigurationRights.h b/mDNSResponder/mDNSMacOSX/PreferencePane/ConfigurationRights.h new file mode 100644 index 00000000..44379c67 --- /dev/null +++ b/mDNSResponder/mDNSMacOSX/PreferencePane/ConfigurationRights.h @@ -0,0 +1,49 @@ +/* + File: ConfigurationRights.h + + Abstract: Defines the rights we need, namely, (i) the right to write to + the system configuration settings for Dynamic DNS and Wide-Area DNS-SD, + and (ii) the right to write to the system keychain to store Dynamic DNS + shared secrets used to perform authorized updates to DDNS servers. + Right now these are both actually the same right: "system.preferences" + + Copyright: (c) Copyright 2005 Apple Computer, Inc. All rights reserved. + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. + ("Apple") in consideration of your agreement to the following terms, and your + use, installation, modification or redistribution of this Apple software + constitutes acceptance of these terms. If you do not agree with these terms, + please do not use, install, modify or redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and subject + to these terms, Apple grants you a personal, non-exclusive license, under Apple's + copyrights in this original Apple software (the "Apple Software"), to use, + reproduce, modify and redistribute the Apple Software, with or without + modifications, in source and/or binary forms; provided that if you redistribute + the Apple Software in its entirety and without modifications, you must retain + this notice and the following text and disclaimers in all such redistributions of + the Apple Software. Neither the name, trademarks, service marks or logos of + Apple Computer, Inc. may be used to endorse or promote products derived from the + Apple Software without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or implied, + are granted by Apple herein, including but not limited to any patent rights that + may be infringed by your derivative works or by other works in which the Apple + Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO + WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN + COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION + OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT + (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define UPDATE_SC_RIGHT "system.preferences" +#define EDIT_SYS_KEYCHAIN_RIGHT "system.preferences" diff --git a/mDNSResponder/mDNSMacOSX/PreferencePane/DNSServiceDiscoveryPref.h b/mDNSResponder/mDNSMacOSX/PreferencePane/DNSServiceDiscoveryPref.h new file mode 100644 index 00000000..f0586591 --- /dev/null +++ b/mDNSResponder/mDNSMacOSX/PreferencePane/DNSServiceDiscoveryPref.h @@ -0,0 +1,170 @@ +/* + File: DNSServiceDiscoveryPref.h + + Abstract: System Preference Pane for Dynamic DNS and Wide-Area DNS Service Discovery + + Copyright: (c) Copyright 2005 Apple Computer, Inc. All rights reserved. + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. + ("Apple") in consideration of your agreement to the following terms, and your + use, installation, modification or redistribution of this Apple software + constitutes acceptance of these terms. If you do not agree with these terms, + please do not use, install, modify or redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and subject + to these terms, Apple grants you a personal, non-exclusive license, under Apple's + copyrights in this original Apple software (the "Apple Software"), to use, + reproduce, modify and redistribute the Apple Software, with or without + modifications, in source and/or binary forms; provided that if you redistribute + the Apple Software in its entirety and without modifications, you must retain + this notice and the following text and disclaimers in all such redistributions of + the Apple Software. Neither the name, trademarks, service marks or logos of + Apple Computer, Inc. may be used to endorse or promote products derived from the + Apple Software without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or implied, + are granted by Apple herein, including but not limited to any patent rights that + may be infringed by your derivative works or by other works in which the Apple + Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO + WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN + COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION + OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT + (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import <Cocoa/Cocoa.h> +#import <PreferencePanes/PreferencePanes.h> +#import <CoreFoundation/CoreFoundation.h> +#import <SecurityInterface/SFAuthorizationView.h> +#import <SystemConfiguration/SystemConfiguration.h> +#import <dns_sd.h> + +typedef struct MyDNSServiceState { + DNSServiceRef service; + CFRunLoopSourceRef source; + CFSocketRef socket; +} MyDNSServiceState; + + +@interface DNSServiceDiscoveryPref : NSPreferencePane +{ + IBOutlet NSTextField *hostName; + IBOutlet NSTextField *sharedSecretName; + IBOutlet NSSecureTextField *sharedSecretValue; + IBOutlet NSComboBox *browseDomainsComboBox; + IBOutlet NSComboBox *regDomainsComboBox; + IBOutlet NSButton *wideAreaCheckBox; + IBOutlet NSButton *hostNameSharedSecretButton; + IBOutlet NSButton *registrationSharedSecretButton; + IBOutlet NSButton *applyButton; + IBOutlet NSButton *revertButton; + IBOutlet NSWindow *sharedSecretWindow; + IBOutlet NSWindow *addBrowseDomainWindow; + IBOutlet NSButton *addBrowseDomainButton; + IBOutlet NSButton *removeBrowseDomainButton; + IBOutlet NSButton *browseOKButton; + IBOutlet NSButton *browseCancelButton; + IBOutlet NSButton *secretOKButton; + IBOutlet NSButton *secretCancelButton; + IBOutlet NSImageView *statusImageView; + IBOutlet NSTabView *tabView; + IBOutlet NSTableView *browseDomainList; + IBOutlet SFAuthorizationView *comboAuthButton; + + NSWindow *mainWindow; + NSString *currentHostName; + NSString *currentRegDomain; + NSArray *currentBrowseDomainsArray; + NSMutableArray *browseDomainsArray; + NSMutableArray *defaultBrowseDomainsArray; + NSString *defaultRegDomain; + + NSString *hostNameSharedSecretName; + NSString *hostNameSharedSecretValue; + NSString *regSharedSecretName; + NSString *regSharedSecretValue; + BOOL currentWideAreaState; + BOOL prefsNeedUpdating; + BOOL toolInstalled; + BOOL browseDomainListEnabled; + BOOL justStartedEditing; + NSImage *successImage; + NSImage *inprogressImage; + NSImage *failureImage; + + MyDNSServiceState regQuery; + MyDNSServiceState browseQuery; + NSMutableArray *browseDataSource; + NSMutableArray *registrationDataSource; +} + +-(IBAction)applyClicked : (id)sender; +-(IBAction)enableBrowseDomainClicked : (id)sender; +-(IBAction)addBrowseDomainClicked : (id)sender; +-(IBAction)removeBrowseDomainClicked : (id)sender; +-(IBAction)revertClicked : (id)sender; +-(IBAction)changeButtonPressed : (id)sender; +-(IBAction)closeMyCustomSheet : (id)sender; +-(IBAction)comboAction : (id)sender; +-(IBAction)wideAreaCheckBoxChanged : (id)sender; + + +-(NSMutableArray *)browseDataSource; +-(NSMutableArray *)registrationDataSource; +-(NSComboBox *)browseDomainsComboBox; +-(NSComboBox *)regDomainsComboBox; +-(NSString *)currentRegDomain; +-(NSMutableArray *)defaultBrowseDomainsArray; +-(NSArray *)currentBrowseDomainsArray; +-(NSString *)currentHostName; +-(NSString *)defaultRegDomain; +-(void)setDefaultRegDomain : (NSString *)domain; + + + +-(void)enableApplyButton; +-(void)disableApplyButton; +-(void)applyCurrentState; +-(void)setBrowseDomainsComboBox; +-(void)setupInitialValues; +-(void)startDomainBrowsing; +-(void)toggleWideAreaBonjour : (BOOL)state; +-(void)updateApplyButtonState; +-(void)enableControls; +-(void)disableControls; +-(void)validateTextFields; +-(void)readPreferences; +-(void)savePreferences; +-(void)restorePreferences; +-(void)watchForPreferenceChanges; +-(void)updateStatusImageView; + + +-(NSString *)sharedSecretKeyName : (NSString * )domain; +-(NSString *)domainForHostName : (NSString *)hostNameString; +-(int)statusForHostName : (NSString * )domain; +-(NSData *)dataForDomainArray : (NSArray *)domainArray; +-(NSData *)dataForDomain : (NSString *)domainName isEnabled : (BOOL)enabled; +-(NSData *)dataForSharedSecret : (NSString *)secret domain : (NSString *)domainName key : (NSString *)keyName; +-(BOOL)domainAlreadyInList : (NSString *)domainString; +-(NSString *)trimCharactersFromDomain : (NSString *)domain; + + +// Delegate methods +-(void)authorizationViewDidAuthorize : (SFAuthorizationView *)view; +-(void)authorizationViewDidDeauthorize : (SFAuthorizationView *)view; +-(void)mainViewDidLoad; +-(int)numberOfItemsInComboBox : (NSComboBox *)aComboBox; +-(id)comboBox : (NSComboBox *)aComboBox objectValueForItemAtIndex : (int)index; +-(void)controlTextDidChange : (NSNotification *) notification; + +@end diff --git a/mDNSResponder/mDNSMacOSX/PreferencePane/DNSServiceDiscoveryPref.m b/mDNSResponder/mDNSMacOSX/PreferencePane/DNSServiceDiscoveryPref.m new file mode 100644 index 00000000..063bc724 --- /dev/null +++ b/mDNSResponder/mDNSMacOSX/PreferencePane/DNSServiceDiscoveryPref.m @@ -0,0 +1,1194 @@ +/* + File: DNSServiceDiscoveryPref.m + + Abstract: System Preference Pane for Dynamic DNS and Wide-Area DNS Service Discovery + + Copyright: (c) Copyright 2005-2011 Apple Computer, Inc. All rights reserved. + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. + ("Apple") in consideration of your agreement to the following terms, and your + use, installation, modification or redistribution of this Apple software + constitutes acceptance of these terms. If you do not agree with these terms, + please do not use, install, modify or redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and subject + to these terms, Apple grants you a personal, non-exclusive license, under Apple's + copyrights in this original Apple software (the "Apple Software"), to use, + reproduce, modify and redistribute the Apple Software, with or without + modifications, in source and/or binary forms; provided that if you redistribute + the Apple Software in its entirety and without modifications, you must retain + this notice and the following text and disclaimers in all such redistributions of + the Apple Software. Neither the name, trademarks, service marks or logos of + Apple Computer, Inc. may be used to endorse or promote products derived from the + Apple Software without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or implied, + are granted by Apple herein, including but not limited to any patent rights that + may be infringed by your derivative works or by other works in which the Apple + Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO + WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN + COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION + OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT + (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import "DNSServiceDiscoveryPref.h" +#import "ConfigurationAuthority.h" +#import "PrivilegedOperations.h" +#import <unistd.h> + +#include "../../Clients/ClientCommon.h" + +#ifndef NSINTEGER_DEFINED +#define NSInteger int +#endif + +@implementation DNSServiceDiscoveryPref + +static NSInteger +MyArrayCompareFunction(id val1, id val2, void *context) +{ + (void)context; // Unused + return CFStringCompare((CFStringRef)val1, (CFStringRef)val2, kCFCompareCaseInsensitive); +} + +static NSInteger +MyDomainArrayCompareFunction(id val1, id val2, void *context) +{ + (void)context; // Unused + NSString *domain1 = [val1 objectForKey:(NSString *)SC_DYNDNS_DOMAIN_KEY]; + NSString *domain2 = [val2 objectForKey:(NSString *)SC_DYNDNS_DOMAIN_KEY]; + return CFStringCompare((CFStringRef)domain1, (CFStringRef)domain2, kCFCompareCaseInsensitive); +} + + +static void NetworkChanged(SCDynamicStoreRef store, CFArrayRef changedKeys, void *context) +{ + (void)store; // Unused + (void)changedKeys; // Unused + DNSServiceDiscoveryPref * me = (DNSServiceDiscoveryPref *)context; + assert(me != NULL); + + [me setupInitialValues]; +} + + +static void ServiceDomainEnumReply( DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, + DNSServiceErrorType errorCode, const char *replyDomain, void *context, DNSServiceFlags enumType) +{ + (void)sdRef; // Unused + (void)interfaceIndex; // Unused + (void)errorCode; // Unused + if (strcmp(replyDomain, "local.") == 0) return; // local domain is not interesting + + DNSServiceDiscoveryPref * me = (DNSServiceDiscoveryPref *)context; + BOOL moreComing = (BOOL)(flags & kDNSServiceFlagsMoreComing); + NSMutableArray * domainArray; + NSMutableArray * defaultBrowseDomainsArray = nil; + NSComboBox * domainComboBox; + NSString * domainString; + NSString * currentDomain = nil; + char decodedDomainString[kDNSServiceMaxDomainName] = "\0"; + char nextLabel[256] = "\0"; + char * buffer = (char *)replyDomain; + + while (*buffer) { + buffer = (char *)GetNextLabel(buffer, nextLabel); + strcat(decodedDomainString, nextLabel); + strcat(decodedDomainString, "."); + } + + // Remove trailing dot from domain name. + decodedDomainString[strlen(decodedDomainString)-1] = '\0'; + + domainString = [[[NSString alloc] initWithUTF8String:(const char *)decodedDomainString] autorelease]; + + if (enumType & kDNSServiceFlagsRegistrationDomains) { + domainArray = [me registrationDataSource]; + domainComboBox = [me regDomainsComboBox]; + currentDomain = [me currentRegDomain]; + } else { + domainArray = [me browseDataSource]; + domainComboBox = [me browseDomainsComboBox]; + defaultBrowseDomainsArray = [me defaultBrowseDomainsArray]; + } + + if (flags & kDNSServiceFlagsAdd) { + [domainArray removeObject:domainString]; // How can I check if an object is in the array? + [domainArray addObject:domainString]; + if ((flags & kDNSServiceFlagsDefault) && (enumType & kDNSServiceFlagsRegistrationDomains)) { + [me setDefaultRegDomain:domainString]; + if ([[domainComboBox stringValue] length] == 0) [domainComboBox setStringValue:domainString]; + } else if ((flags & kDNSServiceFlagsDefault) && !(enumType & kDNSServiceFlagsRegistrationDomains)) { + [defaultBrowseDomainsArray removeObject:domainString]; + [defaultBrowseDomainsArray addObject:domainString]; + } + } + + if (moreComing == NO) { + [domainArray sortUsingFunction:MyArrayCompareFunction context:nil]; + [domainComboBox reloadData]; + } +} + + +static void +browseDomainReply(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, + DNSServiceErrorType errorCode, const char *replyDomain, void *context) +{ + ServiceDomainEnumReply(sdRef, flags, interfaceIndex, errorCode, replyDomain, context, kDNSServiceFlagsBrowseDomains); +} + + +static void +registrationDomainReply(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, + DNSServiceErrorType errorCode, const char *replyDomain, void *context) +{ + ServiceDomainEnumReply(sdRef, flags, interfaceIndex, errorCode, replyDomain, context, kDNSServiceFlagsRegistrationDomains); +} + + + +static void +MyDNSServiceCleanUp(MyDNSServiceState * query) +{ + /* Remove the CFRunLoopSource from the current run loop. */ + CFRunLoopRemoveSource(CFRunLoopGetCurrent(), query->source, kCFRunLoopCommonModes); + CFRelease(query->source); + + /* Invalidate the CFSocket. */ + CFSocketInvalidate(query->socket); + CFRelease(query->socket); + + /* Workaround that gives time to CFSocket's select thread so it can remove the socket from its FD set + before we close the socket by calling DNSServiceRefDeallocate. <rdar://problem/3585273> */ + usleep(1000); + + /* Terminate the connection with the mDNSResponder daemon, which cancels the query. */ + DNSServiceRefDeallocate(query->service); +} + + + +static void +MySocketReadCallback(CFSocketRef s, CFSocketCallBackType type, CFDataRef address, const void * data, void * info) +{ + #pragma unused(s) + #pragma unused(type) + #pragma unused(address) + #pragma unused(data) + + DNSServiceErrorType err; + + MyDNSServiceState * query = (MyDNSServiceState *)info; // context passed in to CFSocketCreateWithNative(). + assert(query != NULL); + + /* Read a reply from the mDNSResponder. */ + err= DNSServiceProcessResult(query->service); + if (err != kDNSServiceErr_NoError) { + fprintf(stderr, "DNSServiceProcessResult returned %d\n", err); + + /* Terminate the query operation and release the CFRunLoopSource and CFSocket. */ + MyDNSServiceCleanUp(query); + } +} + + + +static void +MyDNSServiceAddServiceToRunLoop(MyDNSServiceState * query) +{ + CFSocketNativeHandle sock; + CFOptionFlags sockFlags; + CFSocketContext context = { 0, query, NULL, NULL, NULL }; // Use MyDNSServiceState as context data. + + /* Access the underlying Unix domain socket to communicate with the mDNSResponder daemon. */ + sock = DNSServiceRefSockFD(query->service); + assert(sock != -1); + + /* Create a CFSocket using the Unix domain socket. */ + query->socket = CFSocketCreateWithNative(NULL, sock, kCFSocketReadCallBack, MySocketReadCallback, &context); + assert(query->socket != NULL); + + /* Prevent CFSocketInvalidate from closing DNSServiceRef's socket. */ + sockFlags = CFSocketGetSocketFlags(query->socket); + CFSocketSetSocketFlags(query->socket, sockFlags & (~kCFSocketCloseOnInvalidate)); + + /* Create a CFRunLoopSource from the CFSocket. */ + query->source = CFSocketCreateRunLoopSource(NULL, query->socket, 0); + assert(query->source != NULL); + + /* Add the CFRunLoopSource to the current run loop. */ + CFRunLoopAddSource(CFRunLoopGetCurrent(), query->source, kCFRunLoopCommonModes); +} + + + +-(void)updateStatusImageView +{ + int value = [self statusForHostName:currentHostName]; + if (value == 0) [statusImageView setImage:successImage]; + else if (value > 0) [statusImageView setImage:inprogressImage]; + else [statusImageView setImage:failureImage]; +} + + +- (void)watchForPreferenceChanges +{ + SCDynamicStoreContext context = { 0, self, NULL, NULL, NULL }; + SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("watchForPreferenceChanges"), NetworkChanged, &context); + CFMutableArrayRef keys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); + CFRunLoopSourceRef rls; + + assert(store != NULL); + assert(keys != NULL); + + CFArrayAppendValue(keys, SC_DYNDNS_STATE_KEY); + CFArrayAppendValue(keys, SC_DYNDNS_SETUP_KEY); + + (void)SCDynamicStoreSetNotificationKeys(store, keys, NULL); + + rls = SCDynamicStoreCreateRunLoopSource(NULL, store, 0); + assert(rls != NULL); + + CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopCommonModes); + + CFRelease(keys); + CFRelease(store); +} + + +-(int)statusForHostName:(NSString * )domain +{ + SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("statusForHostName"), NULL, NULL); + NSString *lowercaseDomain = [domain lowercaseString]; + int status = 1; + + assert(store != NULL); + + NSDictionary *dynamicDNS = (NSDictionary *)SCDynamicStoreCopyValue(store, SC_DYNDNS_STATE_KEY); + if (dynamicDNS) { + NSDictionary *hostNames = [dynamicDNS objectForKey:(NSString *)SC_DYNDNS_HOSTNAMES_KEY]; + NSDictionary *infoDict = [hostNames objectForKey:lowercaseDomain]; + if (infoDict) status = [[infoDict objectForKey:(NSString*)SC_DYNDNS_STATUS_KEY] intValue]; + CFRelease(dynamicDNS); + } + CFRelease(store); + + return status; +} + + +- (void)startDomainBrowsing +{ + DNSServiceFlags flags; + OSStatus err = noErr; + + flags = kDNSServiceFlagsRegistrationDomains; + err = DNSServiceEnumerateDomains(®Query.service, flags, 0, registrationDomainReply, (void *)self); + if (err == kDNSServiceErr_NoError) MyDNSServiceAddServiceToRunLoop(®Query); + + flags = kDNSServiceFlagsBrowseDomains; + err = DNSServiceEnumerateDomains(&browseQuery.service, flags, 0, browseDomainReply, (void *)self); + if (err == kDNSServiceErr_NoError) MyDNSServiceAddServiceToRunLoop(&browseQuery); +} + + +-(void)readPreferences +{ + NSDictionary *origDict; + NSArray *regDomainArray; + NSArray *hostArray; + + if (currentRegDomain) [currentRegDomain release]; + if (currentBrowseDomainsArray) [currentBrowseDomainsArray release]; + if (currentHostName) [currentHostName release]; + + SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("com.apple.preference.bonjour"), NULL, NULL); + origDict = (NSDictionary *)SCDynamicStoreCopyValue(store, SC_DYNDNS_SETUP_KEY); + + regDomainArray = [origDict objectForKey:(NSString *)SC_DYNDNS_REGDOMAINS_KEY]; + if (regDomainArray && [regDomainArray count] > 0) { + currentRegDomain = [[[regDomainArray objectAtIndex:0] objectForKey:(NSString *)SC_DYNDNS_DOMAIN_KEY] copy]; + currentWideAreaState = [[[regDomainArray objectAtIndex:0] objectForKey:(NSString *)SC_DYNDNS_ENABLED_KEY] intValue]; + } else { + currentRegDomain = [[NSString alloc] initWithString:@""]; + currentWideAreaState = NO; + } + + currentBrowseDomainsArray = [[origDict objectForKey:(NSString *)SC_DYNDNS_BROWSEDOMAINS_KEY] retain]; + + hostArray = [origDict objectForKey:(NSString *)SC_DYNDNS_HOSTNAMES_KEY]; + if (hostArray && [hostArray count] > 0) { + currentHostName = [[[hostArray objectAtIndex:0] objectForKey:(NSString *)SC_DYNDNS_DOMAIN_KEY] copy]; + } else { + currentHostName = [[NSString alloc] initWithString:@""]; + } + + [origDict release]; + CFRelease(store); +} + + +- (void)tableViewSelectionDidChange:(NSNotification *)notification +{ + [removeBrowseDomainButton setEnabled:[[notification object] numberOfSelectedRows]]; +} + + +- (void)setBrowseDomainsComboBox +{ + NSString * domain = nil; + + if ([defaultBrowseDomainsArray count] > 0) { + NSEnumerator * arrayEnumerator = [defaultBrowseDomainsArray objectEnumerator]; + while ((domain = [arrayEnumerator nextObject]) != NULL) { + if ([self domainAlreadyInList:domain] == NO) break; + } + } + if (domain) [browseDomainsComboBox setStringValue:domain]; + else [browseDomainsComboBox setStringValue:@""]; +} + + +- (IBAction)addBrowseDomainClicked:(id)sender +{ + [self setBrowseDomainsComboBox]; + + [NSApp beginSheet:addBrowseDomainWindow modalForWindow:mainWindow modalDelegate:self + didEndSelector:@selector(addBrowseDomainSheetDidEnd:returnCode:contextInfo:) contextInfo:sender]; + + [browseDomainList deselectAll:sender]; + [self updateApplyButtonState]; +} + + +- (IBAction)removeBrowseDomainClicked:(id)sender +{ + (void)sender; // Unused + int selectedBrowseDomain = [browseDomainList selectedRow]; + [browseDomainsArray removeObjectAtIndex:selectedBrowseDomain]; + [browseDomainList reloadData]; + [self updateApplyButtonState]; +} + + +- (IBAction)enableBrowseDomainClicked:(id)sender +{ + NSTableView *tableView = sender; + NSMutableDictionary *browseDomainDict; + int value; + + browseDomainDict = [[browseDomainsArray objectAtIndex:[tableView clickedRow]] mutableCopy]; + value = [[browseDomainDict objectForKey:(NSString *)SC_DYNDNS_ENABLED_KEY] intValue]; + [browseDomainDict setObject:[[[NSNumber alloc] initWithInt:(!value)] autorelease] forKey:(NSString *)SC_DYNDNS_ENABLED_KEY]; + [browseDomainsArray replaceObjectAtIndex:[tableView clickedRow] withObject:browseDomainDict]; + [tableView reloadData]; + [self updateApplyButtonState]; +} + + + +- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView +{ + (void)tableView; // Unused + int numberOfRows = 0; + + if (browseDomainsArray) { + numberOfRows = [browseDomainsArray count]; + } + return numberOfRows; +} + + +- (void)tabView:(NSTabView *)xtabView didSelectTabViewItem:(NSTabViewItem *)tabViewItem +{ + (void)xtabView; // Unused + (void)tabViewItem; // Unused + [browseDomainList deselectAll:self]; + [mainWindow makeFirstResponder:nil]; +} + + +- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row +{ + (void)tableView; // Unused + NSDictionary *browseDomainDict; + id value = nil; + + if (browseDomainsArray) { + browseDomainDict = [browseDomainsArray objectAtIndex:row]; + if (browseDomainDict) { + if ([[tableColumn identifier] isEqualTo:(NSString *)SC_DYNDNS_ENABLED_KEY]) { + value = [browseDomainDict objectForKey:(NSString *)SC_DYNDNS_ENABLED_KEY]; + } else if ([[tableColumn identifier] isEqualTo:(NSString *)SC_DYNDNS_DOMAIN_KEY]) { + value = [browseDomainDict objectForKey:(NSString *)SC_DYNDNS_DOMAIN_KEY]; + } + } + } + return value; +} + + +- (void)setupInitialValues +{ + [self readPreferences]; + + if (currentHostName) { + [hostName setStringValue:currentHostName]; + [self updateStatusImageView]; + } + + if (browseDomainsArray) { + [browseDomainsArray release]; + browseDomainsArray = nil; + } + + if (currentBrowseDomainsArray) { + browseDomainsArray = [currentBrowseDomainsArray mutableCopy]; + if (browseDomainsArray) { + [browseDomainsArray sortUsingFunction:MyDomainArrayCompareFunction context:nil]; + if ([browseDomainsArray isEqualToArray:currentBrowseDomainsArray] == NO) { + OSStatus err = WriteBrowseDomain((CFDataRef)[self dataForDomainArray:browseDomainsArray]); + if (err != noErr) NSLog(@"WriteBrowseDomain returned %d\n", (int32_t)err); + [currentBrowseDomainsArray release]; + currentBrowseDomainsArray = [browseDomainsArray copy]; + } + } + } else { + browseDomainsArray = nil; + } + [browseDomainList reloadData]; + + if (currentRegDomain && ([currentRegDomain length] > 0)) { + [regDomainsComboBox setStringValue:currentRegDomain]; + [registrationDataSource removeObject:currentRegDomain]; + [registrationDataSource addObject:currentRegDomain]; + [registrationDataSource sortUsingFunction:MyArrayCompareFunction context:nil]; + [regDomainsComboBox reloadData]; + } + + if (currentWideAreaState) { + [self toggleWideAreaBonjour:YES]; + } else { + [self toggleWideAreaBonjour:NO]; + } + + if (hostNameSharedSecretValue) { + [hostNameSharedSecretValue release]; + hostNameSharedSecretValue = nil; + } + + if (regSharedSecretValue) { + [regSharedSecretValue release]; + regSharedSecretValue = nil; + } + + [self updateApplyButtonState]; + [mainWindow makeFirstResponder:nil]; + [browseDomainList deselectAll:self]; + [removeBrowseDomainButton setEnabled:NO]; +} + + + +- (void)awakeFromNib +{ + OSStatus err; + + prefsNeedUpdating = NO; + toolInstalled = NO; + browseDomainListEnabled = NO; + defaultRegDomain = nil; + currentRegDomain = nil; + currentBrowseDomainsArray = nil; + currentHostName = nil; + hostNameSharedSecretValue = nil; + regSharedSecretValue = nil; + browseDomainsArray = nil; + justStartedEditing = YES; + currentWideAreaState = NO; + NSString *successPath = [[NSBundle bundleForClass:[self class]] pathForResource:@"success" ofType:@"tiff"]; + NSString *inprogressPath = [[NSBundle bundleForClass:[self class]] pathForResource:@"inprogress" ofType:@"tiff"]; + NSString *failurePath = [[NSBundle bundleForClass:[self class]] pathForResource:@"failure" ofType:@"tiff"]; + + registrationDataSource = [[NSMutableArray alloc] init]; + browseDataSource = [[NSMutableArray alloc] init]; + defaultBrowseDomainsArray = [[NSMutableArray alloc] init]; + successImage = [[NSImage alloc] initWithContentsOfFile:successPath]; + inprogressImage = [[NSImage alloc] initWithContentsOfFile:inprogressPath]; + failureImage = [[NSImage alloc] initWithContentsOfFile:failurePath]; + + [tabView selectFirstTabViewItem:self]; + [self setupInitialValues]; + [self startDomainBrowsing]; + [self watchForPreferenceChanges]; + + InitConfigAuthority(); + err = EnsureToolInstalled(); + if (err == noErr) toolInstalled = YES; + else { long int tmp = err; fprintf(stderr, "EnsureToolInstalled returned %ld\n", tmp); } + +} + + +- (IBAction)closeMyCustomSheet:(id)sender +{ + BOOL result = [sender isEqualTo:browseOKButton] || [sender isEqualTo:secretOKButton]; + + if (result) [NSApp endSheet:[sender window] returnCode:NSOKButton]; + else [NSApp endSheet:[sender window] returnCode:NSCancelButton]; +} + + +- (void)sharedSecretSheetDidEnd:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo +{ + NSButton * button = (NSButton *)contextInfo; + [sheet orderOut:self]; + [self enableControls]; + + if (returnCode == NSOKButton) { + if ([button isEqualTo:hostNameSharedSecretButton]) { + hostNameSharedSecretName = [[NSString alloc] initWithString:[sharedSecretName stringValue]]; + hostNameSharedSecretValue = [[NSString alloc] initWithString:[sharedSecretValue stringValue]]; + } else { + regSharedSecretName = [[NSString alloc] initWithString:[sharedSecretName stringValue]]; + regSharedSecretValue = [[NSString alloc] initWithString:[sharedSecretValue stringValue]]; + } + [self updateApplyButtonState]; + } + [sharedSecretValue setStringValue:@""]; +} + + +- (BOOL)domainAlreadyInList:(NSString *)domainString +{ + if (browseDomainsArray) { + NSDictionary *domainDict; + NSString *domainName; + NSEnumerator *arrayEnumerator = [browseDomainsArray objectEnumerator]; + while ((domainDict = [arrayEnumerator nextObject]) != NULL) { + domainName = [domainDict objectForKey:(NSString *)SC_DYNDNS_DOMAIN_KEY]; + if ([domainString caseInsensitiveCompare:domainName] == NSOrderedSame) return YES; + } + } + return NO; +} + + +- (NSString *)trimCharactersFromDomain:(NSString *)domain +{ + NSMutableCharacterSet * trimSet = [[[NSCharacterSet whitespaceCharacterSet] mutableCopy] autorelease]; + [trimSet formUnionWithCharacterSet:[NSCharacterSet punctuationCharacterSet]]; + return [domain stringByTrimmingCharactersInSet:trimSet]; +} + + +- (void)addBrowseDomainSheetDidEnd:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo +{ + (void)contextInfo; // Unused + [sheet orderOut:self]; + [self enableControls]; + + if (returnCode == NSOKButton) { + NSString * newBrowseDomainString = [self trimCharactersFromDomain:[browseDomainsComboBox stringValue]]; + NSMutableDictionary *newBrowseDomainDict; + + if (browseDomainsArray == nil) browseDomainsArray = [[NSMutableArray alloc] initWithCapacity:0]; + if ([self domainAlreadyInList:newBrowseDomainString] == NO) { + newBrowseDomainDict = [[[NSMutableDictionary alloc] initWithCapacity:2] autorelease]; + + [newBrowseDomainDict setObject:newBrowseDomainString forKey:(NSString *)SC_DYNDNS_DOMAIN_KEY]; + [newBrowseDomainDict setObject:[[[NSNumber alloc] initWithBool:YES] autorelease] forKey:(NSString *)SC_DYNDNS_ENABLED_KEY]; + + [browseDomainsArray addObject:newBrowseDomainDict]; + [browseDomainsArray sortUsingFunction:MyDomainArrayCompareFunction context:nil]; + [browseDomainList reloadData]; + [self updateApplyButtonState]; + } + } +} + + +-(void)validateTextFields +{ + [hostName validateEditing]; + [browseDomainsComboBox validateEditing]; + [regDomainsComboBox validateEditing]; +} + + +- (IBAction)changeButtonPressed:(id)sender +{ + NSString * keyName; + + [self disableControls]; + [self validateTextFields]; + [mainWindow makeFirstResponder:nil]; + [browseDomainList deselectAll:sender]; + + if ([sender isEqualTo:hostNameSharedSecretButton]) { + if (hostNameSharedSecretValue) { + [sharedSecretValue setStringValue:hostNameSharedSecretValue]; + } else if ((keyName = [self sharedSecretKeyName:[hostName stringValue]]) != NULL) { + [sharedSecretName setStringValue:keyName]; + [sharedSecretValue setStringValue:@"****************"]; + } else { + [sharedSecretName setStringValue:[hostName stringValue]]; + [sharedSecretValue setStringValue:@""]; + } + + } else { + if (regSharedSecretValue) { + [sharedSecretValue setStringValue:regSharedSecretValue]; + } else if ((keyName = [self sharedSecretKeyName:[regDomainsComboBox stringValue]]) != NULL) { + [sharedSecretName setStringValue:keyName]; + [sharedSecretValue setStringValue:@"****************"]; + } else { + [sharedSecretName setStringValue:[regDomainsComboBox stringValue]]; + [sharedSecretValue setStringValue:@""]; + } + } + + [sharedSecretWindow resignFirstResponder]; + + if ([[sharedSecretName stringValue] length] > 0) [sharedSecretWindow makeFirstResponder:sharedSecretValue]; + else [sharedSecretWindow makeFirstResponder:sharedSecretName]; + + [NSApp beginSheet:sharedSecretWindow modalForWindow:mainWindow modalDelegate:self + didEndSelector:@selector(sharedSecretSheetDidEnd:returnCode:contextInfo:) contextInfo:sender]; +} + + +- (IBAction)wideAreaCheckBoxChanged:(id)sender +{ + [self toggleWideAreaBonjour:[sender state]]; + [self updateApplyButtonState]; + [mainWindow makeFirstResponder:nil]; +} + + +- (void)updateApplyButtonState +{ + NSString *hostNameString = [hostName stringValue]; + NSString *regDomainString = [regDomainsComboBox stringValue]; + + NSComparisonResult hostNameResult = [hostNameString compare:currentHostName]; + NSComparisonResult regDomainResult = [regDomainString compare:currentRegDomain]; + + if ((currentHostName && (hostNameResult != NSOrderedSame)) || + (currentRegDomain && (regDomainResult != NSOrderedSame) && ([wideAreaCheckBox state])) || + (currentHostName == nil && ([hostNameString length]) > 0) || + (currentRegDomain == nil && ([regDomainString length]) > 0) || + (currentWideAreaState != [wideAreaCheckBox state]) || + (hostNameSharedSecretValue != nil) || + (regSharedSecretValue != nil) || + (browseDomainsArray && [browseDomainsArray isEqualToArray:currentBrowseDomainsArray] == NO)) + { + [self enableApplyButton]; + } else { + [self disableApplyButton]; + } +} + + + +- (void)controlTextDidChange:(NSNotification *)notification +{ + (void)notification; // Unused + [self updateApplyButtonState]; +} + + + +- (IBAction)comboAction:(id)sender +{ + (void)sender; // Unused + [self updateApplyButtonState]; +} + + +- (id)comboBox:(NSComboBox *)aComboBox objectValueForItemAtIndex:(int)ind +{ + NSString *domain = nil; + if ([aComboBox isEqualTo:browseDomainsComboBox]) domain = [browseDataSource objectAtIndex:ind]; + else if ([aComboBox isEqualTo:regDomainsComboBox]) domain = [registrationDataSource objectAtIndex:ind]; + return domain; +} + + + +- (int)numberOfItemsInComboBox:(NSComboBox *)aComboBox +{ + int count = 0; + if ([aComboBox isEqualTo:browseDomainsComboBox]) count = [browseDataSource count]; + else if ([aComboBox isEqualTo:regDomainsComboBox]) count = [registrationDataSource count]; + return count; +} + + +- (NSMutableArray *)browseDataSource +{ + return browseDataSource; +} + + +- (NSMutableArray *)registrationDataSource +{ + return registrationDataSource; +} + + +- (NSComboBox *)browseDomainsComboBox +{ + return browseDomainsComboBox; +} + + +- (NSComboBox *)regDomainsComboBox +{ + return regDomainsComboBox; +} + + +- (NSString *)currentRegDomain +{ + return currentRegDomain; +} + + +- (NSMutableArray *)defaultBrowseDomainsArray +{ + return defaultBrowseDomainsArray; +} + + +- (NSArray *)currentBrowseDomainsArray +{ + return currentBrowseDomainsArray; +} + + +- (NSString *)currentHostName +{ + return currentHostName; +} + + +- (NSString *)defaultRegDomain +{ + return defaultRegDomain; +} + + +- (void)setDefaultRegDomain:(NSString *)domain +{ + [defaultRegDomain release]; + defaultRegDomain = domain; + [defaultRegDomain retain]; +} + + +- (void)didSelect +{ + [super didSelect]; + mainWindow = [[self mainView] window]; +} + + +- (void)mainViewDidLoad +{ + [comboAuthButton setString:"system.preferences"]; + [comboAuthButton setDelegate:self]; + [comboAuthButton updateStatus:nil]; + [comboAuthButton setAutoupdate:YES]; +} + + + +- (IBAction)applyClicked:(id)sender +{ + (void)sender; // Unused + [self applyCurrentState]; +} + + +- (void)applyCurrentState +{ + [self validateTextFields]; + + if (toolInstalled == YES) { + [self savePreferences]; + [self disableApplyButton]; + [mainWindow makeFirstResponder:nil]; + } +} + + +- (void)enableApplyButton +{ + [applyButton setEnabled:YES]; + [revertButton setEnabled:YES]; + prefsNeedUpdating = YES; +} + + +- (void)disableApplyButton +{ + [applyButton setEnabled:NO]; + [revertButton setEnabled:NO]; + prefsNeedUpdating = NO; +} + + +- (void)toggleWideAreaBonjour:(BOOL)state +{ + [wideAreaCheckBox setState:state]; + [regDomainsComboBox setEnabled:state]; + [registrationSharedSecretButton setEnabled:state]; +} + + +- (IBAction)revertClicked:(id)sender +{ + [self restorePreferences]; + [browseDomainList deselectAll:sender]; + [mainWindow makeFirstResponder:nil]; +} + + +- (void)restorePreferences +{ + [self setupInitialValues]; +} + + +- (void)savePanelWillClose:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo +{ + (void)sheet; // Unused + DNSServiceDiscoveryPref * me = (DNSServiceDiscoveryPref *)contextInfo; + + if (returnCode == NSAlertDefaultReturn) { + [me applyCurrentState]; + } else if (returnCode == NSAlertAlternateReturn ) { + [me restorePreferences]; + } + + [me enableControls]; + [me replyToShouldUnselect:(returnCode != NSAlertOtherReturn)]; +} + + +-(SecKeychainItemRef)copyKeychainItemforDomain:(NSString *)domain +{ + const char * serviceName = [domain UTF8String]; + UInt32 type = 'ddns'; + UInt32 typeLength = sizeof(type); + + SecKeychainAttribute attrs[] = { { kSecServiceItemAttr, strlen(serviceName), (char *)serviceName }, + { kSecTypeItemAttr, typeLength, (UInt32 *)&type } }; + + SecKeychainAttributeList attributes = { sizeof(attrs) / sizeof(attrs[0]), attrs }; + SecKeychainSearchRef searchRef; + SecKeychainItemRef itemRef = NULL; + OSStatus err; + + err = SecKeychainSearchCreateFromAttributes(NULL, kSecGenericPasswordItemClass, &attributes, &searchRef); + if (err == noErr) { + err = SecKeychainSearchCopyNext(searchRef, &itemRef); + if (err != noErr) itemRef = NULL; + } + return itemRef; +} + + +-(NSString *)sharedSecretKeyName:(NSString * )domain +{ + SecKeychainItemRef itemRef = NULL; + NSString *keyName = nil; + OSStatus err; + + err = SecKeychainSetPreferenceDomain(kSecPreferencesDomainSystem); + assert(err == noErr); + + itemRef = [self copyKeychainItemforDomain:[domain lowercaseString]]; + if (itemRef) { + UInt32 tags[1]; + SecKeychainAttributeInfo attrInfo; + SecKeychainAttributeList *attrList = NULL; + SecKeychainAttribute attribute; + unsigned int i; + + tags[0] = kSecAccountItemAttr; + attrInfo.count = 1; + attrInfo.tag = tags; + attrInfo.format = NULL; + + err = SecKeychainItemCopyAttributesAndData(itemRef, &attrInfo, NULL, &attrList, NULL, NULL); + if (err == noErr) { + for (i = 0; i < attrList->count; i++) { + attribute = attrList->attr[i]; + if (attribute.tag == kSecAccountItemAttr) { + keyName = [[NSString alloc] initWithBytes:attribute.data length:attribute.length encoding:NSUTF8StringEncoding]; + break; + } + } + if (attrList) (void)SecKeychainItemFreeAttributesAndData(attrList, NULL); + } + CFRelease(itemRef); + } + return keyName; +} + + +-(NSString *)domainForHostName:(NSString *)hostNameString +{ + NSString * domainName = nil; + char text[64]; + char * ptr = NULL; + + ptr = (char *)[hostNameString UTF8String]; + if (ptr) { + ptr = (char *)GetNextLabel(ptr, text); + domainName = [[NSString alloc] initWithUTF8String:(const char *)ptr]; + } + return ([domainName autorelease]); +} + + +- (NSData *)dataForDomain:(NSString *)domainName isEnabled:(BOOL)enabled +{ + NSMutableArray *domainsArray; + NSMutableDictionary *domainDict = nil; + + if (domainName && [domainName length] > 0) { + domainDict= [[[NSMutableDictionary alloc] initWithCapacity:2] autorelease]; + [domainDict setObject:domainName forKey:(NSString *)SC_DYNDNS_DOMAIN_KEY]; + [domainDict setObject:[[[NSNumber alloc] initWithBool:enabled] autorelease] forKey:(NSString *)SC_DYNDNS_ENABLED_KEY]; + } + domainsArray = [[[NSMutableArray alloc] initWithCapacity:1] autorelease]; + if (domainDict) [domainsArray addObject:domainDict]; + return [NSArchiver archivedDataWithRootObject:domainsArray]; +} + + +- (NSData *)dataForDomainArray:(NSArray *)domainArray +{ + return [NSArchiver archivedDataWithRootObject:domainArray]; +} + + +- (NSData *)dataForSharedSecret:(NSString *)secret domain:(NSString *)domainName key:(NSString *)keyName +{ + NSMutableDictionary *sharedSecretDict = [[[NSMutableDictionary alloc] initWithCapacity:3] autorelease]; + [sharedSecretDict setObject:secret forKey:(NSString *)SC_DYNDNS_SECRET_KEY]; + [sharedSecretDict setObject:[domainName lowercaseString] forKey:(NSString *)SC_DYNDNS_DOMAIN_KEY]; + [sharedSecretDict setObject:keyName forKey:(NSString *)SC_DYNDNS_KEYNAME_KEY]; + return [NSArchiver archivedDataWithRootObject:sharedSecretDict]; +} + + +-(void)savePreferences +{ + NSString *hostNameString = [hostName stringValue]; + NSString *browseDomainString = [browseDomainsComboBox stringValue]; + NSString *regDomainString = [regDomainsComboBox stringValue]; + NSString *tempHostNameSharedSecretName = hostNameSharedSecretName; + NSString *tempRegSharedSecretName = regSharedSecretName; + NSData *browseDomainData = nil; + BOOL regSecretWasSet = NO; + BOOL hostSecretWasSet = NO; + OSStatus err = noErr; + + hostNameString = [self trimCharactersFromDomain:hostNameString]; + browseDomainString = [self trimCharactersFromDomain:browseDomainString]; + regDomainString = [self trimCharactersFromDomain:regDomainString]; + tempHostNameSharedSecretName = [self trimCharactersFromDomain:tempHostNameSharedSecretName]; + tempRegSharedSecretName = [self trimCharactersFromDomain:tempRegSharedSecretName]; + + [hostName setStringValue:hostNameString]; + [regDomainsComboBox setStringValue:regDomainString]; + + // Convert Shared Secret account names to lowercase. + tempHostNameSharedSecretName = [tempHostNameSharedSecretName lowercaseString]; + tempRegSharedSecretName = [tempRegSharedSecretName lowercaseString]; + + // Save hostname shared secret. + if ([hostNameSharedSecretName length] > 0 && ([hostNameSharedSecretValue length] > 0)) { + SetKeyForDomain((CFDataRef)[self dataForSharedSecret:hostNameSharedSecretValue domain:hostNameString key:tempHostNameSharedSecretName]); + [hostNameSharedSecretValue release]; + hostNameSharedSecretValue = nil; + hostSecretWasSet = YES; + } + + // Save registration domain shared secret. + if (([regSharedSecretName length] > 0) && ([regSharedSecretValue length] > 0)) { + SetKeyForDomain((CFDataRef)[self dataForSharedSecret:regSharedSecretValue domain:regDomainString key:tempRegSharedSecretName]); + [regSharedSecretValue release]; + regSharedSecretValue = nil; + regSecretWasSet = YES; + } + + // Save hostname. + if ((currentHostName == NULL) || [currentHostName compare:hostNameString] != NSOrderedSame) { + err = WriteHostname((CFDataRef)[self dataForDomain:hostNameString isEnabled:YES]); + if (err != noErr) NSLog(@"WriteHostname returned %d\n", (int32_t)err); + currentHostName = [hostNameString copy]; + } else if (hostSecretWasSet) { + WriteHostname((CFDataRef)[self dataForDomain:@"" isEnabled:NO]); + usleep(200000); // Temporary hack + if ([currentHostName length] > 0) WriteHostname((CFDataRef)[self dataForDomain:(NSString *)currentHostName isEnabled:YES]); + } + + // Save browse domain. + if (browseDomainsArray && [browseDomainsArray isEqualToArray:currentBrowseDomainsArray] == NO) { + browseDomainData = [self dataForDomainArray:browseDomainsArray]; + err = WriteBrowseDomain((CFDataRef)browseDomainData); + if (err != noErr) NSLog(@"WriteBrowseDomain returned %d\n", (int32_t)err); + currentBrowseDomainsArray = [browseDomainsArray copy]; + } + + // Save registration domain. + if ((currentRegDomain == NULL) || ([currentRegDomain compare:regDomainString] != NSOrderedSame) || (currentWideAreaState != [wideAreaCheckBox state])) { + + err = WriteRegistrationDomain((CFDataRef)[self dataForDomain:regDomainString isEnabled:[wideAreaCheckBox state]]); + if (err != noErr) NSLog(@"WriteRegistrationDomain returned %d\n", (int32_t)err); + + if (currentRegDomain) CFRelease(currentRegDomain); + currentRegDomain = [regDomainString copy]; + + if ([currentRegDomain length] > 0) { + currentWideAreaState = [wideAreaCheckBox state]; + [registrationDataSource removeObject:regDomainString]; + [registrationDataSource addObject:currentRegDomain]; + [registrationDataSource sortUsingFunction:MyArrayCompareFunction context:nil]; + [regDomainsComboBox reloadData]; + } else { + currentWideAreaState = NO; + [self toggleWideAreaBonjour:NO]; + if (defaultRegDomain != nil) [regDomainsComboBox setStringValue:defaultRegDomain]; + } + } else if (regSecretWasSet) { + WriteRegistrationDomain((CFDataRef)[self dataForDomain:@"" isEnabled:NO]); + usleep(200000); // Temporary hack + if ([currentRegDomain length] > 0) WriteRegistrationDomain((CFDataRef)[self dataForDomain:currentRegDomain isEnabled:currentWideAreaState]); + } +} + + +- (NSPreferencePaneUnselectReply)shouldUnselect +{ +#if 1 + if (prefsNeedUpdating == YES) { + + [self disableControls]; + + NSBeginAlertSheet( + @"Apply Configuration Changes?", + @"Apply", + @"Don't Apply", + @"Cancel", + mainWindow, + self, + @selector( savePanelWillClose:returnCode:contextInfo: ), + NULL, + (void *) self, // sender, + @"" ); + return NSUnselectLater; + } +#endif + + return NSUnselectNow; +} + + +-(void)disableControls +{ + [hostName setEnabled:NO]; + [hostNameSharedSecretButton setEnabled:NO]; + [browseDomainsComboBox setEnabled:NO]; + [applyButton setEnabled:NO]; + [revertButton setEnabled:NO]; + [wideAreaCheckBox setEnabled:NO]; + [regDomainsComboBox setEnabled:NO]; + [registrationSharedSecretButton setEnabled:NO]; + [statusImageView setEnabled:NO]; + + browseDomainListEnabled = NO; + [browseDomainList deselectAll:self]; + [browseDomainList setEnabled:NO]; + + [addBrowseDomainButton setEnabled:NO]; + [removeBrowseDomainButton setEnabled:NO]; +} + + +- (BOOL)tableView:(NSTableView *)tableView shouldSelectRow:(NSInteger)row +{ + (void)row; // Unused + (void)tableView; // Unused + return browseDomainListEnabled; +} + + +-(void)enableControls +{ + [hostName setEnabled:YES]; + [hostNameSharedSecretButton setEnabled:YES]; + [browseDomainsComboBox setEnabled:YES]; + [wideAreaCheckBox setEnabled:YES]; + [registrationSharedSecretButton setEnabled:YES]; + [self toggleWideAreaBonjour:[wideAreaCheckBox state]]; + [statusImageView setEnabled:YES]; + [addBrowseDomainButton setEnabled:YES]; + + [browseDomainList setEnabled:YES]; + [browseDomainList deselectAll:self]; + browseDomainListEnabled = YES; + + [removeBrowseDomainButton setEnabled:[browseDomainList numberOfSelectedRows]]; + [applyButton setEnabled:prefsNeedUpdating]; + [revertButton setEnabled:prefsNeedUpdating]; +} + + +- (void)authorizationViewDidAuthorize:(SFAuthorizationView *)view +{ + (void)view; // Unused + [self enableControls]; +} + + +- (void)authorizationViewDidDeauthorize:(SFAuthorizationView *)view +{ + (void)view; // Unused + [self disableControls]; +} + +@end + + +// Note: The C preprocessor stringify operator ('#') makes a string from its argument, without macro expansion +// e.g. If "version" is #define'd to be "4", then STRINGIFY_AWE(version) will return the string "version", not "4" +// To expand "version" to its value before making the string, use STRINGIFY(version) instead +#define STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s) #s +#define STRINGIFY(s) STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s) + +// NOT static -- otherwise the compiler may optimize it out +// The "@(#) " pattern is a special prefix the "what" command looks for +const char VersionString_SCCS[] = "@(#) Bonjour Preference Pane " STRINGIFY(mDNSResponderVersion) " (" __DATE__ " " __TIME__ ")"; + +#if _BUILDING_XCODE_PROJECT_ +// If the process crashes, then this string will be magically included in the automatically-generated crash log +const char *__crashreporter_info__ = VersionString_SCCS + 5; +asm(".desc ___crashreporter_info__, 0x10"); +#endif diff --git a/mDNSResponder/mDNSMacOSX/PreferencePane/English.lproj/DNSServiceDiscoveryPref.nib/classes.nib b/mDNSResponder/mDNSMacOSX/PreferencePane/English.lproj/DNSServiceDiscoveryPref.nib/classes.nib new file mode 100644 index 00000000..58c1f3e5 --- /dev/null +++ b/mDNSResponder/mDNSMacOSX/PreferencePane/English.lproj/DNSServiceDiscoveryPref.nib/classes.nib @@ -0,0 +1,59 @@ +{ + IBClasses = ( + { + ACTIONS = { + addBrowseDomainClicked = id; + applyClicked = id; + changeButtonPressed = id; + closeMyCustomSheet = id; + comboAction = id; + enableBrowseDomainClicked = id; + removeBrowseDomainClicked = id; + revertClicked = id; + wideAreaCheckBoxChanged = id; + }; + CLASS = DNSServiceDiscoveryPref; + LANGUAGE = ObjC; + OUTLETS = { + addBrowseDomainButton = NSButton; + addBrowseDomainWindow = NSWindow; + applyButton = NSButton; + browseCancelButton = NSButton; + browseDomainList = NSTableView; + browseDomainsComboBox = NSComboBox; + browseOKButton = NSButton; + comboAuthButton = SFAuthorizationView; + hostName = NSTextField; + hostNameSharedSecretButton = NSButton; + regDomainsComboBox = NSComboBox; + registrationSharedSecretButton = NSButton; + removeBrowseDomainButton = NSButton; + revertButton = NSButton; + secretCancelButton = NSButton; + secretOKButton = NSButton; + sharedSecretName = NSTextField; + sharedSecretValue = NSSecureTextField; + sharedSecretWindow = NSWindow; + statusImageView = NSImageView; + tabView = NSTabView; + wideAreaCheckBox = NSButton; + }; + SUPERCLASS = NSPreferencePane; + }, + {CLASS = FirstResponder; LANGUAGE = ObjC; SUPERCLASS = NSObject; }, + { + CLASS = NSPreferencePane; + LANGUAGE = ObjC; + OUTLETS = { + "_firstKeyView" = id; + "_initialKeyView" = id; + "_lastKeyView" = id; + "_window" = id; + }; + SUPERCLASS = NSObject; + }, + {CLASS = NSSegmentedControl; LANGUAGE = ObjC; SUPERCLASS = NSControl; }, + {CLASS = SFAuthorizationView; LANGUAGE = ObjC; SUPERCLASS = NSView; } + ); + IBVersion = 1; +}
\ No newline at end of file diff --git a/mDNSResponder/mDNSMacOSX/PreferencePane/English.lproj/DNSServiceDiscoveryPref.nib/info.nib b/mDNSResponder/mDNSMacOSX/PreferencePane/English.lproj/DNSServiceDiscoveryPref.nib/info.nib new file mode 100644 index 00000000..e8dbd926 --- /dev/null +++ b/mDNSResponder/mDNSMacOSX/PreferencePane/English.lproj/DNSServiceDiscoveryPref.nib/info.nib @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>IBDocumentLocation</key> + <string>32 63 547 281 0 0 1024 746 </string> + <key>IBFramework Version</key> + <string>439.0</string> + <key>IBOpenObjects</key> + <array> + <integer>255</integer> + <integer>333</integer> + <integer>12</integer> + </array> + <key>IBSystem Version</key> + <string>8F23</string> +</dict> +</plist> diff --git a/mDNSResponder/mDNSMacOSX/PreferencePane/English.lproj/DNSServiceDiscoveryPref.nib/keyedobjects.nib b/mDNSResponder/mDNSMacOSX/PreferencePane/English.lproj/DNSServiceDiscoveryPref.nib/keyedobjects.nib Binary files differnew file mode 100644 index 00000000..eec01ee4 --- /dev/null +++ b/mDNSResponder/mDNSMacOSX/PreferencePane/English.lproj/DNSServiceDiscoveryPref.nib/keyedobjects.nib diff --git a/mDNSResponder/mDNSMacOSX/PreferencePane/English.lproj/InfoPlist.strings b/mDNSResponder/mDNSMacOSX/PreferencePane/English.lproj/InfoPlist.strings Binary files differnew file mode 100644 index 00000000..e2dfa991 --- /dev/null +++ b/mDNSResponder/mDNSMacOSX/PreferencePane/English.lproj/InfoPlist.strings diff --git a/mDNSResponder/mDNSMacOSX/PreferencePane/Info-PreferencePane.plist b/mDNSResponder/mDNSMacOSX/PreferencePane/Info-PreferencePane.plist new file mode 100644 index 00000000..e5cdb9f2 --- /dev/null +++ b/mDNSResponder/mDNSMacOSX/PreferencePane/Info-PreferencePane.plist @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>CFBundleDevelopmentRegion</key> + <string>English</string> + <key>CFBundleExecutable</key> + <string>Bonjour</string> + <key>CFBundleGetInfoString</key> + <string></string> + <key>CFBundleIconFile</key> + <string>BonjourPref</string> + <key>CFBundleIdentifier</key> + <string>com.apple.preference.bonjour</string> + <key>CFBundleInfoDictionaryVersion</key> + <string>6.0</string> + <key>CFBundleName</key> + <string></string> + <key>CFBundlePackageType</key> + <string>BNDL</string> + <key>CFBundleShortVersionString</key> + <string>1.0</string> + <key>CFBundleSignature</key> + <string>????</string> + <key>CFBundleVersion</key> + <string>1.0</string> + <key>NSMainNibFile</key> + <string>DNSServiceDiscoveryPref</string> + <key>NSPrefPaneIconFile</key> + <string>BonjourPref.tiff</string> + <key>NSPrefPaneIconLabel</key> + <string>Bonjour</string> + <key>NSPrincipalClass</key> + <string>DNSServiceDiscoveryPref</string> +</dict> +</plist> diff --git a/mDNSResponder/mDNSMacOSX/PreferencePane/PrivilegedOperations.c b/mDNSResponder/mDNSMacOSX/PreferencePane/PrivilegedOperations.c new file mode 100644 index 00000000..4c0ffa0e --- /dev/null +++ b/mDNSResponder/mDNSMacOSX/PreferencePane/PrivilegedOperations.c @@ -0,0 +1,230 @@ +/* + File: PrivilegedOperations.c + + Abstract: Interface to "ddnswriteconfig" setuid root tool. + + Copyright: (c) Copyright 2005 Apple Computer, Inc. All rights reserved. + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. + ("Apple") in consideration of your agreement to the following terms, and your + use, installation, modification or redistribution of this Apple software + constitutes acceptance of these terms. If you do not agree with these terms, + please do not use, install, modify or redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and subject + to these terms, Apple grants you a personal, non-exclusive license, under Apple's + copyrights in this original Apple software (the "Apple Software"), to use, + reproduce, modify and redistribute the Apple Software, with or without + modifications, in source and/or binary forms; provided that if you redistribute + the Apple Software in its entirety and without modifications, you must retain + this notice and the following text and disclaimers in all such redistributions of + the Apple Software. Neither the name, trademarks, service marks or logos of + Apple Computer, Inc. may be used to endorse or promote products derived from the + Apple Software without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or implied, + are granted by Apple herein, including but not limited to any patent rights that + may be infringed by your derivative works or by other works in which the Apple + Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO + WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN + COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION + OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT + (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "PrivilegedOperations.h" +#include "ConfigurationAuthority.h" +#include <CoreFoundation/CoreFoundation.h> +#include <SystemConfiguration/SystemConfiguration.h> +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/wait.h> +#include <AssertMacros.h> +#include <Security/Security.h> + +Boolean gToolApproved = false; + +static pid_t execTool(const char *args[]) +// fork/exec and return new pid +{ + pid_t child; + + child = vfork(); + if (child == 0) + { + execv(args[0], (char *const *)args); + printf("exec of %s failed; errno = %d\n", args[0], errno); + _exit(-1); // exec failed + } + else + return child; +} + +OSStatus EnsureToolInstalled(void) +// Make sure that the tool is installed in the right place, with the right privs, and the right version. +{ + CFURLRef bundleURL; + pid_t toolPID; + int status; + OSStatus err = noErr; + const char *args[] = { kToolPath, "0", "V", NULL }; + char toolSourcePath[PATH_MAX] = {}; + char toolInstallerPath[PATH_MAX] = {}; + + if (gToolApproved) + return noErr; + + // Check version of installed tool + toolPID = execTool(args); + if (toolPID > 0) + { + waitpid(toolPID, &status, 0); + if (WIFEXITED(status) && WEXITSTATUS(status) == PRIV_OP_TOOL_VERS) + return noErr; + } + + // Locate our in-bundle copy of privop tool + bundleURL = CFBundleCopyBundleURL(CFBundleGetBundleWithIdentifier(CFSTR("com.apple.preference.bonjour")) ); + if (bundleURL != NULL) + { + CFURLGetFileSystemRepresentation(bundleURL, false, (UInt8*) toolSourcePath, sizeof toolSourcePath); + if (strlcat(toolSourcePath, "/Contents/Resources/" kToolName, sizeof toolSourcePath ) >= sizeof toolSourcePath ) return(-1); + CFURLGetFileSystemRepresentation(bundleURL, false, (UInt8*) toolInstallerPath, sizeof toolInstallerPath); + if (strlcat(toolInstallerPath, "/Contents/Resources/" kToolInstaller, sizeof toolInstallerPath) >= sizeof toolInstallerPath) return(-1); + } + else + return coreFoundationUnknownErr; + + // Obtain authorization and run in-bundle copy as root to install it + { + AuthorizationItem aewpRight = { kAuthorizationRightExecute, strlen(toolInstallerPath), toolInstallerPath, 0 }; + AuthorizationItemSet rights = { 1, &aewpRight }; + AuthorizationRef authRef; + + err = AuthorizationCreate(&rights, (AuthorizationEnvironment*) NULL, + kAuthorizationFlagInteractionAllowed | kAuthorizationFlagExtendRights | + kAuthorizationFlagPreAuthorize, &authRef); + if (err == noErr) + { + char *installerargs[] = { toolSourcePath, NULL }; + err = AuthorizationExecuteWithPrivileges(authRef, toolInstallerPath, 0, installerargs, (FILE**) NULL); + if (err == noErr) { + int pid = wait(&status); + if (pid > 0 && WIFEXITED(status)) { + err = WEXITSTATUS(status); + if (err == noErr) { + gToolApproved = true; + } + } else { + err = -1; + } + } + (void) AuthorizationFree(authRef, kAuthorizationFlagDefaults); + } + } + + return err; +} + + +static OSStatus ExecWithCmdAndParam(const char *subCmd, CFDataRef paramData) +// Execute our privop tool with the supplied subCmd and parameter +{ + OSStatus err = noErr; + int commFD, dataLen; + u_int32_t len; + pid_t child; + char fileNum[16]; + UInt8 *buff; + const char *args[] = { kToolPath, NULL, "A", NULL, NULL }; + AuthorizationExternalForm authExt; + + err = ExternalizeAuthority(&authExt); + require_noerr(err, AuthFailed); + + dataLen = CFDataGetLength(paramData); + buff = (UInt8*) malloc(dataLen * sizeof(UInt8)); + require_action(buff != NULL, AllocBuffFailed, err=memFullErr;); + { + CFRange all = { 0, dataLen }; + CFDataGetBytes(paramData, all, buff); + } + + commFD = fileno(tmpfile()); + sprintf(fileNum, "%d", commFD); + args[1] = fileNum; + args[3] = subCmd; + + // write authority to pipe + len = 0; // tag, unused + write(commFD, &len, sizeof len); + len = sizeof authExt; // len + write(commFD, &len, sizeof len); + write(commFD, &authExt, len); + + // write parameter to pipe + len = 0; // tag, unused + write(commFD, &len, sizeof len); + len = dataLen; // len + write(commFD, &len, sizeof len); + write(commFD, buff, len); + + child = execTool(args); + if (child > 0) { + int status; + waitpid(child, &status, 0); + if (WIFEXITED(status)) + err = WEXITSTATUS(status); + //fprintf(stderr, "child exited; status = %d (%ld)\n", status, err); + } + + close(commFD); + + free(buff); +AllocBuffFailed: +AuthFailed: + return err; +} + +OSStatus +WriteBrowseDomain(CFDataRef domainArrayData) +{ + if (!CurrentlyAuthorized()) + return authFailErr; + return ExecWithCmdAndParam("Wb", domainArrayData); +} + +OSStatus +WriteRegistrationDomain(CFDataRef domainArrayData) +{ + if (!CurrentlyAuthorized()) + return authFailErr; + return ExecWithCmdAndParam("Wd", domainArrayData); +} + +OSStatus +WriteHostname(CFDataRef domainArrayData) +{ + if (!CurrentlyAuthorized()) + return authFailErr; + return ExecWithCmdAndParam("Wh", domainArrayData); +} + +OSStatus +SetKeyForDomain(CFDataRef secretData) +{ + if (!CurrentlyAuthorized()) + return authFailErr; + return ExecWithCmdAndParam("Wk", secretData); +} diff --git a/mDNSResponder/mDNSMacOSX/PreferencePane/PrivilegedOperations.h b/mDNSResponder/mDNSMacOSX/PreferencePane/PrivilegedOperations.h new file mode 100644 index 00000000..91a60daf --- /dev/null +++ b/mDNSResponder/mDNSMacOSX/PreferencePane/PrivilegedOperations.h @@ -0,0 +1,70 @@ +/* + File: PrivilegedOperations.h + + Abstract: Interface to "ddnswriteconfig" setuid root tool. + + Copyright: (c) Copyright 2005 Apple Computer, Inc. All rights reserved. + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. + ("Apple") in consideration of your agreement to the following terms, and your + use, installation, modification or redistribution of this Apple software + constitutes acceptance of these terms. If you do not agree with these terms, + please do not use, install, modify or redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and subject + to these terms, Apple grants you a personal, non-exclusive license, under Apple's + copyrights in this original Apple software (the "Apple Software"), to use, + reproduce, modify and redistribute the Apple Software, with or without + modifications, in source and/or binary forms; provided that if you redistribute + the Apple Software in its entirety and without modifications, you must retain + this notice and the following text and disclaimers in all such redistributions of + the Apple Software. Neither the name, trademarks, service marks or logos of + Apple Computer, Inc. may be used to endorse or promote products derived from the + Apple Software without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or implied, + are granted by Apple herein, including but not limited to any patent rights that + may be infringed by your derivative works or by other works in which the Apple + Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO + WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN + COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION + OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT + (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <CoreServices/CoreServices.h> +#include <CoreFoundation/CoreFoundation.h> + +#define PRIV_OP_TOOL_VERS 4 + +#define kToolName "ddnswriteconfig" +#define kToolPath "/Library/Application Support/Bonjour/" kToolName +#define kToolInstaller "installtool" + +#define SC_DYNDNS_SETUP_KEY CFSTR("Setup:/Network/DynamicDNS") +#define SC_DYNDNS_STATE_KEY CFSTR("State:/Network/DynamicDNS") +#define SC_DYNDNS_REGDOMAINS_KEY CFSTR("RegistrationDomains") +#define SC_DYNDNS_BROWSEDOMAINS_KEY CFSTR("BrowseDomains") +#define SC_DYNDNS_HOSTNAMES_KEY CFSTR("HostNames") +#define SC_DYNDNS_DOMAIN_KEY CFSTR("Domain") +#define SC_DYNDNS_KEYNAME_KEY CFSTR("KeyName") +#define SC_DYNDNS_SECRET_KEY CFSTR("Secret") +#define SC_DYNDNS_ENABLED_KEY CFSTR("Enabled") +#define SC_DYNDNS_STATUS_KEY CFSTR("Status") +#define DYNDNS_KEYCHAIN_DESCRIPTION "Dynamic DNS Key" + + +OSStatus EnsureToolInstalled(void); +OSStatus WriteRegistrationDomain(CFDataRef domainArrayData); +OSStatus WriteBrowseDomain(CFDataRef domainArrayData); +OSStatus WriteHostname(CFDataRef domainArrayData); +OSStatus SetKeyForDomain(CFDataRef secretData); diff --git a/mDNSResponder/mDNSMacOSX/PreferencePane/ddnswriteconfig.m b/mDNSResponder/mDNSMacOSX/PreferencePane/ddnswriteconfig.m new file mode 100644 index 00000000..437879bc --- /dev/null +++ b/mDNSResponder/mDNSMacOSX/PreferencePane/ddnswriteconfig.m @@ -0,0 +1,442 @@ +/* + File: ddnswriteconfig.m + + Abstract: Setuid root tool invoked by Preference Pane to perform + privileged accesses to system configuration preferences and the system keychain. + Invoked by PrivilegedOperations.c. + + Copyright: (c) Copyright 2005 Apple Computer, Inc. All rights reserved. + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. + ("Apple") in consideration of your agreement to the following terms, and your + use, installation, modification or redistribution of this Apple software + constitutes acceptance of these terms. If you do not agree with these terms, + please do not use, install, modify or redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and subject + to these terms, Apple grants you a personal, non-exclusive license, under Apple's + copyrights in this original Apple software (the "Apple Software"), to use, + reproduce, modify and redistribute the Apple Software, with or without + modifications, in source and/or binary forms; provided that if you redistribute + the Apple Software in its entirety and without modifications, you must retain + this notice and the following text and disclaimers in all such redistributions of + the Apple Software. Neither the name, trademarks, service marks or logos of + Apple Computer, Inc. may be used to endorse or promote products derived from the + Apple Software without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or implied, + are granted by Apple herein, including but not limited to any patent rights that + may be infringed by your derivative works or by other works in which the Apple + Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO + WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN + COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION + OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT + (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#import "PrivilegedOperations.h" +#import "ConfigurationRights.h" + +#import <stdio.h> +#import <stdint.h> +#import <stdlib.h> +#import <unistd.h> +#import <fcntl.h> +#import <errno.h> +#import <sys/types.h> +#import <sys/stat.h> +#import <sys/mman.h> +#import <mach-o/dyld.h> +#import <dns_sd.h> +#import <AssertMacros.h> +#import <Security/Security.h> +#import <CoreServices/CoreServices.h> +#import <CoreFoundation/CoreFoundation.h> +#import <SystemConfiguration/SystemConfiguration.h> +#import <Foundation/Foundation.h> + + +static AuthorizationRef gAuthRef = 0; + +static OSStatus +WriteArrayToDynDNS(CFStringRef arrayKey, CFArrayRef domainArray) +{ + SCPreferencesRef store; + OSStatus err = noErr; + CFDictionaryRef origDict; + CFMutableDictionaryRef dict = NULL; + Boolean result; + CFStringRef scKey = CFSTR("/System/Network/DynamicDNS"); + + + // Add domain to the array member ("arrayKey") of the DynamicDNS dictionary + // Will replace duplicate, at head of list + // At this point, we only support a single-item list + store = SCPreferencesCreate(NULL, CFSTR("com.apple.preference.bonjour"), NULL); + require_action(store != NULL, SysConfigErr, err=paramErr;); + require_action(true == SCPreferencesLock( store, true), LockFailed, err=coreFoundationUnknownErr;); + + origDict = SCPreferencesPathGetValue(store, scKey); + if (origDict) { + dict = CFDictionaryCreateMutableCopy(NULL, 0, origDict); + } + + if (!dict) { + dict = CFDictionaryCreateMutable(NULL, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + } + require_action( dict != NULL, NoDict, err=memFullErr;); + + if (CFArrayGetCount(domainArray) > 0) { + CFDictionarySetValue(dict, arrayKey, domainArray); + } else { + CFDictionaryRemoveValue(dict, arrayKey); + } + + result = SCPreferencesPathSetValue(store, scKey, dict); + require_action(result, SCError, err=kernelPrivilegeErr;); + + result = SCPreferencesCommitChanges(store); + require_action(result, SCError, err=kernelPrivilegeErr;); + result = SCPreferencesApplyChanges(store); + require_action(result, SCError, err=kernelPrivilegeErr;); + +SCError: + CFRelease(dict); +NoDict: + SCPreferencesUnlock(store); +LockFailed: + CFRelease(store); +SysConfigErr: + return err; +} + + +static int +readTaggedBlock(int fd, u_int32_t *pTag, u_int32_t *pLen, char **ppBuff) +// Read tag, block len and block data from stream and return. Dealloc *ppBuff via free(). +{ + ssize_t num; + u_int32_t tag, len; // Don't use ssize_t because that's different on 32- vs. 64-bit + int result = 0; + + num = read(fd, &tag, sizeof tag); + require_action(num == sizeof tag, GetTagFailed, result = -1;); + num = read(fd, &len, sizeof len); + require_action(num == sizeof len, GetLenFailed, result = -1;); + + *ppBuff = (char*) malloc( len); + require_action(*ppBuff != NULL, AllocFailed, result = -1;); + + num = read(fd, *ppBuff, len); + if (num == (ssize_t)len) { + *pTag = tag; + *pLen = len; + } else { + free(*ppBuff); + result = -1; + } + +AllocFailed: +GetLenFailed: +GetTagFailed: + return result; +} + + + +static int +SetAuthInfo( int fd) +{ + int result = 0; + u_int32_t tag, len; + char *p; + + result = readTaggedBlock( fd, &tag, &len, &p); + require( result == 0, ReadParamsFailed); + require( len == sizeof(AuthorizationExternalForm), ReadParamsFailed); + require( len == kAuthorizationExternalFormLength, ReadParamsFailed); + + if (gAuthRef != 0) { + (void) AuthorizationFree(gAuthRef, kAuthorizationFlagDefaults); + gAuthRef = 0; + } + + result = AuthorizationCreateFromExternalForm((AuthorizationExternalForm*) p, &gAuthRef); + + free( p); +ReadParamsFailed: + return result; +} + + +static int +HandleWriteDomain(int fd, int domainType) +{ + CFArrayRef domainArray; + CFDataRef domainData; + int result = 0; + u_int32_t tag, len; + char *p; + + AuthorizationItem scAuth = { UPDATE_SC_RIGHT, 0, NULL, 0 }; + AuthorizationRights authSet = { 1, &scAuth }; + + if (noErr != (result = AuthorizationCopyRights(gAuthRef, &authSet, NULL, (AuthorizationFlags)0, NULL))) + return result; + + result = readTaggedBlock(fd, &tag, &len, &p); + require(result == 0, ReadParamsFailed); + + domainData = CFDataCreate(NULL, (UInt8 *)p, len); + domainArray = (CFArrayRef)[NSUnarchiver unarchiveObjectWithData:(NSData *)domainData]; + + if (domainType) { + result = WriteArrayToDynDNS(SC_DYNDNS_REGDOMAINS_KEY, domainArray); + } else { + result = WriteArrayToDynDNS(SC_DYNDNS_BROWSEDOMAINS_KEY, domainArray); + } + +ReadParamsFailed: + return result; +} + + +static int +HandleWriteHostname(int fd) +{ + CFArrayRef domainArray; + CFDataRef domainData; + int result = 0; + u_int32_t tag, len; + char *p; + + AuthorizationItem scAuth = { UPDATE_SC_RIGHT, 0, NULL, 0 }; + AuthorizationRights authSet = { 1, &scAuth }; + + if (noErr != (result = AuthorizationCopyRights(gAuthRef, &authSet, NULL, (AuthorizationFlags) 0, NULL))) + return result; + + result = readTaggedBlock(fd, &tag, &len, &p); + require(result == 0, ReadParamsFailed); + + domainData = CFDataCreate(NULL, (const UInt8 *)p, len); + domainArray = (CFArrayRef)[NSUnarchiver unarchiveObjectWithData:(NSData *)domainData]; + result = WriteArrayToDynDNS(SC_DYNDNS_HOSTNAMES_KEY, domainArray); + +ReadParamsFailed: + return result; +} + + +static SecAccessRef +MyMakeUidAccess(uid_t uid) +{ + // make the "uid/gid" ACL subject + // this is a CSSM_LIST_ELEMENT chain + CSSM_ACL_PROCESS_SUBJECT_SELECTOR selector = { + CSSM_ACL_PROCESS_SELECTOR_CURRENT_VERSION, // selector version + CSSM_ACL_MATCH_UID, // set mask: match uids (only) + uid, // uid to match + 0 // gid (not matched here) + }; + CSSM_LIST_ELEMENT subject2 = { NULL, 0, 0, {{0,0,0}} }; + subject2.Element.Word.Data = (UInt8 *)&selector; + subject2.Element.Word.Length = sizeof(selector); + CSSM_LIST_ELEMENT subject1 = { &subject2, CSSM_ACL_SUBJECT_TYPE_PROCESS, CSSM_LIST_ELEMENT_WORDID, {{0,0,0}} }; + + + // rights granted (replace with individual list if desired) + CSSM_ACL_AUTHORIZATION_TAG rights[] = { + CSSM_ACL_AUTHORIZATION_ANY // everything + }; + // owner component (right to change ACL) + CSSM_ACL_OWNER_PROTOTYPE owner = { + // TypedSubject + { CSSM_LIST_TYPE_UNKNOWN, &subject1, &subject2 }, + // Delegate + false + }; + // ACL entries (any number, just one here) + CSSM_ACL_ENTRY_INFO acls = + { + // CSSM_ACL_ENTRY_PROTOTYPE + { + { CSSM_LIST_TYPE_UNKNOWN, &subject1, &subject2 }, // TypedSubject + false, // Delegate + { sizeof(rights) / sizeof(rights[0]), rights }, // Authorization rights for this entry + { { 0, 0 }, { 0, 0 } }, // CSSM_ACL_VALIDITY_PERIOD + "" // CSSM_STRING EntryTag + }, + // CSSM_ACL_HANDLE + 0 + }; + + SecAccessRef a = NULL; + (void) SecAccessCreateFromOwnerAndACL(&owner, 1, &acls, &a); + return a; +} + + +static OSStatus +MyAddDynamicDNSPassword(SecKeychainRef keychain, SecAccessRef a, UInt32 serviceNameLength, const char *serviceName, + UInt32 accountNameLength, const char *accountName, UInt32 passwordLength, const void *passwordData) +{ + char * description = DYNDNS_KEYCHAIN_DESCRIPTION; + UInt32 descriptionLength = strlen(DYNDNS_KEYCHAIN_DESCRIPTION); + UInt32 type = 'ddns'; + UInt32 creator = 'ddns'; + UInt32 typeLength = sizeof(type); + UInt32 creatorLength = sizeof(creator); + OSStatus err; + + // set up attribute vector (each attribute consists of {tag, length, pointer}) + SecKeychainAttribute attrs[] = { { kSecLabelItemAttr, serviceNameLength, (char *)serviceName }, + { kSecAccountItemAttr, accountNameLength, (char *)accountName }, + { kSecServiceItemAttr, serviceNameLength, (char *)serviceName }, + { kSecDescriptionItemAttr, descriptionLength, (char *)description }, + { kSecTypeItemAttr, typeLength, (UInt32 *)&type }, + { kSecCreatorItemAttr, creatorLength, (UInt32 *)&creator } }; + SecKeychainAttributeList attributes = { sizeof(attrs) / sizeof(attrs[0]), attrs }; + + err = SecKeychainItemCreateFromContent(kSecGenericPasswordItemClass, &attributes, passwordLength, passwordData, keychain, a, NULL); + return err; +} + + +static int +SetKeychainEntry(int fd) +// Create a new entry in system keychain, or replace existing +{ + CFDataRef secretData; + CFDictionaryRef secretDictionary; + CFStringRef keyNameString; + CFStringRef domainString; + CFStringRef secretString; + SecKeychainItemRef item = NULL; + int result = 0; + u_int32_t tag, len; + char *p; + char keyname[kDNSServiceMaxDomainName]; + char domain[kDNSServiceMaxDomainName]; + char secret[kDNSServiceMaxDomainName]; + + AuthorizationItem kcAuth = { EDIT_SYS_KEYCHAIN_RIGHT, 0, NULL, 0 }; + AuthorizationRights authSet = { 1, &kcAuth }; + + if (noErr != (result = AuthorizationCopyRights(gAuthRef, &authSet, NULL, (AuthorizationFlags)0, NULL))) + return result; + + result = readTaggedBlock(fd, &tag, &len, &p); + require_noerr(result, ReadParamsFailed); + + secretData = CFDataCreate(NULL, (UInt8 *)p, len); + secretDictionary = (CFDictionaryRef)[NSUnarchiver unarchiveObjectWithData:(NSData *)secretData]; + + keyNameString = (CFStringRef)CFDictionaryGetValue(secretDictionary, SC_DYNDNS_KEYNAME_KEY); + assert(keyNameString != NULL); + + domainString = (CFStringRef)CFDictionaryGetValue(secretDictionary, SC_DYNDNS_DOMAIN_KEY); + assert(domainString != NULL); + + secretString = (CFStringRef)CFDictionaryGetValue(secretDictionary, SC_DYNDNS_SECRET_KEY); + assert(secretString != NULL); + + CFStringGetCString(keyNameString, keyname, kDNSServiceMaxDomainName, kCFStringEncodingUTF8); + CFStringGetCString(domainString, domain, kDNSServiceMaxDomainName, kCFStringEncodingUTF8); + CFStringGetCString(secretString, secret, kDNSServiceMaxDomainName, kCFStringEncodingUTF8); + + result = SecKeychainSetPreferenceDomain(kSecPreferencesDomainSystem); + if (result == noErr) { + result = SecKeychainFindGenericPassword(NULL, strlen(domain), domain, 0, NULL, 0, NULL, &item); + if (result == noErr) { + result = SecKeychainItemDelete(item); + if (result != noErr) fprintf(stderr, "SecKeychainItemDelete returned %d\n", result); + } + + result = MyAddDynamicDNSPassword(NULL, MyMakeUidAccess(0), strlen(domain), domain, strlen(keyname)+1, keyname, strlen(secret)+1, secret); + if (result != noErr) fprintf(stderr, "MyAddDynamicDNSPassword returned %d\n", result); + if (item) CFRelease(item); + } + +ReadParamsFailed: + return result; +} + + +int main( int argc, char **argv) +/* argv[0] is the exec path; argv[1] is a fd for input data; argv[2]... are operation codes. + The tool supports the following operations: + V -- exit with status PRIV_OP_TOOL_VERS + A -- read AuthInfo from input pipe + Wd -- write registration domain to dynamic store + Wb -- write browse domain to dynamic store + Wh -- write hostname to dynamic store + Wk -- write keychain entry for given account name +*/ +{ + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + int commFD = -1, iArg, result = 0; + + if ( 0 != seteuid( 0)) + return -1; + + if ( argc == 3 && 0 == strcmp( argv[2], "V")) + return PRIV_OP_TOOL_VERS; + + if ( argc > 1) + { + commFD = strtol( argv[1], NULL, 0); + lseek( commFD, 0, SEEK_SET); + } + for ( iArg = 2; iArg < argc && result == 0; iArg++) + { + if ( 0 == strcmp( "A", argv[ iArg])) // get auth info + { + result = SetAuthInfo( commFD); + } + else if ( 0 == strcmp( "Wd", argv[ iArg])) // Write registration domain + { + result = HandleWriteDomain( commFD, 1); + } + else if ( 0 == strcmp( "Wb", argv[ iArg])) // Write browse domain + { + result = HandleWriteDomain( commFD, 0); + } + else if ( 0 == strcmp( "Wh", argv[ iArg])) // Write hostname + { + result = HandleWriteHostname( commFD); + } + else if ( 0 == strcmp( "Wk", argv[ iArg])) // Write keychain entry + { + result = SetKeychainEntry( commFD); + } + } + [pool release]; + return result; +} + +// Note: The C preprocessor stringify operator ('#') makes a string from its argument, without macro expansion +// e.g. If "version" is #define'd to be "4", then STRINGIFY_AWE(version) will return the string "version", not "4" +// To expand "version" to its value before making the string, use STRINGIFY(version) instead +#define STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s) #s +#define STRINGIFY(s) STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s) + +// NOT static -- otherwise the compiler may optimize it out +// The "@(#) " pattern is a special prefix the "what" command looks for +const char VersionString_SCCS[] = "@(#) ddnswriteconfig " STRINGIFY(mDNSResponderVersion) " (" __DATE__ " " __TIME__ ")"; + +#if _BUILDING_XCODE_PROJECT_ +// If the process crashes, then this string will be magically included in the automatically-generated crash log +const char *__crashreporter_info__ = VersionString_SCCS + 5; +asm(".desc ___crashreporter_info__, 0x10"); +#endif diff --git a/mDNSResponder/mDNSMacOSX/PreferencePane/installtool b/mDNSResponder/mDNSMacOSX/PreferencePane/installtool new file mode 100755 index 00000000..ce341c87 --- /dev/null +++ b/mDNSResponder/mDNSMacOSX/PreferencePane/installtool @@ -0,0 +1,94 @@ +#!/usr/bin/perl +# Emacs settings: -*- tab-width: 4 -*- +# +# File: installtool +# +# Abstract: Copy "ddnswriteconfig" to Application Support and make it setuid root. +# +# Copyright: (c) Copyright 2005 Apple Computer, Inc. All rights reserved. +# +# Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. +# ("Apple") in consideration of your agreement to the following terms, and your +# use, installation, modification or redistribution of this Apple software +# constitutes acceptance of these terms. If you do not agree with these terms, +# please do not use, install, modify or redistribute this Apple software. +# +# In consideration of your agreement to abide by the following terms, and subject +# to these terms, Apple grants you a personal, non-exclusive license, under Apple's +# copyrights in this original Apple software (the "Apple Software"), to use, +# reproduce, modify and redistribute the Apple Software, with or without +# modifications, in source and/or binary forms; provided that if you redistribute +# the Apple Software in its entirety and without modifications, you must retain +# this notice and the following text and disclaimers in all such redistributions of +# the Apple Software. Neither the name, trademarks, service marks or logos of +# Apple Computer, Inc. may be used to endorse or promote products derived from the +# Apple Software without specific prior written permission from Apple. Except as +# expressly stated in this notice, no other rights or licenses, express or implied, +# are granted by Apple herein, including but not limited to any patent rights that +# may be infringed by your derivative works or by other works in which the Apple +# Software may be incorporated. +# +# The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO +# WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED +# WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN +# COMBINATION WITH YOUR PRODUCTS. +# +# IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE +# GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION +# OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT +# (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN +# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# Create the Bonjour subdirectory. +# Copy ARGV[0] to $dest and set owner and suid permissions. +# +# This script will be run as root by the AEWP trampoline. +# + +use File::Temp qw/ :mktemp /; + +$dest_dir = "/Library/Application Support/Bonjour"; +$dest = $dest_dir . "/ddnswriteconfig"; + +$template = ".XXXXXX"; + +# Perl seems to think this code is running setuid root, so it applies its security checks. +# See <http://www.monster-submit.com/resources/docs/pod/perlsec.html>. +# In fact this is NOT a setuid script. It is a normal unprivileged user-level script -- +# but it is run as root when properly authorized by a user with an admin password, +# via the AuthorizationExecuteWithPrivileges() call. +# We therefore have to do this trick pattern match to 'untaint' the source file specified in $ARGV[0]. +if ($ARGV[0] =~ /^(.+)$/) { $src = $1; } + +# Also clear $ENV{PATH} so we don't get "Insecure $ENV{PATH}" fatal errors +$ENV{PATH} = ""; + +if (! -d $dest_dir) { + $dest_tmp_dir = mkdtemp ($dest_dir . $template); + (chown 0, 80, $dest_tmp_dir) or cleanup_dir(); + (chmod 0755, $dest_tmp_dir) or cleanup_dir(); + (rename $dest_tmp_dir, $dest_dir) or cleanup_dir(); +} + +$dest_tmp = mktemp ($dest . $template); + +if ($src ne '') { + system ('/bin/cp', '-f', $src, $dest_tmp) and cleanup(); + (chown 0, 80, $dest_tmp) or cleanup(); + (chmod 04555, $dest_tmp) or cleanup(); + (rename $dest_tmp, $dest) or cleanup(); +} +exit (0); + +sub cleanup { + unlink $dest_tmp; + exit (1); +} + +sub cleanup_dir { + unlink $dest_tmp_dir; + exit (1); +} diff --git a/mDNSResponder/mDNSMacOSX/Private/dns_services.c b/mDNSResponder/mDNSMacOSX/Private/dns_services.c new file mode 100644 index 00000000..d0e9e6ca --- /dev/null +++ b/mDNSResponder/mDNSMacOSX/Private/dns_services.c @@ -0,0 +1,212 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2012 Apple Inc. All rights reserved. + * + * PRIVATE DNSX CLIENT LIBRARY --FOR Apple Platforms ONLY OSX/iOS-- + * Resides in /usr/lib/libdns_services.dylib + */ + +#include "dns_services.h" +#include "dns_xpc.h" +#include <xpc/xpc.h> +#include <Block.h> +#include <stdio.h> +#include <stdlib.h> +#include <syslog.h> + +//************************************************************************************************************* +// Globals + +#define connection_t xpc_connection_t + +struct _DNSXConnRef_t +{ + connection_t conn_ref; // xpc_connection between client and daemon + dispatch_queue_t lib_q; // internal queue created in library itself + void *AppCallBack; // Callback function ptr for Client + dispatch_queue_t client_q; // Queue specified by client for scheduling its Callback +}; + +//************************************************************************************************************* +// Utility Functions + +static bool LogDebugEnabled() +{ + return false; +} + +static void LogDebug(const char *prefix, xpc_object_t o) +{ + if (!LogDebugEnabled()) + return; + + char *desc = xpc_copy_description(o); + syslog(LOG_INFO, "%s: %s", prefix, desc); + free(desc); +} + +//************************************************************************************************************** + +void DNSXRefDeAlloc(DNSXConnRef connRef) +{ + if (!connRef) + { + syslog(LOG_WARNING, "dns_services: DNSXRefDeAlloc called with NULL DNSXConnRef"); + return; + } + + // Schedule this work on the internal library queue + dispatch_sync(connRef->lib_q, ^{ + + xpc_release(connRef->conn_ref); + connRef->AppCallBack = NULL; + dispatch_release(connRef->client_q); + + }); + + dispatch_release(connRef->lib_q); + free(connRef); + + syslog(LOG_INFO, "dns_services: DNSXRefDeAlloc successfully DeAllocated connRef"); + +} + +// Sends the Msg(Dictionary) to the Server +static DNSXErrorType SendMsgToServer(DNSXConnRef *connRef, xpc_object_t msg, bool old_conn) +{ + DNSXErrorType errx = kDNSX_NoError; + + LogDebug("dns_services: SendMsgToServer", msg); + + xpc_connection_set_event_handler((*connRef)->conn_ref, ^(xpc_object_t recv_msg) + { + xpc_type_t type = xpc_get_type(recv_msg); + + if (type == XPC_TYPE_DICTIONARY) + { + LogDebug("dns_services: SendMsgToServer SUCCESS CALLBACK FROM SERVER", recv_msg); + syslog(LOG_INFO, "dns_services: Successfully Sent Msg to the Daemon"); + uint64_t daemon_status = xpc_dictionary_get_uint64(recv_msg, kDNSDaemonReply); + + // Schedule the AppCallBacks on the Client Specified Queue + switch (daemon_status) + { + case kDNSDaemonEngaged: + dispatch_async((*connRef)->client_q, ^{ + ((DNSXEnableProxyReply)(*connRef)->AppCallBack)((*connRef), kDNSX_Engaged); + }); + break; + case kDNSMsgReceived: + dispatch_async((*connRef)->client_q, ^{ + ((DNSXEnableProxyReply)(*connRef)->AppCallBack)((*connRef), kDNSX_NoError); + }); + break; + default: + dispatch_async((*connRef)->client_q, ^{ + ((DNSXEnableProxyReply)(*connRef)->AppCallBack)((*connRef), kDNSX_UnknownErr); + }); + break; + } + + } + else + { + LogDebug("dns_services: SendMsgToServer UNEXPECTED CALLBACK FROM SERVER", recv_msg); + syslog(LOG_WARNING, "dns_services: Connection failed since NO privileges to access service OR Daemon NOT Running"); + dispatch_async((*connRef)->client_q, ^{ + ((DNSXEnableProxyReply)(*connRef)->AppCallBack)((*connRef), kDNSX_DaemonNotRunning); + }); + } + }); + + // To prevent Over-Resume of a connection + if (!old_conn) + xpc_connection_resume((*connRef)->conn_ref); + xpc_connection_send_message((*connRef)->conn_ref, msg); + if (!errx) + syslog(LOG_INFO, "dns_services: SendMSgToServer sent Msg Dict successfully to Daemon"); + return errx; +} + +// Creates a new DNSX Connection Reference(DNSXConnRef). +// If DNSXConnRef exists, you may want to use that depending on the use case +static DNSXErrorType InitConnection(DNSXConnRef *connRef, const char *servname, dispatch_queue_t clientq, void *AppCallBack) +{ + if (!connRef) + { + syslog(LOG_WARNING, "dns_services: InitConnection() called with NULL DNSXConnRef"); + return kDNSX_BadParam; + } + + *connRef = malloc(sizeof(struct _DNSXConnRef_t)); + if (!(*connRef)) + { + syslog(LOG_WARNING, "dns_services: InitConnection() No memory to allocate"); + return kDNSX_NoMem; + } + + // Initialize the DNSXConnRef + dispatch_retain(clientq); + (*connRef)->client_q = clientq; + (*connRef)->AppCallBack = AppCallBack; + (*connRef)->lib_q = dispatch_queue_create("com.apple.mDNSResponder.libdns_services.q", NULL); + (*connRef)->conn_ref = xpc_connection_create_mach_service(servname, (*connRef)->lib_q, XPC_CONNECTION_MACH_SERVICE_PRIVILEGED); + + syslog(LOG_INFO, "dns_services: InitConnection() successfully create a new DNSXConnRef"); + return kDNSX_NoError; +} + +DNSXErrorType DNSXEnableProxy(DNSXConnRef *connRef, DNSProxyParameters proxyparam, IfIndex inIfindexArr[MaxInputIf], + IfIndex outIfindex, dispatch_queue_t clientq, DNSXEnableProxyReply callBack) +{ + + DNSXErrorType errx = kDNSX_NoError; + bool old_conn = false; + + // Sanity Checks + if (!connRef || !callBack || !clientq) + { + syslog(LOG_WARNING, "dns_services: DNSXEnableProxy called with NULL DNSXConnRef OR Callback OR ClientQ parameter"); + return kDNSX_BadParam; + } + + // If no connRef, get it from InitConnection() + if (!*connRef) + { + errx = InitConnection(connRef, kDNSProxyService, clientq, callBack); + if (errx) // On error InitConnection() leaves *connRef set to NULL + { + syslog(LOG_WARNING, "dns_services: Since InitConnection() returned %d error returning w/o sending msg", errx); + return errx; + } + } + else // Client already has a valid connRef + { + old_conn = true; + } + + // Create Dictionary To Send + xpc_object_t dict = xpc_dictionary_create(NULL, NULL, 0); + if (!dict) + { + syslog(LOG_WARNING, "dns_services: DNSXEnableProxy could not create the Msg Dict To Send!"); + DNSXRefDeAlloc(*connRef); + return kDNSX_DictError; + } + + xpc_dictionary_set_uint64(dict, kDNSProxyParameters, proxyparam); + + xpc_dictionary_set_uint64(dict, kDNSInIfindex0, inIfindexArr[0]); + xpc_dictionary_set_uint64(dict, kDNSInIfindex1, inIfindexArr[1]); + xpc_dictionary_set_uint64(dict, kDNSInIfindex2, inIfindexArr[2]); + xpc_dictionary_set_uint64(dict, kDNSInIfindex3, inIfindexArr[3]); + xpc_dictionary_set_uint64(dict, kDNSInIfindex4, inIfindexArr[4]); + + xpc_dictionary_set_uint64(dict, kDNSOutIfindex, outIfindex); + + errx = SendMsgToServer(connRef, dict, old_conn); + xpc_release(dict); + + return errx; +} + diff --git a/mDNSResponder/mDNSMacOSX/Private/dns_services.h b/mDNSResponder/mDNSMacOSX/Private/dns_services.h new file mode 100644 index 00000000..7b74e10d --- /dev/null +++ b/mDNSResponder/mDNSMacOSX/Private/dns_services.h @@ -0,0 +1,124 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2012 Apple Inc. All rights reserved. + * + * + * @header Interface to DNSX SPI + * + * @discussion Describes the functions and data structures + * that make up the DNSX SPI + */ + +#ifndef _DNS_SERVICES_H +#define _DNS_SERVICES_H + +#include <dispatch/dispatch.h> + +// DNSXConnRef: Opaque internal data type +typedef struct _DNSXConnRef_t *DNSXConnRef; + +typedef enum +{ + kDNSX_NoError = 0, + kDNSX_UnknownErr = -65537, /* 0xFFFE FFFF */ + kDNSX_NoMem = -65539, + kDNSX_BadParam = -65540, + kDNSX_DaemonNotRunning = -65563, /* Background daemon not running */ + kDNSX_DictError = -65565, /* Dictionary Error */ + kDNSX_Engaged = -65566, /* DNS Proxy is in use by another client */ + kDNSX_Timeout = -65568 +} DNSXErrorType; + +// A max of 5 input interfaces can be processed at one time +#define MaxInputIf 5 +#define IfIndex uint64_t +#define kDNSIfindexAny 0 + +// Enable DNS Proxy with an appropriate parameter defined below +typedef enum +{ + kDNSProxyEnable = 1 + // Other values reserved for future use +} DNSProxyParameters; + +/********************************************************************************************* +* +* Enable DNS Proxy Functionality +* +*********************************************************************************************/ + +/* DNSXEnableProxy : Turns ON the DNS Proxy (Details below) + * + * DNSXEnableProxyReply() parameters: + * + * connRef: The DNSXConnRef initialized by DNSXEnableProxy(). + * + * errCode: Will be kDNSX_NoError on success, otherwise will indicate the + * failure that occurred. Other parameters are undefined if + * errCode is nonzero. + * + */ + +typedef void (*DNSXEnableProxyReply) +( + DNSXConnRef connRef, + DNSXErrorType errCode +); + +/* DNSXEnableProxy + * + * Enables the DNS Proxy functionality which will remain ON until the client terminates explictly (or exits/crashes). + * Client can turn it OFF by passing the returned DNSXConnRef to DNSXRefDeAlloc() + * + * DNSXEnableProxy() Parameters: + * + * connRef: A pointer to DNSXConnRef that is initialized to NULL when called for the first + * time. If the call succeeds it will be initialized to a non-NULL value. + * Client terminates the DNS Proxy by passing this DNSXConnRef to DNSXRefDeAlloc(). + * + * proxyparam: Enable DNS Proxy functionality with parameters that are described in + * DNSProxyParameters above. + * + * inIfindexArr[MaxInputIf]: List of input interfaces from which the DNS queries will be accepted and + * forwarded to the output interface specified below. The daemon processes + * MaxInputIf entries in the list. For eg. if one has less than MaxInputIfs + * values, just initialize the other values to be 0. Note: This field needs to + * be initialized by the client. + * + * outIfindex: Output interface on which the query will be forwarded. + * Passing kDNSIfindexAny causes DNS Queries to be sent on the primary interface. + * + * clientq: Queue the client wants to schedule the callBack on (Note: Must not be NULL) + * + * callBack: CallBack function for the client that indicates success or failure. + * Note: callback may be invoked more than once, For eg. if enabling DNS Proxy + * first succeeds and the daemon possibly crashes sometime later. + * + * return value: Returns kDNSX_NoError when no error otherwise returns an error code indicating + * the error that occurred. Note: A return value of kDNSX_NoError does not mean + * that DNS Proxy was successfully enabled. The callBack may asynchronously + * return an error (such as kDNSX_DaemonNotRunning/ kDNSX_Engaged) + * + */ + +DNSXErrorType DNSXEnableProxy +( + DNSXConnRef *connRef, + DNSProxyParameters proxyparam, + IfIndex inIfindexArr[MaxInputIf], + IfIndex outIfindex, + dispatch_queue_t clientq, + DNSXEnableProxyReply callBack +); + +/* DNSXRefDeAlloc() + * + * Terminate a connection with the daemon and free memory associated with the DNSXConnRef. + * Used to Disable DNS Proxy on that connection. + * + * connRef: A DNSXConnRef initialized by any of the DNSX*() calls. + * + */ +void DNSXRefDeAlloc(DNSXConnRef connRef); + +#endif /* _DNS_SERVICES_H */ diff --git a/mDNSResponder/mDNSMacOSX/Private/dns_xpc.h b/mDNSResponder/mDNSMacOSX/Private/dns_xpc.h new file mode 100644 index 00000000..10ae01fa --- /dev/null +++ b/mDNSResponder/mDNSMacOSX/Private/dns_xpc.h @@ -0,0 +1,33 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2012 Apple Inc. All rights reserved. + * + * Defines the common interface between mDNSResponder and the Private ClientLibrary(libdnsprivate.dylib) + * Uses XPC as the IPC Mechanism + * + */ + +#ifndef DNS_XPC_H +#define DNS_XPC_H + +#define kDNSProxyService "com.apple.mDNSResponder.dnsproxy" + +#define kDNSProxyParameters "DNSProxyParameters" + +#define kDNSInIfindex0 "InputArrayInterfaceIndex[0]" +#define kDNSInIfindex1 "InputArrayInterfaceIndex[1]" +#define kDNSInIfindex2 "InputArrayInterfaceIndex[2]" +#define kDNSInIfindex3 "InputArrayInterfaceIndex[3]" +#define kDNSInIfindex4 "InputArrayInterfaceIndex[4]" + +#define kDNSOutIfindex "OutputInterfaceIndex" + +#define kDNSDaemonReply "DaemonReplyStatusToClient" + +typedef enum +{ + kDNSMsgReceived = 0, + kDNSDaemonEngaged +} DaemonReplyStatusCodes; + +#endif // DNS_XPC_H diff --git a/mDNSResponder/mDNSMacOSX/Private/xpc_services.c b/mDNSResponder/mDNSMacOSX/Private/xpc_services.c new file mode 100644 index 00000000..7a0e29fb --- /dev/null +++ b/mDNSResponder/mDNSMacOSX/Private/xpc_services.c @@ -0,0 +1,255 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2012 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/xpc.h> +#include <xpc/private.h> // 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(&mDNSStorage); + DNSProxyInit(&mDNSStorage, IpIfArr, OpIf); + if (proxy_off) // Open skts only if proxy was OFF else we may end up opening extra skts + mDNSPlatformInitDNSProxySkts(&mDNSStorage, ProxyUDPCallback, ProxyTCPCallback); + KQueueUnlock(&mDNSStorage, "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(&mDNSStorage); + mDNSPlatformCloseDNSProxySkts(&mDNSStorage); + // TBD: Close TCP Sockets + DNSProxyTerminate(&mDNSStorage); + KQueueUnlock(&mDNSStorage, "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(NULL, NULL, 0); + if (reply) + { + xpc_dictionary_set_uint64(reply, kDNSDaemonReply, kDNSDaemonEngaged); + 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; + } + } + + // Return Success Status to the client + xpc_object_t response = xpc_dictionary_create(NULL, NULL, 0); + if (response) + { + xpc_dictionary_set_uint64(response, kDNSDaemonReply, kDNSMsgReceived); + 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 +mDNSlocal 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"); + } + + return entitled; +} + +mDNSlocal void accept_dps_client(xpc_connection_t conn) +{ + uid_t euid; + euid = xpc_connection_get_euid(conn); + + if (euid != 0 || !IsEntitled(conn, kDNSProxyService)) + { + LogMsg("accept_dps_client: DNSProxyService Client Pid[%d] is missing Entitlement or is not root!", (int) xpc_connection_get_pid(conn)); + 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); + } + // We hit the case below only if Client Terminated DPS Connection OR Crashed + else + { + 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 (((int)xpc_connection_get_pid(conn)) == 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); + } + // Ideally, we would never hit the cases below + else if (type == XPC_TYPE_ERROR) + { + 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(); +} + +#else // !UNICAST_DISABLED + +mDNSexport void xpc_server_init() +{ + return; +} + +mDNSexport void xpcserver_info(mDNS *const m) +{ + (void) m; + + return; +} + +#endif // !UNICAST_DISABLED + diff --git a/mDNSResponder/mDNSMacOSX/Private/xpc_services.h b/mDNSResponder/mDNSMacOSX/Private/xpc_services.h new file mode 100644 index 00000000..50081bed --- /dev/null +++ b/mDNSResponder/mDNSMacOSX/Private/xpc_services.h @@ -0,0 +1,21 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2012 Apple Inc. All rights reserved. + * + * + * + * File: xpc_services.h + * + * Contains: Interfaces necessary to talk to xpc_services.c + * + */ + +#ifndef XPC_SERVICES_H +#define XPC_SERVICES_H + +#include "mDNSEmbeddedAPI.h" + +extern void xpc_server_init(void); +extern void xpcserver_info(mDNS *const m); + +#endif // XPC_SERVICES_H diff --git a/mDNSResponder/mDNSMacOSX/README.privsep b/mDNSResponder/mDNSMacOSX/README.privsep new file mode 100644 index 00000000..130e3276 --- /dev/null +++ b/mDNSResponder/mDNSMacOSX/README.privsep @@ -0,0 +1,40 @@ +On Mac OS X, mDNSResponder now runs with user-ID and group-ID +"_mdnsresponder". In order to perform certain privileged operations, a +helper (unimagintively called mDNSResponderHelper) runs as root when +needed and handles requests from mDNSResponder. + + +* A new LaunchD job com.apple.mDNSResponderHelper starts + mDNSResponderHelper on demand. The helper exits after approximately + 10 seconds of idle time. + +* The com.apple.mDNSResponder LaunchD job specifies the account under + which to run, so that mDNSResponder starts as _mdnsresponder. + +* A subdirectory named "mdns" and owned by _mdnsresponder has been + created in /var/run. The PID file and uDNS server socket has been + moved to that subdirectory. + +* There are currently six remote procedure calls handled by + mDNSResponderHelper: mDNSPreferencesSetName, mDNSKeychainGetSecrets, + mDNSConfigureServer, and mDNSAutoTunnelSetKeys + +* mDNSPreferencesSetName allows mDNSResponder to set the computer name + or local host name, and displays a notification if there was a + conflict. + +* mDNSKeychainGetSecrets causes mDNSResponderHelper to collect DNS + keys from the system keychain. SetDomainSecrets uses the result to + populate AuthInfoList. One could refactor this code further so that + mDNSResponderHelper performs all the cryptographic operations, with + the result that a compromise of mDNSResponder does not compromise + keys. But I think that may be more change than is advisable at this + point. + +* On the advice of the Security.framework team, I've used + SecKeychainSetPreferenceDomain to ensure that the system keychain is + references whenever a NULL SecKeychainRef is used. Wherever a + SecKeychainRef is needed, NULL is now specified. + +* mDNSConfigureServer, and mDNSAutoTunnelSetKeys do various setup and + teardown for BTMM. diff --git a/mDNSResponder/mDNSMacOSX/VPNService.c b/mDNSResponder/mDNSMacOSX/VPNService.c new file mode 100644 index 00000000..8c1bf1d0 --- /dev/null +++ b/mDNSResponder/mDNSMacOSX/VPNService.c @@ -0,0 +1,35 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2013 Apple Computer, 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 "mDNSMacOSX.h" +#include <SystemConfiguration/VPNAppLayerPrivate.h> + +mDNSexport mDNSs32 mDNSPlatformGetServiceID(mDNS *const m, DNSQuestion *q) +{ + (void) m; + int sid; + + if (q->pid) + { + sid = VPNAppLayerGetMatchingServiceIdentifier(q->pid, NULL); + } + else + { + sid = VPNAppLayerGetMatchingServiceIdentifier(0, q->uuid); + } + LogInfo("mDNSPlatformGetServiceID: returning %d for %##s (%s)", sid, q->qname.c, DNSTypeName(q->qtype)); + return sid; +} diff --git a/mDNSResponder/mDNSMacOSX/com.apple.networking.mDNSResponder b/mDNSResponder/mDNSMacOSX/com.apple.networking.mDNSResponder new file mode 100644 index 00000000..d07c99d0 --- /dev/null +++ b/mDNSResponder/mDNSMacOSX/com.apple.networking.mDNSResponder @@ -0,0 +1 @@ +? [= LoggerID com.apple.networking.mDNSResponder] file /Library/Logs/CrashReporter/com.apple.networking.mDNSResponder.log rotate file_max=1M compress diff --git a/mDNSResponder/mDNSMacOSX/daemon.c b/mDNSResponder/mDNSMacOSX/daemon.c new file mode 100644 index 00000000..3b3ed393 --- /dev/null +++ b/mDNSResponder/mDNSMacOSX/daemon.c @@ -0,0 +1,3052 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2002-2011 Apple Computer, 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 <sys/types.h> +#include <errno.h> +#include <signal.h> +#include <unistd.h> +#include <paths.h> +#include <fcntl.h> +#include <launch.h> +#include <launch_priv.h> // for launch_socket_service_check_in() +#include <vproc.h> +#include <pwd.h> +#include <sys/event.h> +#include <pthread.h> +#include <sandbox.h> +#include <SystemConfiguration/SCDynamicStoreCopyDHCPInfo.h> +#include <asl.h> +#include <syslog.h> +#include <err.h> +#include <sysexits.h> +#include <bootstrap_priv.h> // for bootstrap_check_in() + +#include "DNSServiceDiscoveryRequestServer.h" +#include "DNSServiceDiscoveryReply.h" + +#include "uDNS.h" +#include "DNSCommon.h" +#include "mDNSMacOSX.h" // Defines the specific types needed to run mDNS on this platform + +#include "uds_daemon.h" // Interface to the server side implementation of dns_sd.h +#include "xpc_services.h" // Interface to XPC services + +#include "../mDNSMacOSX/DNSServiceDiscovery.h" +#include "helper.h" + +static aslclient log_client = NULL; +static aslmsg log_msg = NULL; + +// Used on Embedded Side for Reading mDNSResponder Managed Preferences Profile +#if TARGET_OS_EMBEDDED +#define kmDNSEnableLoggingStr CFSTR("EnableLogging") +#define kmDNSResponderPrefIDStr "com.apple.mDNSResponder.plist" +#define kmDNSResponderPrefID CFSTR(kmDNSResponderPrefIDStr) +#endif + +//************************************************************************************************************* +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - Globals +#endif + +static mDNS_PlatformSupport PlatformStorage; + +// Start off with a default cache of 16K (99 records) +// Each time we grow the cache we add another 99 records +// 99 * 164 = 16236 bytes. +// This fits in four 4kB pages, with 148 bytes spare for memory block headers and similar overhead +#define RR_CACHE_SIZE ((16*1024) / sizeof(CacheRecord)) +static CacheEntity rrcachestorage[RR_CACHE_SIZE]; + +static mach_port_t m_port = MACH_PORT_NULL; + +#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM +mDNSlocal void PrepareForIdle(void *m_param); +#else // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM +static mach_port_t client_death_port = MACH_PORT_NULL; +static mach_port_t signal_port = MACH_PORT_NULL; +#endif // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM + +static dnssd_sock_t *launchd_fds = mDNSNULL; +static mDNSu32 launchd_fds_count = 0; + +// mDNS Mach Message Timeout, in milliseconds. +// We need this to be short enough that we don't deadlock the mDNSResponder if a client +// fails to service its mach message queue, but long enough to give a well-written +// client a chance to service its mach message queue without getting cut off. +// Empirically, 50ms seems to work, so we set the timeout to 250ms to give +// even extra-slow clients a fair chance before we cut them off. +#define MDNS_MM_TIMEOUT 250 + +static mDNSBool advertise = mDNS_Init_AdvertiseLocalAddresses; // By default, advertise addresses (& other records) via multicast + +extern mDNSBool StrictUnicastOrdering; +extern mDNSBool AlwaysAppendSearchDomains; + +//************************************************************************************************************* +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - +#pragma mark - Active client list structures +#endif + +typedef struct DNSServiceDomainEnumeration_struct DNSServiceDomainEnumeration; +struct DNSServiceDomainEnumeration_struct +{ + DNSServiceDomainEnumeration *next; + mach_port_t ClientMachPort; + DNSQuestion dom; // Question asking for domains + DNSQuestion def; // Question asking for default domain +}; + +typedef struct DNSServiceBrowserResult_struct DNSServiceBrowserResult; +struct DNSServiceBrowserResult_struct +{ + DNSServiceBrowserResult *next; + int resultType; + domainname result; +}; + +typedef struct DNSServiceBrowser_struct DNSServiceBrowser; + +typedef struct DNSServiceBrowserQuestion +{ + struct DNSServiceBrowserQuestion *next; + DNSQuestion q; + domainname domain; +} DNSServiceBrowserQuestion; + +struct DNSServiceBrowser_struct +{ + DNSServiceBrowser *next; + mach_port_t ClientMachPort; + DNSServiceBrowserQuestion *qlist; + DNSServiceBrowserResult *results; + mDNSs32 lastsuccess; + mDNSBool DefaultDomain; // was the browse started on an explicit domain? + domainname type; // registration type +}; + +typedef struct DNSServiceResolver_struct DNSServiceResolver; +struct DNSServiceResolver_struct +{ + DNSServiceResolver *next; + mach_port_t ClientMachPort; + ServiceInfoQuery q; + ServiceInfo i; + mDNSs32 ReportTime; +}; + +// A single registered service: ServiceRecordSet + bookkeeping +// Note that we duplicate some fields from parent DNSServiceRegistration object +// to facilitate cleanup, when instances and parent may be deallocated at different times. +typedef struct ServiceInstance +{ + struct ServiceInstance *next; + mach_port_t ClientMachPort; + mDNSBool autoname; // Set if this name is tied to the Computer Name + mDNSBool renameonmemfree; // Set if we just got a name conflict and now need to automatically pick a new name + domainlabel name; + domainname domain; + ServiceRecordSet srs; + // Don't add any fields after ServiceRecordSet. + // This is where the implicit extra space goes if we allocate an oversized ServiceRecordSet object +} ServiceInstance; + +// A client-created service. May reference several ServiceInstance objects if default +// settings cause registration in multiple domains. +typedef struct DNSServiceRegistration +{ + struct DNSServiceRegistration *next; + mach_port_t ClientMachPort; + mDNSBool DefaultDomain; + mDNSBool autoname; + size_t rdsize; + int NumSubTypes; + char regtype[MAX_ESCAPED_DOMAIN_NAME]; // for use in AllocateSubtypes + domainlabel name; // used only if autoname is false + domainname type; + mDNSIPPort port; + unsigned char txtinfo[1024]; + size_t txt_len; + uint32_t NextRef; + ServiceInstance *regs; +} DNSServiceRegistration; + +static DNSServiceDomainEnumeration *DNSServiceDomainEnumerationList = NULL; +static DNSServiceBrowser *DNSServiceBrowserList = NULL; +static DNSServiceResolver *DNSServiceResolverList = NULL; +static DNSServiceRegistration *DNSServiceRegistrationList = NULL; + +// We keep a list of client-supplied event sources in KQSocketEventSource records +typedef struct KQSocketEventSource +{ + struct KQSocketEventSource *next; + int fd; + KQueueEntry kqs; +} KQSocketEventSource; + +static KQSocketEventSource *gEventSources; + +//************************************************************************************************************* +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - +#pragma mark - General Utility Functions +#endif + +#if APPLE_OSX_mDNSResponder && MACOSX_MDNS_MALLOC_DEBUGGING + +char _malloc_options[] = "AXZ"; + +mDNSexport void LogMemCorruption(const char *format, ...) +{ + char buffer[512]; + va_list ptr; + va_start(ptr,format); + buffer[mDNS_vsnprintf((char *)buffer, sizeof(buffer), format, ptr)] = 0; + va_end(ptr); + LogMsg("!!!! %s !!!!", buffer); + NotifyOfElusiveBug("Memory Corruption", buffer); +#if ForceAlerts + *(long*)0 = 0; // Trick to crash and get a stack trace right here, if that's what we want +#endif +} + +mDNSlocal void validatelists(mDNS *const m) +{ + // Check local lists + KQSocketEventSource *k; + for (k = gEventSources; k; k=k->next) + if (k->next == (KQSocketEventSource *)~0 || k->fd < 0) + LogMemCorruption("gEventSources: %p is garbage (%d)", k, k->fd); + + // Check Mach client lists + DNSServiceDomainEnumeration *e; + for (e = DNSServiceDomainEnumerationList; e; e=e->next) + if (e->next == (DNSServiceDomainEnumeration *)~0 || e->ClientMachPort == 0 || e->ClientMachPort == (mach_port_t) ~0) + LogMemCorruption("DNSServiceDomainEnumerationList: %p is garbage (%X)", e, e->ClientMachPort); + + DNSServiceBrowser *b; + for (b = DNSServiceBrowserList; b; b=b->next) + if (b->next == (DNSServiceBrowser *)~0 || b->ClientMachPort == 0 || b->ClientMachPort == (mach_port_t) ~0) + LogMemCorruption("DNSServiceBrowserList: %p is garbage (%X)", b, b->ClientMachPort); + + DNSServiceResolver *l; + for (l = DNSServiceResolverList; l; l=l->next) + if (l->next == (DNSServiceResolver *)~0 || l->ClientMachPort == 0 || l->ClientMachPort == (mach_port_t) ~0) + LogMemCorruption("DNSServiceResolverList: %p is garbage (%X)", l, l->ClientMachPort); + + DNSServiceRegistration *r; + for (r = DNSServiceRegistrationList; r; r=r->next) + if (r->next == (DNSServiceRegistration *)~0 || r->ClientMachPort == 0 || r->ClientMachPort == (mach_port_t) ~0) + LogMemCorruption("DNSServiceRegistrationList: %p is garbage (%X)", r, r->ClientMachPort); + + // Check Unix Domain Socket client lists (uds_daemon.c) + uds_validatelists(); + + // Check core mDNS lists + AuthRecord *rr; + for (rr = m->ResourceRecords; rr; rr=rr->next) + { + if (rr->next == (AuthRecord *)~0 || rr->resrec.RecordType == 0 || rr->resrec.RecordType == 0xFF) + LogMemCorruption("ResourceRecords list: %p is garbage (%X)", rr, rr->resrec.RecordType); + if (rr->resrec.name != &rr->namestorage) + LogMemCorruption("ResourceRecords list: %p name %p does not point to namestorage %p %##s", + rr, rr->resrec.name->c, rr->namestorage.c, rr->namestorage.c); + } + + for (rr = m->DuplicateRecords; rr; rr=rr->next) + if (rr->next == (AuthRecord *)~0 || rr->resrec.RecordType == 0 || rr->resrec.RecordType == 0xFF) + LogMemCorruption("DuplicateRecords list: %p is garbage (%X)", rr, rr->resrec.RecordType); + + rr = m->NewLocalRecords; + if (rr) + if (rr->next == (AuthRecord *)~0 || rr->resrec.RecordType == 0 || rr->resrec.RecordType == 0xFF) + LogMemCorruption("NewLocalRecords: %p is garbage (%X)", rr, rr->resrec.RecordType); + + rr = m->CurrentRecord; + if (rr) + if (rr->next == (AuthRecord *)~0 || rr->resrec.RecordType == 0 || rr->resrec.RecordType == 0xFF) + LogMemCorruption("CurrentRecord: %p is garbage (%X)", rr, rr->resrec.RecordType); + + DNSQuestion *q; + for (q = m->Questions; q; q=q->next) + if (q->next == (DNSQuestion*)~0 || q->ThisQInterval == (mDNSs32) ~0) + LogMemCorruption("Questions list: %p is garbage (%lX %p)", q, q->ThisQInterval, q->next); + + CacheGroup *cg; + CacheRecord *cr; + mDNSu32 slot; + FORALL_CACHERECORDS(slot, cg, cr) + { + if (cr->resrec.RecordType == 0 || cr->resrec.RecordType == 0xFF) + LogMemCorruption("Cache slot %lu: %p is garbage (%X)", slot, cr, cr->resrec.RecordType); + if (cr->CRActiveQuestion) + { + for (q = m->Questions; q; q=q->next) if (q == cr->CRActiveQuestion) break; + if (!q) LogMemCorruption("Cache slot %lu: CRActiveQuestion %p not in m->Questions list %s", slot, cr->CRActiveQuestion, CRDisplayString(m, cr)); + } + } + + // Check core uDNS lists + udns_validatelists(m); + + // Check platform-layer lists + NetworkInterfaceInfoOSX *i; + for (i = m->p->InterfaceList; i; i = i->next) + if (i->next == (NetworkInterfaceInfoOSX *)~0 || !i->m || i->m == (mDNS *)~0) + LogMemCorruption("m->p->InterfaceList: %p is garbage (%p)", i, i->ifinfo.ifname); + + ClientTunnel *t; + for (t = m->TunnelClients; t; t=t->next) + if (t->next == (ClientTunnel *)~0 || t->dstname.c[0] > 63) + LogMemCorruption("m->TunnelClients: %p is garbage (%d)", t, t->dstname.c[0]); +} + +mDNSexport void *mallocL(char *msg, unsigned int size) +{ + // Allocate space for two words of sanity checking data before the requested block + mDNSu32 *mem = malloc(sizeof(mDNSu32) * 2 + size); + if (!mem) + { LogMsg("malloc( %s : %d ) failed", msg, size); return(NULL); } + else + { + if (size > 24000) LogMsg("malloc( %s : %lu ) = %p suspiciously large", msg, size, &mem[2]); + else if (MACOSX_MDNS_MALLOC_DEBUGGING >= 2) LogMsg("malloc( %s : %lu ) = %p", msg, size, &mem[2]); + mem[0] = 0xDEAD1234; + mem[1] = size; + //mDNSPlatformMemZero(&mem[2], size); + memset(&mem[2], 0xFF, size); + validatelists(&mDNSStorage); + return(&mem[2]); + } +} + +mDNSexport void freeL(char *msg, void *x) +{ + if (!x) + LogMsg("free( %s @ NULL )!", msg); + else + { + mDNSu32 *mem = ((mDNSu32 *)x) - 2; + if (mem[0] != 0xDEAD1234) { LogMsg("free( %s @ %p ) !!!! NOT ALLOCATED !!!!", msg, &mem[2]); return; } + if (mem[1] > 24000) LogMsg("free( %s : %ld @ %p) suspiciously large", msg, mem[1], &mem[2]); + else if (MACOSX_MDNS_MALLOC_DEBUGGING >= 2) LogMsg("free( %s : %ld @ %p)", msg, mem[1], &mem[2]); + //mDNSPlatformMemZero(mem, sizeof(mDNSu32) * 2 + mem[1]); + memset(mem, 0xFF, sizeof(mDNSu32) * 2 + mem[1]); + validatelists(&mDNSStorage); + free(mem); + } +} + +#endif + +//************************************************************************************************************* +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - +#pragma mark - Mach client request handlers +#endif + +//************************************************************************************************************* +// Client Death Detection + +// This gets called after ALL constituent records of the Service Record Set have been deregistered +mDNSlocal void FreeServiceInstance(ServiceInstance *x) +{ + ServiceRecordSet *s = &x->srs; + ExtraResourceRecord *e = x->srs.Extras, *tmp; + + while (e) + { + e->r.RecordContext = e; + tmp = e; + e = e->next; + FreeExtraRR(&mDNSStorage, &tmp->r, mStatus_MemFree); + } + + if (s->RR_TXT.resrec.rdata != &s->RR_TXT.rdatastorage) + freeL("TXT RData", s->RR_TXT.resrec.rdata); + + if (s->SubTypes) freeL("ServiceSubTypes", s->SubTypes); + freeL("ServiceInstance", x); +} + +// AbortClient finds whatever client is identified by the given Mach port, +// stops whatever operation that client was doing, and frees its memory. +// In the case of a service registration, the actual freeing may be deferred +// until we get the mStatus_MemFree message, if necessary +mDNSlocal void AbortClient(mach_port_t ClientMachPort, void *m) +{ + DNSServiceDomainEnumeration **e = &DNSServiceDomainEnumerationList; + DNSServiceBrowser **b = &DNSServiceBrowserList; + DNSServiceResolver **l = &DNSServiceResolverList; + DNSServiceRegistration **r = &DNSServiceRegistrationList; + + while (*e && (*e)->ClientMachPort != ClientMachPort) e = &(*e)->next; + if (*e) + { + DNSServiceDomainEnumeration *x = *e; + *e = (*e)->next; + if (m && m != x) + LogMsg("%5d: DNSServiceDomainEnumeration(%##s) STOP; WARNING m %p != x %p", ClientMachPort, x->dom.qname.c, m, x); + else LogOperation("%5d: DNSServiceDomainEnumeration(%##s) STOP", ClientMachPort, x->dom.qname.c); + mDNS_StopGetDomains(&mDNSStorage, &x->dom); + mDNS_StopGetDomains(&mDNSStorage, &x->def); + freeL("DNSServiceDomainEnumeration", x); + return; + } + + while (*b && (*b)->ClientMachPort != ClientMachPort) b = &(*b)->next; + if (*b) + { + DNSServiceBrowser *x = *b; + DNSServiceBrowserQuestion *freePtr, *qptr = x->qlist; + *b = (*b)->next; + while (qptr) + { + if (m && m != x) + LogMsg("%5d: DNSServiceBrowse(%##s) STOP; WARNING m %p != x %p", ClientMachPort, qptr->q.qname.c, m, x); + else LogOperation("%5d: DNSServiceBrowse(%##s) STOP", ClientMachPort, qptr->q.qname.c); + mDNS_StopBrowse(&mDNSStorage, &qptr->q); + freePtr = qptr; + qptr = qptr->next; + freeL("DNSServiceBrowserQuestion", freePtr); + } + while (x->results) + { + DNSServiceBrowserResult *t = x->results; + x->results = x->results->next; + freeL("DNSServiceBrowserResult", t); + } + freeL("DNSServiceBrowser", x); + return; + } + + while (*l && (*l)->ClientMachPort != ClientMachPort) l = &(*l)->next; + if (*l) + { + DNSServiceResolver *x = *l; + *l = (*l)->next; + if (m && m != x) + LogMsg("%5d: DNSServiceResolve(%##s) STOP; WARNING m %p != x %p", ClientMachPort, x->i.name.c, m, x); + else LogOperation("%5d: DNSServiceResolve(%##s) STOP", ClientMachPort, x->i.name.c); + mDNS_StopResolveService(&mDNSStorage, &x->q); + freeL("DNSServiceResolver", x); + return; + } + + while (*r && (*r)->ClientMachPort != ClientMachPort) r = &(*r)->next; + if (*r) + { + ServiceInstance *si = NULL; + DNSServiceRegistration *x = *r; + *r = (*r)->next; + + si = x->regs; + while (si) + { + ServiceInstance *instance = si; + si = si->next; + instance->renameonmemfree = mDNSfalse; + if (m && m != x) LogMsg("%5d: DNSServiceRegistration(%##s, %u) STOP; WARNING m %p != x %p", ClientMachPort, instance->srs.RR_SRV.resrec.name->c, SRS_PORT(&instance->srs), m, x); + else LogOperation("%5d: DNSServiceRegistration(%##s, %u) STOP", ClientMachPort, instance->srs.RR_SRV.resrec.name->c, SRS_PORT(&instance->srs)); + + // If mDNS_DeregisterService() returns mStatus_NoError, that means that the service was found in the list, + // is sending its goodbye packet, and we'll get an mStatus_MemFree message when we can free the memory. + // If mDNS_DeregisterService() returns an error, it means that the service had already been removed from + // the list, so we should go ahead and free the memory right now + if (mDNS_DeregisterService(&mDNSStorage, &instance->srs)) FreeServiceInstance(instance); // FreeServiceInstance invalidates pointer + } + x->regs = NULL; + freeL("DNSServiceRegistration", x); + return; + } + + LogMsg("%5d: died or deallocated, but no record of client can be found!", ClientMachPort); +} + +#define AbortBlockedClient(C,MSG,M) AbortClientWithLogMessage((C), "stopped accepting Mach messages", " (" MSG ")", (M)) + +mDNSlocal void AbortClientWithLogMessage(mach_port_t c, char *reason, char *msg, void *m) +{ + DNSServiceDomainEnumeration *e = DNSServiceDomainEnumerationList; + DNSServiceBrowser *b = DNSServiceBrowserList; + DNSServiceResolver *l = DNSServiceResolverList; + DNSServiceRegistration *r = DNSServiceRegistrationList; + DNSServiceBrowserQuestion *qptr; + + while (e && e->ClientMachPort != c) e = e->next; + while (b && b->ClientMachPort != c) b = b->next; + while (l && l->ClientMachPort != c) l = l->next; + while (r && r->ClientMachPort != c) r = r->next; + + if (e) LogMsg("%5d: DomainEnumeration(%##s) %s%s", c, e->dom.qname.c, reason, msg); + else if (b) + { + for (qptr = b->qlist; qptr; qptr = qptr->next) + LogMsg("%5d: Browser(%##s) %s%s", c, qptr->q.qname.c, reason, msg); + } + else if (l) LogMsg("%5d: Resolver(%##s) %s%s", c, l->i.name.c, reason, msg); + else if (r) + { + ServiceInstance *si; + for (si = r->regs; si; si = si->next) + LogMsg("%5d: Registration(%##s) %s%s", c, si->srs.RR_SRV.resrec.name->c, reason, msg); + } + else LogMsg("%5d: (%s) %s, but no record of client can be found!", c, reason, msg); + + AbortClient(c, m); +} + +mDNSlocal mDNSBool CheckForExistingClient(mach_port_t c) +{ + DNSServiceDomainEnumeration *e = DNSServiceDomainEnumerationList; + DNSServiceBrowser *b = DNSServiceBrowserList; + DNSServiceResolver *l = DNSServiceResolverList; + DNSServiceRegistration *r = DNSServiceRegistrationList; + DNSServiceBrowserQuestion *qptr; + + while (e && e->ClientMachPort != c) e = e->next; + while (b && b->ClientMachPort != c) b = b->next; + while (l && l->ClientMachPort != c) l = l->next; + while (r && r->ClientMachPort != c) r = r->next; + if (e) LogMsg("%5d: DomainEnumeration(%##s) already exists!", c, e->dom.qname.c); + if (b) + { + for (qptr = b->qlist; qptr; qptr = qptr->next) + LogMsg("%5d: Browser(%##s) already exists!", c, qptr->q.qname.c); + } + if (l) LogMsg("%5d: Resolver(%##s) already exists!", c, l->i.name.c); + if (r) LogMsg("%5d: Registration(%##s) already exists!", c, r->regs ? r->regs->srs.RR_SRV.resrec.name->c : NULL); + return(e || b || l || r); +} + +#ifndef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM + +mDNSlocal void ClientDeathCallback(CFMachPortRef unusedport, void *voidmsg, CFIndex size, void *info) +{ + KQueueLock(&mDNSStorage); + mach_msg_header_t *msg = (mach_msg_header_t *)voidmsg; + (void)unusedport; // Unused + (void)size; // Unused + (void)info; // Unused + if (msg->msgh_id == MACH_NOTIFY_DEAD_NAME) + { + const mach_dead_name_notification_t *const deathMessage = (mach_dead_name_notification_t *)msg; + AbortClient(deathMessage->not_port, NULL); + + /* Deallocate the send right that came in the dead name notification */ + mach_port_destroy(mach_task_self(), deathMessage->not_port); + } + KQueueUnlock(&mDNSStorage, "Mach AbortClient"); +} + +#endif // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM + +mDNSlocal void EnableDeathNotificationForClient(mach_port_t ClientMachPort, void *m) +{ +#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM + dispatch_source_t mach_source = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_SEND, ClientMachPort, 0, dispatch_get_main_queue()); + if (mach_source == mDNSNULL) + { + AbortClientWithLogMessage(ClientMachPort, "died/deallocated before we could enable death notification", "", m); + return; + } + dispatch_source_set_event_handler(mach_source, ^{ + mach_port_destroy(mach_task_self(), ClientMachPort); + }); + dispatch_resume(mach_source); +#else // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM + mach_port_t prev; + kern_return_t r = mach_port_request_notification(mach_task_self(), ClientMachPort, MACH_NOTIFY_DEAD_NAME, 0, + client_death_port, MACH_MSG_TYPE_MAKE_SEND_ONCE, &prev); + // If the port already died while we were thinking about it, then abort the operation right away + if (r != KERN_SUCCESS) + AbortClientWithLogMessage(ClientMachPort, "died/deallocated before we could enable death notification", "", m); +#endif // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM +} + +//************************************************************************************************************* +// Domain Enumeration + +mDNSlocal void DomainEnumFound(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord) +{ + kern_return_t status; + char buffer[MAX_ESCAPED_DOMAIN_NAME]; + DNSServiceDomainEnumerationReplyResultType rt; + DNSServiceDomainEnumeration *x = (DNSServiceDomainEnumeration *)question->QuestionContext; + (void)m; // Unused + + debugf("DomainEnumFound: %##s PTR %##s", answer->name->c, answer->rdata->u.name.c); + if (answer->rrtype != kDNSType_PTR) return; + if (!x) { debugf("DomainEnumFound: DNSServiceDomainEnumeration is NULL"); return; } + + if (AddRecord) + { + if (question == &x->dom) rt = DNSServiceDomainEnumerationReplyAddDomain; + else rt = DNSServiceDomainEnumerationReplyAddDomainDefault; + } + else + { + if (question == &x->dom) rt = DNSServiceDomainEnumerationReplyRemoveDomain; + else return; + } + + LogOperation("%5d: DNSServiceDomainEnumeration(%##s) %##s %s", + x->ClientMachPort, x->dom.qname.c, answer->rdata->u.name.c, + !AddRecord ? "RemoveDomain" : + question == &x->dom ? "AddDomain" : "AddDomainDefault"); + + ConvertDomainNameToCString(&answer->rdata->u.name, buffer); + status = DNSServiceDomainEnumerationReply_rpc(x->ClientMachPort, rt, buffer, 0, MDNS_MM_TIMEOUT); + if (status == MACH_SEND_TIMED_OUT) + AbortBlockedClient(x->ClientMachPort, "enumeration", x); +} + +mDNSexport kern_return_t provide_DNSServiceDomainEnumerationCreate_rpc(mach_port_t unusedserver, mach_port_t client, + int regDom) +{ + // Check client parameter + (void)unusedserver; // Unused + mStatus err = mStatus_NoError; + const char *errormsg = "Unknown"; + if (client == (mach_port_t)-1) { err = mStatus_Invalid; errormsg = "Client id -1 invalid"; goto fail; } + if (CheckForExistingClient(client)) { err = mStatus_Invalid; errormsg = "Client id already in use"; goto fail; } + + mDNS_DomainType dt1 = regDom ? mDNS_DomainTypeRegistration : mDNS_DomainTypeBrowse; + mDNS_DomainType dt2 = regDom ? mDNS_DomainTypeRegistrationDefault : mDNS_DomainTypeBrowseDefault; + + // Allocate memory, and handle failure + DNSServiceDomainEnumeration *x = mallocL("DNSServiceDomainEnumeration", sizeof(*x)); + if (!x) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; } + + // Set up object, and link into list + x->ClientMachPort = client; + x->next = DNSServiceDomainEnumerationList; + DNSServiceDomainEnumerationList = x; + + verbosedebugf("%5d: Enumerate %s Domains", client, regDom ? "Registration" : "Browsing"); + + // Do the operation + err = mDNS_GetDomains(&mDNSStorage, &x->dom, dt1, NULL, mDNSInterface_LocalOnly, DomainEnumFound, x); + if (!err) err = mDNS_GetDomains(&mDNSStorage, &x->def, dt2, NULL, mDNSInterface_LocalOnly, DomainEnumFound, x); + if (err) { AbortClient(client, x); errormsg = "mDNS_GetDomains"; goto fail; } + + // Succeeded: Wrap up and return + LogOperation("%5d: DNSServiceDomainEnumeration(%##s) START", client, x->dom.qname.c); + EnableDeathNotificationForClient(client, x); + return(mStatus_NoError); + +fail: + LogMsg("%5d: DNSServiceDomainEnumeration(%d) failed: %s (%d)", client, regDom, errormsg, err); + return(err); +} + +//************************************************************************************************************* +// Browse for services + +mDNSlocal void FoundInstance(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord) +{ + (void)m; // Unused + + if (answer->rrtype != kDNSType_PTR) + { LogMsg("FoundInstance: Should not be called with rrtype %d (not a PTR record)", answer->rrtype); return; } + + domainlabel name; + domainname type, domain; + if (!DeconstructServiceName(&answer->rdata->u.name, &name, &type, &domain)) + { + LogMsg("FoundInstance: %##s PTR %##s received from network is not valid DNS-SD service pointer", + answer->name->c, answer->rdata->u.name.c); + return; + } + + DNSServiceBrowserResult *x = mallocL("DNSServiceBrowserResult", sizeof(*x)); + if (!x) { LogMsg("FoundInstance: Failed to allocate memory for result %##s", answer->rdata->u.name.c); return; } + + verbosedebugf("FoundInstance: %s %##s", AddRecord ? "Add" : "Rmv", answer->rdata->u.name.c); + AssignDomainName(&x->result, &answer->rdata->u.name); + if (AddRecord) + x->resultType = DNSServiceBrowserReplyAddInstance; + else x->resultType = DNSServiceBrowserReplyRemoveInstance; + x->next = NULL; + + DNSServiceBrowser *browser = (DNSServiceBrowser *)question->QuestionContext; + DNSServiceBrowserResult **p = &browser->results; + while (*p) p = &(*p)->next; + *p = x; + + LogOperation("%5d: DNSServiceBrowse(%##s, %s) RESULT %s %s", + browser->ClientMachPort, question->qname.c, DNSTypeName(question->qtype), AddRecord ? "Add" : "Rmv", RRDisplayString(m, answer)); +} + +mDNSlocal mStatus AddDomainToBrowser(DNSServiceBrowser *browser, const domainname *d) +{ + mStatus err = mStatus_NoError; + DNSServiceBrowserQuestion *ptr, *question = NULL; + + for (ptr = browser->qlist; ptr; ptr = ptr->next) + { + if (SameDomainName(&ptr->q.qname, d)) + { debugf("Domain %##s already contained in browser", d->c); return mStatus_AlreadyRegistered; } + } + + question = mallocL("DNSServiceBrowserQuestion", sizeof(DNSServiceBrowserQuestion)); + if (!question) { LogMsg("Error: malloc"); return mStatus_NoMemoryErr; } + AssignDomainName(&question->domain, d); + question->next = browser->qlist; + LogOperation("%5d: DNSServiceBrowse(%##s%##s) START", browser->ClientMachPort, browser->type.c, d->c); + err = mDNS_StartBrowse(&mDNSStorage, &question->q, &browser->type, d, mDNSNULL, mDNSInterface_Any, 0, mDNSfalse, mDNSfalse, FoundInstance, browser); + if (!err) + browser->qlist = question; + else + { + LogMsg("Error: AddDomainToBrowser: mDNS_StartBrowse %d", err); + freeL("DNSServiceBrowserQuestion", question); + } + return err; +} + +mDNSexport void machserver_automatic_browse_domain_changed(const domainname *d, mDNSBool add) +{ + DNSServiceBrowser *ptr; + for (ptr = DNSServiceBrowserList; ptr; ptr = ptr->next) + { + if (ptr->DefaultDomain) + { + if (add) + { + mStatus err = AddDomainToBrowser(ptr, d); + if (err && err != mStatus_AlreadyRegistered) LogMsg("Default browse in domain %##s for client %5d failed. Continuing", d, ptr->ClientMachPort); + } + else + { + DNSServiceBrowserQuestion **q = &ptr->qlist; + while (*q) + { + if (SameDomainName(&(*q)->domain, d)) + { + DNSServiceBrowserQuestion *rem = *q; + *q = (*q)->next; + mDNS_StopQueryWithRemoves(&mDNSStorage, &rem->q); + freeL("DNSServiceBrowserQuestion", rem); + return; + } + q = &(*q)->next; + } + LogMsg("Requested removal of default domain %##s not in client %5d's list", d->c, ptr->ClientMachPort); + } + } + } +} + +mDNSexport kern_return_t provide_DNSServiceBrowserCreate_rpc(mach_port_t unusedserver, mach_port_t client, + DNSCString regtype, DNSCString domain) +{ + // Check client parameter + (void)unusedserver; // Unused + mStatus err = mStatus_NoError; + const char *errormsg = "Unknown"; + + if (client == (mach_port_t)-1) { err = mStatus_Invalid; errormsg = "Client id -1 invalid"; goto fail; } + if (CheckForExistingClient(client)) { err = mStatus_Invalid; errormsg = "Client id already in use"; goto fail; } + + // Check other parameters + domainname t, d; + t.c[0] = 0; + mDNSs32 NumSubTypes = ChopSubTypes(regtype, mDNSNULL); // Note: Modifies regtype string to remove trailing subtypes + if (NumSubTypes < 0 || NumSubTypes > 1) { errormsg = "Bad Service SubType"; goto badparam; } + if (NumSubTypes == 1 && !AppendDNSNameString(&t, regtype + strlen(regtype) + 1)) + { errormsg = "Bad Service SubType"; goto badparam; } + if (!regtype[0] || !AppendDNSNameString(&t, regtype)) { errormsg = "Illegal regtype"; goto badparam; } + domainname temp; + if (!MakeDomainNameFromDNSNameString(&temp, regtype)) { errormsg = "Illegal regtype"; goto badparam; } + if (temp.c[0] > 15 && (!domain || domain[0] == 0)) domain = "local."; // For over-long service types, we only allow domain "local" + + // Allocate memory, and handle failure + DNSServiceBrowser *x = mallocL("DNSServiceBrowser", sizeof(*x)); + if (!x) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; } + + // Set up object, and link into list + AssignDomainName(&x->type, &t); + x->ClientMachPort = client; + x->results = NULL; + x->lastsuccess = 0; + x->qlist = NULL; + x->next = DNSServiceBrowserList; + DNSServiceBrowserList = x; + + if (domain[0]) + { + // Start browser for an explicit domain + x->DefaultDomain = mDNSfalse; + if (!MakeDomainNameFromDNSNameString(&d, domain)) { errormsg = "Illegal domain"; goto badparam; } + err = AddDomainToBrowser(x, &d); + if (err) { AbortClient(client, x); errormsg = "AddDomainToBrowser"; goto fail; } + } + else + { + DNameListElem *sdPtr; + // Start browser on all domains + x->DefaultDomain = mDNStrue; + if (!AutoBrowseDomains) { AbortClient(client, x); errormsg = "GetSearchDomainList"; goto fail; } + for (sdPtr = AutoBrowseDomains; sdPtr; sdPtr = sdPtr->next) + { + err = AddDomainToBrowser(x, &sdPtr->name); + if (err) + { + // only terminally bail if .local fails + if (!SameDomainName(&localdomain, &sdPtr->name)) + LogMsg("Default browse in domain %##s failed. Continuing", sdPtr->name.c); + else { AbortClient(client, x); errormsg = "AddDomainToBrowser"; goto fail; } + } + } + } + + // Succeeded: Wrap up and return + EnableDeathNotificationForClient(client, x); + return(mStatus_NoError); + +badparam: + err = mStatus_BadParamErr; +fail: + LogMsg("%5d: DNSServiceBrowse(\"%s\", \"%s\") failed: %s (%d)", client, regtype, domain, errormsg, err); + return(err); +} + +//************************************************************************************************************* +// Resolve Service Info + +mDNSlocal void FoundInstanceInfo(mDNS *const m, ServiceInfoQuery *query) +{ + kern_return_t status; + DNSServiceResolver *x = (DNSServiceResolver *)query->ServiceInfoQueryContext; + NetworkInterfaceInfoOSX *ifx = IfindexToInterfaceInfoOSX(m, query->info->InterfaceID); + if (query->info->InterfaceID == mDNSInterface_LocalOnly || query->info->InterfaceID == mDNSInterface_P2P) ifx = mDNSNULL; + struct sockaddr_storage interface; + struct sockaddr_storage address; + char cstring[1024]; + int i, pstrlen = query->info->TXTinfo[0]; + (void)m; // Unused + + //debugf("FoundInstanceInfo %.4a %.4a %##s", &query->info->InterfaceAddr, &query->info->ip, &query->info->name); + + if (query->info->TXTlen > sizeof(cstring)) return; + + mDNSPlatformMemZero(&interface, sizeof(interface)); + mDNSPlatformMemZero(&address, sizeof(address)); + + if (ifx && ifx->ifinfo.ip.type == mDNSAddrType_IPv4) + { + struct sockaddr_in *s = (struct sockaddr_in*)&interface; + s->sin_len = sizeof(*s); + s->sin_family = AF_INET; + s->sin_port = 0; + s->sin_addr.s_addr = ifx->ifinfo.ip.ip.v4.NotAnInteger; + } + else if (ifx && ifx->ifinfo.ip.type == mDNSAddrType_IPv6) + { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)&interface; + sin6->sin6_len = sizeof(*sin6); + sin6->sin6_family = AF_INET6; + sin6->sin6_flowinfo = 0; + sin6->sin6_port = 0; + sin6->sin6_addr = *(struct in6_addr*)&ifx->ifinfo.ip.ip.v6; + sin6->sin6_scope_id = ifx->scope_id; + } + + if (query->info->ip.type == mDNSAddrType_IPv4) + { + struct sockaddr_in *s = (struct sockaddr_in*)&address; + s->sin_len = sizeof(*s); + s->sin_family = AF_INET; + s->sin_port = query->info->port.NotAnInteger; + s->sin_addr.s_addr = query->info->ip.ip.v4.NotAnInteger; + } + else + { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)&address; + sin6->sin6_len = sizeof(*sin6); + sin6->sin6_family = AF_INET6; + sin6->sin6_port = query->info->port.NotAnInteger; + sin6->sin6_flowinfo = 0; + sin6->sin6_addr = *(struct in6_addr*)&query->info->ip.ip.v6; + sin6->sin6_scope_id = ifx ? ifx->scope_id : 0; + } + + // The OS X DNSServiceResolverResolve() API is defined using a C-string, + // but the mDNS_StartResolveService() call actually returns a packed block of P-strings. + // Hence we have to convert the P-string(s) to a C-string before returning the result to the client. + // ASCII-1 characters are used in the C-string as boundary markers, + // to indicate the boundaries between the original constituent P-strings. + for (i=1; i<query->info->TXTlen; i++) + { + if (--pstrlen >= 0) + cstring[i-1] = query->info->TXTinfo[i]; + else + { + cstring[i-1] = 1; + pstrlen = query->info->TXTinfo[i]; + } + } + cstring[i-1] = 0; // Put the terminating NULL on the end + + LogOperation("%5d: DNSServiceResolver(%##s) -> %#a:%u", x->ClientMachPort, + x->i.name.c, &query->info->ip, mDNSVal16(query->info->port)); + status = DNSServiceResolverReply_rpc(x->ClientMachPort, + (char*)&interface, (char*)&address, cstring, 0, MDNS_MM_TIMEOUT); + if (status == MACH_SEND_TIMED_OUT) + AbortBlockedClient(x->ClientMachPort, "resolve", x); +} + +mDNSexport kern_return_t provide_DNSServiceResolverResolve_rpc(mach_port_t unusedserver, mach_port_t client, + DNSCString name, DNSCString regtype, DNSCString domain) +{ + // Check client parameter + (void)unusedserver; // Unused + mStatus err = mStatus_NoError; + const char *errormsg = "Unknown"; + if (client == (mach_port_t)-1) { err = mStatus_Invalid; errormsg = "Client id -1 invalid"; goto fail; } + if (CheckForExistingClient(client)) { err = mStatus_Invalid; errormsg = "Client id already in use"; goto fail; } + + // Check other parameters + domainlabel n; + domainname t, d, srv; + if (!name[0] || !MakeDomainLabelFromLiteralString(&n, name)) { errormsg = "Bad Instance Name"; goto badparam; } + if (!regtype[0] || !MakeDomainNameFromDNSNameString(&t, regtype)) { errormsg = "Bad Service Type"; goto badparam; } + if (!domain[0] || !MakeDomainNameFromDNSNameString(&d, domain)) { errormsg = "Bad Domain"; goto badparam; } + if (!ConstructServiceName(&srv, &n, &t, &d)) { errormsg = "Bad Name"; goto badparam; } + + // Allocate memory, and handle failure + DNSServiceResolver *x = mallocL("DNSServiceResolver", sizeof(*x)); + if (!x) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; } + + // Set up object, and link into list + x->ClientMachPort = client; + x->i.InterfaceID = mDNSInterface_Any; + x->i.name = srv; + x->ReportTime = NonZeroTime(mDNS_TimeNow(&mDNSStorage) + 130 * mDNSPlatformOneSecond); + x->next = DNSServiceResolverList; + DNSServiceResolverList = x; + + // Do the operation + LogOperation("%5d: DNSServiceResolve(%##s) START", client, x->i.name.c); + err = mDNS_StartResolveService(&mDNSStorage, &x->q, &x->i, FoundInstanceInfo, x); + if (err) { AbortClient(client, x); errormsg = "mDNS_StartResolveService"; goto fail; } + + // Succeeded: Wrap up and return + EnableDeathNotificationForClient(client, x); + return(mStatus_NoError); + +badparam: + err = mStatus_BadParamErr; +fail: + LogMsg("%5d: DNSServiceResolve(\"%s\", \"%s\", \"%s\") failed: %s (%d)", client, name, regtype, domain, errormsg, err); + return(err); +} + +//************************************************************************************************************* +// Registration + +mDNSexport void RecordUpdatedNiceLabel(mDNS *const m, mDNSs32 delay) +{ + m->p->NotifyUser = NonZeroTime(m->timenow + delay); +} + +mDNSlocal void RegCallback(mDNS *const m, ServiceRecordSet *const srs, mStatus result) +{ + ServiceInstance *si = (ServiceInstance*)srs->ServiceContext; + + if (result == mStatus_NoError) + { + kern_return_t status; + LogOperation("%5d: DNSServiceRegistration(%##s, %u) Name Registered", si->ClientMachPort, srs->RR_SRV.resrec.name->c, SRS_PORT(srs)); + status = DNSServiceRegistrationReply_rpc(si->ClientMachPort, result, MDNS_MM_TIMEOUT); + if (status == MACH_SEND_TIMED_OUT) + AbortBlockedClient(si->ClientMachPort, "registration success", si); + if (si->autoname && CountPeerRegistrations(m, srs) == 0) + RecordUpdatedNiceLabel(m, 0); // Successfully got new name, tell user immediately + } + + else if (result == mStatus_NameConflict) + { + LogOperation("%5d: DNSServiceRegistration(%##s, %u) Name Conflict", si->ClientMachPort, srs->RR_SRV.resrec.name->c, SRS_PORT(srs)); + // Note: By the time we get the mStatus_NameConflict message, the service is already deregistered + // and the memory is free, so we don't have to wait for an mStatus_MemFree message as well. + if (si->autoname && CountPeerRegistrations(m, srs) == 0) + { + // On conflict for an autoname service, rename and reregister *all* autoname services + IncrementLabelSuffix(&m->nicelabel, mDNStrue); + mDNS_ConfigChanged(m); + } + else if (si->autoname) + { + mDNS_RenameAndReregisterService(m, srs, mDNSNULL); + return; + } + else + { + // If we get a name conflict, we tell the client about it, and then they are expected to dispose + // of their registration in the usual way (which we will catch via client death notification). + // If the Mach queue is full, we forcibly abort the client immediately. + kern_return_t status = DNSServiceRegistrationReply_rpc(si->ClientMachPort, result, MDNS_MM_TIMEOUT); + if (status == MACH_SEND_TIMED_OUT) + AbortBlockedClient(si->ClientMachPort, "registration conflict", NULL); + } + } + + else if (result == mStatus_MemFree) + { + if (si->renameonmemfree) // We intentionally terminated registration so we could re-register with new name + { + debugf("RegCallback renaming %#s to %#s", si->name.c, m->nicelabel.c); + si->renameonmemfree = mDNSfalse; + si->name = m->nicelabel; + mDNS_RenameAndReregisterService(m, srs, &si->name); + } + else + { + // SANITY CHECK: make sure service instance is no longer in any ServiceRegistration's list + DNSServiceRegistration *r; + for (r = DNSServiceRegistrationList; r; r = r->next) + { + ServiceInstance **sp = &r->regs; + while (*sp) + { + if (*sp == si) { LogMsg("RegCallback: %##s Still in list; removing", srs->RR_SRV.resrec.name->c); *sp = (*sp)->next; break; } + sp = &(*sp)->next; + } + } + // END SANITY CHECK + FreeServiceInstance(si); + } + } + + else if (result != mStatus_NATTraversal) + LogMsg("%5d: DNSServiceRegistration(%##s, %u) Unknown Result %d", si->ClientMachPort, srs->RR_SRV.resrec.name->c, SRS_PORT(srs), result); +} + +mDNSlocal mStatus AddServiceInstance(DNSServiceRegistration *x, const domainname *domain) +{ + mStatus err = 0; + ServiceInstance *si = NULL; + AuthRecord *SubTypes = NULL; + + for (si = x->regs; si; si = si->next) + { + if (SameDomainName(&si->domain, domain)) + { LogMsg("Requested addition of domain %##s already in list", domain->c); return mStatus_AlreadyRegistered; } + } + + SubTypes = AllocateSubTypes(x->NumSubTypes, x->regtype, mDNSNULL); + if (x->NumSubTypes && !SubTypes) return mStatus_NoMemoryErr; + + si = mallocL("ServiceInstance", sizeof(*si) - sizeof(RDataBody) + x->rdsize); + if (!si) return mStatus_NoMemoryErr; + + si->ClientMachPort = x->ClientMachPort; + si->renameonmemfree = mDNSfalse; + si->autoname = x->autoname; + si->name = x->autoname ? mDNSStorage.nicelabel : x->name; + si->domain = *domain; + si->srs.AnonData = mDNSNULL; + + err = mDNS_RegisterService(&mDNSStorage, &si->srs, &si->name, &x->type, domain, NULL, + x->port, x->txtinfo, x->txt_len, SubTypes, x->NumSubTypes, mDNSInterface_Any, RegCallback, si, 0); + if (!err) + { + si->next = x->regs; + x->regs = si; + } + else + { + LogMsg("Error %d for registration of service in domain %##s", err, domain->c); + freeL("ServiceInstance", si); + } + return err; +} + +mDNSexport void machserver_automatic_registration_domain_changed(const domainname *d, mDNSBool add) +{ + DNSServiceRegistration *reg; + + for (reg = DNSServiceRegistrationList; reg; reg = reg->next) + { + if (reg->DefaultDomain) + { + if (add) + AddServiceInstance(reg, d); + else + { + ServiceInstance **si = ®->regs; + while (*si) + { + if (SameDomainName(&(*si)->domain, d)) + { + ServiceInstance *s = *si; + *si = (*si)->next; + if (mDNS_DeregisterService(&mDNSStorage, &s->srs)) FreeServiceInstance(s); // only free memory synchronously on error + break; + } + si = &(*si)->next; + } + if (!si) debugf("Requested removal of default domain %##s not in client %5d's list", d, reg->ClientMachPort); // normal if registration failed + } + } + } +} + +mDNSexport kern_return_t provide_DNSServiceRegistrationCreate_rpc(mach_port_t unusedserver, mach_port_t client, + DNSCString name, DNSCString regtype, DNSCString domain, IPPort IpPort, DNSCString txtRecord) +{ + (void)unusedserver; // Unused + mStatus err = mStatus_NoError; + const char *errormsg = "Unknown"; + + // older versions of this code passed the port via mach IPC as an int. + // we continue to pass it as 4 bytes to maintain binary compatibility, + // but now ensure that the network byte order is preserved by using a struct + mDNSIPPort port; + port.b[0] = IpPort.bytes[2]; + port.b[1] = IpPort.bytes[3]; + + if (client == (mach_port_t)-1) { err = mStatus_Invalid; errormsg = "Client id -1 invalid"; goto fail; } + if (CheckForExistingClient(client)) { err = mStatus_Invalid; errormsg = "Client id already in use"; goto fail; } + + // Check for sub-types after the service type + size_t reglen = strlen(regtype) + 1; + if (reglen > MAX_ESCAPED_DOMAIN_NAME) { errormsg = "reglen too long"; goto badparam; } + mDNSs32 NumSubTypes = ChopSubTypes(regtype, mDNSNULL); // Note: Modifies regtype string to remove trailing subtypes + if (NumSubTypes < 0) { errormsg = "Bad Service SubType"; goto badparam; } + + // Check other parameters + domainlabel n; + domainname t, d; + domainname srv; + if (!name[0]) n = mDNSStorage.nicelabel; + else if (!MakeDomainLabelFromLiteralString(&n, name)) { errormsg = "Bad Instance Name"; goto badparam; } + if (!regtype[0] || !MakeDomainNameFromDNSNameString(&t, regtype)) { errormsg = "Bad Service Type"; goto badparam; } + if (!MakeDomainNameFromDNSNameString(&d, *domain ? domain : "local.")) { errormsg = "Bad Domain"; goto badparam; } + if (!ConstructServiceName(&srv, &n, &t, &d)) { errormsg = "Bad Name"; goto badparam; } + + unsigned char txtinfo[1024] = ""; + unsigned int data_len = 0; + unsigned int size = sizeof(RDataBody); + unsigned char *pstring = &txtinfo[data_len]; + char *ptr = txtRecord; + + // The OS X DNSServiceRegistrationCreate() API is defined using a C-string, + // but the mDNS_RegisterService() call actually requires a packed block of P-strings. + // Hence we have to convert the C-string to a P-string. + // ASCII-1 characters are allowed in the C-string as boundary markers, + // so that a single C-string can be used to represent one or more P-strings. + while (*ptr) + { + if (++data_len >= sizeof(txtinfo)) { errormsg = "TXT record too long"; goto badtxt; } + if (*ptr == 1) // If this is our boundary marker, start a new P-string + { + pstring = &txtinfo[data_len]; + pstring[0] = 0; + ptr++; + } + else + { + if (pstring[0] == 255) { errormsg = "TXT record invalid (component longer than 255)"; goto badtxt; } + pstring[++pstring[0]] = *ptr++; + } + } + + data_len++; + if (size < data_len) + size = data_len; + + // Some clients use mDNS for lightweight copy protection, registering a pseudo-service with + // a port number of zero. When two instances of the protected client are allowed to run on one + // machine, we don't want to see misleading "Bogus client" messages in syslog and the console. + if (!mDNSIPPortIsZero(port)) + { + int count = CountExistingRegistrations(&srv, port); + if (count) + LogMsg("%5d: Client application registered %d identical instances of service %##s port %u.", + client, count+1, srv.c, mDNSVal16(port)); + } + + // Allocate memory, and handle failure + DNSServiceRegistration *x = mallocL("DNSServiceRegistration", sizeof(*x)); + if (!x) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; } + mDNSPlatformMemZero(x, sizeof(*x)); + + // Set up object, and link into list + x->ClientMachPort = client; + x->DefaultDomain = !domain[0]; + x->autoname = (!name[0]); + x->rdsize = size; + x->NumSubTypes = NumSubTypes; + memcpy(x->regtype, regtype, reglen); + x->name = n; + x->type = t; + x->port = port; + memcpy(x->txtinfo, txtinfo, 1024); + x->txt_len = data_len; + x->NextRef = 0; + x->regs = NULL; + + x->next = DNSServiceRegistrationList; + DNSServiceRegistrationList = x; + + LogOperation("%5d: DNSServiceRegistration(\"%s\", \"%s\", \"%s\", %u) START", + x->ClientMachPort, name, regtype, domain, mDNSVal16(port)); + + err = AddServiceInstance(x, &d); + if (err) { AbortClient(client, x); errormsg = "mDNS_RegisterService"; goto fail; } // bail if .local (or explicit domain) fails + + if (x->DefaultDomain) + { + DNameListElem *p; + for (p = AutoRegistrationDomains; p; p = p->next) + AddServiceInstance(x, &p->name); + } + + // Succeeded: Wrap up and return + EnableDeathNotificationForClient(client, x); + return(mStatus_NoError); + +badtxt: + LogMsg("%5d: TXT record: %.100s...", client, txtRecord); +badparam: + err = mStatus_BadParamErr; +fail: + LogMsg("%5d: DNSServiceRegister(\"%s\", \"%s\", \"%s\", %d) failed: %s (%d)", + client, name, regtype, domain, mDNSVal16(port), errormsg, err); + return(err); +} + +mDNSlocal void mDNSPreferencesSetNames(mDNS *const m, int key, domainlabel *old, domainlabel *new) +{ + domainlabel *prevold, *prevnew; + switch (key) + { + case kmDNSComputerName: + case kmDNSLocalHostName: + if (key == kmDNSComputerName) + { + prevold = &m->p->prevoldnicelabel; + prevnew = &m->p->prevnewnicelabel; + } + else + { + prevold = &m->p->prevoldhostlabel; + prevnew = &m->p->prevnewhostlabel; + } + // There are a few cases where we need to invoke the helper. + // + // 1. If the "old" label and "new" label are not same, it means there is a conflict. We need + // to invoke the helper so that it pops up a dialogue to inform the user about the + // conflict + // + // 2. If the "old" label and "new" label are same, it means the user has set the host/nice label + // through the preferences pane. We may have to inform the helper as it may have popped up + // a dialogue previously (due to a conflict) and it needs to suppress it now. We can avoid invoking + // the helper in this case if the previous values (old and new) that we told helper last time + // are same. If the previous old and new values are same, helper does not care. + // + // Note: "new" can be NULL when we have repeated conflicts and we are asking helper to give up. "old" + // is not called with NULL today, but this makes it future proof. + if (!old || !new || !SameDomainLabelCS(old->c, new->c) || + !SameDomainLabelCS(old->c, prevold->c) || + !SameDomainLabelCS(new->c, prevnew->c)) + { + if (old) + *prevold = *old; + else + prevold->c[0] = 0; + if (new) + *prevnew = *new; + else + prevnew->c[0] = 0; + mDNSPreferencesSetName(key, old, new); + } + else + { + LogInfo("mDNSPreferencesSetNames not invoking helper %s %#s, %s %#s, old %#s, new %#s", + (key == kmDNSComputerName ? "prevoldnicelabel" : "prevoldhostlabel"), prevold->c, + (key == kmDNSComputerName ? "prevnewnicelabel" : "prevnewhostlabel"), prevnew->c, + old->c, new->c); + } + break; + default: + LogMsg("mDNSPreferencesSetNames: unrecognized key: %d", key); + return; + } +} + +mDNSlocal void mDNS_StatusCallback(mDNS *const m, mStatus result) +{ + (void)m; // Unused + if (result == mStatus_NoError) + { + if (!SameDomainLabelCS(m->p->userhostlabel.c, m->hostlabel.c)) + LogInfo("Local Hostname changed from \"%#s.local\" to \"%#s.local\"", m->p->userhostlabel.c, m->hostlabel.c); + // One second pause in case we get a Computer Name update too -- don't want to alert the user twice + RecordUpdatedNiceLabel(m, mDNSPlatformOneSecond); + } + else if (result == mStatus_NameConflict) + { + LogInfo("Local Hostname conflict for \"%#s.local\"", m->hostlabel.c); + if (!m->p->HostNameConflict) m->p->HostNameConflict = NonZeroTime(m->timenow); + else if (m->timenow - m->p->HostNameConflict > 60 * mDNSPlatformOneSecond) + { + // Tell the helper we've given up + mDNSPreferencesSetNames(m, kmDNSLocalHostName, &m->p->userhostlabel, NULL); + } + } + else if (result == mStatus_GrowCache) + { + // Allocate another chunk of cache storage + CacheEntity *storage = mallocL("mStatus_GrowCache", sizeof(CacheEntity) * RR_CACHE_SIZE); + //LogInfo("GrowCache %d * %d = %d", sizeof(CacheEntity), RR_CACHE_SIZE, sizeof(CacheEntity) * RR_CACHE_SIZE); + if (storage) mDNS_GrowCache(m, storage, RR_CACHE_SIZE); + } + else if (result == mStatus_ConfigChanged) + { + // Tell the helper we've seen a change in the labels. It will dismiss the name conflict alert if needed. + mDNSPreferencesSetNames(m, kmDNSComputerName, &m->p->usernicelabel, &m->nicelabel); + mDNSPreferencesSetNames(m, kmDNSLocalHostName, &m->p->userhostlabel, &m->hostlabel); + + // First we check our list of old Mach-based registered services, to see if any need to be updated to a new name + DNSServiceRegistration *r; + for (r = DNSServiceRegistrationList; r; r=r->next) + if (r->autoname) + { + ServiceInstance *si; + for (si = r->regs; si; si = si->next) + { + if (!SameDomainLabelCS(si->name.c, m->nicelabel.c)) + { + debugf("NetworkChanged renaming %##s to %#s", si->srs.RR_SRV.resrec.name->c, m->nicelabel.c); + si->renameonmemfree = mDNStrue; + if (mDNS_DeregisterService_drt(m, &si->srs, mDNS_Dereg_rapid)) + RegCallback(m, &si->srs, mStatus_MemFree); // If service deregistered already, we can re-register immediately + } + } + } + + // Then we call into the UDS daemon code, to let it do the same + udsserver_handle_configchange(m); + } +} + +//************************************************************************************************************* +// Add / Update / Remove records from existing Registration + +mDNSexport kern_return_t provide_DNSServiceRegistrationAddRecord_rpc(mach_port_t unusedserver, mach_port_t client, + int type, const char *data, mach_msg_type_number_t data_len, uint32_t ttl, natural_t *reference) +{ + // Check client parameter + uint32_t id; + mStatus err = mStatus_NoError; + const char *errormsg = "Unknown"; + DNSServiceRegistration *x = DNSServiceRegistrationList; + if (client == (mach_port_t)-1) { err = mStatus_Invalid; errormsg = "Client id -1 invalid"; goto fail; } + ServiceInstance *si; + size_t size; + (void)unusedserver; // Unused + while (x && x->ClientMachPort != client) x = x->next; + if (!x) { err = mStatus_BadReferenceErr; errormsg = "No such client"; goto fail; } + + // Check other parameters + if (data_len > 8192) { err = mStatus_BadParamErr; errormsg = "data_len > 8K"; goto fail; } + if (data_len > sizeof(RDataBody)) size = data_len; + else size = sizeof(RDataBody); + + id = x->NextRef++; + *reference = (natural_t)id; + for (si = x->regs; si; si = si->next) + { + // Allocate memory, and handle failure + ExtraResourceRecord *extra = mallocL("ExtraResourceRecord", sizeof(*extra) - sizeof(RDataBody) + size); + if (!extra) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; } + + // Fill in type, length, and data of new record + extra->r.resrec.rrtype = type; + extra->r.rdatastorage.MaxRDLength = size; + extra->r.resrec.rdlength = data_len; + memcpy(&extra->r.rdatastorage.u.data, data, data_len); + + // Do the operation + LogOperation("%5d: DNSServiceRegistrationAddRecord(%##s, type %d, length %d) REF %p", + client, si->srs.RR_SRV.resrec.name->c, type, data_len, extra); + err = mDNS_AddRecordToService(&mDNSStorage, &si->srs, extra, &extra->r.rdatastorage, ttl, 0); + + if (err) + { + freeL("Extra Resource Record", extra); + errormsg = "mDNS_AddRecordToService"; + goto fail; + } + + extra->ClientID = id; + } + + return mStatus_NoError; + +fail: + LogMsg("%5d: DNSServiceRegistrationAddRecord(%##s, type %d, length %d) failed: %s (%d)", client, x ? x->name.c : (mDNSu8*)"\x8" "«NULL»", type, data_len, errormsg, err); + return mStatus_UnknownErr; +} + +mDNSlocal void UpdateCallback(mDNS *const m, AuthRecord *const rr, RData *OldRData, mDNSu16 OldRDLen) +{ + (void)m; // Unused + (void)OldRDLen; // Unused + if (OldRData != &rr->rdatastorage) + freeL("Old RData", OldRData); +} + +mDNSlocal mStatus UpdateRecord(ServiceRecordSet *srs, mach_port_t client, AuthRecord *rr, const char *data, mach_msg_type_number_t data_len, uint32_t ttl) +{ + // Check client parameter + mStatus err = mStatus_NoError; + const char *errormsg = "Unknown"; + const domainname *name = (const domainname *)""; + + name = srs->RR_SRV.resrec.name; + + unsigned int size = sizeof(RDataBody); + if (size < data_len) + size = data_len; + + // Allocate memory, and handle failure + RData *newrdata = mallocL("RData", sizeof(*newrdata) - sizeof(RDataBody) + size); + if (!newrdata) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; } + + // Fill in new length, and data + newrdata->MaxRDLength = size; + memcpy(&newrdata->u, data, data_len); + + // BIND named (name daemon) doesn't allow TXT records with zero-length rdata. This is strictly speaking correct, + // since RFC 1035 specifies a TXT record as "One or more <character-string>s", not "Zero or more <character-string>s". + // Since some legacy apps try to create zero-length TXT records, we'll silently correct it here. + if (rr->resrec.rrtype == kDNSType_TXT && data_len == 0) { data_len = 1; newrdata->u.txt.c[0] = 0; } + + // Do the operation + LogOperation("%5d: DNSServiceRegistrationUpdateRecord(%##s, new length %d)", + client, srs->RR_SRV.resrec.name->c, data_len); + + err = mDNS_Update(&mDNSStorage, rr, ttl, data_len, newrdata, UpdateCallback); + if (err) + { + errormsg = "mDNS_Update"; + freeL("RData", newrdata); + return err; + } + return(mStatus_NoError); + +fail: + LogMsg("%5d: DNSServiceRegistrationUpdateRecord(%##s, %d) failed: %s (%d)", client, name->c, data_len, errormsg, err); + return(err); +} + +mDNSexport kern_return_t provide_DNSServiceRegistrationUpdateRecord_rpc(mach_port_t unusedserver, mach_port_t client, + natural_t reference, const char *data, mach_msg_type_number_t data_len, uint32_t ttl) +{ + // Check client parameter + mStatus err = mStatus_NoError; + const char *errormsg = "Unknown"; + const domainname *name = (const domainname *)""; + ServiceInstance *si; + + (void)unusedserver; // unused + if (client == (mach_port_t)-1) { err = mStatus_Invalid; errormsg = "Client id -1 invalid"; goto fail; } + DNSServiceRegistration *x = DNSServiceRegistrationList; + while (x && x->ClientMachPort != client) x = x->next; + if (!x) { err = mStatus_BadReferenceErr; errormsg = "No such client"; goto fail; } + + // Check other parameters + if (data_len > 8192) { err = mStatus_BadParamErr; errormsg = "data_len > 8K"; goto fail; } + + for (si = x->regs; si; si = si->next) + { + AuthRecord *r = NULL; + + // Find the record we're updating. NULL reference means update the primary TXT record + if (!reference) r = &si->srs.RR_TXT; + else + { + ExtraResourceRecord *ptr; + for (ptr = si->srs.Extras; ptr; ptr = ptr->next) + { + if ((natural_t)ptr->ClientID == reference) + { r = &ptr->r; break; } + } + if (!r) { err = mStatus_BadReferenceErr; errormsg = "No such record"; goto fail; } + } + err = UpdateRecord(&si->srs, client, r, data, data_len, ttl); + if (err) goto fail; //!!!KRS this will cause failures for non-local defaults! + } + + return mStatus_NoError; + +fail: + LogMsg("%5d: DNSServiceRegistrationUpdateRecord(%##s, %X, %d) failed: %s (%d)", client, name->c, reference, data_len, errormsg, err); + return(err); +} + +mDNSlocal mStatus RemoveRecord(ServiceRecordSet *srs, ExtraResourceRecord *extra, mach_port_t client) +{ + const domainname *const name = srs->RR_SRV.resrec.name; + mStatus err = mStatus_NoError; + + // Do the operation + LogOperation("%5d: DNSServiceRegistrationRemoveRecord(%##s)", client, srs->RR_SRV.resrec.name->c); + + err = mDNS_RemoveRecordFromService(&mDNSStorage, srs, extra, FreeExtraRR, extra); + if (err) LogMsg("%5d: DNSServiceRegistrationRemoveRecord (%##s) failed: %d", client, name->c, err); + + return err; +} + +mDNSexport kern_return_t provide_DNSServiceRegistrationRemoveRecord_rpc(mach_port_t unusedserver, mach_port_t client, + natural_t reference) +{ + // Check client parameter + (void)unusedserver; // Unused + mStatus err = mStatus_NoError; + const char *errormsg = "Unknown"; + if (client == (mach_port_t)-1) { err = mStatus_Invalid; errormsg = "Client id -1 invalid"; goto fail; } + DNSServiceRegistration *x = DNSServiceRegistrationList; + ServiceInstance *si; + + while (x && x->ClientMachPort != client) x = x->next; + if (!x) { err = mStatus_BadReferenceErr; errormsg = "No such client"; goto fail; } + + for (si = x->regs; si; si = si->next) + { + ExtraResourceRecord *e; + for (e = si->srs.Extras; e; e = e->next) + { + if ((natural_t)e->ClientID == reference) + { + err = RemoveRecord(&si->srs, e, client); + break; + } + } + if (!e) { err = mStatus_BadReferenceErr; errormsg = "No such reference"; goto fail; } + } + + return mStatus_NoError; + +fail: + LogMsg("%5d: DNSServiceRegistrationRemoveRecord(%X) failed: %s (%d)", client, reference, errormsg, err); + return(err); +} + +//************************************************************************************************************* +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - +#pragma mark - Startup, shutdown, and supporting code +#endif + +mDNSlocal void ExitCallback(int sig) +{ + (void)sig; // Unused + LogMsg("%s stopping", mDNSResponderVersionString); + + debugf("ExitCallback: Aborting MIG clients"); + while (DNSServiceDomainEnumerationList) + AbortClient(DNSServiceDomainEnumerationList->ClientMachPort, DNSServiceDomainEnumerationList); + while (DNSServiceBrowserList) + AbortClient(DNSServiceBrowserList->ClientMachPort, DNSServiceBrowserList); + while (DNSServiceResolverList) + AbortClient(DNSServiceResolverList->ClientMachPort, DNSServiceResolverList); + while (DNSServiceRegistrationList) + AbortClient(DNSServiceRegistrationList->ClientMachPort, DNSServiceRegistrationList); + + if (udsserver_exit() < 0) + LogMsg("ExitCallback: udsserver_exit failed"); + + debugf("ExitCallback: mDNS_StartExit"); + mDNS_StartExit(&mDNSStorage); +} + +#ifndef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM + +mDNSlocal void DNSserverCallback(CFMachPortRef port, void *msg, CFIndex size, void *info) +{ + mig_reply_error_t *request = msg; + mig_reply_error_t *reply; + mach_msg_return_t mr; + int options; + (void)port; // Unused + (void)size; // Unused + (void)info; // Unused + + KQueueLock(&mDNSStorage); + + /* allocate a reply buffer */ + reply = CFAllocatorAllocate(NULL, provide_DNSServiceDiscoveryRequest_subsystem.maxsize, 0); + + /* call the MiG server routine */ + (void) DNSServiceDiscoveryRequest_server(&request->Head, &reply->Head); + + if (!(reply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) && (reply->RetCode != KERN_SUCCESS)) + { + if (reply->RetCode == MIG_NO_REPLY) + { + /* + * This return code is a little tricky -- it appears that the + * demux routine found an error of some sort, but since that + * error would not normally get returned either to the local + * user or the remote one, we pretend it's ok. + */ + CFAllocatorDeallocate(NULL, reply); + goto done; + } + + /* + * destroy any out-of-line data in the request buffer but don't destroy + * the reply port right (since we need that to send an error message). + */ + request->Head.msgh_remote_port = MACH_PORT_NULL; + mach_msg_destroy(&request->Head); + } + + if (reply->Head.msgh_remote_port == MACH_PORT_NULL) + { + /* no reply port, so destroy the reply */ + if (reply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) + mach_msg_destroy(&reply->Head); + CFAllocatorDeallocate(NULL, reply); + goto done; + } + + /* + * send reply. + * + * We don't want to block indefinitely because the client + * isn't receiving messages from the reply port. + * If we have a send-once right for the reply port, then + * this isn't a concern because the send won't block. + * If we have a send right, we need to use MACH_SEND_TIMEOUT. + * To avoid falling off the kernel's fast RPC path unnecessarily, + * we only supply MACH_SEND_TIMEOUT when absolutely necessary. + */ + + options = MACH_SEND_MSG; + if (MACH_MSGH_BITS_REMOTE(reply->Head.msgh_bits) == MACH_MSG_TYPE_MOVE_SEND_ONCE) + options |= MACH_SEND_TIMEOUT; + + mr = mach_msg(&reply->Head, /* msg */ + options, /* option */ + reply->Head.msgh_size, /* send_size */ + 0, /* rcv_size */ + MACH_PORT_NULL, /* rcv_name */ + MACH_MSG_TIMEOUT_NONE, /* timeout */ + MACH_PORT_NULL); /* notify */ + + /* Has a message error occurred? */ + switch (mr) + { + case MACH_SEND_INVALID_DEST: + case MACH_SEND_TIMED_OUT: + /* the reply can't be delivered, so destroy it */ + mach_msg_destroy(&reply->Head); + break; + + default: + /* Includes success case. */ + break; + } + + CFAllocatorDeallocate(NULL, reply); + +done: + KQueueUnlock(&mDNSStorage, "Mach client event"); +} + +// Send a mach_msg to ourselves (since that is signal safe) telling us to cleanup and exit +mDNSlocal void HandleSIG(int sig) +{ + kern_return_t status; + mach_msg_header_t header; + + // WARNING: can't call syslog or fprintf from signal handler + header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, 0); + header.msgh_remote_port = signal_port; + header.msgh_local_port = MACH_PORT_NULL; + header.msgh_size = sizeof(header); + header.msgh_id = sig; + + status = mach_msg(&header, MACH_SEND_MSG | MACH_SEND_TIMEOUT, header.msgh_size, + 0, MACH_PORT_NULL, 0, MACH_PORT_NULL); + + if (status != MACH_MSG_SUCCESS) + { + if (status == MACH_SEND_TIMED_OUT) mach_msg_destroy(&header); + if (sig == SIGTERM || sig == SIGINT) exit(-1); + } +} + +#endif // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM + +mDNSlocal void INFOCallback(void) +{ + mDNSs32 utc = mDNSPlatformUTC(); + NetworkInterfaceInfoOSX *i; + DNSServer *s; + McastResolver *mr; + + // Create LoggerID(Key)->com.apple.networking.mDNSResponder(Value) pair when SIGINFO is received. + // This key-value pair is used as a condition by syslogd to Log to com.apple.networking.mDNSResponder.log file + // present in /etc/asl/com.apple.networking.mDNSResponder. + asl_set(log_msg, "LoggerID", "com.apple.networking.mDNSResponder"); + + LogMsg("---- BEGIN STATE LOG ---- %s %s %d", mDNSResponderVersionString, OSXVers ? "OSXVers" : "iOSVers", OSXVers ? OSXVers : iOSVers); + + udsserver_info(&mDNSStorage); + xpcserver_info(&mDNSStorage); + + LogMsgNoIdent("----- KQSocketEventSources -----"); + if (!gEventSources) LogMsgNoIdent("<None>"); + else + { + KQSocketEventSource *k; + for (k = gEventSources; k; k=k->next) + LogMsgNoIdent("%3d %s %s", k->fd, k->kqs.KQtask, k->fd == mDNSStorage.uds_listener_skt ? "Listener for incoming UDS clients" : " "); + } + + LogMsgNoIdent("------ Network Interfaces ------"); + if (!mDNSStorage.p->InterfaceList) LogMsgNoIdent("<None>"); + else + { + for (i = mDNSStorage.p->InterfaceList; i; i = i->next) + { + // Allow six characters for interface name, for names like "vmnet8" + if (!i->Exists) + LogMsgNoIdent("%p %2ld, Registered %p, %s %-6s(%lu) %.6a %.6a %#-14a dormant for %d seconds", + i, i->ifinfo.InterfaceID, i->Registered, + i->sa_family == AF_INET ? "v4" : i->sa_family == AF_INET6 ? "v6" : "??", i->ifinfo.ifname, i->scope_id, &i->ifinfo.MAC, &i->BSSID, + &i->ifinfo.ip, utc - i->LastSeen); + else + { + const CacheRecord *sps[3]; + FindSPSInCache(&mDNSStorage, &i->ifinfo.NetWakeBrowse, sps); + LogMsgNoIdent("%p %2ld, Registered %p, %s %-6s(%lu) %.6a %.6a %s %s %-15.4a %s %s %s %s %#a", + i, i->ifinfo.InterfaceID, i->Registered, + i->sa_family == AF_INET ? "v4" : i->sa_family == AF_INET6 ? "v6" : "??", i->ifinfo.ifname, i->scope_id, &i->ifinfo.MAC, &i->BSSID, + i->ifinfo.InterfaceActive ? "Active" : " ", + i->ifinfo.IPv4Available ? "v4" : " ", + i->ifinfo.IPv4Available ? (mDNSv4Addr*)&i->ifa_v4addr : &zerov4Addr, + i->ifinfo.IPv6Available ? "v6" : " ", + i->ifinfo.Advertise ? "A" : " ", + i->ifinfo.McastTxRx ? "M" : " ", + !(i->ifinfo.InterfaceActive && i->ifinfo.NetWake) ? " " : !sps[0] ? "p" : "P", + &i->ifinfo.ip); + + if (sps[0]) LogMsgNoIdent(" %13d %#s", SPSMetric(sps[0]->resrec.rdata->u.name.c), sps[0]->resrec.rdata->u.name.c); + if (sps[1]) LogMsgNoIdent(" %13d %#s", SPSMetric(sps[1]->resrec.rdata->u.name.c), sps[1]->resrec.rdata->u.name.c); + if (sps[2]) LogMsgNoIdent(" %13d %#s", SPSMetric(sps[2]->resrec.rdata->u.name.c), sps[2]->resrec.rdata->u.name.c); + } + } + } + + LogMsgNoIdent("--------- DNS Servers(%d) ----------", NumUnicastDNSServers); + if (!mDNSStorage.DNSServers) LogMsgNoIdent("<None>"); + else + { + for (s = mDNSStorage.DNSServers; s; s = s->next) + { + NetworkInterfaceInfoOSX *ifx = IfindexToInterfaceInfoOSX(&mDNSStorage, s->interface); + LogMsgNoIdent("DNS Server %##s %s%s%#a:%d %d %s %d %d %s %s %s %s %s", + s->domain.c, ifx ? ifx->ifinfo.ifname : "", ifx ? " " : "", &s->addr, mDNSVal16(s->port), + s->penaltyTime ? s->penaltyTime - mDNS_TimeNow(&mDNSStorage) : 0, DNSScopeToString(s->scoped), + s->timeout, s->resGroupID, + s->teststate == DNSServer_Untested ? "(Untested)" : + s->teststate == DNSServer_Passed ? "" : + s->teststate == DNSServer_Failed ? "(Failed)" : + s->teststate == DNSServer_Disabled ? "(Disabled)" : "(Unknown state)", + s->req_A ? "v4" : "!v4", + s->req_AAAA ? "v6" : "!v6", + s->cellIntf ? "cell" : "!cell", + s->DNSSECAware ? "DNSSECAware" : "!DNSSECAware"); + } + } + mDNSs32 now = mDNS_TimeNow(&mDNSStorage); + LogMsgNoIdent("v4answers %d", mDNSStorage.p->v4answers); + LogMsgNoIdent("v6answers %d", mDNSStorage.p->v6answers); + LogMsgNoIdent("Last DNS Trigger: %d ms ago", (now - mDNSStorage.p->DNSTrigger)); + + LogMsgNoIdent("--------- Mcast Resolvers ----------"); + if (!mDNSStorage.McastResolvers) LogMsgNoIdent("<None>"); + else + { + for (mr = mDNSStorage.McastResolvers; mr; mr = mr->next) + LogMsgNoIdent("Mcast Resolver %##s timeout %u", mr->domain.c, mr->timeout); + } + + LogMsgNoIdent("Timenow 0x%08lX (%d)", (mDNSu32)now, now); + LogMsg("---- END STATE LOG ---- %s %s %d", mDNSResponderVersionString, OSXVers ? "OSXVers" : "iOSVers", OSXVers ? OSXVers : iOSVers); + + // If logging is disabled, only then clear the key we set at the top of this func + if (!mDNS_LoggingEnabled) + asl_unset(log_msg, "LoggerID"); +} + +mDNSlocal void DebugSetFilter() +{ + if (!log_client) + return; + + // When USR1 is turned on, we log only the LOG_WARNING and LOG_NOTICE messages by default. + // The user has to manually do "syslog -c mDNSResponder -i" to get the LOG_INFO messages + // also to be logged. Most of the times, we need the INFO level messages for debugging. + // Hence, we set the filter to INFO level when USR1 logging is turned on to avoid + // having the user to do this extra step manually. + + if (mDNS_LoggingEnabled) + { + asl_set_filter(log_client, ASL_FILTER_MASK_UPTO(ASL_LEVEL_INFO)); + asl_set(log_msg, "LoggerID", "com.apple.networking.mDNSResponder"); + // Create LoggerID(Key)->com.apple.networking.mDNSResponder(Value) pair when USR1 Logging is Enabled. + // This key-value pair is used as a condition by syslogd to Log to com.apple.networking.mDNSResponder.log file + // present in /etc/asl/com.apple.networking.mDNSResponder. + } + else + { + asl_set_filter(log_client, ASL_FILTER_MASK_UPTO(ASL_LEVEL_ERR)); + asl_unset(log_msg, "LoggerID"); + // Clear the key-value pair when USR1 Logging is Disabled, as we do not want to log to + // com.apple.networking.mDNSResponder.log file in this case. + } +} + +mDNSexport void mDNSPlatformLogToFile(int log_level, const char *buffer) +{ + int asl_level = ASL_LEVEL_ERR; + + if (!log_client) + { + syslog(log_level, "%s", buffer); + return; + } + switch (log_level) + { + case LOG_ERR: + asl_level = ASL_LEVEL_ERR; + break; + case LOG_WARNING: + asl_level = ASL_LEVEL_WARNING; + break; + case LOG_NOTICE: + asl_level = ASL_LEVEL_NOTICE; + break; + case LOG_INFO: + asl_level = ASL_LEVEL_INFO; + break; + case LOG_DEBUG: + asl_level = ASL_LEVEL_DEBUG; + break; + default: + break; + } + asl_log(log_client, log_msg, asl_level, "%s", buffer); +} + +// Writes the state out to the dynamic store and also affects the ASL filter level +mDNSexport void UpdateDebugState() +{ + mDNSu32 one = 1; + mDNSu32 zero = 0; + + CFMutableDictionaryRef dict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + if (!dict) + { + LogMsg("UpdateDebugState: Could not create dict"); + return; + } + + CFNumberRef numOne = CFNumberCreate(NULL, kCFNumberSInt32Type, &one); + if (!numOne) + { + LogMsg("UpdateDebugState: Could not create CFNumber one"); + return; + } + CFNumberRef numZero = CFNumberCreate(NULL, kCFNumberSInt32Type, &zero); + if (!numZero) + { + LogMsg("UpdateDebugState: Could not create CFNumber zero"); + CFRelease(numOne); + return; + } + + if (mDNS_LoggingEnabled) + CFDictionarySetValue(dict, CFSTR("VerboseLogging"), numOne); + else + CFDictionarySetValue(dict, CFSTR("VerboseLogging"), numZero); + + if (mDNS_PacketLoggingEnabled) + CFDictionarySetValue(dict, CFSTR("PacketLogging"), numOne); + else + CFDictionarySetValue(dict, CFSTR("PacketLogging"), numZero); + + if (mDNS_McastLoggingEnabled) + CFDictionarySetValue(dict, CFSTR("McastLogging"), numOne); + else + CFDictionarySetValue(dict, CFSTR("McastLogging"), numZero); + + if (mDNS_McastTracingEnabled) + CFDictionarySetValue(dict, CFSTR("McastTracing"), numOne); + else + CFDictionarySetValue(dict, CFSTR("McastTracing"), numZero); + + CFRelease(numOne); + CFRelease(numZero); + mDNSDynamicStoreSetConfig(kmDNSDebugState, mDNSNULL, dict); + CFRelease(dict); + // If we turned off USR1 logging, we need to reset the filter + DebugSetFilter(); +} + +#if TARGET_OS_EMBEDDED +mDNSlocal void Prefschanged() +{ + mDNSBool mDNSProf_installed; + LogMsg("Prefschanged: mDNSResponder Managed Preferences have changed"); + mDNSProf_installed = GetmDNSManagedPref(kmDNSEnableLoggingStr); + dispatch_async(dispatch_get_main_queue(), + ^{ + if (mDNSProf_installed) + { + mDNS_LoggingEnabled = mDNS_PacketLoggingEnabled = 1; + } + else + { + LogMsg("Prefschanged: mDNSDebugProfile is uninstalled -> Turning OFF USR1/USR2 Logging with SIGINFO o/p"); + INFOCallback(); + mDNS_LoggingEnabled = mDNS_PacketLoggingEnabled = 0; + } + UpdateDebugState(); + // If Logging Enabled: Start Logging to com.apple.networking.mDNSResponder.log (has to be LogInfo) + LogInfo("Prefschanged: mDNSDebugProfile is installed -> Turned ON USR1/USR2 Logging"); + }); + return; +} +#endif //TARGET_OS_EMBEDDED + +#ifndef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM + +mDNSlocal void SignalCallback(CFMachPortRef port, void *msg, CFIndex size, void *info) +{ + (void)port; // Unused + (void)size; // Unused + (void)info; // Unused + mach_msg_header_t *msg_header = (mach_msg_header_t *)msg; + mDNS *const m = &mDNSStorage; + + // We're running on the CFRunLoop (Mach port) thread, not the kqueue thread, so we need to grab the KQueueLock before proceeding + KQueueLock(m); + switch(msg_header->msgh_id) + { + case SIGURG: + m->mDNSOppCaching = m->mDNSOppCaching ? mDNSfalse : mDNStrue; + LogMsg("SIGURG: Opportunistic Caching %s", m->mDNSOppCaching ? "Enabled" : "Disabled"); + // FALL THROUGH to purge the cache so that we re-do the caching based on the new setting + case SIGHUP: { + mDNSu32 slot; + CacheGroup *cg; + CacheRecord *rr; + LogMsg("SIGHUP: Purge cache"); + mDNS_Lock(m); + FORALL_CACHERECORDS(slot, cg, rr) + { + mDNS_PurgeCacheResourceRecord(m, rr); + } + // Restart unicast and multicast queries + mDNSCoreRestartQueries(m); + mDNS_Unlock(m); + } break; + case SIGINT: + case SIGTERM: ExitCallback(msg_header->msgh_id); break; + case SIGINFO: INFOCallback(); break; + case SIGUSR1: mDNS_LoggingEnabled = mDNS_LoggingEnabled ? 0 : 1; + LogMsg("SIGUSR1: Logging %s", mDNS_LoggingEnabled ? "Enabled" : "Disabled"); + WatchDogReportingThreshold = mDNS_LoggingEnabled ? 50 : 250; + UpdateDebugState(); + // If Logging Enabled: Start Logging to com.apple.networking.mDNSResponder.log + LogInfo("USR1 Logging Enabled: Start Logging to mDNSResponder Log file"); + break; + case SIGUSR2: mDNS_PacketLoggingEnabled = mDNS_PacketLoggingEnabled ? 0 : 1; + LogMsg("SIGUSR2: Packet Logging %s", mDNS_PacketLoggingEnabled ? "Enabled" : "Disabled"); + mDNS_McastTracingEnabled = (mDNS_PacketLoggingEnabled && mDNS_McastLoggingEnabled) ? mDNStrue : mDNSfalse; + LogInfo("SIGUSR2: Multicast Tracing is %s", mDNS_McastTracingEnabled ? "Enabled" : "Disabled"); + UpdateDebugState(); + break; + case SIGPROF: mDNS_McastLoggingEnabled = mDNS_McastLoggingEnabled ? mDNSfalse : mDNStrue; + LogMsg("SIGPROF: Multicast Logging %s", mDNS_McastLoggingEnabled ? "Enabled" : "Disabled"); + LogMcastStateInfo(m, mDNSfalse, mDNStrue, mDNStrue); + mDNS_McastTracingEnabled = (mDNS_PacketLoggingEnabled && mDNS_McastLoggingEnabled) ? mDNStrue : mDNSfalse; + LogMsg("SIGPROF: Multicast Tracing is %s", mDNS_McastTracingEnabled ? "Enabled" : "Disabled"); + UpdateDebugState(); + break; + case SIGTSTP: mDNS_LoggingEnabled = mDNS_PacketLoggingEnabled = mDNS_McastLoggingEnabled = mDNS_McastTracingEnabled = mDNSfalse; + LogMsg("All mDNSResponder Debug Logging/Tracing Disabled (USR1/USR2/PROF)"); + UpdateDebugState(); + break; + + default: LogMsg("SignalCallback: Unknown signal %d", msg_header->msgh_id); break; + } + KQueueUnlock(m, "Unix Signal"); +} + +// MachServerName is com.apple.mDNSResponder (Supported only till 10.9.x) +mDNSlocal kern_return_t mDNSDaemonInitialize(void) +{ + mStatus err; + CFMachPortRef s_port; + CFRunLoopSourceRef s_rls; + CFRunLoopSourceRef d_rls; + + s_port = CFMachPortCreateWithPort(NULL, m_port, DNSserverCallback, NULL, NULL); + CFMachPortRef d_port = CFMachPortCreate(NULL, ClientDeathCallback, NULL, NULL); + + err = mDNS_Init(&mDNSStorage, &PlatformStorage, + rrcachestorage, RR_CACHE_SIZE, + advertise, + mDNS_StatusCallback, mDNS_Init_NoInitCallbackContext); + + if (err) { LogMsg("Daemon start: mDNS_Init failed %d", err); return(err); } + + client_death_port = CFMachPortGetPort(d_port); + + s_rls = CFMachPortCreateRunLoopSource(NULL, s_port, 0); + CFRunLoopAddSource(PlatformStorage.CFRunLoop, s_rls, kCFRunLoopDefaultMode); + CFRelease(s_rls); + + d_rls = CFMachPortCreateRunLoopSource(NULL, d_port, 0); + CFRunLoopAddSource(PlatformStorage.CFRunLoop, d_rls, kCFRunLoopDefaultMode); + CFRelease(d_rls); + + CFMachPortRef i_port = CFMachPortCreate(NULL, SignalCallback, NULL, NULL); + CFRunLoopSourceRef i_rls = CFMachPortCreateRunLoopSource(NULL, i_port, 0); + signal_port = CFMachPortGetPort(i_port); + CFRunLoopAddSource(PlatformStorage.CFRunLoop, i_rls, kCFRunLoopDefaultMode); + CFRelease(i_rls); + + if (mDNS_DebugMode) printf("Service registered with Mach Port %d\n", m_port); + return(err); +} + +#else // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM + +// SignalDispatch is mostly just a copy/paste of entire code block from SignalCallback above. +// The common code should be a subroutine, or we end up having to fix bugs in two places all the time. +// The same applies to mDNSDaemonInitialize, much of which is just a copy/paste of chunks +// of code from above. Alternatively we could remove the duplicated source code by having +// single routines, with the few differing parts bracketed with "#ifndef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM" + +mDNSlocal void SignalDispatch(dispatch_source_t source) +{ + int sig = (int)dispatch_source_get_handle(source); + mDNS *const m = &mDNSStorage; + KQueueLock(m); + switch(sig) + { + case SIGHUP: { + mDNSu32 slot; + CacheGroup *cg; + CacheRecord *rr; + LogMsg("SIGHUP: Purge cache"); + mDNS_Lock(m); + FORALL_CACHERECORDS(slot, cg, rr) + { + mDNS_PurgeCacheResourceRecord(m, rr); + } + // Restart unicast and multicast queries + mDNSCoreRestartQueries(m); + mDNS_Unlock(m); + } break; + case SIGINT: + case SIGTERM: ExitCallback(sig); break; + case SIGINFO: INFOCallback(); break; + case SIGUSR1: mDNS_LoggingEnabled = mDNS_LoggingEnabled ? 0 : 1; + LogMsg("SIGUSR1: Logging %s", mDNS_LoggingEnabled ? "Enabled" : "Disabled"); + WatchDogReportingThreshold = mDNS_LoggingEnabled ? 50 : 250; + UpdateDebugState(); + break; + case SIGUSR2: mDNS_PacketLoggingEnabled = mDNS_PacketLoggingEnabled ? 0 : 1; + LogMsg("SIGUSR2: Packet Logging %s", mDNS_PacketLoggingEnabled ? "Enabled" : "Disabled"); + UpdateDebugState(); + break; + default: LogMsg("SignalCallback: Unknown signal %d", sig); break; + } + KQueueUnlock(m, "Unix Signal"); +} + +mDNSlocal void mDNSSetupSignal(dispatch_queue_t queue, int sig) +{ + signal(sig, SIG_IGN); + dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, sig, 0, queue); + + if (source) + { + dispatch_source_set_event_handler(source, ^{SignalDispatch(source);}); + // Start processing signals + dispatch_resume(source); + } + else + { + LogMsg("mDNSSetupSignal: Cannot setup signal %d", sig); + } +} + +// On 10.2 the MachServerName is DNSServiceDiscoveryServer +// On 10.3 and later, the MachServerName is com.apple.mDNSResponder +mDNSlocal kern_return_t mDNSDaemonInitialize(void) +{ + mStatus err; + dispatch_source_t mach_source; + dispatch_queue_t queue = dispatch_get_main_queue(); + + err = mDNS_Init(&mDNSStorage, &PlatformStorage, + rrcachestorage, RR_CACHE_SIZE, + advertise, + mDNS_StatusCallback, mDNS_Init_NoInitCallbackContext); + + if (err) { LogMsg("Daemon start: mDNS_Init failed %d", err); return(err); } + + mach_source = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV, m_port, 0, queue); + if (mach_source == mDNSNULL) {LogMsg("mDNSDaemonInitialize: Error creating source for m_port"); return -1;} + dispatch_source_set_event_handler(mach_source, ^{ + dispatch_mig_server(mach_source, sizeof(union __RequestUnion__DNSServiceDiscoveryReply_subsystem), + DNSServiceDiscoveryRequest_server); + }); + dispatch_resume(mach_source); + + mDNSSetupSignal(queue, SIGHUP); + mDNSSetupSignal(queue, SIGINT); + mDNSSetupSignal(queue, SIGTERM); + mDNSSetupSignal(queue, SIGINFO); + mDNSSetupSignal(queue, SIGUSR1); + mDNSSetupSignal(queue, SIGUSR2); + mDNSSetupSignal(queue, SIGURG); + + // Create a custom handler for doing the housekeeping work. This is either triggered + // by the timer or an event source + PlatformStorage.custom = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_ADD, 0, 0, queue); + if (PlatformStorage.custom == mDNSNULL) {LogMsg("mDNSDaemonInitialize: Error creating custom source"); return -1;} + dispatch_source_set_event_handler(PlatformStorage.custom, ^{PrepareForIdle(&mDNSStorage);}); + dispatch_resume(PlatformStorage.custom); + + // Create a timer source to trigger housekeeping work. The houskeeping work itself + // is done in the custom handler that we set below. + + PlatformStorage.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue); + if (PlatformStorage.timer == mDNSNULL) {LogMsg("mDNSDaemonInitialize: Error creating timer source"); return -1;} + + // As the API does not support one shot timers, we pass zero for the interval. In the custom handler, we + // always reset the time to the new time computed. In effect, we ignore the interval + dispatch_source_set_timer(PlatformStorage.timer, DISPATCH_TIME_NOW, 1000ull * 1000000000, 0); + dispatch_source_set_event_handler(PlatformStorage.timer, ^{ + dispatch_source_merge_data(PlatformStorage.custom, 1); + }); + dispatch_resume(PlatformStorage.timer); + + LogMsg("DaemonIntialize done successfully"); + + if (mDNS_DebugMode) printf("Service registered with Mach Port %d\n", m_port); + return(err); +} + +#endif // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM + +mDNSlocal mDNSs32 mDNSDaemonIdle(mDNS *const m) +{ + mDNSs32 now = mDNS_TimeNow(m); + + // 1. If we need to set domain secrets, do so before handling the network change + // Detailed reason: + // BTMM domains listed in DynStore Setup:/Network/BackToMyMac are added to the registration domains list, + // and we need to setup the associated AutoTunnel DomainAuthInfo entries before that happens. + if (m->p->KeyChainTimer && now - m->p->KeyChainTimer >= 0) + { + m->p->KeyChainTimer = 0; + mDNS_Lock(m); + SetDomainSecrets(m); + mDNS_Unlock(m); + } + + // 2. If we have network change events to handle, do them before calling mDNS_Execute() + // Detailed reason: + // mDNSMacOSXNetworkChanged() currently closes and re-opens its sockets. If there are received packets waiting, they are lost. + // mDNS_Execute() generates packets, including multicasts that are looped back to ourself. + // If we call mDNS_Execute() first, and generate packets, and then call mDNSMacOSXNetworkChanged() immediately afterwards + // we then systematically lose our own looped-back packets. + if (m->p->NetworkChanged && now - m->p->NetworkChanged >= 0) mDNSMacOSXNetworkChanged(m); + + if (m->p->RequestReSleep && now - m->p->RequestReSleep >= 0) { m->p->RequestReSleep = 0; mDNSPowerRequest(0, 0); } + + // 3. Call mDNS_Execute() to let mDNSCore do what it needs to do + mDNSs32 nextevent = mDNS_Execute(m); + + if (m->p->NetworkChanged) + if (nextevent - m->p->NetworkChanged > 0) + nextevent = m->p->NetworkChanged; + + if (m->p->KeyChainTimer) + if (nextevent - m->p->KeyChainTimer > 0) + nextevent = m->p->KeyChainTimer; + + if (m->p->RequestReSleep) + if (nextevent - m->p->RequestReSleep > 0) + nextevent = m->p->RequestReSleep; + + // 4. Deliver any waiting browse messages to clients + DNSServiceBrowser *b = DNSServiceBrowserList; + + while (b) + { + // Note: Need to advance b to the next element BEFORE we call DeliverInstance(), because in the + // event that the client Mach queue overflows, DeliverInstance() will call AbortBlockedClient() + // and that will cause the DNSServiceBrowser object's memory to be freed before it returns + DNSServiceBrowser *x = b; + b = b->next; + if (x->results) // Try to deliver the list of results + { + while (x->results) + { + DNSServiceBrowserResult *const r = x->results; + domainlabel name; + domainname type, domain; + DeconstructServiceName(&r->result, &name, &type, &domain); // Don't need to check result; already validated in FoundInstance() + char cname[MAX_DOMAIN_LABEL+1]; // Unescaped name: up to 63 bytes plus C-string terminating NULL. + char ctype[MAX_ESCAPED_DOMAIN_NAME]; + char cdom [MAX_ESCAPED_DOMAIN_NAME]; + ConvertDomainLabelToCString_unescaped(&name, cname); + ConvertDomainNameToCString(&type, ctype); + ConvertDomainNameToCString(&domain, cdom); + DNSServiceDiscoveryReplyFlags flags = (r->next) ? DNSServiceDiscoverReplyFlagsMoreComing : 0; + kern_return_t status = DNSServiceBrowserReply_rpc(x->ClientMachPort, r->resultType, cname, ctype, cdom, flags, 1); + // If we failed to send the mach message, try again in one second + if (status == MACH_SEND_TIMED_OUT) + { + if (nextevent - now > mDNSPlatformOneSecond) + nextevent = now + mDNSPlatformOneSecond; + break; + } + else + { + x->lastsuccess = now; + x->results = x->results->next; + freeL("DNSServiceBrowserResult", r); + } + } + // If this client hasn't read a single message in the last 60 seconds, abort it + if (now - x->lastsuccess >= 60 * mDNSPlatformOneSecond) + AbortBlockedClient(x->ClientMachPort, "browse", x); + } + } + + DNSServiceResolver *l; + for (l = DNSServiceResolverList; l; l=l->next) + if (l->ReportTime && now - l->ReportTime >= 0) + { + l->ReportTime = 0; + LogMsgNoIdent("Client application bug: DNSServiceResolver(%##s) active for over two minutes. " + "This places considerable burden on the network.", l->i.name.c); + } + + if (m->p->NotifyUser) + { + if (m->p->NotifyUser - now < 0) + { + if (!SameDomainLabelCS(m->p->usernicelabel.c, m->nicelabel.c)) + { + LogMsg("Name Conflict: Updated Computer Name from \"%#s\" to \"%#s\"", m->p->usernicelabel.c, m->nicelabel.c); + mDNSPreferencesSetNames(m, kmDNSComputerName, &m->p->usernicelabel, &m->nicelabel); + m->p->usernicelabel = m->nicelabel; + } + if (!SameDomainLabelCS(m->p->userhostlabel.c, m->hostlabel.c)) + { + LogMsg("Name Conflict: Updated Local Hostname from \"%#s.local\" to \"%#s.local\"", m->p->userhostlabel.c, m->hostlabel.c); + mDNSPreferencesSetNames(m, kmDNSLocalHostName, &m->p->userhostlabel, &m->hostlabel); + m->p->HostNameConflict = 0; // Clear our indicator, now name change has been successful + m->p->userhostlabel = m->hostlabel; + } + m->p->NotifyUser = 0; + } + else + if (nextevent - m->p->NotifyUser > 0) + nextevent = m->p->NotifyUser; + } + + return(nextevent); +} + +// Right now we consider *ALL* of our DHCP leases +// It might make sense to be a bit more selective and only consider the leases on interfaces +// (a) that are capable and enabled for wake-on-LAN, and +// (b) where we have found (and successfully registered with) a Sleep Proxy +// If we can't be woken for traffic on a given interface, then why keep waking to renew its lease? +mDNSlocal mDNSu32 DHCPWakeTime(void) +{ + mDNSu32 e = 24 * 3600; // Maximum maintenance wake interval is 24 hours + const CFAbsoluteTime now = CFAbsoluteTimeGetCurrent(); + if (!now) LogMsg("DHCPWakeTime: CFAbsoluteTimeGetCurrent failed"); + else + { + int ic, j; + + const void *pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL, kSCDynamicStoreDomainState, kSCCompAnyRegex, kSCEntNetDHCP); + if (!pattern) + { + LogMsg("DHCPWakeTime: SCDynamicStoreKeyCreateNetworkServiceEntity failed\n"); + return e; + } + CFArrayRef dhcpinfo = CFArrayCreate(NULL, (const void **)&pattern, 1, &kCFTypeArrayCallBacks); + CFRelease(pattern); + if (dhcpinfo) + { + SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("DHCP-LEASES"), NULL, NULL); + if (store) + { + CFDictionaryRef dict = SCDynamicStoreCopyMultiple(store, NULL, dhcpinfo); + if (dict) + { + ic = CFDictionaryGetCount(dict); + const void *vals[ic]; + CFDictionaryGetKeysAndValues(dict, NULL, vals); + + for (j = 0; j < ic; j++) + { + const CFDictionaryRef dhcp = (CFDictionaryRef)vals[j]; + if (dhcp) + { + const CFDateRef start = DHCPInfoGetLeaseStartTime(dhcp); + const CFDataRef lease = DHCPInfoGetOptionData(dhcp, 51); // Option 51 = IP Address Lease Time + if (!start || !lease || CFDataGetLength(lease) < 4) + LogMsg("DHCPWakeTime: SCDynamicStoreCopyDHCPInfo index %d failed " + "CFDateRef start %p CFDataRef lease %p CFDataGetLength(lease) %d", + j, start, lease, lease ? CFDataGetLength(lease) : 0); + else + { + const UInt8 *d = CFDataGetBytePtr(lease); + if (!d) LogMsg("DHCPWakeTime: CFDataGetBytePtr %d failed", j); + else + { + const mDNSu32 elapsed = now - CFDateGetAbsoluteTime(start); + const mDNSu32 lifetime = (mDNSs32) ((mDNSs32)d[0] << 24 | (mDNSs32)d[1] << 16 | (mDNSs32)d[2] << 8 | d[3]); + const mDNSu32 remaining = lifetime - elapsed; + const mDNSu32 wake = remaining > 60 ? remaining - remaining/10 : 54; // Wake at 90% of the lease time + LogSPS("DHCP Address Lease Elapsed %6u Lifetime %6u Remaining %6u Wake %6u", elapsed, lifetime, remaining, wake); + if (e > wake) e = wake; + } + } + } + } + CFRelease(dict); + } + CFRelease(store); + } + CFRelease(dhcpinfo); + } + } + return(e); +} + +// We deliberately schedule our wakeup for halfway between when we'd *like* it and when we *need* it. +// For example, if our DHCP lease expires in two hours, we'll typically renew it at the halfway point, after one hour. +// If we scheduled our wakeup for the one-hour renewal time, that might be just seconds from now, and sleeping +// for a few seconds and then waking again is silly and annoying. +// If we scheduled our wakeup for the two-hour expiry time, and we were slow to wake, we might lose our lease. +// Scheduling our wakeup for halfway in between -- 90 minutes -- avoids short wakeups while still +// allowing us an adequate safety margin to renew our lease before we lose it. + +mDNSlocal mDNSBool AllowSleepNow(mDNS *const m, mDNSs32 now) +{ + mDNSBool ready = mDNSCoreReadyForSleep(m, now); + if (m->SleepState && !ready && now - m->SleepLimit < 0) return(mDNSfalse); + + m->p->WakeAtUTC = 0; + int result = kIOReturnSuccess; + CFDictionaryRef opts = NULL; + + // If the sleep request was cancelled, and we're no longer planning to sleep, don't need to + // do the stuff below, but we *DO* still need to acknowledge the sleep message we received. + if (!m->SleepState) + LogMsg("AllowSleepNow: Sleep request was canceled with %d ticks remaining", m->SleepLimit - now); + else + { + if (!m->SystemWakeOnLANEnabled || !mDNSCoreHaveAdvertisedMulticastServices(m)) + LogSPS("AllowSleepNow: Not scheduling wakeup: SystemWakeOnLAN %s enabled; %s advertised services", + m->SystemWakeOnLANEnabled ? "is" : "not", + mDNSCoreHaveAdvertisedMulticastServices(m) ? "have" : "no"); + else + { + mDNSs32 dhcp = DHCPWakeTime(); + LogSPS("ComputeWakeTime: DHCP Wake %d", dhcp); + mDNSs32 interval = mDNSCoreIntervalToNextWake(m, now) / mDNSPlatformOneSecond; + if (interval > dhcp) interval = dhcp; + + // If we're not ready to sleep (failed to register with Sleep Proxy, maybe because of + // transient network problem) then schedule a wakeup in one hour to try again. Otherwise, + // a single SPS failure could result in a remote machine falling permanently asleep, requiring + // someone to go to the machine in person to wake it up again, which would be unacceptable. + if (!ready && interval > 3600) interval = 3600; + + //interval = 48; // For testing + +#ifdef kIOPMAcknowledgmentOptionSystemCapabilityRequirements + if (m->p->IOPMConnection) // If lightweight-wake capability is available, use that + { + const CFDateRef WakeDate = CFDateCreate(NULL, CFAbsoluteTimeGetCurrent() + interval); + if (!WakeDate) LogMsg("ScheduleNextWake: CFDateCreate failed"); + else + { + const mDNSs32 reqs = kIOPMSystemPowerStateCapabilityNetwork; + const CFNumberRef Requirements = CFNumberCreate(NULL, kCFNumberSInt32Type, &reqs); + if (!Requirements) LogMsg("ScheduleNextWake: CFNumberCreate failed"); + else + { + const void *OptionKeys[2] = { CFSTR("WakeDate"), CFSTR("Requirements") }; + const void *OptionVals[2] = { WakeDate, Requirements }; + opts = CFDictionaryCreate(NULL, (void*)OptionKeys, (void*)OptionVals, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + if (!opts) LogMsg("ScheduleNextWake: CFDictionaryCreate failed"); + CFRelease(Requirements); + } + CFRelease(WakeDate); + } + LogSPS("AllowSleepNow: Will request lightweight wakeup in %d seconds", interval); + } + else // else schedule the wakeup using the old API instead to +#endif + { + // If we wake within +/- 30 seconds of our requested time we'll assume the system woke for us, + // so we should put it back to sleep. To avoid frustrating the user, we always request at least + // 60 seconds sleep, so if they immediately re-wake the system within seconds of it going to sleep, + // we then shouldn't hit our 30-second window, and we won't attempt to re-sleep the machine. + if (interval < 60) interval = 60; + + result = mDNSPowerRequest(1, interval); + + if (result == kIOReturnNotReady) + { + int r; + LogMsg("AllowSleepNow: Requested wakeup in %d seconds unsuccessful; retrying with longer intervals", interval); + // IOPMSchedulePowerEvent fails with kIOReturnNotReady (-536870184/0xe00002d8) if the + // requested wake time is "too soon", but there's no API to find out what constitutes + // "too soon" on any given OS/hardware combination, so if we get kIOReturnNotReady + // we just have to iterate with successively longer intervals until it doesn't fail. + // We preserve the value of "result" because if our original power request was deemed "too soon" + // for the machine to get to sleep and wake back up again, we attempt to cancel the sleep request, + // since the implication is that the system won't manage to be awake again at the time we need it. + do + { + interval += (interval < 20) ? 1 : ((interval+3) / 4); + r = mDNSPowerRequest(1, interval); + } + while (r == kIOReturnNotReady); + if (r) LogMsg("AllowSleepNow: Requested wakeup in %d seconds unsuccessful: %d %X", interval, r, r); + else LogSPS("AllowSleepNow: Requested later wakeup in %d seconds; will also attempt IOCancelPowerChange", interval); + } + else + { + if (result) LogMsg("AllowSleepNow: Requested wakeup in %d seconds unsuccessful: %d %X", interval, result, result); + else LogSPS("AllowSleepNow: Requested wakeup in %d seconds", interval); + } + m->p->WakeAtUTC = mDNSPlatformUTC() + interval; + } + } + + m->SleepState = SleepState_Sleeping; + // We used to clear our interface list to empty state here before going to sleep. + // The applications that try to connect to an external server during maintenance wakes, saw + // DNS resolution errors as we don't have any interfaces (most queries use SuppressUnusable + // flag). Thus, we don't remove our interfaces anymore on sleep. + } + + LogSPS("AllowSleepNow: %s(%lX) %s at %ld (%d ticks remaining)", +#if !TARGET_OS_EMBEDDED && defined(kIOPMAcknowledgmentOptionSystemCapabilityRequirements) + (m->p->IOPMConnection) ? "IOPMConnectionAcknowledgeEventWithOptions" : +#endif + (result == kIOReturnSuccess) ? "IOAllowPowerChange" : "IOCancelPowerChange", + m->p->SleepCookie, ready ? "ready for sleep" : "giving up", now, m->SleepLimit - now); + + m->SleepLimit = 0; // Don't clear m->SleepLimit until after we've logged it above + m->TimeSlept = mDNSPlatformUTC(); + + // accumulate total time awake for this statistics gathering interval + if (m->StatStartTime) + { + m->ActiveStatTime += (m->TimeSlept - m->StatStartTime); + + // indicate this value is invalid until reinitialzed on wakeup + m->StatStartTime = 0; + } + +#if !TARGET_OS_EMBEDDED && defined(kIOPMAcknowledgmentOptionSystemCapabilityRequirements) + if (m->p->IOPMConnection) IOPMConnectionAcknowledgeEventWithOptions(m->p->IOPMConnection, m->p->SleepCookie, opts); + else +#endif + if (result == kIOReturnSuccess) IOAllowPowerChange (m->p->PowerConnection, m->p->SleepCookie); + else IOCancelPowerChange(m->p->PowerConnection, m->p->SleepCookie); + + if (opts) CFRelease(opts); + return(mDNStrue); +} + +#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM + +mDNSexport void TriggerEventCompletion() +{ + debugf("TriggerEventCompletion: Merge data"); + dispatch_source_merge_data(PlatformStorage.custom, 1); +} + +mDNSlocal void PrepareForIdle(void *m_param) +{ + mDNS *m = m_param; + int64_t time_offset; + dispatch_time_t dtime; + + const int multiplier = 1000000000 / mDNSPlatformOneSecond; + + // This is the main work loop: + // (1) First we give mDNSCore a chance to finish off any of its deferred work and calculate the next sleep time + // (2) Then we make sure we've delivered all waiting browse messages to our clients + // (3) Then we sleep for the time requested by mDNSCore, or until the next event, whichever is sooner + + debugf("PrepareForIdle: called"); + // Run mDNS_Execute to find out the time we next need to wake up + mDNSs32 start = mDNSPlatformRawTime(); + mDNSs32 nextTimerEvent = udsserver_idle(mDNSDaemonIdle(m)); + mDNSs32 end = mDNSPlatformRawTime(); + if (end - start >= WatchDogReportingThreshold) + LogInfo("CustomSourceHandler:WARNING: Idle task took %dms to complete", end - start); + + mDNSs32 now = mDNS_TimeNow(m); + + if (m->ShutdownTime) + { + if (mDNSStorage.ResourceRecords) + { + LogInfo("Cannot exit yet; Resource Record still exists: %s", ARDisplayString(m, mDNSStorage.ResourceRecords)); + if (mDNS_LoggingEnabled) usleep(10000); // Sleep 10ms so that we don't flood syslog with too many messages + } + if (mDNS_ExitNow(m, now)) + { + LogInfo("IdleLoop: mDNS_FinalExit"); + mDNS_FinalExit(&mDNSStorage); + usleep(1000); // Little 1ms pause before exiting, so we don't lose our final syslog messages + exit(0); + } + if (nextTimerEvent - m->ShutdownTime >= 0) + nextTimerEvent = m->ShutdownTime; + } + + if (m->SleepLimit) + if (!AllowSleepNow(m, now)) + if (nextTimerEvent - m->SleepLimit >= 0) + nextTimerEvent = m->SleepLimit; + + // Convert absolute wakeup time to a relative time from now + mDNSs32 ticks = nextTimerEvent - now; + if (ticks < 1) ticks = 1; + + static mDNSs32 RepeatedBusy = 0; // Debugging sanity check, to guard against CPU spins + if (ticks > 1) + RepeatedBusy = 0; + else + { + ticks = 1; + if (++RepeatedBusy >= mDNSPlatformOneSecond) { ShowTaskSchedulingError(&mDNSStorage); RepeatedBusy = 0; } + } + + time_offset = ((mDNSu32)ticks / mDNSPlatformOneSecond) * 1000000000 + (ticks % mDNSPlatformOneSecond) * multiplier; + dtime = dispatch_time(DISPATCH_TIME_NOW, time_offset); + dispatch_source_set_timer(PlatformStorage.timer, dtime, 1000ull*1000000000, 0); + debugf("PrepareForIdle: scheduling timer with ticks %d", ticks); + return; +} + +#else // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM + +mDNSlocal void KQWokenFlushBytes(int fd, __unused short filter, __unused void *context) +{ + // Read all of the bytes so we won't wake again. + char buffer[100]; + while (recv(fd, buffer, sizeof(buffer), MSG_DONTWAIT) > 0) continue; +} + +mDNSlocal void * KQueueLoop(void *m_param) +{ + mDNS *m = m_param; + int numevents = 0; + +#if USE_SELECT_WITH_KQUEUEFD + fd_set readfds; + FD_ZERO(&readfds); + const int multiplier = 1000000 / mDNSPlatformOneSecond; +#else + const int multiplier = 1000000000 / mDNSPlatformOneSecond; +#endif + + pthread_mutex_lock(&PlatformStorage.BigMutex); + LogInfo("Starting time value 0x%08lX (%ld)", (mDNSu32)mDNSStorage.timenow_last, mDNSStorage.timenow_last); + + // This is the main work loop: + // (1) First we give mDNSCore a chance to finish off any of its deferred work and calculate the next sleep time + // (2) Then we make sure we've delivered all waiting browse messages to our clients + // (3) Then we sleep for the time requested by mDNSCore, or until the next event, whichever is sooner + // (4) On wakeup we first process *all* events + // (5) then when no more events remain, we go back to (1) to finish off any deferred work and do it all again + for ( ; ; ) + { + #define kEventsToReadAtOnce 1 + struct kevent new_events[kEventsToReadAtOnce]; + + // Run mDNS_Execute to find out the time we next need to wake up + mDNSs32 start = mDNSPlatformRawTime(); + mDNSs32 nextTimerEvent = udsserver_idle(mDNSDaemonIdle(m)); + mDNSs32 end = mDNSPlatformRawTime(); + if (end - start >= WatchDogReportingThreshold) + LogInfo("WARNING: Idle task took %dms to complete", end - start); + + mDNSs32 now = mDNS_TimeNow(m); + + if (m->ShutdownTime) + { + if (mDNSStorage.ResourceRecords) + { + AuthRecord *rr; + for (rr = mDNSStorage.ResourceRecords; rr; rr=rr->next) + { + LogInfo("Cannot exit yet; Resource Record still exists: %s", ARDisplayString(m, rr)); + if (mDNS_LoggingEnabled) usleep(10000); // Sleep 10ms so that we don't flood syslog with too many messages + } + } + if (mDNS_ExitNow(m, now)) + { + LogInfo("mDNS_FinalExit"); + mDNS_FinalExit(&mDNSStorage); + usleep(1000); // Little 1ms pause before exiting, so we don't lose our final syslog messages + exit(0); + } + if (nextTimerEvent - m->ShutdownTime >= 0) + nextTimerEvent = m->ShutdownTime; + } + + if (m->SleepLimit) + if (!AllowSleepNow(m, now)) + if (nextTimerEvent - m->SleepLimit >= 0) + nextTimerEvent = m->SleepLimit; + + // Convert absolute wakeup time to a relative time from now + mDNSs32 ticks = nextTimerEvent - now; + if (ticks < 1) ticks = 1; + + static mDNSs32 RepeatedBusy = 0; // Debugging sanity check, to guard against CPU spins + if (ticks > 1) + RepeatedBusy = 0; + else + { + ticks = 1; + if (++RepeatedBusy >= mDNSPlatformOneSecond) { ShowTaskSchedulingError(&mDNSStorage); RepeatedBusy = 0; } + } + + verbosedebugf("KQueueLoop: Handled %d events; now sleeping for %d ticks", numevents, ticks); + numevents = 0; + + // Release the lock, and sleep until: + // 1. Something interesting happens like a packet arriving, or + // 2. The other thread writes a byte to WakeKQueueLoopFD to poke us and make us wake up, or + // 3. The timeout expires + pthread_mutex_unlock(&PlatformStorage.BigMutex); + +#if USE_SELECT_WITH_KQUEUEFD + struct timeval timeout; + timeout.tv_sec = ticks / mDNSPlatformOneSecond; + timeout.tv_usec = (ticks % mDNSPlatformOneSecond) * multiplier; + FD_SET(KQueueFD, &readfds); + if (select(KQueueFD+1, &readfds, NULL, NULL, &timeout) < 0) + { LogMsg("select(%d) failed errno %d (%s)", KQueueFD, errno, strerror(errno)); sleep(1); } +#else + struct timespec timeout; + timeout.tv_sec = ticks / mDNSPlatformOneSecond; + timeout.tv_nsec = (ticks % mDNSPlatformOneSecond) * multiplier; + // In my opinion, you ought to be able to call kevent() with nevents set to zero, + // and have it work similarly to the way it does with nevents non-zero -- + // i.e. it waits until either an event happens or the timeout expires, and then wakes up. + // In fact, what happens if you do this is that it just returns immediately. So, we have + // to pass nevents set to one, and then we just ignore the event it gives back to us. -- SC + if (kevent(KQueueFD, NULL, 0, new_events, 1, &timeout) < 0) + { LogMsg("kevent(%d) failed errno %d (%s)", KQueueFD, errno, strerror(errno)); sleep(1); } +#endif + + pthread_mutex_lock(&PlatformStorage.BigMutex); + // We have to ignore the event we may have been told about above, because that + // was done without holding the lock, and between the time we woke up and the + // time we reclaimed the lock the other thread could have done something that + // makes the event no longer valid. Now we have the lock, we call kevent again + // and this time we can safely process the events it tells us about. + + static const struct timespec zero_timeout = { 0, 0 }; + int events_found; + while ((events_found = kevent(KQueueFD, NULL, 0, new_events, kEventsToReadAtOnce, &zero_timeout)) != 0) + { + if (events_found > kEventsToReadAtOnce || (events_found < 0 && errno != EINTR)) + { + // Not sure what to do here, our kqueue has failed us - this isn't ideal + LogMsg("ERROR: KQueueLoop - kevent failed errno %d (%s)", errno, strerror(errno)); + exit(errno); + } + + numevents += events_found; + + int i; + for (i = 0; i < events_found; i++) + { + const KQueueEntry *const kqentry = new_events[i].udata; + mDNSs32 stime = mDNSPlatformRawTime(); + const char *const KQtask = kqentry->KQtask; // Grab a copy in case KQcallback deletes the task + kqentry->KQcallback(new_events[i].ident, new_events[i].filter, kqentry->KQcontext); + mDNSs32 etime = mDNSPlatformRawTime(); + if (etime - stime >= WatchDogReportingThreshold) + LogInfo("WARNING: %s took %dms to complete", KQtask, etime - stime); + } + } + } + + return NULL; +} + +#endif // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM + +mDNSlocal void LaunchdCheckin(void) +{ + // Ask launchd for our socket + launch_data_t resp_sd = launch_socket_service_check_in(); + if (!resp_sd) + { + LogMsg("launch_socket_service_check_in returned NULL"); + return; + } + else + { + launch_data_t skts = launch_data_dict_lookup(resp_sd, LAUNCH_JOBKEY_SOCKETS); + if (!skts) LogMsg("launch_data_dict_lookup LAUNCH_JOBKEY_SOCKETS returned NULL"); + else + { + launch_data_t skt = launch_data_dict_lookup(skts, "Listeners"); + if (!skt) LogMsg("launch_data_dict_lookup Listeners returned NULL"); + else + { + launchd_fds_count = launch_data_array_get_count(skt); + if (launchd_fds_count == 0) LogMsg("launch_data_array_get_count(skt) returned 0"); + else + { + launchd_fds = mallocL("LaunchdCheckin", sizeof(dnssd_sock_t) * launchd_fds_count); + if (!launchd_fds) LogMsg("LaunchdCheckin: malloc failed"); + else + { + size_t i; + for(i = 0; i < launchd_fds_count; i++) + { + launch_data_t s = launch_data_array_get_index(skt, i); + if (!s) + { + launchd_fds[i] = dnssd_InvalidSocket; + LogMsg("launch_data_array_get_index(skt, %d) returned NULL", i); + } + else + { + launchd_fds[i] = launch_data_get_fd(s); + LogInfo("Launchd Unix Domain Socket [%d]: %d", i, launchd_fds[i]); + } + } + } + // In some early versions of 10.4.x, the permissions on the UDS were not set correctly, so we fix them here + chmod(MDNS_UDS_SERVERPATH, S_IRUSR|S_IWUSR | S_IRGRP|S_IWGRP | S_IROTH|S_IWOTH); + } + } + } + } + launch_data_free(resp_sd); +} + +static mach_port_t RegisterMachService(const char *service_name) +{ + mach_port_t port = MACH_PORT_NULL; + kern_return_t kr; + + if (KERN_SUCCESS != (kr = bootstrap_check_in(bootstrap_port, (char *)service_name, &port))) + { + LogMsg("RegisterMachService: %d %X %s", kr, kr, mach_error_string(kr)); + return MACH_PORT_NULL; + } + + if (KERN_SUCCESS != (kr = mach_port_insert_right(mach_task_self(), port, port, MACH_MSG_TYPE_MAKE_SEND))) + { + LogMsg("RegisterMachService: %d %X %s", kr, kr, mach_error_string(kr)); + mach_port_deallocate(mach_task_self(), port); + return MACH_PORT_NULL; + } + + return port; +} + +extern int sandbox_init(const char *profile, uint64_t flags, char **errorbuf) __attribute__((weak_import)); + +mDNSexport int main(int argc, char **argv) +{ + int i; + kern_return_t status; + + mDNSMacOSXSystemBuildNumber(NULL); + LogMsg("%s starting %s %d", mDNSResponderVersionString, OSXVers ? "OSXVers" : "iOSVers", OSXVers ? OSXVers : iOSVers); + +#if 0 + LogMsg("CacheRecord %d", sizeof(CacheRecord)); + LogMsg("CacheGroup %d", sizeof(CacheGroup)); + LogMsg("ResourceRecord %d", sizeof(ResourceRecord)); + LogMsg("RData_small %d", sizeof(RData_small)); + + LogMsg("sizeof(CacheEntity) %d", sizeof(CacheEntity)); + LogMsg("RR_CACHE_SIZE %d", RR_CACHE_SIZE); + LogMsg("block usage %d", sizeof(CacheEntity) * RR_CACHE_SIZE); + LogMsg("block wastage %d", 16*1024 - sizeof(CacheEntity) * RR_CACHE_SIZE); +#endif + + if (0 == geteuid()) + { + LogMsg("mDNSResponder cannot be run as root !! Exiting.."); + return -1; + } + + for (i=1; i<argc; i++) + { + if (!strcasecmp(argv[i], "-d" )) mDNS_DebugMode = mDNStrue; + if (!strcasecmp(argv[i], "-NoMulticastAdvertisements")) advertise = mDNS_Init_DontAdvertiseLocalAddresses; + if (!strcasecmp(argv[i], "-DisableSleepProxyClient" )) DisableSleepProxyClient = mDNStrue; + if (!strcasecmp(argv[i], "-DebugLogging" )) mDNS_LoggingEnabled = mDNStrue; + if (!strcasecmp(argv[i], "-UnicastPacketLogging" )) mDNS_PacketLoggingEnabled = mDNStrue; + if (!strcasecmp(argv[i], "-OfferSleepProxyService" )) + OfferSleepProxyService = (i+1 < argc && mDNSIsDigit(argv[i+1][0]) && mDNSIsDigit(argv[i+1][1]) && argv[i+1][2]==0) ? atoi(argv[++i]) : 100; + if (!strcasecmp(argv[i], "-UseInternalSleepProxy" )) + UseInternalSleepProxy = (i+1<argc && mDNSIsDigit(argv[i+1][0]) && argv[i+1][1]==0) ? atoi(argv[++i]) : 1; + if (!strcasecmp(argv[i], "-StrictUnicastOrdering" )) StrictUnicastOrdering = mDNStrue; + if (!strcasecmp(argv[i], "-AlwaysAppendSearchDomains")) AlwaysAppendSearchDomains = mDNStrue; + } + + // Note that mDNSPlatformInit will set DivertMulticastAdvertisements in the mDNS structure + if (!advertise) LogMsg("Administratively prohibiting multicast advertisements"); + +#ifndef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM + + signal(SIGHUP, HandleSIG); // (Debugging) Purge the cache to check for cache handling bugs + signal(SIGINT, HandleSIG); // Ctrl-C: Detach from Mach BootstrapService and exit cleanly + signal(SIGPIPE, SIG_IGN); // Don't want SIGPIPE signals -- we'll handle EPIPE errors directly + signal(SIGTERM, HandleSIG); // Machine shutting down: Detach from and exit cleanly like Ctrl-C + signal(SIGINFO, HandleSIG); // (Debugging) Write state snapshot to syslog + signal(SIGUSR1, HandleSIG); // (Debugging) Enable Logging + signal(SIGUSR2, HandleSIG); // (Debugging) Enable Packet Logging + signal(SIGURG, HandleSIG); // (Debugging) Toggle Opportunistic Caching + signal(SIGPROF, HandleSIG); // (Debugging) Toggle Multicast Logging + signal(SIGTSTP, HandleSIG); // (Debugging) Disable all Debug Logging (USR1/USR2/PROF) + +#endif // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM + + mDNSStorage.p = &PlatformStorage; // Make sure mDNSStorage.p is set up, because validatelists uses it + // Need to Start XPC Server Before LaunchdCheckin() (Reason: rdar11023750) + xpc_server_init(); + LaunchdCheckin(); + +#ifndef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM + + // Create the kqueue, mutex and thread to support KQSockets + KQueueFD = kqueue(); + if (KQueueFD == -1) { LogMsg("kqueue() failed errno %d (%s)", errno, strerror(errno)); status = errno; goto exit; } + + i = pthread_mutex_init(&PlatformStorage.BigMutex, NULL); + if (i == -1) { LogMsg("pthread_mutex_init() failed errno %d (%s)", errno, strerror(errno)); status = errno; goto exit; } + + int fdpair[2] = {0, 0}; + i = socketpair(AF_UNIX, SOCK_STREAM, 0, fdpair); + if (i == -1) { LogMsg("socketpair() failed errno %d (%s)", errno, strerror(errno)); status = errno; goto exit; } + + // Socket pair returned us two identical sockets connected to each other + // We will use the first socket to send the second socket. The second socket + // will be added to the kqueue so it will wake when data is sent. + static const KQueueEntry wakeKQEntry = { KQWokenFlushBytes, NULL, "kqueue wakeup after CFRunLoop event" }; + + PlatformStorage.WakeKQueueLoopFD = fdpair[0]; + KQueueSet(fdpair[1], EV_ADD, EVFILT_READ, &wakeKQEntry); + +#endif // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM + + // Invoke sandbox profile /usr/share/sandbox/mDNSResponder.sb +#if MDNS_NO_SANDBOX + LogMsg("Note: Compiled without Apple Sandbox support"); +#else // MDNS_NO_SANDBOX + if (!sandbox_init) + LogMsg("Note: Running without Apple Sandbox support (not available on this OS)"); + else + { + char *sandbox_msg; + uint64_t sandbox_flags = SANDBOX_NAMED; + + int sandbox_err = sandbox_init("mDNSResponder", sandbox_flags, &sandbox_msg); + if (sandbox_err) + { + LogMsg("WARNING: sandbox_init error %s", sandbox_msg); + // If we have errors in the sandbox during development, to prevent + // exiting, uncomment the following line. + //sandbox_free_error(sandbox_msg); + + errx(EX_OSERR, "sandbox_init() failed: %s", sandbox_msg); + } + else LogInfo("Now running under Apple Sandbox restrictions"); + } +#endif // MDNS_NO_SANDBOX + + // We use BeginTransactionAtShutdown in the plist that ensures that we will + // receive a SIGTERM during shutdown rather than a SIGKILL. But launchd (due to some + // limitation) currently requires us to still start and end the transaction for + // its proper initialization. + vproc_transaction_t vt = vproc_transaction_begin(NULL); + if (vt) vproc_transaction_end(NULL, vt); + + m_port = RegisterMachService(kmDNSResponderServName); + // We should ALWAYS receive our Mach port from RegisterMachService() but sanity check before initializing daemon + if (m_port == MACH_PORT_NULL) + { + LogMsg("! MACH PORT IS NULL ! bootstrap_checkin failed to give a mach port"); + return -1; + } + + status = mDNSDaemonInitialize(); + if (status) { LogMsg("Daemon start: mDNSDaemonInitialize failed"); goto exit; } + + status = udsserver_init(launchd_fds, launchd_fds_count); + if (status) { LogMsg("Daemon start: udsserver_init failed"); goto exit; } + + log_client = asl_open(NULL, "mDNSResponder", 0); + log_msg = asl_new(ASL_TYPE_MSG); + +#if TARGET_OS_EMBEDDED + _scprefs_observer_watch(scprefs_observer_type_global, kmDNSResponderPrefIDStr, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), + ^{ + Prefschanged(); + }); +#endif + + mDNSMacOSXNetworkChanged(&mDNSStorage); + UpdateDebugState(); + +#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM + LogInfo("Daemon Start: Using LibDispatch"); + // CFRunLoopRun runs both CFRunLoop sources and dispatch sources + CFRunLoopRun(); +#else // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM + // Start the kqueue thread + pthread_t KQueueThread; + i = pthread_create(&KQueueThread, NULL, KQueueLoop, &mDNSStorage); + if (i == -1) { LogMsg("pthread_create() failed errno %d (%s)", errno, strerror(errno)); status = errno; goto exit; } + if (status == 0) + { + CFRunLoopRun(); + LogMsg("ERROR: CFRunLoopRun Exiting."); + mDNS_Close(&mDNSStorage); + } +#endif // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM + + LogMsg("%s exiting", mDNSResponderVersionString); + +exit: + return(status); +} + +// uds_daemon.c support routines ///////////////////////////////////////////// + +// Arrange things so that when data appears on fd, callback is called with context +mDNSexport mStatus udsSupportAddFDToEventLoop(int fd, udsEventCallback callback, void *context, void **platform_data) +{ + KQSocketEventSource **p = &gEventSources; + (void) platform_data; + while (*p && (*p)->fd != fd) p = &(*p)->next; + if (*p) { LogMsg("udsSupportAddFDToEventLoop: ERROR fd %d already has EventLoop source entry", fd); return mStatus_AlreadyRegistered; } + + KQSocketEventSource *newSource = (KQSocketEventSource*) mallocL("KQSocketEventSource", sizeof *newSource); + if (!newSource) return mStatus_NoMemoryErr; + + newSource->next = mDNSNULL; + newSource->fd = fd; + newSource->kqs.KQcallback = callback; + newSource->kqs.KQcontext = context; + newSource->kqs.KQtask = "UDS client"; +#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM + newSource->kqs.readSource = mDNSNULL; + newSource->kqs.writeSource = mDNSNULL; + newSource->kqs.fdClosed = mDNSfalse; +#endif // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM + + if (KQueueSet(fd, EV_ADD, EVFILT_READ, &newSource->kqs) == 0) + { + *p = newSource; + return mStatus_NoError; + } + + LogMsg("KQueueSet failed for fd %d errno %d (%s)", fd, errno, strerror(errno)); + freeL("KQSocketEventSource", newSource); + return mStatus_BadParamErr; +} + +int udsSupportReadFD(dnssd_sock_t fd, char *buf, int len, int flags, void *platform_data) +{ + (void) platform_data; + return recv(fd, buf, len, flags); +} + +mDNSexport mStatus udsSupportRemoveFDFromEventLoop(int fd, void *platform_data) // Note: This also CLOSES the file descriptor +{ + KQSocketEventSource **p = &gEventSources; + (void) platform_data; + while (*p && (*p)->fd != fd) p = &(*p)->next; + if (*p) + { + KQSocketEventSource *s = *p; + *p = (*p)->next; + // We don't have to explicitly do a kqueue EV_DELETE here because closing the fd + // causes the kernel to automatically remove any associated kevents + mDNSPlatformCloseFD(&s->kqs, s->fd); + freeL("KQSocketEventSource", s); + return mStatus_NoError; + } + LogMsg("udsSupportRemoveFDFromEventLoop: ERROR fd %d not found in EventLoop source list", fd); + return mStatus_NoSuchNameErr; +} + +#if _BUILDING_XCODE_PROJECT_ +// If mDNSResponder crashes, then this string will be magically included in the automatically-generated crash log +const char *__crashreporter_info__ = mDNSResponderVersionString; +asm (".desc ___crashreporter_info__, 0x10"); +#endif + +// For convenience when using the "strings" command, this is the last thing in the file +// The "@(#) " pattern is a special prefix the "what" command looks for +mDNSexport const char mDNSResponderVersionString_SCCS[] = "@(#) mDNSResponder " STRINGIFY(mDNSResponderVersion) " (" __DATE__ " " __TIME__ ")"; diff --git a/mDNSResponder/mDNSMacOSX/dnsctl-entitlements.plist b/mDNSResponder/mDNSMacOSX/dnsctl-entitlements.plist new file mode 100644 index 00000000..fb9c3a30 --- /dev/null +++ b/mDNSResponder/mDNSMacOSX/dnsctl-entitlements.plist @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>com.apple.mDNSResponder.dnsproxy</key> + <true/> +</dict> +</plist> diff --git a/mDNSResponder/mDNSMacOSX/helper-entitlements.plist b/mDNSResponder/mDNSMacOSX/helper-entitlements.plist new file mode 100644 index 00000000..cf478cd9 --- /dev/null +++ b/mDNSResponder/mDNSMacOSX/helper-entitlements.plist @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>com.apple.SystemConfiguration.SCDynamicStore-write-access</key> + <true/> + <key>com.apple.SystemConfiguration.SCPreferences-write-access</key> + <array> + <string>com.apple.AutoWake.xml</string> + </array> +</dict> +</plist> diff --git a/mDNSResponder/mDNSMacOSX/helper-error.h b/mDNSResponder/mDNSMacOSX/helper-error.h new file mode 100644 index 00000000..2e463b0a --- /dev/null +++ b/mDNSResponder/mDNSMacOSX/helper-error.h @@ -0,0 +1,49 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2007 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. + */ + +ERROR(kmDNSHelperCommunicationFailed, "Mach communication failed") +ERROR(kmDNSHelperNotAuthorized, "Not authorized") +ERROR(kmDNSHelperCreationFailed, "Object creation failed") +ERROR(kmDNSHelperInvalidPList, "Invalid property list") +ERROR(kmDNSHelperInvalidNameKey, "Invalid name key") +ERROR(kmDNSHelperInvalidConfigKey, "Invalid configuration key") +ERROR(kmDNSHelperTypeError, "Object was not of expected type") +ERROR(kmDNSHelperPreferencesFailed, "Could not create preferences session") +ERROR(kmDNSHelperPreferencesLockFailed, "Could not lock preferences") +ERROR(kmDNSHelperPreferencesSetFailed, "Could not update preferences") +ERROR(kmDNSHelperKeychainCopyDefaultFailed, "Could not copy keychain default") +ERROR(kmDNSHelperKeychainSearchCreationFailed, "Could not create keychain search") +ERROR(kmDNSHelperPListWriteFailed, "Could not write property list to stream") +ERROR(kmDNSHelperResultTooLarge, "Result too large") +ERROR(kmDNSHelperInterfaceCreationFailed, "Could not create auto-tunnel interface") +ERROR(kmDNSHelperInterfaceDeletionFailed, "Could not delete auto-tunnel interface") +ERROR(kmDNSHelperInvalidInterfaceState, "Invalid interface state requested") +ERROR(kmDNSHelperInvalidServerState, "Invalid server state requested") +ERROR(kmDNSHelperRacoonConfigCreationFailed, "Could not create racoon configuration file") +ERROR(kmDNSHelperRacoonStartFailed, "Could not start racoon") +ERROR(kmDNSHelperRacoonNotificationFailed, "Could not notify racoon") +ERROR(kmDNSHelperInvalidTunnelSetKeysOperation, "Invalid tunnel setkey operation requested") +ERROR(kmDNSHelperInvalidNetworkAddress, "Invalid network address") +ERROR(kmDNSHelperRouteAdditionFailed, "Could not add route") +ERROR(kmDNSHelperRouteDeletionFailed, "Could not remove route") +ERROR(kmDNSHelperRoutingSocketCreationFailed, "Could not create routing socket") +ERROR(kmDNSHelperDatagramSocketCreationFailed, "Could not create datagram socket") +ERROR(kmDNSHelperIPsecPolicyCreationFailed, "Could not create IPsec policy") +ERROR(kmDNSHelperIPsecPolicySetFailed, "Could not set IPsec policy") +ERROR(kmDNSHelperIPsecRemoveSAFailed, "Could not remove IPsec SA") +ERROR(kmDNSHelperIPsecPolicySocketCreationFailed, "Could not create IPsec policy socket") +ERROR(kmDNSHelperIPsecDisabled, "IPSec support was not compiled in to the helper") diff --git a/mDNSResponder/mDNSMacOSX/helper-main.c b/mDNSResponder/mDNSMacOSX/helper-main.c new file mode 100644 index 00000000..52779e85 --- /dev/null +++ b/mDNSResponder/mDNSMacOSX/helper-main.c @@ -0,0 +1,298 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2007 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. + */ + +#define _FORTIFY_SOURCE 2 + +#include <CoreFoundation/CoreFoundation.h> +#include <sys/cdefs.h> +#include <sys/time.h> +#include <sys/types.h> +#include <mach/mach.h> +#include <mach/mach_error.h> +#include <servers/bootstrap.h> +#include <asl.h> +#include <launch.h> +#include <pwd.h> +#include <pthread.h> +#include <stdarg.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <time.h> +#include <unistd.h> +#include <Security/Security.h> +#include "helper.h" +#include "helper-server.h" +#include "helpermsg.h" +#include "helpermsgServer.h" +#include <vproc.h> + +#if TARGET_OS_EMBEDDED +#define NO_SECURITYFRAMEWORK 1 +#endif + +#ifndef LAUNCH_JOBKEY_MACHSERVICES +#define LAUNCH_JOBKEY_MACHSERVICES "MachServices" +#define LAUNCH_DATA_MACHPORT 10 +#define launch_data_get_machport launch_data_get_fd +#endif + +union max_msg_size +{ + union __RequestUnion__proxy_helper_subsystem req; + union __ReplyUnion__proxy_helper_subsystem rep; +}; + +static const mach_msg_size_t MAX_MSG_SIZE = sizeof(union max_msg_size) + MAX_TRAILER_SIZE; +static aslclient logclient = NULL; +static int opt_debug; +static pthread_t idletimer_thread; + +unsigned long maxidle = 15; +unsigned long actualidle = 3600; + +CFRunLoopRef gRunLoop = NULL; +CFRunLoopTimerRef gTimer = NULL; + +mach_port_t gPort = MACH_PORT_NULL; + +static void helplogv(int level, const char *fmt, va_list ap) +{ + if (NULL == logclient) { vfprintf(stderr, fmt, ap); fflush(stderr); } + else asl_vlog(logclient, NULL, level, fmt, ap); +} + +void helplog(int level, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + helplogv(level, fmt, ap); + va_end(ap); +} + +// for safe_vproc +void LogMsgWithLevel(mDNSLogLevel_t logLevel, const char *fmt, ...) +{ + (void)logLevel; + va_list ap; + va_start(ap, fmt); + // safe_vproc only calls LogMsg, so assume logLevel maps to ASL_LEVEL_ERR + helplog(ASL_LEVEL_ERR, fmt, ap); + va_end(ap); +} + +static void handle_sigterm(int sig) +{ + // debug("entry sig=%d", sig); Can't use syslog from within a signal handler + assert(sig == SIGTERM); + (void)proxy_mDNSExit(gPort); +} + +static void initialize_logging(void) +{ + logclient = asl_open(NULL, kmDNSHelperServiceName, (opt_debug ? ASL_OPT_STDERR : 0)); + if (NULL == logclient) { fprintf(stderr, "Could not initialize ASL logging.\n"); fflush(stderr); return; } + if (opt_debug) asl_set_filter(logclient, ASL_FILTER_MASK_UPTO(ASL_LEVEL_DEBUG)); +} + +static void initialize_id(void) +{ + static char login[] = "_mdnsresponder"; + struct passwd hardcode; + struct passwd *pwd = &hardcode; // getpwnam(login); + hardcode.pw_uid = 65; + hardcode.pw_gid = 65; + + if (!pwd) { helplog(ASL_LEVEL_ERR, "Could not find account name `%s'. I will only help root.", login); return; } + mDNSResponderUID = pwd->pw_uid; + mDNSResponderGID = pwd->pw_gid; +} + +static void diediedie(CFRunLoopTimerRef timer, void *context) +{ + debug("entry %p %p %d", timer, context, maxidle); + assert(gTimer == timer); + if (maxidle) + (void)proxy_mDNSExit(gPort); +} + +void pause_idle_timer(void) +{ + debug("entry"); + assert(gTimer); + assert(gRunLoop); + CFRunLoopRemoveTimer(gRunLoop, gTimer, kCFRunLoopDefaultMode); +} + +void unpause_idle_timer(void) +{ + debug("entry"); + assert(gRunLoop); + assert(gTimer); + CFRunLoopAddTimer(gRunLoop, gTimer, kCFRunLoopDefaultMode); +} + +void update_idle_timer(void) +{ + debug("entry"); + assert(gTimer); + CFRunLoopTimerSetNextFireDate(gTimer, CFAbsoluteTimeGetCurrent() + actualidle); +} + +static void *idletimer(void *context) +{ + debug("entry context=%p", context); + gRunLoop = CFRunLoopGetCurrent(); + + unpause_idle_timer(); + + for (;;) + { + debug("Running CFRunLoop"); + CFRunLoopRun(); + sleep(1); + } + + return NULL; +} + +static int initialize_timer() +{ + gTimer = CFRunLoopTimerCreate(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent() + actualidle, actualidle, 0, 0, diediedie, NULL); + int err = 0; + + debug("entry"); + if (0 != (err = pthread_create(&idletimer_thread, NULL, idletimer, NULL))) + helplog(ASL_LEVEL_ERR, "Could not start idletimer thread: %s", strerror(err)); + + return err; +} + +static mach_port_t register_service(const char *service_name) +{ + mach_port_t port = MACH_PORT_NULL; + kern_return_t kr; + + if (KERN_SUCCESS != (kr = bootstrap_check_in(bootstrap_port, (char *)service_name, &port))) + { + helplog(ASL_LEVEL_ERR, "bootstrap_check_in: %d %X %s", kr, kr, mach_error_string(kr)); + return MACH_PORT_NULL; + } + + if (KERN_SUCCESS != (kr = mach_port_insert_right(mach_task_self(), port, port, MACH_MSG_TYPE_MAKE_SEND))) + { + helplog(ASL_LEVEL_ERR, "mach_port_insert_right: %d %X %s", kr, kr, mach_error_string(kr)); + mach_port_deallocate(mach_task_self(), port); + return MACH_PORT_NULL; + } + + return port; +} + +int main(int ac, char *av[]) +{ + char *p = NULL; + kern_return_t kr = KERN_FAILURE; + long n; + int ch; + mach_msg_header_t hdr; + + while ((ch = getopt(ac, av, "dt:")) != -1) + switch (ch) + { + case 'd': opt_debug = 1; break; + case 't': + n = strtol(optarg, &p, 0); + if ('\0' == optarg[0] || '\0' != *p || n > LONG_MAX || n < 0) + { fprintf(stderr, "Invalid idle timeout: %s\n", optarg); exit(EXIT_FAILURE); } + maxidle = n; + break; + case '?': + default: + fprintf(stderr, "Usage: mDNSResponderHelper [-d] [-t maxidle]\n"); + exit(EXIT_FAILURE); + } + ac -= optind; + av += optind; + + initialize_logging(); + helplog(ASL_LEVEL_INFO, "Starting"); + initialize_id(); + +#ifndef NO_SECURITYFRAMEWORK + // We should normally be running as a system daemon. However, that might not be the case in some scenarios (e.g. debugging). + // Explicitly ensure that our Keychain operations utilize the system domain. + if (opt_debug) SecKeychainSetPreferenceDomain(kSecPreferencesDomainSystem); +#endif + gPort = register_service(kmDNSHelperServiceName); + if (!gPort) + exit(EXIT_FAILURE); + + if (maxidle) actualidle = maxidle; + + signal(SIGTERM, handle_sigterm); + + // We use BeginTransactionAtShutdown in the plist that ensures that we will + // receive a SIGTERM during shutdown rather than a SIGKILL. But launchd (due to some + // limitation) currently requires us to still start and end the transaction for + // its proper initialization. + vproc_transaction_t vt = vproc_transaction_begin(NULL); + if (vt) vproc_transaction_end(NULL, vt); + + if (initialize_timer()) exit(EXIT_FAILURE); + for (n=0; n<100000; n++) if (!gRunLoop) usleep(100); + if (!gRunLoop) + { + helplog(ASL_LEVEL_ERR, "gRunLoop not set after waiting"); + exit(EXIT_FAILURE); + } + + for(;;) + { + hdr.msgh_bits = 0; + hdr.msgh_local_port = gPort; + hdr.msgh_remote_port = MACH_PORT_NULL; + hdr.msgh_size = sizeof(hdr); + hdr.msgh_id = 0; + kr = mach_msg(&hdr, MACH_RCV_LARGE | MACH_RCV_MSG, 0, hdr.msgh_size, gPort, 0, 0); + if (MACH_RCV_TOO_LARGE != kr) + helplog(ASL_LEVEL_ERR, "main MACH_RCV_MSG error: %d %X %s", kr, kr, mach_error_string(kr)); + + kr = mach_msg_server_once(helper_server, MAX_MSG_SIZE, gPort, + MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AUDIT) | MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0)); + if (KERN_SUCCESS != kr) + { helplog(ASL_LEVEL_ERR, "mach_msg_server: %d %X %s", kr, kr, mach_error_string(kr)); exit(EXIT_FAILURE); } + + } + exit(EXIT_SUCCESS); +} + +// Note: The C preprocessor stringify operator ('#') makes a string from its argument, without macro expansion +// e.g. If "version" is #define'd to be "4", then STRINGIFY_AWE(version) will return the string "version", not "4" +// To expand "version" to its value before making the string, use STRINGIFY(version) instead +#define STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s) # s +#define STRINGIFY(s) STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s) + +// For convenience when using the "strings" command, this is the last thing in the file +// The "@(#) " pattern is a special prefix the "what" command looks for +const char VersionString_SCCS[] = "@(#) mDNSResponderHelper " STRINGIFY(mDNSResponderVersion) " (" __DATE__ " " __TIME__ ")"; + +#if _BUILDING_XCODE_PROJECT_ +// If the process crashes, then this string will be magically included in the automatically-generated crash log +const char *__crashreporter_info__ = VersionString_SCCS + 5; +asm (".desc ___crashreporter_info__, 0x10"); +#endif diff --git a/mDNSResponder/mDNSMacOSX/helper-server.h b/mDNSResponder/mDNSMacOSX/helper-server.h new file mode 100644 index 00000000..1c391a01 --- /dev/null +++ b/mDNSResponder/mDNSMacOSX/helper-server.h @@ -0,0 +1,31 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2007 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. + */ + +#ifndef H_HELPER_SERVER_H +#define H_HELPER_SERVER_H + +extern void helplog(int, const char *, ...); +extern void pause_idle_timer(void); +extern void unpause_idle_timer(void); +extern void update_idle_timer(void); +extern uid_t mDNSResponderUID; +extern uid_t mDNSResponderGID; +extern CFRunLoopRef gRunLoop; +#define debug(...) debug_(__func__, __VA_ARGS__) +extern void debug_(const char *func, const char *fmt, ...); + +#endif /* H_HELPER_SERVER_H */ diff --git a/mDNSResponder/mDNSMacOSX/helper-stubs.c b/mDNSResponder/mDNSMacOSX/helper-stubs.c new file mode 100644 index 00000000..29fd9aed --- /dev/null +++ b/mDNSResponder/mDNSMacOSX/helper-stubs.c @@ -0,0 +1,496 @@ +/* + * 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" +#include <dispatch/dispatch.h> +#include <arpa/inet.h> + +// +// 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. +// +static dispatch_queue_t HelperQueue; + +#define ERROR(x, y) y, +static const char *errorstring[] = +{ + #include "helper-error.h" + NULL +}; +#undef ERROR + +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; +} + +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) +{ + 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); + dispatch_async(HelperQueue, ^{ + + kern_return_t kr = KERN_FAILURE; + int retry = 0; + int err = 0; + + LogInfo("%s: oldname %s newname %s", __func__, names.oldname, names.newname); + MACHRETRYLOOP_BEGIN(kr, retry, err, fin); + kr = proxy_mDNSPreferencesSetName(getHelperPort(retry), key, names.oldname, names.newname); + MACHRETRYLOOP_END(kr, retry, err, fin); + +fin: + (void)err; + }); +} + +void mDNSRequestBPF(void) +{ + dispatch_async(HelperQueue, ^{ + + kern_return_t kr = KERN_FAILURE; + int retry = 0, err = 0; + LogInfo("%s: BPF", __func__); + 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 +{ + char *titleCopy = NULL; + char *msgCopy = NULL; + + if (title) + { + int len = strlen(title); + titleCopy = mDNSPlatformMemAllocate(len + 1); + if (!titleCopy) + { + LogMsg("mDNSNotify: titleCopy NULL for %s", msg); + return; + } + mDNSPlatformMemCopy(titleCopy, title, len); + titleCopy[len] = 0; + } + if (msg) + { + int len = strlen(msg); + msgCopy = mDNSPlatformMemAllocate(len + 1); + if (!msgCopy) + { + LogMsg("mDNSNotify: msgCopy NULL for %s", msg); + return; + } + mDNSPlatformMemCopy(msgCopy, msg, len); + msgCopy[len] = 0; + } + + dispatch_async(HelperQueue, ^{ + + kern_return_t kr = KERN_FAILURE; + int retry = 0, err = 0; + + LogInfo("%s: title %s, msg %s", __func__, titleCopy, msgCopy); + + MACHRETRYLOOP_BEGIN(kr, retry, err, fin); + kr = proxy_mDNSNotify(getHelperPort(retry), titleCopy, msgCopy); + MACHRETRYLOOP_END(kr, retry, err, fin); +fin: + if (titleCopy) + mDNSPlatformMemFree(titleCopy); + if (msgCopy) + mDNSPlatformMemFree(msgCopy); + (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) +{ + struct + { + // Assume the prefix is no larger than 10 chars + char fqdnStr[MAX_ESCAPED_DOMAIN_NAME + 10]; + } name; + + mDNSPlatformMemZero(name.fqdnStr, MAX_DOMAIN_LABEL + 10); + + if (fqdn) + { + mDNSPlatformStrCopy(name.fqdnStr, prefix); + ConvertDomainNameToCString(fqdn, name.fqdnStr + mDNSPlatformStrLen(prefix)); + } + + dispatch_async(HelperQueue, ^{ + + kern_return_t kr = KERN_SUCCESS; + int retry = 0, err = 0; + + LogInfo("%s: fqdnStr %s", __func__, name.fqdnStr); + + MACHRETRYLOOP_BEGIN(kr, retry, err, fin); + kr = proxy_mDNSConfigureServer(getHelperPort(retry), updown, name.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) +{ + char *ip_addr_copy = NULL; + char *eth_addr_copy = NULL; + + if (eth_addr) + { + int len = strlen(eth_addr); + eth_addr_copy = mDNSPlatformMemAllocate(len + 1); + if (!eth_addr_copy) + { + LogMsg("mDNSSendWakeupPacket: eth_addr_copy NULL for %s", eth_addr); + return; + } + mDNSPlatformMemCopy(eth_addr_copy, eth_addr, len); + eth_addr_copy[len] = 0; + } + if (ip_addr) + { + int len = strlen(ip_addr); + ip_addr_copy = mDNSPlatformMemAllocate(len + 1); + if (!ip_addr_copy) + { + LogMsg("mDNSSendWakeupPacket: ip_addr_copy NULL for %s", ip_addr); + return; + } + mDNSPlatformMemCopy(ip_addr_copy, ip_addr, len); + ip_addr_copy[len] = 0; + } + dispatch_async(HelperQueue, ^{ + + kern_return_t kr = KERN_SUCCESS; + int retry = 0, err = 0; + + LogInfo("%s: Entered ethernet address %s, ip address %s", __func__, eth_addr_copy, ip_addr_copy); + + MACHRETRYLOOP_BEGIN(kr, retry, err, fin); + kr = proxy_mDNSSendWakeupPacket(getHelperPort(retry), ifid, eth_addr_copy, ip_addr_copy, iteration); + MACHRETRYLOOP_END(kr, retry, err, fin); +fin: + if (eth_addr_copy) + mDNSPlatformMemFree(eth_addr_copy); + if (ip_addr_copy) + mDNSPlatformMemFree(ip_addr_copy); + (void) err; + }); +} + +void mDNSPacketFilterControl(uint32_t command, char * ifname, uint32_t count, pfArray_t portArray, pfArray_t protocolArray) +{ + struct + { + pfArray_t portArray; + pfArray_t protocolArray; + } pfa; + char *ifnameCopy = NULL; + + mDNSPlatformMemCopy(pfa.portArray, portArray, sizeof(pfArray_t)); + mDNSPlatformMemCopy(pfa.protocolArray, protocolArray, sizeof(pfArray_t)); + if (ifname) + { + int len = strlen(ifname); + ifnameCopy = mDNSPlatformMemAllocate(len + 1); + if (!ifnameCopy) + { + LogMsg("mDNSPacketFilterControl: ifnameCopy NULL"); + return; + } + mDNSPlatformMemCopy(ifnameCopy, ifname, len); + ifnameCopy[len] = 0; + } + dispatch_async(HelperQueue, ^{ + + kern_return_t kr = KERN_SUCCESS; + int retry = 0, err = 0; + + LogInfo("%s, ifname %s", __func__, ifnameCopy); + + MACHRETRYLOOP_BEGIN(kr, retry, err, fin); + kr = proxy_mDNSPacketFilterControl(getHelperPort(retry), command, ifnameCopy, count, (uint16_t *)pfa.portArray, (uint16_t *)pfa.protocolArray); + MACHRETRYLOOP_END(kr, retry, err, fin); +fin: + if (ifnameCopy) + mDNSPlatformMemFree(ifnameCopy); + (void) err; + }); +} + +void mDNSSendKeepalive(v6addr_t sadd, v6addr_t dadd, uint16_t lport, uint16_t rport, unsigned seq, unsigned ack, uint16_t win) +{ + struct + { + v6addr_t sadd; + v6addr_t dadd; + } addr; + + mDNSPlatformMemCopy(addr.sadd, sadd, sizeof(v6addr_t)); + mDNSPlatformMemCopy(addr.dadd, dadd, sizeof(v6addr_t)); + + dispatch_async(HelperQueue, ^{ + + kern_return_t kr = KERN_FAILURE; + int retry = 0, err = 0; + char buf1[INET6_ADDRSTRLEN]; + char buf2[INET6_ADDRSTRLEN]; + + buf1[0] = 0; + buf2[0] = 0; + + inet_ntop(AF_INET6, addr.sadd, buf1, sizeof(buf1)); + inet_ntop(AF_INET6, addr.dadd, buf2, sizeof(buf2)); + LogInfo("%s: sadd is %s, dadd is %s", __func__, buf1, buf2); + + MACHRETRYLOOP_BEGIN(kr, retry, err, fin); + kr = proxy_mDNSSendKeepalive(getHelperPort(retry), (uint8_t *)addr.sadd, (uint8_t *)addr.dadd, lport, rport, seq, ack, win); + MACHRETRYLOOP_END(kr, retry, err, fin); +fin: + (void) err; + }); +} + +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) +{ + kern_return_t kr = KERN_FAILURE; + int retry = 0, err = 0; + MACHRETRYLOOP_BEGIN(kr, retry, err, fin); + kr = proxy_mDNSRetrieveTCPInfo(getHelperPort(retry), family, (uint8_t *)laddr, lport, (uint8_t *)raddr, rport, seq, ack, win, intfid); + MACHRETRYLOOP_END(kr, retry, err, fin); +fin: + return err; +} + +void mDNSGetRemoteMAC(mDNS *const m, int family, v6addr_t raddr) +{ + struct { + v6addr_t addr; + } dst; + + mDNSPlatformMemCopy(dst.addr, raddr, sizeof(v6addr_t)); + dispatch_async(HelperQueue, ^{ + kern_return_t kr = KERN_FAILURE; + int retry = 0, err = 0; + ethaddr_t eth; + IPAddressMACMapping *addrMapping; + + MACHRETRYLOOP_BEGIN(kr, retry, err, fin); + kr = proxy_mDNSGetRemoteMAC(getHelperPort(retry), family, (uint8_t *)dst.addr, eth); + MACHRETRYLOOP_END(kr, retry, err, fin); + // If the call to get the remote MAC address succeeds, allocate and copy + // the values and schedule a task to update the MAC address in the TCP Keepalive record. + if (kr == KERN_SUCCESS) + { + addrMapping = (IPAddressMACMapping *)malloc(sizeof(IPAddressMACMapping)); + snprintf(addrMapping->ethaddr, sizeof(addrMapping->ethaddr), "%02x:%02x:%02x:%02x:%02x:%02x", + eth[0], eth[1], eth[2], eth[3], eth[4], eth[5]); + if (family == AF_INET) + { + addrMapping->ipaddr.type = mDNSAddrType_IPv4; + mDNSPlatformMemCopy(addrMapping->ipaddr.ip.v4.b, dst.addr, sizeof(v6addr_t)); + } + else + { + addrMapping->ipaddr.type = mDNSAddrType_IPv6; + mDNSPlatformMemCopy(addrMapping->ipaddr.ip.v6.b, dst.addr, sizeof(v6addr_t)); + } + mDNSPlatformDispatchAsync(m, addrMapping, UpdateRMACCallback); + } +fin: + (void) err; + }); + +} + +void mDNSStoreSPSMACAddress(int family, v6addr_t spsaddr, char *ifname) +{ + struct { + v6addr_t saddr; + } addr; + mDNSPlatformMemCopy(addr.saddr, spsaddr, sizeof(v6addr_t)); + + dispatch_async(HelperQueue, ^{ + kern_return_t kr = KERN_FAILURE; + int retry = 0, err = 0; + MACHRETRYLOOP_BEGIN(kr, retry, err, fin); + kr = proxy_mDNSStoreSPSMACAddress(getHelperPort(retry), family, (uint8_t *)addr.saddr, ifname); + MACHRETRYLOOP_END(kr, retry, err, fin); +fin: + (void)err; + }); +} diff --git a/mDNSResponder/mDNSMacOSX/helper.c b/mDNSResponder/mDNSMacOSX/helper.c new file mode 100644 index 00000000..0251e709 --- /dev/null +++ b/mDNSResponder/mDNSMacOSX/helper.c @@ -0,0 +1,2867 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * 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 <sys/cdefs.h> +#include <arpa/inet.h> +#include <bsm/libbsm.h> +#include <net/if.h> +#include <net/route.h> +#include <net/if_dl.h> +#include <net/if_types.h> +#include <netinet/in.h> +#include <netinet/if_ether.h> +#include <netinet6/in6_var.h> +#include <netinet6/nd6.h> +#include <netinet6/ipsec.h> +#include <sys/ioctl.h> +#include <sys/param.h> +#include <sys/socket.h> +#include <asl.h> +#include <ctype.h> +#include <dirent.h> +#include <errno.h> +#include <fcntl.h> +#include <signal.h> +#include <stdarg.h> +#include <stdbool.h> +#include <string.h> +#include <unistd.h> +#include <Security/Security.h> +#include <SystemConfiguration/SystemConfiguration.h> +#include <SystemConfiguration/SCPreferencesSetSpecific.h> +#include <TargetConditionals.h> +#include <IOKit/pwr_mgt/IOPMLib.h> +#include <net/bpf.h> +#include <sys/sysctl.h> + +#include "mDNSEmbeddedAPI.h" +#include "dns_sd.h" +#include "dnssd_ipc.h" +#include "libpfkey.h" +#include "helper.h" +#include "helpermsgServer.h" +#include "helper-server.h" +#include "ipsec_options.h" +#include "P2PPacketFilter.h" + +#include <netinet/ip.h> +#include <netinet/tcp.h> + +#ifndef RTF_IFSCOPE +#define RTF_IFSCOPE 0x1000000 +#endif + +#if TARGET_OS_EMBEDDED +#ifndef MDNS_NO_IPSEC +#define MDNS_NO_IPSEC 1 +#endif +#define NO_CFUSERNOTIFICATION 1 +#define NO_SECURITYFRAMEWORK 1 +#endif + +// Embed the client stub code here, so we can access private functions like ConnectToServer, create_hdr, deliver_request +#include "../mDNSShared/dnssd_ipc.c" +#include "../mDNSShared/dnssd_clientstub.c" + +typedef struct sadb_x_policy *ipsec_policy_t; + +unsigned short InetChecksum(unsigned short *ptr,int nbytes); +unsigned long in_cksum(unsigned short *ptr,int nbytes); +void TCPCheckSum(int af, struct tcphdr *t, int tcplen, v6addr_t sadd6, v6addr_t dadd6); + +uid_t mDNSResponderUID; +gid_t mDNSResponderGID; + +void +debug_(const char *func, const char *fmt, ...) +{ + char buf[2048]; + va_list ap; + + va_start(ap, fmt); + vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + helplog(ASL_LEVEL_DEBUG, "%s: %s", func, buf); +} + +static int +authorized(audit_token_t *token) +{ + int ok = 0; + pid_t pid = (pid_t)-1; + uid_t euid = (uid_t)-1; + + audit_token_to_au32(*token, NULL, &euid, NULL, NULL, NULL, &pid, NULL, + NULL); + ok = (euid == mDNSResponderUID || euid == 0); + if (!ok) + helplog(ASL_LEVEL_NOTICE, + "Unauthorized access by euid=%lu pid=%lu", + (unsigned long)euid, (unsigned long)pid); + return ok; +} + +kern_return_t +do_mDNSExit(__unused mach_port_t port, audit_token_t token) +{ + debug("entry"); + if (!authorized(&token)) + goto fin; + helplog(ASL_LEVEL_INFO, "exit"); + exit(0); + +fin: + debug("fin"); + return KERN_SUCCESS; +} + +kern_return_t do_mDNSRequestBPF(__unused mach_port_t port, audit_token_t token) +{ + if (!authorized(&token)) return KERN_SUCCESS; + DNSServiceRef ref; + DNSServiceErrorType err = ConnectToServer(&ref, 0, send_bpf, NULL, NULL, NULL); + if (err) { helplog(ASL_LEVEL_ERR, "do_mDNSRequestBPF: ConnectToServer %d", err); return err; } + + char *ptr; + size_t len = sizeof(DNSServiceFlags); + ipc_msg_hdr *hdr = create_hdr(send_bpf, &len, &ptr, 0, ref); + if (!hdr) { DNSServiceRefDeallocate(ref); return kDNSServiceErr_NoMemory; } + put_flags(0, &ptr); + deliver_request(hdr, ref); // Will free hdr for us + DNSServiceRefDeallocate(ref); + update_idle_timer(); + return KERN_SUCCESS; +} + +kern_return_t do_mDNSPowerRequest(__unused mach_port_t port, int key, int interval, int *err, audit_token_t token) +{ + *err = -1; + if (!authorized(&token)) { *err = kmDNSHelperNotAuthorized; goto fin; } + + CFArrayRef events = IOPMCopyScheduledPowerEvents(); + if (events) + { + int i; + CFIndex count = CFArrayGetCount(events); + for (i=0; i<count; i++) + { + CFDictionaryRef dict = CFArrayGetValueAtIndex(events, i); + CFStringRef id = CFDictionaryGetValue(dict, CFSTR(kIOPMPowerEventAppNameKey)); + if (CFEqual(id, CFSTR("mDNSResponderHelper"))) + { + CFDateRef EventTime = CFDictionaryGetValue(dict, CFSTR(kIOPMPowerEventTimeKey)); + CFStringRef EventType = CFDictionaryGetValue(dict, CFSTR(kIOPMPowerEventTypeKey)); + IOReturn result = IOPMCancelScheduledPowerEvent(EventTime, id, EventType); + //helplog(ASL_LEVEL_ERR, "Deleting old event %s"); + if (result) helplog(ASL_LEVEL_ERR, "IOPMCancelScheduledPowerEvent %d failed %d", i, result); + } + } + CFRelease(events); + } + + if (key < 0) // mDNSPowerRequest(-1,-1) means "clear any stale schedules" (see above) + *err = 0; + else if (key == 0) // mDNSPowerRequest(0, 0) means "sleep now" + { + IOReturn r = IOPMSleepSystem(IOPMFindPowerManagement(MACH_PORT_NULL)); + if (r) { usleep(100000); helplog(ASL_LEVEL_ERR, "IOPMSleepSystem %d", r); } + *err = r; + } + else if (key > 0) + { + CFDateRef w = CFDateCreate(NULL, CFAbsoluteTimeGetCurrent() + interval); + if (w) + { + IOReturn r = IOPMSchedulePowerEvent(w, CFSTR("mDNSResponderHelper"), key ? CFSTR(kIOPMAutoWake) : CFSTR(kIOPMAutoSleep)); + if (r) { usleep(100000); helplog(ASL_LEVEL_ERR, "IOPMSchedulePowerEvent(%d) %d %x", interval, r, r); } + *err = r; + CFRelease(w); + } + } +fin: + update_idle_timer(); + return KERN_SUCCESS; +} + +kern_return_t do_mDNSSetLocalAddressCacheEntry(__unused mach_port_t port, int ifindex, int family, v6addr_t ip, ethaddr_t eth, int *err, audit_token_t token) +{ + #define IPv6FMTSTRING "%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X" + #define IPv6FMTARGS ip[0], ip[1], ip[2], ip[3], ip[4], ip[5], ip[6], ip[7], ip[8], ip[9], ip[10], ip[11], ip[12], ip[13], ip[14], ip[15] + #if 0 + if (family == 4) + helplog(ASL_LEVEL_ERR, "do_mDNSSetLocalAddressCacheEntry %d IPv%d %d.%d.%d.%d %02X:%02X:%02X:%02X:%02X:%02X", + ifindex, family, ip[0], ip[1], ip[2], ip[3], eth[0], eth[1], eth[2], eth[3], eth[4], eth[5]); + else + helplog(ASL_LEVEL_ERR, "do_mDNSSetLocalAddressCacheEntry %d IPv%d " IPv6FMTSTRING " %02X:%02X:%02X:%02X:%02X:%02X", + ifindex, family, IPv6FMTARGS, eth[0], eth[1], eth[2], eth[3], eth[4], eth[5]); + #endif + + *err = -1; + if (!authorized(&token)) { *err = kmDNSHelperNotAuthorized; goto fin; } + + static int s = -1, seq = 0; + if (s < 0) + { + s = socket(PF_ROUTE, SOCK_RAW, 0); + if (s < 0) helplog(ASL_LEVEL_ERR, "do_mDNSSetLocalAddressCacheEntry: socket(PF_ROUTE, SOCK_RAW, 0) failed %d (%s)", errno, strerror(errno)); + } + + if (s >= 0) + { + struct timeval tv; + gettimeofday(&tv, 0); + if (family == 4) + { + struct { struct rt_msghdr hdr; struct sockaddr_inarp dst; struct sockaddr_dl sdl; } rtmsg; + memset(&rtmsg, 0, sizeof(rtmsg)); + + rtmsg.hdr.rtm_msglen = sizeof(rtmsg); + rtmsg.hdr.rtm_version = RTM_VERSION; + rtmsg.hdr.rtm_type = RTM_ADD; + rtmsg.hdr.rtm_index = ifindex; + rtmsg.hdr.rtm_flags = RTF_HOST | RTF_STATIC | RTF_IFSCOPE; + rtmsg.hdr.rtm_addrs = RTA_DST | RTA_GATEWAY; + rtmsg.hdr.rtm_pid = 0; + rtmsg.hdr.rtm_seq = seq++; + rtmsg.hdr.rtm_errno = 0; + rtmsg.hdr.rtm_use = 0; + rtmsg.hdr.rtm_inits = RTV_EXPIRE; + rtmsg.hdr.rtm_rmx.rmx_expire = tv.tv_sec + 30; + + rtmsg.dst.sin_len = sizeof(rtmsg.dst); + rtmsg.dst.sin_family = AF_INET; + rtmsg.dst.sin_port = 0; + rtmsg.dst.sin_addr.s_addr = *(in_addr_t*)ip; + rtmsg.dst.sin_srcaddr.s_addr = 0; + rtmsg.dst.sin_tos = 0; + rtmsg.dst.sin_other = 0; + + rtmsg.sdl.sdl_len = sizeof(rtmsg.sdl); + rtmsg.sdl.sdl_family = AF_LINK; + rtmsg.sdl.sdl_index = ifindex; + rtmsg.sdl.sdl_type = IFT_ETHER; + rtmsg.sdl.sdl_nlen = 0; + rtmsg.sdl.sdl_alen = ETHER_ADDR_LEN; + rtmsg.sdl.sdl_slen = 0; + + // Target MAC address goes in rtmsg.sdl.sdl_data[0..5]; (See LLADDR() in /usr/include/net/if_dl.h) + memcpy(rtmsg.sdl.sdl_data, eth, sizeof(ethaddr_t)); + + int len = write(s, (char *)&rtmsg, sizeof(rtmsg)); + if (len < 0) + helplog(ASL_LEVEL_ERR, "do_mDNSSetLocalAddressCacheEntry: write(%d) interface %d address %d.%d.%d.%d seq %d result %d errno %d (%s)", + sizeof(rtmsg), ifindex, ip[0], ip[1], ip[2], ip[3], rtmsg.hdr.rtm_seq, len, errno, strerror(errno)); + len = read(s, (char *)&rtmsg, sizeof(rtmsg)); + if (len < 0 || rtmsg.hdr.rtm_errno) + helplog(ASL_LEVEL_ERR, "do_mDNSSetLocalAddressCacheEntry: read (%d) interface %d address %d.%d.%d.%d seq %d result %d errno %d (%s) %d", + sizeof(rtmsg), ifindex, ip[0], ip[1], ip[2], ip[3], rtmsg.hdr.rtm_seq, len, errno, strerror(errno), rtmsg.hdr.rtm_errno); + + *err = 0; + } + else + { + struct { struct rt_msghdr hdr; struct sockaddr_in6 dst; struct sockaddr_dl sdl; } rtmsg; + memset(&rtmsg, 0, sizeof(rtmsg)); + + rtmsg.hdr.rtm_msglen = sizeof(rtmsg); + rtmsg.hdr.rtm_version = RTM_VERSION; + rtmsg.hdr.rtm_type = RTM_ADD; + rtmsg.hdr.rtm_index = ifindex; + rtmsg.hdr.rtm_flags = RTF_HOST | RTF_STATIC | RTF_IFSCOPE; + rtmsg.hdr.rtm_addrs = RTA_DST | RTA_GATEWAY; + rtmsg.hdr.rtm_pid = 0; + rtmsg.hdr.rtm_seq = seq++; + rtmsg.hdr.rtm_errno = 0; + rtmsg.hdr.rtm_use = 0; + rtmsg.hdr.rtm_inits = RTV_EXPIRE; + rtmsg.hdr.rtm_rmx.rmx_expire = tv.tv_sec + 30; + + rtmsg.dst.sin6_len = sizeof(rtmsg.dst); + rtmsg.dst.sin6_family = AF_INET6; + rtmsg.dst.sin6_port = 0; + rtmsg.dst.sin6_flowinfo = 0; + rtmsg.dst.sin6_addr = *(struct in6_addr*)ip; + rtmsg.dst.sin6_scope_id = ifindex; + + rtmsg.sdl.sdl_len = sizeof(rtmsg.sdl); + rtmsg.sdl.sdl_family = AF_LINK; + rtmsg.sdl.sdl_index = ifindex; + rtmsg.sdl.sdl_type = IFT_ETHER; + rtmsg.sdl.sdl_nlen = 0; + rtmsg.sdl.sdl_alen = ETHER_ADDR_LEN; + rtmsg.sdl.sdl_slen = 0; + + // Target MAC address goes in rtmsg.sdl.sdl_data[0..5]; (See LLADDR() in /usr/include/net/if_dl.h) + memcpy(rtmsg.sdl.sdl_data, eth, sizeof(ethaddr_t)); + + int len = write(s, (char *)&rtmsg, sizeof(rtmsg)); + if (len < 0) + helplog(ASL_LEVEL_ERR, "do_mDNSSetLocalAddressCacheEntry: write(%d) interface %d address " IPv6FMTSTRING " seq %d result %d errno %d (%s)", + sizeof(rtmsg), ifindex, IPv6FMTARGS, rtmsg.hdr.rtm_seq, len, errno, strerror(errno)); + len = read(s, (char *)&rtmsg, sizeof(rtmsg)); + if (len < 0 || rtmsg.hdr.rtm_errno) + helplog(ASL_LEVEL_ERR, "do_mDNSSetLocalAddressCacheEntry: read (%d) interface %d address " IPv6FMTSTRING " seq %d result %d errno %d (%s) %d", + sizeof(rtmsg), ifindex, IPv6FMTARGS, rtmsg.hdr.rtm_seq, len, errno, strerror(errno), rtmsg.hdr.rtm_errno); + + *err = 0; + } + + } + +fin: + update_idle_timer(); + return KERN_SUCCESS; +} + +kern_return_t do_mDNSNotify(__unused mach_port_t port, const char *title, const char *msg, audit_token_t token) +{ + if (!authorized(&token)) return KERN_SUCCESS; + +#ifndef NO_CFUSERNOTIFICATION + static const char footer[] = "(Note: This message only appears on machines with 17.x.x.x IP addresses — i.e. at Apple — not on customer machines.)"; + CFStringRef alertHeader = CFStringCreateWithCString(NULL, title, kCFStringEncodingUTF8); + CFStringRef alertBody = CFStringCreateWithCString(NULL, msg, kCFStringEncodingUTF8); + CFStringRef alertFooter = CFStringCreateWithCString(NULL, footer, kCFStringEncodingUTF8); + CFStringRef alertMessage = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@\r\r%@"), alertBody, alertFooter); + CFRelease(alertBody); + CFRelease(alertFooter); + int err = CFUserNotificationDisplayNotice(0.0, kCFUserNotificationStopAlertLevel, NULL, NULL, NULL, alertHeader, alertMessage, NULL); + if (err) helplog(ASL_LEVEL_ERR, "CFUserNotificationDisplayNotice returned %d", err); + CFRelease(alertHeader); + CFRelease(alertMessage); +#else + (void)title; + (void)msg; +#endif /* NO_CFUSERNOTIFICATION */ + + update_idle_timer(); + return KERN_SUCCESS; +} + +char usercompname[MAX_DOMAIN_LABEL+1] = {0}; // the last computer name the user saw +char userhostname[MAX_DOMAIN_LABEL+1] = {0}; // the last local host name the user saw +char lastcompname[MAX_DOMAIN_LABEL+1] = {0}; // the last computer name saved to preferences +char lasthostname[MAX_DOMAIN_LABEL+1] = {0}; // the last local host name saved to preferences + +#ifndef NO_CFUSERNOTIFICATION +static CFStringRef CFS_OQ = NULL; +static CFStringRef CFS_CQ = NULL; +static CFStringRef CFS_Format = NULL; +static CFStringRef CFS_ComputerName = NULL; +static CFStringRef CFS_ComputerNameMsg = NULL; +static CFStringRef CFS_LocalHostName = NULL; +static CFStringRef CFS_LocalHostNameMsg = NULL; +static CFStringRef CFS_Problem = NULL; + +static CFUserNotificationRef gNotification = NULL; +static CFRunLoopSourceRef gNotificationRLS = NULL; + +static void NotificationCallBackDismissed(CFUserNotificationRef userNotification, CFOptionFlags responseFlags) +{ + debug("entry"); + (void)responseFlags; // Unused + if (userNotification != gNotification) helplog(ASL_LEVEL_ERR, "NotificationCallBackDismissed: Wrong CFUserNotificationRef"); + if (gNotificationRLS) + { + // Caution: don't use CFRunLoopGetCurrent() here, because the currently executing thread may not be our "CFRunLoopRun" thread. + // We need to explicitly specify the desired CFRunLoop from which we want to remove this event source. + CFRunLoopRemoveSource(gRunLoop, gNotificationRLS, kCFRunLoopDefaultMode); + CFRelease(gNotificationRLS); + gNotificationRLS = NULL; + CFRelease(gNotification); + gNotification = NULL; + } + // By dismissing the alert, the user has conceptually acknowleged the rename. + // (e.g. the machine's name is now officially "computer-2.local", not "computer.local".) + // If we get *another* conflict, the new alert should refer to the 'old' name + // as now being "computer-2.local", not "computer.local" + usercompname[0] = 0; + userhostname[0] = 0; + lastcompname[0] = 0; + lasthostname[0] = 0; + update_idle_timer(); + unpause_idle_timer(); +} + +static void ShowNameConflictNotification(CFMutableArrayRef header, CFStringRef subtext) +{ + CFMutableDictionaryRef dictionary = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + if (!dictionary) return; + + debug("entry"); + + CFDictionarySetValue(dictionary, kCFUserNotificationAlertHeaderKey, header); + CFDictionarySetValue(dictionary, kCFUserNotificationAlertMessageKey, subtext); + + CFURLRef urlRef = CFURLCreateWithFileSystemPath(NULL, CFSTR("/System/Library/CoreServices/mDNSResponder.bundle"), kCFURLPOSIXPathStyle, true); + if (urlRef) { CFDictionarySetValue(dictionary, kCFUserNotificationLocalizationURLKey, urlRef); CFRelease(urlRef); } + + if (gNotification) // If notification already on-screen, update it in place + CFUserNotificationUpdate(gNotification, 0, kCFUserNotificationCautionAlertLevel, dictionary); + else // else, we need to create it + { + SInt32 error; + gNotification = CFUserNotificationCreate(NULL, 0, kCFUserNotificationCautionAlertLevel, &error, dictionary); + if (!gNotification || error) { helplog(ASL_LEVEL_ERR, "ShowNameConflictNotification: CFUserNotificationRef: Error %d", error); return; } + gNotificationRLS = CFUserNotificationCreateRunLoopSource(NULL, gNotification, NotificationCallBackDismissed, 0); + if (!gNotificationRLS) { helplog(ASL_LEVEL_ERR,"ShowNameConflictNotification: RLS"); CFRelease(gNotification); gNotification = NULL; return; } + // Caution: don't use CFRunLoopGetCurrent() here, because the currently executing thread may not be our "CFRunLoopRun" thread. + // We need to explicitly specify the desired CFRunLoop to which we want to add this event source. + CFRunLoopAddSource(gRunLoop, gNotificationRLS, kCFRunLoopDefaultMode); + debug("gRunLoop=%p gNotification=%p gNotificationRLS=%p", gRunLoop, gNotification, gNotificationRLS); + pause_idle_timer(); + } + + CFRelease(dictionary); +} + +static CFMutableArrayRef GetHeader(const char* oldname, const char* newname, const CFStringRef msg, const char* suffix) +{ + CFMutableArrayRef alertHeader = NULL; + + const CFStringRef cfoldname = CFStringCreateWithCString(NULL, oldname, kCFStringEncodingUTF8); + // NULL newname means we've given up trying to construct a name that doesn't conflict + const CFStringRef cfnewname = newname ? CFStringCreateWithCString(NULL, newname, kCFStringEncodingUTF8) : NULL; + // We tag a zero-width non-breaking space at the end of the literal text to guarantee that, no matter what + // arbitrary computer name the user may choose, this exact text (with zero-width non-breaking space added) + // can never be one that occurs in the Localizable.strings translation file. + if (!cfoldname) + helplog(ASL_LEVEL_ERR,"Could not construct CFStrings for old=%s", newname); + else if (newname && !cfnewname) + helplog(ASL_LEVEL_ERR,"Could not construct CFStrings for new=%s", newname); + else + { + const CFStringRef s1 = CFStringCreateWithFormat(NULL, NULL, CFS_Format, cfoldname, suffix); + const CFStringRef s2 = cfnewname ? CFStringCreateWithFormat(NULL, NULL, CFS_Format, cfnewname, suffix) : NULL; + + alertHeader = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); + + if (!s1) + helplog(ASL_LEVEL_ERR, "Could not construct secondary CFString for old=%s", oldname); + else if (cfnewname && !s2) + helplog(ASL_LEVEL_ERR, "Could not construct secondary CFString for new=%s", newname); + else if (!alertHeader) + helplog(ASL_LEVEL_ERR, "Could not construct CFArray for notification"); + else + { + // Make sure someone is logged in. We don't want this popping up over the login window + uid_t uid; + gid_t gid; + CFStringRef userName = SCDynamicStoreCopyConsoleUser(NULL, &uid, &gid); + if (userName) + { + CFRelease(userName); + CFArrayAppendValue(alertHeader, msg); // Opening phrase of message, provided by caller + CFArrayAppendValue(alertHeader, CFS_OQ); CFArrayAppendValue(alertHeader, s1); CFArrayAppendValue(alertHeader, CFS_CQ); + CFArrayAppendValue(alertHeader, CFSTR(" is already in use on this network. ")); + if (s2) + { + CFArrayAppendValue(alertHeader, CFSTR("The name has been changed to ")); + CFArrayAppendValue(alertHeader, CFS_OQ); CFArrayAppendValue(alertHeader, s2); CFArrayAppendValue(alertHeader, CFS_CQ); + CFArrayAppendValue(alertHeader, CFSTR(".")); + } + else + CFArrayAppendValue(alertHeader, CFSTR("All attempts to find an available name by adding a number to the name were also unsuccessful.")); + } + } + if (s1) CFRelease(s1); + if (s2) CFRelease(s2); + } + if (cfoldname) CFRelease(cfoldname); + if (cfnewname) CFRelease(cfnewname); + + return alertHeader; +} +#endif /* ndef NO_CFUSERNOTIFICATION */ + +static void update_notification(void) +{ +#ifndef NO_CFUSERNOTIFICATION + debug("entry ucn=%s, uhn=%s, lcn=%s, lhn=%s", usercompname, userhostname, lastcompname, lasthostname); + if (!CFS_OQ) + { + // Note: the "\xEF\xBB\xBF" byte sequence in the CFS_Format string is the UTF-8 encoding of the zero-width non-breaking space character. + // By appending this invisible character on the end of literal names, we ensure the these strings cannot inadvertently match any string + // in the localization file -- since we know for sure that none of our strings in the localization file contain the ZWNBS character. + // + // For languages that are written right to left, when we mix English (host names could be in english with brackets etc. and the + // rest in Arabic) we need unicode markups for proper formatting. The Unicode sequence 202C (UTF8 E2 80 AC), 200E (UTF8 E2 80 8E) and + // 202B (UTF8 E2 80 AB) helps with the formatting. See <rdar://problem/8629082> for more details. + CFS_OQ = CFStringCreateWithCString(NULL, "“\xE2\x80\xAB", kCFStringEncodingUTF8); + CFS_CQ = CFStringCreateWithCString(NULL, "\xE2\x80\xAC”", kCFStringEncodingUTF8); + CFS_Format = CFStringCreateWithCString(NULL, "%@%s\xEF\xBB\xBF\xE2\x80\x8E", kCFStringEncodingUTF8); + CFS_ComputerName = CFStringCreateWithCString(NULL, "The name of your computer ", kCFStringEncodingUTF8); + CFS_ComputerNameMsg = CFStringCreateWithCString(NULL, "To change the name of your computer, " + "open System Preferences and click Sharing, then type the name in the Computer Name field.", kCFStringEncodingUTF8); + CFS_LocalHostName = CFStringCreateWithCString(NULL, "This computer’s local hostname ", kCFStringEncodingUTF8); + CFS_LocalHostNameMsg = CFStringCreateWithCString(NULL, "To change the local hostname, " + "open System Preferences and click Sharing, then click “Edit” and type the name in the Local Hostname field.", kCFStringEncodingUTF8); + CFS_Problem = CFStringCreateWithCString(NULL, "This may indicate a problem with the local network. " + "Please inform your network administrator.", kCFStringEncodingUTF8); + } + + if (!usercompname[0] && !userhostname[0]) + { + if (gNotificationRLS) + { + debug("canceling notification %p", gNotification); + CFUserNotificationCancel(gNotification); + unpause_idle_timer(); + } + } + else + { + CFMutableArrayRef header = NULL; + CFStringRef* subtext = NULL; + if (userhostname[0] && !lasthostname[0]) // we've given up trying to construct a name that doesn't conflict + { + header = GetHeader(userhostname, NULL, CFS_LocalHostName, ".local"); + subtext = &CFS_Problem; + } + else if (usercompname[0]) + { + header = GetHeader(usercompname, lastcompname, CFS_ComputerName, ""); + subtext = &CFS_ComputerNameMsg; + } + else + { + header = GetHeader(userhostname, lasthostname, CFS_LocalHostName, ".local"); + subtext = &CFS_LocalHostNameMsg; + } + ShowNameConflictNotification(header, *subtext); + CFRelease(header); + } +#endif +} + +kern_return_t +do_mDNSPreferencesSetName(__unused mach_port_t port, int key, const char* old, const char* new, audit_token_t token) +{ + SCPreferencesRef session = NULL; + Boolean ok = FALSE; + Boolean locked = FALSE; + CFStringRef cfstr = NULL; + char* user = NULL; + char* last = NULL; + Boolean needUpdate = FALSE; + + debug("entry %s old=%s new=%s", key==kmDNSComputerName ? "ComputerName" : (key==kmDNSLocalHostName ? "LocalHostName" : "UNKNOWN"), old, new); + if (!authorized(&token)) goto fin; + + switch ((enum mDNSPreferencesSetNameKey)key) + { + case kmDNSComputerName: + user = usercompname; + last = lastcompname; + break; + case kmDNSLocalHostName: + user = userhostname; + last = lasthostname; + break; + default: + debug("unrecognized key: %d", key); + goto fin; + } + + if (!last) + { + helplog(ASL_LEVEL_ERR, "%s: no last ptr", __func__); + goto fin; + } + + if (!user) + { + helplog(ASL_LEVEL_ERR, "%s: no user ptr", __func__); + goto fin; + } + + if (0 == strncmp(old, new, MAX_DOMAIN_LABEL+1)) + { + // old and new are same means the config changed i.e, the user has set something in the preferences pane. + // This means the conflict has been resolved. We need to dismiss the dialogue. + if (last[0] && 0 != strncmp(last, new, MAX_DOMAIN_LABEL+1)) + { + last[0] = 0; + user[0] = 0; + needUpdate = TRUE; + } + goto fin; + } + else + { + // old and new are not same, this means there is a conflict. For the first conflict, we show + // the old value and the new value. For all subsequent conflicts, while the dialogue is still + // up, we do a real time update of the "new" value in the dialogue. That's why we update just + // "last" here and not "user". + if (strncmp(last, new, MAX_DOMAIN_LABEL+1)) + { + strncpy(last, new, MAX_DOMAIN_LABEL); + needUpdate = TRUE; + } + } + + // If we are not showing the dialogue, we need to remember the first "old" value so that + // we maintain the same through the lifetime of the dialogue. Subsequence conflicts don't + // update the "old" value. + if (!user[0]) + { + strncpy(user, old, MAX_DOMAIN_LABEL); + needUpdate = TRUE; + } + + if (!new[0]) // we've given up trying to construct a name that doesn't conflict + goto fin; + + cfstr = CFStringCreateWithCString(NULL, new, kCFStringEncodingUTF8); + + session = SCPreferencesCreate(NULL, CFSTR(kmDNSHelperServiceName), NULL); + + if (cfstr == NULL || session == NULL) + { + debug("SCPreferencesCreate failed"); + goto fin; + } + if (!SCPreferencesLock(session, 0)) + { + debug("lock failed"); + goto fin; + } + locked = TRUE; + + switch ((enum mDNSPreferencesSetNameKey)key) + { + case kmDNSComputerName: + { + // We want to write the new Computer Name to System Preferences, without disturbing the user-selected + // system-wide default character set used for things like AppleTalk NBP and NETBIOS service advertising. + // Note that this encoding is not used for the computer name, but since both are set by the same call, + // we need to take care to set the name without changing the character set. + CFStringEncoding encoding = kCFStringEncodingUTF8; + CFStringRef unused = SCDynamicStoreCopyComputerName(NULL, &encoding); + if (unused) { CFRelease(unused); unused = NULL; } + else encoding = kCFStringEncodingUTF8; + + ok = SCPreferencesSetComputerName(session, cfstr, encoding); + } + break; + case kmDNSLocalHostName: + ok = SCPreferencesSetLocalHostName(session, cfstr); + break; + default: + break; + } + + if (!ok || !SCPreferencesCommitChanges(session) || + !SCPreferencesApplyChanges(session)) + { + debug("SCPreferences update failed"); + goto fin; + } + debug("succeeded"); + +fin: + if (NULL != cfstr) + CFRelease(cfstr); + if (NULL != session) + { + if (locked) + SCPreferencesUnlock(session); + CFRelease(session); + } + update_idle_timer(); + if (needUpdate) update_notification(); + return KERN_SUCCESS; +} + +enum DNSKeyFormat +{ + formatNotDNSKey, formatDdnsTypeItem, formatDnsPrefixedServiceItem, formatBtmmPrefixedServiceItem +}; + +// On Mac OS X on Intel, the four-character string seems to be stored backwards, at least sometimes. +// I suspect some overenthusiastic inexperienced engineer said, "On Intel everything's backwards, +// therefore I need to add some byte swapping in this API to make this four-character string backwards too." +// To cope with this we allow *both* "ddns" and "sndd" as valid item types. + + +#ifndef NO_SECURITYFRAMEWORK +static const char btmmprefix[] = "btmmdns:"; +static const char dnsprefix[] = "dns:"; +static const char ddns[] = "ddns"; +static const char ddnsrev[] = "sndd"; + +static enum DNSKeyFormat +getDNSKeyFormat(SecKeychainItemRef item, SecKeychainAttributeList **attributesp) +{ + static UInt32 tags[4] = + { + kSecTypeItemAttr, kSecServiceItemAttr, kSecAccountItemAttr, kSecLabelItemAttr + }; + static SecKeychainAttributeInfo attributeInfo = + { + sizeof(tags)/sizeof(tags[0]), tags, NULL + }; + SecKeychainAttributeList *attributes = NULL; + enum DNSKeyFormat format; + Boolean malformed = FALSE; + OSStatus status = noErr; + int i = 0; + + *attributesp = NULL; + if (noErr != (status = SecKeychainItemCopyAttributesAndData(item, + &attributeInfo, NULL, &attributes, NULL, NULL))) + { + debug("SecKeychainItemCopyAttributesAndData %d - skipping", + status); + goto skip; + } + if (attributeInfo.count != attributes->count) + malformed = TRUE; + for (i = 0; !malformed && i < (int)attributeInfo.count; ++i) + if (attributeInfo.tag[i] != attributes->attr[i].tag) + malformed = TRUE; + if (malformed) + { + debug( + "malformed result from SecKeychainItemCopyAttributesAndData - skipping"); + goto skip; + } + + debug("entry (\"%.*s\", \"%.*s\", \"%.*s\")", + (int)attributes->attr[0].length, attributes->attr[0].data, + (int)attributes->attr[1].length, attributes->attr[1].data, + (int)attributes->attr[2].length, attributes->attr[2].data); + if (attributes->attr[1].length >= MAX_ESCAPED_DOMAIN_NAME + + sizeof(dnsprefix)-1) + { + debug("kSecServiceItemAttr too long (%u) - skipping", + (unsigned int)attributes->attr[1].length); + goto skip; + } + if (attributes->attr[2].length >= MAX_ESCAPED_DOMAIN_NAME) + { + debug("kSecAccountItemAttr too long (%u) - skipping", + (unsigned int)attributes->attr[2].length); + goto skip; + } + if (attributes->attr[1].length >= sizeof(dnsprefix)-1 && + 0 == strncasecmp(attributes->attr[1].data, dnsprefix, + sizeof(dnsprefix)-1)) + format = formatDnsPrefixedServiceItem; + else if (attributes->attr[1].length >= sizeof(btmmprefix)-1 && + 0 == strncasecmp(attributes->attr[1].data, btmmprefix, sizeof(btmmprefix)-1)) + format = formatBtmmPrefixedServiceItem; + else if (attributes->attr[0].length == sizeof(ddns)-1 && + 0 == strncasecmp(attributes->attr[0].data, ddns, sizeof(ddns)-1)) + format = formatDdnsTypeItem; + else if (attributes->attr[0].length == sizeof(ddnsrev)-1 && + 0 == strncasecmp(attributes->attr[0].data, ddnsrev, sizeof(ddnsrev)-1)) + format = formatDdnsTypeItem; + else + { + debug("uninterested in this entry"); + goto skip; + } + *attributesp = attributes; + debug("accepting this entry"); + return format; + +skip: + SecKeychainItemFreeAttributesAndData(attributes, NULL); + return formatNotDNSKey; +} + +// Insert the attributes as defined by mDNSKeyChainAttributes +static CFPropertyListRef +getKeychainItemInfo(SecKeychainItemRef item, + SecKeychainAttributeList *attributes, enum DNSKeyFormat format) +{ + CFMutableArrayRef entry = NULL; + CFDataRef data = NULL; + OSStatus status = noErr; + UInt32 keylen = 0; + void *keyp = 0; + + if (NULL == (entry = CFArrayCreateMutable(NULL, 0, + &kCFTypeArrayCallBacks))) + { + debug("CFArrayCreateMutable failed"); + goto error; + } + + // Insert the Account attribute (kmDNSKcWhere) + switch ((enum DNSKeyFormat)format) + { + case formatDdnsTypeItem: + data = CFDataCreate(kCFAllocatorDefault, + attributes->attr[1].data, attributes->attr[1].length); + break; + case formatDnsPrefixedServiceItem: + case formatBtmmPrefixedServiceItem: + data = CFDataCreate(kCFAllocatorDefault, + attributes->attr[1].data, attributes->attr[1].length); + break; + default: + assert("unknown DNSKeyFormat value"); + break; + } + if (NULL == data) + { + debug("CFDataCreate for attr[1] failed"); + goto error; + } + CFArrayAppendValue(entry, data); + CFRelease(data); + + // Insert the Where attribute (kmDNSKcAccount) + if (NULL == (data = CFDataCreate(kCFAllocatorDefault, + attributes->attr[2].data, attributes->attr[2].length))) + { + debug("CFDataCreate for attr[2] failed"); + goto error; + } + CFArrayAppendValue(entry, data); + CFRelease(data); + + // Insert the Key attribute (kmDNSKcKey) + if (noErr != (status = SecKeychainItemCopyAttributesAndData(item, NULL, + NULL, NULL, &keylen, &keyp))) + { + debug("could not retrieve key for \"%.*s\": %d", + (int)attributes->attr[1].length, attributes->attr[1].data, + status); + goto error; + } + data = CFDataCreate(kCFAllocatorDefault, keyp, keylen); + SecKeychainItemFreeAttributesAndData(NULL, keyp); + if (NULL == data) + { + debug("CFDataCreate for keyp failed"); + goto error; + } + CFArrayAppendValue(entry, data); + CFRelease(data); + + // Insert the Name attribute (kmDNSKcName) + if (NULL == (data = CFDataCreate(kCFAllocatorDefault, + attributes->attr[3].data, attributes->attr[3].length))) + { + debug("CFDataCreate for attr[3] failed"); + goto error; + } + CFArrayAppendValue(entry, data); + CFRelease(data); + return entry; + +error: + if (NULL != entry) + CFRelease(entry); + return NULL; +} +#endif + +kern_return_t +do_mDNSKeychainGetSecrets(__unused mach_port_t port, __unused unsigned int *numsecrets, + __unused vm_offset_t *secrets, __unused mach_msg_type_number_t *secretsCnt, __unused int *err, + __unused audit_token_t token) +{ +#ifndef NO_SECURITYFRAMEWORK + CFWriteStreamRef stream = NULL; + CFDataRef result = NULL; + CFPropertyListRef entry = NULL; + CFMutableArrayRef keys = NULL; + SecKeychainRef skc = NULL; + SecKeychainItemRef item = NULL; + SecKeychainSearchRef search = NULL; + SecKeychainAttributeList *attributes = NULL; + enum DNSKeyFormat format; + OSStatus status = 0; + + debug("entry"); + *err = 0; + *numsecrets = 0; + *secrets = (vm_offset_t)NULL; + if (!authorized(&token)) + { + *err = kmDNSHelperNotAuthorized; + goto fin; + } + if (NULL == (keys = CFArrayCreateMutable(NULL, 0, + &kCFTypeArrayCallBacks))) + { + debug("CFArrayCreateMutable failed"); + *err = kmDNSHelperCreationFailed; + goto fin; + } + if (noErr != (status = SecKeychainCopyDefault(&skc))) + { + *err = kmDNSHelperKeychainCopyDefaultFailed; + goto fin; + } + if (noErr != (status = SecKeychainSearchCreateFromAttributes(skc, kSecGenericPasswordItemClass, NULL, &search))) + { + *err = kmDNSHelperKeychainSearchCreationFailed; + goto fin; + } + for (status = SecKeychainSearchCopyNext(search, &item); + noErr == status; + status = SecKeychainSearchCopyNext(search, &item)) + { + if (formatNotDNSKey != (format = getDNSKeyFormat(item, + &attributes)) && + NULL != (entry = getKeychainItemInfo(item, attributes, + format))) + { + CFArrayAppendValue(keys, entry); + CFRelease(entry); + } + SecKeychainItemFreeAttributesAndData(attributes, NULL); + CFRelease(item); + } + if (errSecItemNotFound != status) + helplog(ASL_LEVEL_ERR, "%s: SecKeychainSearchCopyNext failed: %d", + __func__, status); + if (NULL == (stream = + CFWriteStreamCreateWithAllocatedBuffers(kCFAllocatorDefault, + kCFAllocatorDefault))) + { + *err = kmDNSHelperCreationFailed; + debug("CFWriteStreamCreateWithAllocatedBuffers failed"); + goto fin; + } + CFWriteStreamOpen(stream); + if (0 == CFPropertyListWriteToStream(keys, stream, + kCFPropertyListBinaryFormat_v1_0, NULL)) + { + *err = kmDNSHelperPListWriteFailed; + debug("CFPropertyListWriteToStream failed"); + goto fin; + } + result = CFWriteStreamCopyProperty(stream, + kCFStreamPropertyDataWritten); + if (KERN_SUCCESS != vm_allocate(mach_task_self(), secrets, + CFDataGetLength(result), VM_FLAGS_ANYWHERE)) + { + *err = kmDNSHelperCreationFailed; + debug("vm_allocate failed"); + goto fin; + } + CFDataGetBytes(result, CFRangeMake(0, CFDataGetLength(result)), + (void *)*secrets); + *secretsCnt = CFDataGetLength(result); + *numsecrets = CFArrayGetCount(keys); + debug("succeeded"); + +fin: + debug("returning %u secrets", *numsecrets); + if (NULL != stream) + { + CFWriteStreamClose(stream); + CFRelease(stream); + } + if (NULL != result) + CFRelease(result); + if (NULL != keys) + CFRelease(keys); + if (NULL != search) + CFRelease(search); + if (NULL != skc) + CFRelease(skc); + update_idle_timer(); + return KERN_SUCCESS; +#else + return KERN_FAILURE; +#endif +} + +#ifndef MDNS_NO_IPSEC +typedef enum _mDNSTunnelPolicyWhich +{ + kmDNSTunnelPolicySetup, + kmDNSTunnelPolicyTeardown, + kmDNSTunnelPolicyGenerate +} mDNSTunnelPolicyWhich; + +// For kmDNSTunnelPolicySetup, you can setup IPv6-in-IPv6 tunnel or IPv6-in-IPv4 tunnel +// kmDNSNoTunnel is used for other Policy types +typedef enum _mDNSTunnelType +{ + kmDNSNoTunnel, + kmDNSIPv6IPv4Tunnel, + kmDNSIPv6IPv6Tunnel +} mDNSTunnelType; + +static const uint8_t kWholeV6Mask = 128; + +#endif /* ifndef MDNS_NO_IPSEC */ + +#ifndef MDNS_NO_IPSEC + +static const char g_racoon_config_dir[] = "/var/run/racoon/"; +static const char g_racoon_config_dir_old[] = "/etc/racoon/remote/"; + +CF_EXPORT CFDictionaryRef _CFCopySystemVersionDictionary(void); +CF_EXPORT const CFStringRef _kCFSystemVersionBuildVersionKey; + +// Major version 6 is 10.2.x (Jaguar) +// Major version 7 is 10.3.x (Panther) +// Major version 8 is 10.4.x (Tiger) +// Major version 9 is 10.5.x (Leopard) +// Major version 10 is 10.6.x (SnowLeopard) +static int MacOSXSystemBuildNumber(char* letter_out, int* minor_out) +{ + int major = 0, minor = 0; + char letter = 0, buildver[256]="<Unknown>"; + CFDictionaryRef vers = _CFCopySystemVersionDictionary(); + if (vers) + { + CFStringRef cfbuildver = CFDictionaryGetValue(vers, _kCFSystemVersionBuildVersionKey); + if (cfbuildver) CFStringGetCString(cfbuildver, buildver, sizeof(buildver), kCFStringEncodingUTF8); + sscanf(buildver, "%d%c%d", &major, &letter, &minor); + CFRelease(vers); + } + else + helplog(ASL_LEVEL_NOTICE, "_CFCopySystemVersionDictionary failed"); + + if (!major) { major=10; letter = 'A'; minor = 190; helplog(ASL_LEVEL_NOTICE, "Note: No Major Build Version number found; assuming 10A190"); } + if (letter_out) *letter_out = letter; + if (minor_out) *minor_out = minor; + return(major); +} + +static int UseOldRacoon() +{ + static int g_oldRacoon = -1; + + if (g_oldRacoon == -1) + { + char letter = 0; + int minor = 0; + g_oldRacoon = (MacOSXSystemBuildNumber(&letter, &minor) < 10); + debug("%s", g_oldRacoon ? "old" : "new"); + } + + return g_oldRacoon; +} + +static int RacoonSignal() +{ + return UseOldRacoon() ? SIGHUP : SIGUSR1; +} + +static const char* GetRacoonConfigDir() +{ + return UseOldRacoon() ? g_racoon_config_dir_old : g_racoon_config_dir; +} + +static const char* GetOldRacoonConfigDir() +{ + return UseOldRacoon() ? NULL : g_racoon_config_dir_old; +} + +static const char racoon_config_file[] = "anonymous.conf"; +static const char racoon_config_file_orig[] = "anonymous.conf.orig"; + +static const char configHeader[] = "# BackToMyMac\n"; + +static int IsFamiliarRacoonConfiguration(const char* racoon_config_path) +{ + int fd = open(racoon_config_path, O_RDONLY); + debug("entry %s", racoon_config_path); + if (0 > fd) + { + helplog(ASL_LEVEL_NOTICE, "open \"%s\" failed: %s", racoon_config_path, strerror(errno)); + return 0; + } + else + { + char header[sizeof(configHeader)] = {0}; + ssize_t bytesRead = read(fd, header, sizeof(header)-1); + close(fd); + if (bytesRead != sizeof(header)-1) return 0; + return (0 == memcmp(header, configHeader, sizeof(header)-1)); + } +} + +static void +revertAnonymousRacoonConfiguration(const char* dir) +{ + if (!dir) return; + + debug("entry %s", dir); + + char racoon_config_path[64]; + strlcpy(racoon_config_path, dir, sizeof(racoon_config_path)); + strlcat(racoon_config_path, racoon_config_file, sizeof(racoon_config_path)); + + struct stat s; + int ret = stat(racoon_config_path, &s); + debug("stat(%s): %d errno=%d", racoon_config_path, ret, errno); + if (ret == 0) + { + if (IsFamiliarRacoonConfiguration(racoon_config_path)) + { + helplog(ASL_LEVEL_INFO, "\"%s\" looks familiar, unlinking", racoon_config_path); + unlink(racoon_config_path); + } + else + { + helplog(ASL_LEVEL_NOTICE, "\"%s\" does not look familiar, leaving in place", racoon_config_path); + return; + } + } + else if (errno != ENOENT) + { + helplog(ASL_LEVEL_NOTICE, "stat failed for \"%s\", leaving in place: %s", racoon_config_path, strerror(errno)); + return; + } + + char racoon_config_path_orig[64]; + strlcpy(racoon_config_path_orig, dir, sizeof(racoon_config_path_orig)); + strlcat(racoon_config_path_orig, racoon_config_file_orig, sizeof(racoon_config_path_orig)); + + ret = stat(racoon_config_path_orig, &s); + debug("stat(%s): %d errno=%d", racoon_config_path_orig, ret, errno); + if (ret == 0) + { + if (0 > rename(racoon_config_path_orig, racoon_config_path)) + helplog(ASL_LEVEL_NOTICE, "rename \"%s\" \"%s\" failed: %s", racoon_config_path_orig, racoon_config_path, strerror(errno)); + else + debug("reverted \"%s\" to \"%s\"", racoon_config_path_orig, racoon_config_path); + } + else if (errno != ENOENT) + { + helplog(ASL_LEVEL_NOTICE, "stat failed for \"%s\", leaving in place: %s", racoon_config_path_orig, strerror(errno)); + return; + } +} + +static void +moveAsideAnonymousRacoonConfiguration(const char* dir) +{ + if (!dir) return; + + debug("entry %s", dir); + + char racoon_config_path[64]; + strlcpy(racoon_config_path, dir, sizeof(racoon_config_path)); + strlcat(racoon_config_path, racoon_config_file, sizeof(racoon_config_path)); + + struct stat s; + int ret = stat(racoon_config_path, &s); + if (ret == 0) + { + if (IsFamiliarRacoonConfiguration(racoon_config_path)) + { + helplog(ASL_LEVEL_INFO, "\"%s\" looks familiar, unlinking", racoon_config_path); + unlink(racoon_config_path); + } + else + { + char racoon_config_path_orig[64]; + strlcpy(racoon_config_path_orig, dir, sizeof(racoon_config_path_orig)); + strlcat(racoon_config_path_orig, racoon_config_file_orig, sizeof(racoon_config_path_orig)); + if (0 > rename(racoon_config_path, racoon_config_path_orig)) // If we didn't write it, move it to the side so it can be reverted later + helplog(ASL_LEVEL_NOTICE, "rename \"%s\" to \"%s\" failed: %s", racoon_config_path, racoon_config_path_orig, strerror(errno)); + else + debug("successfully renamed \"%s\" to \"%s\"", racoon_config_path, racoon_config_path_orig); + } + } + else if (errno != ENOENT) + { + helplog(ASL_LEVEL_NOTICE, "stat failed for \"%s\", leaving in place: %s", racoon_config_path, strerror(errno)); + return; + } +} + +static int +ensureExistenceOfRacoonConfigDir(const char* const racoon_config_dir) +{ + struct stat s; + int ret = stat(racoon_config_dir, &s); + if (ret != 0) + { + if (errno != ENOENT) + { + helplog(ASL_LEVEL_ERR, "stat of \"%s\" failed (%d): %s", + racoon_config_dir, ret, strerror(errno)); + return -1; + } + else + { + ret = mkdir(racoon_config_dir, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); + if (ret != 0) + { + helplog(ASL_LEVEL_ERR, "mkdir \"%s\" failed: %s", + racoon_config_dir, strerror(errno)); + return -1; + } + else + helplog(ASL_LEVEL_INFO, "created directory \"%s\"", racoon_config_dir); + } + } + else if (!(s.st_mode & S_IFDIR)) + { + helplog(ASL_LEVEL_ERR, "\"%s\" is not a directory!", + racoon_config_dir); + return -1; + } + + return 0; +} + +static int +createAnonymousRacoonConfiguration(const char *fqdn) +{ + static const char config1[] = + "remote anonymous {\n" + " exchange_mode aggressive;\n" + " doi ipsec_doi;\n" + " situation identity_only;\n" + " verify_identifier off;\n" + " generate_policy on;\n" + " shared_secret keychain_by_id \""; + static const char config2[] = + "\";\n" + " nonce_size 16;\n" + " lifetime time 15 min;\n" + " initial_contact on;\n" + " support_proxy on;\n" + " nat_traversal force;\n" + " proposal_check claim;\n" + " proposal {\n" + " encryption_algorithm aes;\n" + " hash_algorithm sha256;\n" + " authentication_method pre_shared_key;\n" + " dh_group 2;\n" + " lifetime time 15 min;\n" + " }\n" + " proposal {\n" + " encryption_algorithm aes;\n" + " hash_algorithm sha1;\n" + " authentication_method pre_shared_key;\n" + " dh_group 2;\n" + " lifetime time 15 min;\n" + " }\n" + "}\n\n" + "sainfo anonymous { \n" + " pfs_group 2;\n" + " lifetime time 10 min;\n" + " encryption_algorithm aes;\n" + " authentication_algorithm hmac_sha256,hmac_sha1;\n" + " compression_algorithm deflate;\n" + "}\n"; + char tmp_config_path[64]; + char racoon_config_path[64]; + const char* const racoon_config_dir = GetRacoonConfigDir(); + const char* const racoon_config_dir_old = GetOldRacoonConfigDir(); + int fd = -1; + + debug("entry"); + + if (0 > ensureExistenceOfRacoonConfigDir(racoon_config_dir)) + return -1; + + strlcpy(tmp_config_path, racoon_config_dir, sizeof(tmp_config_path)); + strlcat(tmp_config_path, "tmp.XXXXXX", sizeof(tmp_config_path)); + + fd = mkstemp(tmp_config_path); + + if (0 > fd) + { + helplog(ASL_LEVEL_ERR, "mkstemp \"%s\" failed: %s", + tmp_config_path, strerror(errno)); + return -1; + } + write(fd, configHeader, sizeof(configHeader)-1); + write(fd, config1, sizeof(config1)-1); + write(fd, fqdn, strlen(fqdn)); + write(fd, config2, sizeof(config2)-1); + close(fd); + + strlcpy(racoon_config_path, racoon_config_dir, sizeof(racoon_config_path)); + strlcat(racoon_config_path, racoon_config_file, sizeof(racoon_config_path)); + + moveAsideAnonymousRacoonConfiguration(racoon_config_dir_old); + moveAsideAnonymousRacoonConfiguration(racoon_config_dir); + + if (0 > rename(tmp_config_path, racoon_config_path)) + { + unlink(tmp_config_path); + helplog(ASL_LEVEL_ERR, "rename \"%s\" \"%s\" failed: %s", + tmp_config_path, racoon_config_path, strerror(errno)); + revertAnonymousRacoonConfiguration(racoon_config_dir_old); + revertAnonymousRacoonConfiguration(racoon_config_dir); + return -1; + } + + debug("successfully renamed \"%s\" \"%s\"", tmp_config_path, racoon_config_path); + return 0; +} + +static int +notifyRacoon(void) +{ + debug("entry"); + static const char racoon_pid_path[] = "/var/run/racoon.pid"; + char buf[] = "18446744073709551615"; /* largest 64-bit integer */ + char *p = NULL; + ssize_t n = 0; + unsigned long m = 0; + int fd = open(racoon_pid_path, O_RDONLY); + + if (0 > fd) + { + debug("open \"%s\" failed, and that's OK: %s", racoon_pid_path, + strerror(errno)); + return kmDNSHelperRacoonNotificationFailed; + } + n = read(fd, buf, sizeof(buf)-1); + close(fd); + if (1 > n) + { + debug("read of \"%s\" failed: %s", racoon_pid_path, + n == 0 ? "empty file" : strerror(errno)); + return kmDNSHelperRacoonNotificationFailed; + } + buf[n] = '\0'; + m = strtoul(buf, &p, 10); + if (*p != '\0' && !isspace(*p)) + { + debug("invalid PID \"%s\" (around '%c')", buf, *p); + return kmDNSHelperRacoonNotificationFailed; + } + if (2 > m) + { + debug("refusing to kill PID %lu", m); + return kmDNSHelperRacoonNotificationFailed; + } + if (0 != kill(m, RacoonSignal())) + { + debug("Could not signal racoon (%lu): %s", m, strerror(errno)); + return kmDNSHelperRacoonNotificationFailed; + } + debug("Sent racoon (%lu) signal %d", m, RacoonSignal()); + return 0; +} + +static void +closefds(int from) +{ + int fd = 0; + struct dirent entry, *entryp = NULL; + DIR *dirp = opendir("/dev/fd"); + + if (dirp == NULL) + { + /* fall back to the erroneous getdtablesize method */ + for (fd = from; fd < getdtablesize(); ++fd) + close(fd); + return; + } + while (0 == readdir_r(dirp, &entry, &entryp) && NULL != entryp) + { + fd = atoi(entryp->d_name); + if (fd >= from && fd != dirfd(dirp)) + close(fd); + } + closedir(dirp); +} + +static int +startRacoonOld(void) +{ + debug("entry"); + char * const racoon_args[] = { "/usr/sbin/racoon", "-e", NULL }; + ssize_t n = 0; + pid_t pid = 0; + int status = 0; + + if (0 == (pid = fork())) + { + closefds(0); + execve(racoon_args[0], racoon_args, NULL); + helplog(ASL_LEVEL_ERR, "execve of \"%s\" failed: %s", + racoon_args[0], strerror(errno)); + exit(2); + } + helplog(ASL_LEVEL_NOTICE, "racoon (pid=%lu) started", + (unsigned long)pid); + n = waitpid(pid, &status, 0); + if (-1 == n) + { + helplog(ASL_LEVEL_ERR, "Unexpected waitpid failure: %s", + strerror(errno)); + return kmDNSHelperRacoonStartFailed; + } + else if (pid != n) + { + helplog(ASL_LEVEL_ERR, "Unexpected waitpid return value %d", + (int)n); + return kmDNSHelperRacoonStartFailed; + } + else if (WIFSIGNALED(status)) + { + helplog(ASL_LEVEL_ERR, + "racoon (pid=%lu) terminated due to signal %d", + (unsigned long)pid, WTERMSIG(status)); + return kmDNSHelperRacoonStartFailed; + } + else if (WIFSTOPPED(status)) + { + helplog(ASL_LEVEL_ERR, + "racoon (pid=%lu) has stopped due to signal %d", + (unsigned long)pid, WSTOPSIG(status)); + return kmDNSHelperRacoonStartFailed; + } + else if (0 != WEXITSTATUS(status)) + { + helplog(ASL_LEVEL_ERR, + "racoon (pid=%lu) exited with status %d", + (unsigned long)pid, WEXITSTATUS(status)); + return kmDNSHelperRacoonStartFailed; + } + debug("racoon (pid=%lu) daemonized normally", (unsigned long)pid); + return 0; +} + +// constant and structure for the racoon control socket +#define VPNCTL_CMD_PING 0x0004 +typedef struct vpnctl_hdr_struct +{ + u_int16_t msg_type; + u_int16_t flags; + u_int32_t cookie; + u_int32_t reserved; + u_int16_t result; + u_int16_t len; +} vpnctl_hdr; + +static int +startRacoon(void) +{ + debug("entry"); + int fd = socket(PF_UNIX, SOCK_STREAM, 0); + if (0 > fd) + { + helplog(ASL_LEVEL_ERR, "Could not create endpoint for racoon control socket: %d %s", + errno, strerror(errno)); + return kmDNSHelperRacoonStartFailed; + } + + struct sockaddr_un saddr; + memset(&saddr, 0, sizeof(saddr)); + saddr.sun_family = AF_UNIX; + saddr.sun_len = sizeof(saddr); + static const char racoon_control_sock_path[] = "/var/run/vpncontrol.sock"; + strcpy(saddr.sun_path, racoon_control_sock_path); + int result = connect(fd, (struct sockaddr*) &saddr, saddr.sun_len); + if (0 > result) + { + helplog(ASL_LEVEL_ERR, "Could not connect racoon control socket %s: %d %s", + racoon_control_sock_path, errno, strerror(errno)); + return kmDNSHelperRacoonStartFailed; + } + + u_int32_t btmm_cookie = 0x4d4d5442; + vpnctl_hdr h = { htons(VPNCTL_CMD_PING), 0, btmm_cookie, 0, 0, 0 }; + size_t bytes = 0; + ssize_t ret = 0; + + while (bytes < sizeof(vpnctl_hdr)) + { + ret = write(fd, ((unsigned char*)&h)+bytes, sizeof(vpnctl_hdr) - bytes); + if (ret == -1) + { + helplog(ASL_LEVEL_ERR, "Could not write to racoon control socket: %d %s", + errno, strerror(errno)); + return kmDNSHelperRacoonStartFailed; + } + bytes += ret; + } + + int nfds = fd + 1; + fd_set fds; + int counter = 0; + struct timeval tv; + bytes = 0; + h.cookie = 0; + + for (counter = 0; counter < 100; counter++) + { + FD_ZERO(&fds); + FD_SET(fd, &fds); + tv = (struct timeval){ 0, 10000 }; // 10 milliseconds * 100 iterations = 1 second max wait time + + result = select(nfds, &fds, (fd_set*)NULL, (fd_set*)NULL, &tv); + if (result > 0) + { + if (FD_ISSET(fd, &fds)) + { + ret = read(fd, ((unsigned char*)&h)+bytes, sizeof(vpnctl_hdr) - bytes); + + if (ret == -1) + { + helplog(ASL_LEVEL_ERR, "Could not read from racoon control socket: %d %s", + strerror(errno)); + break; + } + bytes += ret; + if (bytes >= sizeof(vpnctl_hdr)) break; + } + else + { + debug("select returned but fd_isset not on expected fd\n"); + } + } + else if (result < 0) + { + debug("select returned %d errno %d %s\n", result, errno, strerror(errno)); + if (errno != EINTR) break; + } + } + + close(fd); + + if (bytes < sizeof(vpnctl_hdr) || h.cookie != btmm_cookie) return kmDNSHelperRacoonStartFailed; + + debug("racoon started"); + return 0; +} + +static int +kickRacoon(void) +{ + if ( 0 == notifyRacoon() ) + return 0; + return UseOldRacoon() ? startRacoonOld() : startRacoon(); +} + +#endif /* ndef MDNS_NO_IPSEC */ + +int +do_mDNSConfigureServer(__unused mach_port_t port, int updown, const char *fqdn, audit_token_t token) +{ +#ifndef MDNS_NO_IPSEC + debug("entry"); + if (!authorized(&token)) goto fin; + + switch ((enum mDNSUpDown)updown) + { + case kmDNSUp: + if (0 != createAnonymousRacoonConfiguration(fqdn)) goto fin; + break; + case kmDNSDown: + revertAnonymousRacoonConfiguration(GetOldRacoonConfigDir()); + revertAnonymousRacoonConfiguration(GetRacoonConfigDir()); + break; + default: + goto fin; + } + + if (0 != kickRacoon()) + goto fin; + debug("succeeded"); + +fin: +#else + (void)port; (void)updown; (void)fqdn; (void)token; +#endif + update_idle_timer(); + return KERN_SUCCESS; +} + +#ifndef MDNS_NO_IPSEC + +static unsigned int routeSeq = 1; + +static int +setupTunnelRoute(v6addr_t local, v6addr_t remote) +{ + struct + { + struct rt_msghdr hdr; + struct sockaddr_in6 dst; + struct sockaddr_in6 gtwy; + } msg; + int err = 0; + int s = -1; + + if (0 > (s = socket(PF_ROUTE, SOCK_RAW, AF_INET))) + { + helplog(ASL_LEVEL_ERR, "socket(PF_ROUTE, ...) failed: %s", + strerror(errno)); + err = kmDNSHelperRoutingSocketCreationFailed; + goto fin; + } + memset(&msg, 0, sizeof(msg)); + msg.hdr.rtm_msglen = sizeof(msg); + msg.hdr.rtm_type = RTM_ADD; + /* The following flags are set by `route add -inet6 -host ...` */ + msg.hdr.rtm_flags = RTF_UP | RTF_GATEWAY | RTF_HOST | RTF_STATIC; + msg.hdr.rtm_version = RTM_VERSION; + msg.hdr.rtm_seq = routeSeq++; + msg.hdr.rtm_addrs = RTA_DST | RTA_GATEWAY; + msg.hdr.rtm_inits = RTV_MTU; + msg.hdr.rtm_rmx.rmx_mtu = 1280; + + msg.dst.sin6_len = sizeof(msg.dst); + msg.dst.sin6_family = AF_INET6; + memcpy(&msg.dst.sin6_addr, remote, sizeof(msg.dst.sin6_addr)); + + msg.gtwy.sin6_len = sizeof(msg.gtwy); + msg.gtwy.sin6_family = AF_INET6; + memcpy(&msg.gtwy.sin6_addr, local, sizeof(msg.gtwy.sin6_addr)); + + /* send message, ignore error when route already exists */ + if (0 > write(s, &msg, msg.hdr.rtm_msglen)) + { + int errno_ = errno; + + debug("write to routing socket failed: %s", strerror(errno_)); + if (EEXIST != errno_) + { + err = kmDNSHelperRouteAdditionFailed; + goto fin; + } + } + +fin: + if (0 <= s) + close(s); + return err; +} + +static int +teardownTunnelRoute(v6addr_t remote) +{ + struct + { + struct rt_msghdr hdr; + struct sockaddr_in6 dst; + } msg; + int err = 0; + int s = -1; + + if (0 > (s = socket(PF_ROUTE, SOCK_RAW, AF_INET))) + { + helplog(ASL_LEVEL_ERR, "socket(PF_ROUTE, ...) failed: %s", + strerror(errno)); + err = kmDNSHelperRoutingSocketCreationFailed; + goto fin; + } + memset(&msg, 0, sizeof(msg)); + + msg.hdr.rtm_msglen = sizeof(msg); + msg.hdr.rtm_type = RTM_DELETE; + msg.hdr.rtm_version = RTM_VERSION; + msg.hdr.rtm_seq = routeSeq++; + msg.hdr.rtm_addrs = RTA_DST; + + msg.dst.sin6_len = sizeof(msg.dst); + msg.dst.sin6_family = AF_INET6; + memcpy(&msg.dst.sin6_addr, remote, sizeof(msg.dst.sin6_addr)); + if (0 > write(s, &msg, msg.hdr.rtm_msglen)) + { + int errno_ = errno; + + debug("write to routing socket failed: %s", strerror(errno_)); + if (ESRCH != errno_) + { + err = kmDNSHelperRouteDeletionFailed; + goto fin; + } + } + +fin: + if (0 <= s) + close(s); + return err; +} + +static int +v4addr_to_string(v4addr_t addr, char *buf, size_t buflen) +{ + if (NULL == inet_ntop(AF_INET, addr, buf, buflen)) + { + helplog(ASL_LEVEL_ERR, "inet_ntop failed: %s", + strerror(errno)); + return kmDNSHelperInvalidNetworkAddress; + } + else + return 0; +} + +static int +v6addr_to_string(v6addr_t addr, char *buf, size_t buflen) +{ + if (NULL == inet_ntop(AF_INET6, addr, buf, buflen)) + { + helplog(ASL_LEVEL_ERR, "inet_ntop failed: %s", + strerror(errno)); + return kmDNSHelperInvalidNetworkAddress; + } + else + return 0; +} + +/* Caller owns object returned in `policy' */ +static int +generateTunnelPolicy(mDNSTunnelPolicyWhich which, mDNSTunnelType type, int in, + v4addr_t src, uint16_t src_port, + v4addr_t dst, uint16_t dst_port, + v6addr_t src6, v6addr_t dst6, + ipsec_policy_t *policy, size_t *len) +{ + char srcs[INET_ADDRSTRLEN], dsts[INET_ADDRSTRLEN]; + char srcs6[INET6_ADDRSTRLEN], dsts6[INET6_ADDRSTRLEN]; + char buf[512]; + char *inOut = in ? "in" : "out"; + ssize_t n = 0; + int err = 0; + + *policy = NULL; + *len = 0; + + switch (which) + { + case kmDNSTunnelPolicySetup: + if (type == kmDNSIPv6IPv4Tunnel) + { + if (0 != (err = v4addr_to_string(src, srcs, sizeof(srcs)))) + goto fin; + if (0 != (err = v4addr_to_string(dst, dsts, sizeof(dsts)))) + goto fin; + n = snprintf(buf, sizeof(buf), + "%s ipsec esp/tunnel/%s[%u]-%s[%u]/require", + inOut, srcs, src_port, dsts, dst_port); + } + else if (type == kmDNSIPv6IPv6Tunnel) + { + if (0 != (err = v6addr_to_string(src6, srcs6, sizeof(srcs6)))) + goto fin; + if (0 != (err = v6addr_to_string(dst6, dsts6, sizeof(dsts6)))) + goto fin; + n = snprintf(buf, sizeof(buf), + "%s ipsec esp/tunnel/%s-%s/require", + inOut, srcs6, dsts6); + } + break; + case kmDNSTunnelPolicyTeardown: + n = strlcpy(buf, inOut, sizeof(buf)); + break; + case kmDNSTunnelPolicyGenerate: + n = snprintf(buf, sizeof(buf), "%s generate", inOut); + break; + default: + err = kmDNSHelperIPsecPolicyCreationFailed; + goto fin; + } + + if (n >= (int)sizeof(buf)) + { + err = kmDNSHelperResultTooLarge; + goto fin; + } + + debug("policy=\"%s\"", buf); + if (NULL == (*policy = (ipsec_policy_t)ipsec_set_policy(buf, n))) + { + helplog(ASL_LEVEL_ERR, + "Could not create IPsec policy from \"%s\"", buf); + err = kmDNSHelperIPsecPolicyCreationFailed; + goto fin; + } + *len = ((ipsec_policy_t)(*policy))->sadb_x_policy_len * 8; + +fin: + return err; +} + +static int +sendPolicy(int s, int setup, + struct sockaddr *src, uint8_t src_bits, + struct sockaddr *dst, uint8_t dst_bits, + ipsec_policy_t policy, size_t len) +{ + static unsigned int policySeq = 0; + int err = 0; + + debug("entry, setup=%d", setup); + if (setup) + err = pfkey_send_spdadd(s, src, src_bits, dst, dst_bits, -1, + (char *)policy, len, policySeq++); + else + err = pfkey_send_spddelete(s, src, src_bits, dst, dst_bits, -1, + (char *)policy, len, policySeq++); + if (0 > err) + { + helplog(ASL_LEVEL_ERR, "Could not set IPsec policy: %s", + ipsec_strerror()); + err = kmDNSHelperIPsecPolicySetFailed; + goto fin; + } + else + err = 0; + debug("succeeded"); + +fin: + return err; +} + +static int +removeSA(int s, struct sockaddr *src, struct sockaddr *dst) +{ + int err = 0; + + debug("entry"); + err = pfkey_send_delete_all(s, SADB_SATYPE_ESP, IPSEC_MODE_ANY, src, dst); + if (0 > err) + { + helplog(ASL_LEVEL_ERR, "Could not remove IPsec SA: %s", ipsec_strerror()); + err = kmDNSHelperIPsecRemoveSAFailed; + goto fin; + } + err = pfkey_send_delete_all(s, SADB_SATYPE_ESP, IPSEC_MODE_ANY, dst, src); + if (0 > err) + { + helplog(ASL_LEVEL_ERR, "Could not remove IPsec SA: %s", ipsec_strerror()); + err = kmDNSHelperIPsecRemoveSAFailed; + goto fin; + } + else + err = 0; + + debug("succeeded"); + +fin: + return err; +} + +static int +doTunnelPolicy(mDNSTunnelPolicyWhich which, mDNSTunnelType type, + v6addr_t loc_inner, uint8_t loc_bits, + v4addr_t loc_outer, uint16_t loc_port, + v6addr_t rmt_inner, uint8_t rmt_bits, + v4addr_t rmt_outer, uint16_t rmt_port, + v6addr_t loc_outer6, v6addr_t rmt_outer6) +{ + struct sockaddr_in6 sin6_loc; + struct sockaddr_in6 sin6_rmt; + ipsec_policy_t policy = NULL; + size_t len = 0; + int s = -1; + int err = 0; + + debug("entry"); + if (0 > (s = pfkey_open())) + { + helplog(ASL_LEVEL_ERR, + "Could not create IPsec policy socket: %s", + ipsec_strerror()); + err = kmDNSHelperIPsecPolicySocketCreationFailed; + goto fin; + } + + memset(&sin6_loc, 0, sizeof(sin6_loc)); + sin6_loc.sin6_len = sizeof(sin6_loc); + sin6_loc.sin6_family = AF_INET6; + sin6_loc.sin6_port = htons(0); + memcpy(&sin6_loc.sin6_addr, loc_inner, sizeof(sin6_loc.sin6_addr)); + + memset(&sin6_rmt, 0, sizeof(sin6_rmt)); + sin6_rmt.sin6_len = sizeof(sin6_rmt); + sin6_rmt.sin6_family = AF_INET6; + sin6_rmt.sin6_port = htons(0); + memcpy(&sin6_rmt.sin6_addr, rmt_inner, sizeof(sin6_rmt.sin6_addr)); + + int setup = which != kmDNSTunnelPolicyTeardown; + + if (0 != (err = generateTunnelPolicy(which, type, 1, + rmt_outer, rmt_port, + loc_outer, loc_port, + rmt_outer6, loc_outer6, + &policy, &len))) + goto fin; + if (0 != (err = sendPolicy(s, setup, + (struct sockaddr *)&sin6_rmt, rmt_bits, + (struct sockaddr *)&sin6_loc, loc_bits, + policy, len))) + goto fin; + if (NULL != policy) + { + free(policy); + policy = NULL; + } + if (0 != (err = generateTunnelPolicy(which, type, 0, + loc_outer, loc_port, + rmt_outer, rmt_port, + loc_outer6, rmt_outer6, + &policy, &len))) + goto fin; + if (0 != (err = sendPolicy(s, setup, + (struct sockaddr *)&sin6_loc, loc_bits, + (struct sockaddr *)&sin6_rmt, rmt_bits, + policy, len))) + goto fin; + + if (which == kmDNSTunnelPolicyTeardown) + { + if (rmt_port) // Outer tunnel is IPv4 + { + if (loc_outer && rmt_outer) + { + struct sockaddr_in sin_loc; + struct sockaddr_in sin_rmt; + memset(&sin_loc, 0, sizeof(sin_loc)); + sin_loc.sin_len = sizeof(sin_loc); + sin_loc.sin_family = AF_INET; + memcpy(&sin_loc.sin_addr, loc_outer, sizeof(sin_loc.sin_addr)); + + memset(&sin_rmt, 0, sizeof(sin_rmt)); + sin_rmt.sin_len = sizeof(sin_rmt); + sin_rmt.sin_family = AF_INET; + memcpy(&sin_rmt.sin_addr, rmt_outer, sizeof(sin_rmt.sin_addr)); + if (0 != (err = removeSA(s, (struct sockaddr *)&sin_loc, (struct sockaddr *)&sin_rmt))) + goto fin; + } + } + else + { + if (loc_outer6 && rmt_outer6) + { + struct sockaddr_in6 sin6_lo; + struct sockaddr_in6 sin6_rm; + + memset(&sin6_lo, 0, sizeof(sin6_lo)); + sin6_lo.sin6_len = sizeof(sin6_lo); + sin6_lo.sin6_family = AF_INET6; + memcpy(&sin6_lo.sin6_addr, loc_outer6, sizeof(sin6_lo.sin6_addr)); + + memset(&sin6_rm, 0, sizeof(sin6_rm)); + sin6_rm.sin6_len = sizeof(sin6_rm); + sin6_rm.sin6_family = AF_INET6; + memcpy(&sin6_rm.sin6_addr, rmt_outer6, sizeof(sin6_rm.sin6_addr)); + if (0 != (err = removeSA(s, (struct sockaddr *)&sin6_lo, (struct sockaddr *)&sin6_rm))) + goto fin; + } + } + } + + + debug("succeeded"); + +fin: + if (s >= 0) + pfkey_close(s); + if (NULL != policy) + free(policy); + return err; +} + +#endif /* ndef MDNS_NO_IPSEC */ + +int +do_mDNSAutoTunnelSetKeys(__unused mach_port_t port, int replacedelete, + v6addr_t loc_inner, v6addr_t loc_outer6, uint16_t loc_port, + v6addr_t rmt_inner, v6addr_t rmt_outer6, uint16_t rmt_port, + const char *id, int *err, audit_token_t token) +{ +#ifndef MDNS_NO_IPSEC + static const char config[] = + "%s" + "remote %s [%u] {\n" + " disconnect_on_idle idle_timeout 600 idle_direction idle_outbound;\n" + " exchange_mode aggressive;\n" + " doi ipsec_doi;\n" + " situation identity_only;\n" + " verify_identifier off;\n" + " generate_policy on;\n" + " my_identifier user_fqdn \"%s\";\n" + " shared_secret keychain \"%s\";\n" + " nonce_size 16;\n" + " lifetime time 15 min;\n" + " initial_contact on;\n" + " support_proxy on;\n" + " nat_traversal force;\n" + " proposal_check claim;\n" + " proposal {\n" + " encryption_algorithm aes;\n" + " hash_algorithm sha256;\n" + " authentication_method pre_shared_key;\n" + " dh_group 2;\n" + " lifetime time 15 min;\n" + " }\n" + " proposal {\n" + " encryption_algorithm aes;\n" + " hash_algorithm sha1;\n" + " authentication_method pre_shared_key;\n" + " dh_group 2;\n" + " lifetime time 15 min;\n" + " }\n" + "}\n\n" + "sainfo address %s any address %s any {\n" + " pfs_group 2;\n" + " lifetime time 10 min;\n" + " encryption_algorithm aes;\n" + " authentication_algorithm hmac_sha256,hmac_sha1;\n" + " compression_algorithm deflate;\n" + "}\n\n" + "sainfo address %s any address %s any {\n" + " pfs_group 2;\n" + " lifetime time 10 min;\n" + " encryption_algorithm aes;\n" + " authentication_algorithm hmac_sha256,hmac_sha1;\n" + " compression_algorithm deflate;\n" + "}\n"; + char path[PATH_MAX] = ""; + char li[INET6_ADDRSTRLEN], lo[INET_ADDRSTRLEN], lo6[INET6_ADDRSTRLEN], + ri[INET6_ADDRSTRLEN], ro[INET_ADDRSTRLEN], ro6[INET6_ADDRSTRLEN]; + FILE *fp = NULL; + int fd = -1; + char tmp_path[PATH_MAX] = ""; + v4addr_t loc_outer, rmt_outer; + + debug("entry"); + *err = 0; + if (!authorized(&token)) + { + *err = kmDNSHelperNotAuthorized; + goto fin; + } + switch ((enum mDNSAutoTunnelSetKeysReplaceDelete)replacedelete) + { + case kmDNSAutoTunnelSetKeysReplace: + case kmDNSAutoTunnelSetKeysDelete: + break; + default: + *err = kmDNSHelperInvalidTunnelSetKeysOperation; + goto fin; + } + + if (0 != (*err = v6addr_to_string(loc_inner, li, sizeof(li)))) + goto fin; + if (0 != (*err = v6addr_to_string(rmt_inner, ri, sizeof(ri)))) + goto fin; + + debug("loc_inner=%s rmt_inner=%s", li, ri); + if (!rmt_port) + { + loc_outer[0] = loc_outer[1] = loc_outer[2] = loc_outer[3] = 0; + rmt_outer[0] = rmt_outer[1] = rmt_outer[2] = rmt_outer[3] = 0; + + if (0 != (*err = v6addr_to_string(loc_outer6, lo6, sizeof(lo6)))) + goto fin; + if (0 != (*err = v6addr_to_string(rmt_outer6, ro6, sizeof(ro6)))) + goto fin; + debug("IPv6 outer tunnel: loc_outer6=%s rmt_outer6=%s", lo6, ro6); + if ((int)sizeof(path) <= snprintf(path, sizeof(path), + "%s%s.conf", GetRacoonConfigDir(), ro6)) + { + *err = kmDNSHelperResultTooLarge; + goto fin; + } + } + else + { + loc_outer[0] = loc_outer6[0]; + loc_outer[1] = loc_outer6[1]; + loc_outer[2] = loc_outer6[2]; + loc_outer[3] = loc_outer6[3]; + + rmt_outer[0] = rmt_outer6[0]; + rmt_outer[1] = rmt_outer6[1]; + rmt_outer[2] = rmt_outer6[2]; + rmt_outer[3] = rmt_outer6[3]; + + if (0 != (*err = v4addr_to_string(loc_outer, lo, sizeof(lo)))) + goto fin; + if (0 != (*err = v4addr_to_string(rmt_outer, ro, sizeof(ro)))) + goto fin; + debug("IPv4 outer tunnel: loc_outer=%s loc_port=%u rmt_outer=%s rmt_port=%u", + lo, loc_port, ro, rmt_port); + + if ((int)sizeof(path) <= snprintf(path, sizeof(path), + "%s%s.%u.conf", GetRacoonConfigDir(), ro, + rmt_port)) + { + *err = kmDNSHelperResultTooLarge; + goto fin; + } + } + + + + if (kmDNSAutoTunnelSetKeysReplace == replacedelete) + { + if (0 > ensureExistenceOfRacoonConfigDir(GetRacoonConfigDir())) + { + *err = kmDNSHelperRacoonConfigCreationFailed; + goto fin; + } + if ((int)sizeof(tmp_path) <= + snprintf(tmp_path, sizeof(tmp_path), "%s.XXXXXX", path)) + { + *err = kmDNSHelperResultTooLarge; + goto fin; + } + if (0 > (fd = mkstemp(tmp_path))) + { + helplog(ASL_LEVEL_ERR, "mkstemp \"%s\" failed: %s", + tmp_path, strerror(errno)); + *err = kmDNSHelperRacoonConfigCreationFailed; + goto fin; + } + if (NULL == (fp = fdopen(fd, "w"))) + { + helplog(ASL_LEVEL_ERR, "fdopen: %s", + strerror(errno)); + *err = kmDNSHelperRacoonConfigCreationFailed; + goto fin; + } + fd = -1; + fprintf(fp, config, configHeader, (!rmt_port ? ro6 : ro), rmt_port, id, id, ri, li, li, ri); + fclose(fp); + fp = NULL; + if (0 > rename(tmp_path, path)) + { + helplog(ASL_LEVEL_ERR, + "rename \"%s\" \"%s\" failed: %s", + tmp_path, path, strerror(errno)); + *err = kmDNSHelperRacoonConfigCreationFailed; + goto fin; + } + } + else + { + if (0 != unlink(path)) + debug("unlink \"%s\" failed: %s", path, + strerror(errno)); + } + + if (0 != (*err = doTunnelPolicy(kmDNSTunnelPolicyTeardown, kmDNSNoTunnel, + loc_inner, kWholeV6Mask, loc_outer, loc_port, + rmt_inner, kWholeV6Mask, rmt_outer, rmt_port, loc_outer6, rmt_outer6))) + goto fin; + if (kmDNSAutoTunnelSetKeysReplace == replacedelete && + 0 != (*err = doTunnelPolicy(kmDNSTunnelPolicySetup, (!rmt_port ? kmDNSIPv6IPv6Tunnel : kmDNSIPv6IPv4Tunnel), + loc_inner, kWholeV6Mask, loc_outer, loc_port, + rmt_inner, kWholeV6Mask, rmt_outer, rmt_port, loc_outer6, rmt_outer6))) + goto fin; + + if (0 != (*err = teardownTunnelRoute(rmt_inner))) + goto fin; + if (kmDNSAutoTunnelSetKeysReplace == replacedelete && + 0 != (*err = setupTunnelRoute(loc_inner, rmt_inner))) + goto fin; + + if (kmDNSAutoTunnelSetKeysReplace == replacedelete && + 0 != (*err = kickRacoon())) + goto fin; + + debug("succeeded"); + +fin: + if (NULL != fp) + fclose(fp); + if (0 <= fd) + close(fd); + unlink(tmp_path); +#else + (void)replacedelete; (void)loc_inner; (void)loc_outer6; (void)loc_port; (void)rmt_inner; + (void)rmt_outer6; (void)rmt_port; (void)id; (void)token; + + *err = kmDNSHelperIPsecDisabled; +#endif /* MDNS_NO_IPSEC */ + update_idle_timer(); + return KERN_SUCCESS; +} + +kern_return_t +do_mDNSSendWakeupPacket(__unused mach_port_t port, unsigned ifid, const char *eth_addr, const char *ip_addr, int iteration, audit_token_t token) +{ + int bpf_fd, i, j; + struct ifreq ifr; + char ifname[IFNAMSIZ]; + char packet[512]; + char *ptr = packet; + char bpf_device[12]; + struct ether_addr *ea; + (void) ip_addr; // unused + (void) iteration; // unused + (void) token; // unused + + if (if_indextoname(ifid, ifname) == NULL) + { + helplog(ASL_LEVEL_ERR, "do_mDNSSendWakeupPacket invalid interface index %u", ifid); + return errno; + } + + ea = ether_aton(eth_addr); + if (ea == NULL) + { + helplog(ASL_LEVEL_ERR, "do_mDNSSendWakeupPacket invalid ethernet address %s", eth_addr); + return errno; + } + + for (i = 0; i < 100; i++) + { + snprintf(bpf_device, sizeof(bpf_device), "/dev/bpf%d", i); + bpf_fd = open(bpf_device, O_RDWR, 0); + if (bpf_fd == -1) + continue; + else break; + } + + if (bpf_fd == -1) + { + helplog(ASL_LEVEL_ERR, "do_mDNSSendWakeupPacket cannot find a bpf device"); + return ENXIO; + } + + memset(&ifr, 0, sizeof(ifr)); + strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); + + if (ioctl(bpf_fd, BIOCSETIF, (char *)&ifr) < 0) + { + helplog(ASL_LEVEL_ERR, "do_mDNSSendWakeupPacket BIOCSETIF failed %s", strerror(errno)); + return errno; + } + + // 0x00 Destination address + for (i=0; i<6; i++) *ptr++ = ea->octet[i]; + + // 0x06 Source address (Note: Since we don't currently set the BIOCSHDRCMPLT option, BPF will fill in the real interface address for us) + for (i=0; i<6; i++) *ptr++ = 0; + + // 0x0C Ethertype (0x0842) + *ptr++ = 0x08; + *ptr++ = 0x42; + + // 0x0E Wakeup sync sequence + for (i=0; i<6; i++) *ptr++ = 0xFF; + + // 0x14 Wakeup data + for (j=0; j<16; j++) for (i=0; i<6; i++) *ptr++ = ea->octet[i]; + + // 0x74 Password + for (i=0; i<6; i++) *ptr++ = 0; + + if (write(bpf_fd, packet, ptr - packet) < 0) + { + helplog(ASL_LEVEL_ERR, "do_mDNSSendWakeupPacket write failed %s", strerror(errno)); + return errno; + } + helplog(ASL_LEVEL_INFO, "do_mDNSSendWakeupPacket sent unicast eth_addr %s, ip_addr %s", eth_addr, ip_addr); + // Send a broadcast one to handle ethernet switches that don't flood forward packets with + // unknown mac addresses. + for (i=0; i<6; i++) packet[i] = 0xFF; + if (write(bpf_fd, packet, ptr - packet) < 0) + { + helplog(ASL_LEVEL_ERR, "do_mDNSSendWakeupPacket write failed %s", strerror(errno)); + return errno; + } + helplog(ASL_LEVEL_INFO, "do_mDNSSendWakeupPacket sent broadcast eth_addr %s, ip_addr %s", eth_addr, ip_addr); + close(bpf_fd); + return KERN_SUCCESS; +} + +// Open the specified port for protocol in the P2P firewall. +kern_return_t +do_mDNSPacketFilterControl(__unused mach_port_t port, uint32_t command, const char * ifname, uint32_t count, pfArray_t portArray, pfArray_t protocolArray, audit_token_t token) +{ + (void) token; // unused + int error; + kern_return_t result = KERN_SUCCESS; + + helplog(ASL_LEVEL_INFO, "do_mDNSPacketFilterControl: command %d ifname %s, count %d", + command, ifname, count); + + switch (command) + { + case PF_SET_RULES: + error = P2PPacketFilterAddBonjourRuleSet(ifname, count, portArray, protocolArray); + if (error) + { + helplog(ASL_LEVEL_ERR, "P2PPacketFilterAddBonjourRuleSet failed %s", strerror(error)); + result = KERN_FAILURE; + } + break; + + case PF_CLEAR_RULES: + error = P2PPacketFilterClearBonjourRules(); + if (error) + { + helplog(ASL_LEVEL_ERR, "P2PPacketFilterClearBonjourRules failed %s", strerror(error)); + result = KERN_FAILURE; + } + break; + + default: + helplog(ASL_LEVEL_ERR, "do_mDNSPacketFilterControl: invalid command %d", command); + result = KERN_INVALID_ARGUMENT; + break; + } + + return result; +} + +unsigned long +in_cksum(unsigned short *ptr,int nbytes) +{ + unsigned long sum; + u_short oddbyte; + + /* + * Our algorithm is simple, using a 32-bit accumulator (sum), + * we add sequential 16-bit words to it, and at the end, fold back + * all the carry bits from the top 16 bits into the lower 16 bits. + */ + sum = 0; + while (nbytes > 1) { + sum += *ptr++; + nbytes -= 2; + } + + /* mop up an odd byte, if necessary */ + if (nbytes == 1) { + /* make sure top half is zero */ + oddbyte = 0; + + /* one byte only */ + *((u_char *)&oddbyte) = *(u_char *)ptr; + sum += oddbyte; + } + /* Add back carry outs from top 16 bits to low 16 bits. */ + sum = (sum >> 16) + (sum & 0xffff); + + /* add carry */ + sum += (sum >> 16); + + return sum; +} + +unsigned short +InetChecksum(unsigned short *ptr,int nbytes) +{ + unsigned long sum; + + sum = in_cksum(ptr, nbytes); + return (unsigned short)~sum; +} + +void TCPCheckSum(int af, struct tcphdr *t, int tcplen, v6addr_t sadd6, v6addr_t dadd6) +{ + unsigned long sum = 0; + unsigned short *ptr; + + /* TCP header checksum */ + sum = in_cksum((unsigned short *)t, tcplen); + + if (af == AF_INET) + { + /* Pseudo header */ + ptr = (unsigned short *)sadd6; + sum += *ptr++; + sum += *ptr++; + ptr = (unsigned short *)dadd6; + sum += *ptr++; + sum += *ptr++; + } + else if (af == AF_INET6) + { + /* Pseudo header */ + ptr = (unsigned short *)sadd6; + sum += *ptr++; + sum += *ptr++; + sum += *ptr++; + sum += *ptr++; + sum += *ptr++; + sum += *ptr++; + sum += *ptr++; + sum += *ptr++; + ptr = (unsigned short *)dadd6; + sum += *ptr++; + sum += *ptr++; + sum += *ptr++; + sum += *ptr++; + sum += *ptr++; + sum += *ptr++; + sum += *ptr++; + sum += *ptr++; + } + + sum += htons(tcplen); + sum += htons(IPPROTO_TCP); + + while (sum >> 16) + sum = (sum >> 16) + (sum & 0xFFFF); + + t->th_sum = ~sum; + +} + +kern_return_t do_mDNSSendKeepalive(__unused mach_port_t port, v6addr_t sadd6, v6addr_t dadd6, uint16_t lport, uint16_t rport, unsigned seq, unsigned ack, uint16_t win, audit_token_t token) +{ + struct packet4 { + struct ip ip; + struct tcphdr tcp; + } packet4; + struct packet6 { + struct tcphdr tcp; + } packet6; + int sock, on; + struct tcphdr *t; + int af; + struct sockaddr_storage ss_to; + struct sockaddr_in *sin_to = (struct sockaddr_in *)&ss_to; + struct sockaddr_in6 *sin6_to = (struct sockaddr_in6 *)&ss_to; + void *packet; + ssize_t packetlen; + char ctlbuf[CMSG_SPACE(sizeof(struct in6_pktinfo))]; + struct msghdr msghdr; + struct iovec iov; + ssize_t len; + + if (!authorized(&token)) + { + helplog(ASL_LEVEL_ERR, "mDNSSendKeepalive: Not authorized"); + return kmDNSHelperNotAuthorized; + } + + helplog(ASL_LEVEL_ERR, "mDNSSendKeepalive: called"); + + // all the incoming arguments are in network order + if ((*(unsigned *)(sadd6 +4) == 0) && (*(unsigned *)(sadd6 + 8) == 0) && (*(unsigned *)(sadd6 + 12) == 0)) + { + af = AF_INET; + memset(&packet4, 0, sizeof (packet4)); + + /* Fill in all the IP header information - should be in host order*/ + packet4.ip.ip_v = 4; /* 4-bit Version */ + packet4.ip.ip_hl = 5; /* 4-bit Header Length */ + packet4.ip.ip_tos = 0; /* 8-bit Type of service */ + packet4.ip.ip_len = 40; /* 16-bit Total length */ + packet4.ip.ip_id = 9864; /* 16-bit ID field */ + packet4.ip.ip_off = 0; /* 13-bit Fragment offset */ + packet4.ip.ip_ttl = 63; /* 8-bit Time To Live */ + packet4.ip.ip_p = IPPROTO_TCP; /* 8-bit Protocol */ + packet4.ip.ip_sum = 0; /* 16-bit Header checksum (below) */ + memcpy(&packet4.ip.ip_src.s_addr, sadd6, 4); + memcpy(&packet4.ip.ip_dst.s_addr, dadd6, 4); + + /* IP header checksum */ + packet4.ip.ip_sum = InetChecksum((unsigned short *)&packet4.ip, 20); + t = &packet4.tcp; + packet = &packet4; + packetlen = 40; // sum of IPv4 header len(20) and TCP header len(20) + } + else + { + af = AF_INET6; + memset(&packet6, 0, sizeof (packet6)); + t = &packet6.tcp; + packet = &packet6; + // We don't send IPv6 header, hence just the TCP header len (20) + packetlen = 20; + } + + /* Fill in all the TCP header information */ + t->th_sport = lport; /* 16-bit Source port number */ + t->th_dport = rport; /* 16-bit Destination port */ + t->th_seq = seq; /* 32-bit Sequence Number */ + t->th_ack = ack; /* 32-bit Acknowledgement Number */ + t->th_off = 5; /* Data offset */ + t->th_flags = TH_ACK; + t->th_win = win; + t->th_sum = 0; /* 16-bit checksum (below) */ + t->th_urp = 0; /* 16-bit urgent offset */ + + TCPCheckSum(af, t, 20, sadd6, dadd6); + + /* Open up a RAW socket */ + if ((sock = socket(af, SOCK_RAW, IPPROTO_TCP)) < 0) + { + helplog(ASL_LEVEL_ERR, "mDNSSendKeepalive: socket %s", strerror(errno)); + return errno; + } + + + if (af == AF_INET) + { + on = 1; + if (setsockopt(sock, IPPROTO_IP, IP_HDRINCL, &on, sizeof (on))) + { + close(sock); + helplog(ASL_LEVEL_ERR, "mDNSSendKeepalive: setsockopt %s", strerror(errno)); + return errno; + } + + memset(sin_to, 0, sizeof(struct sockaddr_in)); + sin_to->sin_len = sizeof(struct sockaddr_in); + sin_to->sin_family = AF_INET; + memcpy(&sin_to->sin_addr, sadd6, sizeof(struct in_addr)); + sin_to->sin_port = rport; + + msghdr.msg_control = NULL; + msghdr.msg_controllen = 0; + + } + else + { + struct cmsghdr *ctl; + + memset(sin6_to, 0, sizeof(struct sockaddr_in6)); + sin6_to->sin6_len = sizeof(struct sockaddr_in6); + sin6_to->sin6_family = AF_INET6; + memcpy(&sin6_to->sin6_addr, dadd6, sizeof(struct in6_addr)); + + sin6_to->sin6_port = rport; + sin6_to->sin6_flowinfo = 0; + + + msghdr.msg_control = ctlbuf; + msghdr.msg_controllen = sizeof(ctlbuf); + ctl = CMSG_FIRSTHDR(&msghdr); + ctl->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); + ctl->cmsg_level = IPPROTO_IPV6; + ctl->cmsg_type = IPV6_PKTINFO; + struct in6_pktinfo *pktinfo = (struct in6_pktinfo *) CMSG_DATA(ctl); + memcpy(&pktinfo->ipi6_addr, sadd6, sizeof(struct in6_addr)); + pktinfo->ipi6_ifindex = 0; + } + + msghdr.msg_name = (struct sockaddr *)&ss_to; + msghdr.msg_namelen = ss_to.ss_len; + iov.iov_base = packet; + iov.iov_len = packetlen; + msghdr.msg_iov = &iov; + msghdr.msg_iovlen = 1; + msghdr.msg_flags = 0; +again: + len = sendmsg(sock, &msghdr, 0); + if (len == -1) + { + if (errno == EINTR) + goto again; + } + + if (len != packetlen) + { + helplog(ASL_LEVEL_ERR, "mDNSSendKeepalive: sendmsg failed %s", strerror(errno)); + } + else + { + char source[INET6_ADDRSTRLEN], dest[INET6_ADDRSTRLEN]; + + inet_ntop(af, (void *)sadd6, source, sizeof(source)); + inet_ntop(af, (void *)dadd6, dest, sizeof(dest)); + + helplog(ASL_LEVEL_ERR, "mDNSSendKeepalive: Success Source %s:%d, Dest %s:%d, %u, %u, %u", source, ntohs(lport), dest, ntohs(rport), ntohl(seq), ntohl(ack), ntohs(win)); + + } + close(sock); + return KERN_SUCCESS; +} + + +kern_return_t do_mDNSRetrieveTCPInfo(__unused mach_port_t port, 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, audit_token_t token) +{ + struct tcp_info ti; + struct info_tuple itpl; + int mib[4]; + unsigned int miblen; + size_t len; + size_t sz; + + memset(&itpl, 0, sizeof(struct info_tuple)); + memset(&ti, 0, sizeof(struct tcp_info)); + + if (!authorized(&token)) + { + helplog(ASL_LEVEL_ERR, "mDNSRetrieveTCPInfo: Not authorized"); + return kmDNSHelperNotAuthorized; + } + + if (family == AF_INET) + { + memcpy(&itpl.itpl_local_sin.sin_addr, laddr, sizeof(struct in_addr)); + memcpy(&itpl.itpl_remote_sin.sin_addr, raddr, sizeof(struct in_addr)); + itpl.itpl_local_sin.sin_port = lport; + itpl.itpl_remote_sin.sin_port = rport; + itpl.itpl_local_sin.sin_family = AF_INET; + itpl.itpl_remote_sin.sin_family = AF_INET; + } + else + { + memcpy(&itpl.itpl_local_sin6.sin6_addr, laddr, sizeof(struct in6_addr)); + memcpy(&itpl.itpl_remote_sin6.sin6_addr, raddr, sizeof(struct in6_addr)); + itpl.itpl_local_sin6.sin6_port = lport; + itpl.itpl_remote_sin6.sin6_port = rport; + itpl.itpl_local_sin6.sin6_family = AF_INET6; + itpl.itpl_remote_sin6.sin6_family = AF_INET6; + } + itpl.itpl_proto = IPPROTO_TCP; + sz = sizeof(mib)/sizeof(mib[0]); + if (sysctlnametomib("net.inet.tcp.info", mib, &sz) == -1) + { + helplog(ASL_LEVEL_ERR, "do_RetrieveTCPInfo: sysctlnametomib failed %d, %s", errno, strerror(errno)); + return errno; + } + miblen = (unsigned int)sz; + len = sizeof(struct tcp_info); + if (sysctl(mib, miblen, &ti, &len, &itpl, sizeof(struct info_tuple)) == -1) + { + helplog(ASL_LEVEL_ERR, "do_RetrieveTCPInfo: sysctl failed %d, %s", errno, strerror(errno)); + return errno; + } + + *seq = ti.tcpi_snd_nxt - 1; + *ack = ti.tcpi_rcv_nxt; + *win = ti.tcpi_rcv_space >> ti.tcpi_rcv_wscale; + *intfid = ti.tcpi_last_outif; + return KERN_SUCCESS; +} + +static int getMACAddress(int family, v6addr_t raddr, v6addr_t gaddr, int *gfamily, ethaddr_t eth) +{ + struct + { + struct rt_msghdr m_rtm; + char m_space[512]; + } m_rtmsg; + + struct rt_msghdr *rtm = &(m_rtmsg.m_rtm); + char *cp = m_rtmsg.m_space; + int seq = 6367, sock, rlen, i; + struct sockaddr_in *sin = NULL; + struct sockaddr_in6 *sin6 = NULL; + struct sockaddr_dl *sdl = NULL; + struct sockaddr_storage sins; + struct sockaddr_dl sdl_m; + +#define NEXTADDR(w, s, len) \ + if (rtm->rtm_addrs & (w)) \ + { \ + bcopy((char *)s, cp, len); \ + cp += len; \ + } + + bzero(&sins, sizeof(struct sockaddr_storage)); + bzero(&sdl_m, sizeof(struct sockaddr_dl)); + bzero((char *)&m_rtmsg, sizeof(m_rtmsg)); + + sock = socket(PF_ROUTE, SOCK_RAW, 0); + if (sock < 0) + { + helplog(ASL_LEVEL_ERR, "mDNSGetRemoteMAC: Can not open the socket - %s", strerror(errno)); + return errno; + } + + rtm->rtm_addrs |= RTA_DST | RTA_GATEWAY; + rtm->rtm_type = RTM_GET; + rtm->rtm_flags = 0; + rtm->rtm_version = RTM_VERSION; + rtm->rtm_seq = ++seq; + + sdl_m.sdl_len = sizeof(sdl_m); + sdl_m.sdl_family = AF_LINK; + if (family == AF_INET) + { + sin = (struct sockaddr_in*)&sins; + sin->sin_family = AF_INET; + sin->sin_len = sizeof(struct sockaddr_in); + memcpy(&sin->sin_addr, raddr, sizeof(struct in_addr)); + NEXTADDR(RTA_DST, sin, sin->sin_len); + } + else if (family == AF_INET6) + { + sin6 = (struct sockaddr_in6 *)&sins; + sin6->sin6_len = sizeof(struct sockaddr_in6); + sin6->sin6_family = AF_INET6; + memcpy(&sin6->sin6_addr, raddr, sizeof(struct in6_addr)); + NEXTADDR(RTA_DST, sin6, sin6->sin6_len); + } + NEXTADDR(RTA_GATEWAY, &sdl_m, sdl_m.sdl_len); + rtm->rtm_msglen = rlen = cp - (char *)&m_rtmsg; + + if (write(sock, (char *)&m_rtmsg, rlen) < 0) + { + helplog(ASL_LEVEL_INFO, "do_mDNSGetRemoteMAC: writing to routing socket: %s", strerror(errno)); + close(sock); + return errno; + } + + do + { + rlen = read(sock, (char *)&m_rtmsg, sizeof(m_rtmsg)); + } + while (rlen > 0 && (rtm->rtm_seq != seq || rtm->rtm_pid != getpid())); + + if (rlen < 0) + helplog(ASL_LEVEL_ERR, "do_mDNSGetRemoteMAC: Read from routing socket failed"); + + if (family == AF_INET) + { + sin = (struct sockaddr_in *) (rtm + 1); + sdl = (struct sockaddr_dl *) (sin->sin_len + (char *) sin); + } + else if (family == AF_INET6) + { + sin6 = (struct sockaddr_in6 *) (rtm +1); + sdl = (struct sockaddr_dl *) (sin6->sin6_len + (char *) sin6); + } + // If the address is not on the local net, we get the IP address of the gateway. + // We would have to repeat the process to get the MAC address of the gateway + *gfamily = sdl->sdl_family; + if (sdl->sdl_family == AF_INET) + { + struct sockaddr_in *new_sin = (struct sockaddr_in *)(sin->sin_len +(char*) sin); + memcpy(gaddr, &new_sin->sin_addr, sizeof(struct in_addr)); + close(sock); + return -1; + } + else if (sdl->sdl_family == AF_INET6) + { + struct sockaddr_in6 *new_sin6 = (struct sockaddr_in6 *)(sin6->sin6_len +(char*) sin6); + memcpy(gaddr, &new_sin6->sin6_addr, sizeof(struct in6_addr)); + close(sock); + return -1; + } + + unsigned char *ptr = (unsigned char *)LLADDR(sdl); + for (i = 0; i < ETHER_ADDR_LEN; i++) + (eth)[i] = *(ptr +i); + + close(sock); + return KERN_SUCCESS; +} + +kern_return_t do_mDNSGetRemoteMAC(__unused mach_port_t port, int family, v6addr_t raddr, ethaddr_t eth, audit_token_t token) +{ + int ret = 0; + v6addr_t gateway; + int gfamily; + int count = 0; + + if (!authorized(&token)) + { + helplog(ASL_LEVEL_ERR, "mDNSGetRemoteMAC: Not authorized"); + return kmDNSHelperNotAuthorized; + } + + do + { + ret = getMACAddress(family, raddr, gateway, &gfamily, eth); + if (ret == -1) + { + memcpy(raddr, gateway, sizeof(family)); + family = gfamily; + count++; + } + } + while ((ret == -1) && (count < 5)); + return ret; +} + + +kern_return_t do_mDNSStoreSPSMACAddress(__unused mach_port_t port, int family, v6addr_t spsaddr, const char *ifname, audit_token_t token) +{ + ethaddr_t eth; + char spsip[INET6_ADDRSTRLEN]; + int ret = 0; + CFStringRef sckey = NULL; + SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("mDNSResponder:StoreSPSMACAddress"), NULL, NULL); + SCDynamicStoreRef ipstore = SCDynamicStoreCreate(NULL, CFSTR("mDNSResponder:GetIPv6Addresses"), NULL, NULL); + CFMutableDictionaryRef dict = NULL; + CFStringRef entityname = NULL; + CFDictionaryRef ipdict = NULL; + CFArrayRef addrs = NULL; + + if (!authorized(&token)) + { + helplog(ASL_LEVEL_ERR, "mDNSStoreSPSMAC: Not authorized"); + return kmDNSHelperNotAuthorized; + } + + if ((store == NULL) || (ipstore == NULL)) + { + helplog(ASL_LEVEL_ERR, "Unable to access SC Dynamic Store"); + return KERN_FAILURE; + } + + // Get the MAC address of the Sleep Proxy Server + memset(eth, 0, sizeof(eth)); + ret = do_mDNSGetRemoteMAC(port, family, spsaddr, eth, token); + if (ret != KERN_SUCCESS) + { + helplog(ASL_LEVEL_ERR, "mDNSStoreSPSMAC: Failed to determine the MAC address"); + goto fin; + } + + // Create/Update the dynamic store entry for the specified interface + sckey = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%s%s%s"), "State:/Network/Interface/", ifname, "/BonjourSleepProxyAddress"); + dict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + if (!dict) + { + helplog(ASL_LEVEL_ERR, "SPSCreateDict: Could not create CFDictionary dict"); + ret = KERN_FAILURE; + goto fin; + } + + CFStringRef macaddr = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%02x:%02x:%02x:%02x:%02x:%02x"), eth[0], eth[1], eth[2], eth[3], eth[4], eth[5]); + CFDictionarySetValue(dict, CFSTR("MACAddress"), macaddr); + if (NULL != macaddr) CFRelease(macaddr); + + if( NULL == inet_ntop(family, (void *)spsaddr, spsip, sizeof(spsip))) + { + helplog(ASL_LEVEL_ERR, "inet_ntop failed: %s", strerror(errno)); + ret = kmDNSHelperInvalidNetworkAddress; + goto fin; + } + + CFStringRef ipaddr = CFStringCreateWithCString(NULL, spsip, kCFStringEncodingUTF8); + CFDictionarySetValue(dict, CFSTR("IPAddress"), ipaddr); + if (NULL != ipaddr) CFRelease(ipaddr); + + // Get the current IPv6 addresses on this interface and store them so NAs can be sent on wakeup + if ((entityname = CFStringCreateWithFormat(NULL, NULL, CFSTR("State:/Network/Interface/%s/IPv6"), ifname)) != NULL) + { + if ((ipdict = SCDynamicStoreCopyValue(ipstore, entityname)) != NULL) + { + if((addrs = CFDictionaryGetValue(ipdict, CFSTR("Addresses"))) != NULL) + { + addrs = CFRetain(addrs); + CFDictionarySetValue(dict, CFSTR("RegisteredAddresses"), addrs); + } + } + } + SCDynamicStoreSetValue(store, sckey, dict); + +fin: + if (store) CFRelease(store); + if (ipstore) CFRelease(ipstore); + if (sckey) CFRelease(sckey); + if (dict) CFRelease(dict); + if (ipdict) CFRelease(ipdict); + if (entityname) CFRelease(entityname); + if (addrs) CFRelease(addrs); + + update_idle_timer(); + return ret; +} diff --git a/mDNSResponder/mDNSMacOSX/helper.h b/mDNSResponder/mDNSMacOSX/helper.h new file mode 100644 index 00000000..a2982372 --- /dev/null +++ b/mDNSResponder/mDNSMacOSX/helper.h @@ -0,0 +1,84 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * 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. + */ + +#ifndef H_HELPER_H +#define H_HELPER_H + +#define kmDNSHelperServiceName "com.apple.mDNSResponderHelper" + +enum mDNSPreferencesSetNameKey +{ + kmDNSComputerName = 1, + kmDNSLocalHostName +}; + +enum mDNSUpDown +{ + kmDNSUp = 1, + kmDNSDown +}; + +enum mDNSAutoTunnelSetKeysReplaceDelete +{ + kmDNSAutoTunnelSetKeysReplace = 1, + kmDNSAutoTunnelSetKeysDelete +}; + +// helper parses the system keychain and returns the information to mDNSResponder. +// It returns four attributes. Attributes are defined after how they show up in +// keychain access utility (the actual attribute name to retrieve these are different). +enum mDNSKeyChainAttributes +{ + kmDNSKcWhere, // Where + kmDNSKcAccount, // Account + kmDNSKcKey, // Key + kmDNSKcName // Name +}; + +#define ERROR(x, y) x, +enum mDNSHelperErrors +{ + mDNSHelperErrorBase = 2300, + #include "helper-error.h" + mDNSHelperErrorEnd +}; +#undef ERROR + +#include "mDNSEmbeddedAPI.h" +#include "helpermsg-types.h" + +extern const char *mDNSHelperError(int errornum); + +extern mStatus mDNSHelperInit(void); +extern void mDNSRequestBPF(void); +extern int mDNSPowerRequest(int key, int interval); +extern int mDNSSetLocalAddressCacheEntry(int ifindex, int family, const v6addr_t ip, const ethaddr_t eth); +extern void mDNSNotify(const char *title, const char *msg); // Both strings are UTF-8 text +extern void mDNSPreferencesSetName(int key, domainlabel *old, domainlabel *new); +extern int mDNSKeychainGetSecrets(CFArrayRef *secrets); +extern void mDNSConfigureServer(int updown, const char *const prefix, const domainname *const fqdn); +extern 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); +extern void mDNSSendWakeupPacket(unsigned ifid, char *eth_addr, char *ip_addr, int iteration); +extern void mDNSPacketFilterControl(uint32_t command, char * ifname, uint32_t count, pfArray_t portArray, pfArray_t protocolArray); +extern void mDNSSendKeepalive(v6addr_t sadd, v6addr_t dadd, uint16_t lport, uint16_t rport, unsigned seq, unsigned ack, uint16_t win); +extern 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); +extern void mDNSGetRemoteMAC(mDNS *const m, int family, v6addr_t raddr); +extern void mDNSStoreSPSMACAddress(int family, v6addr_t spsaddr, char *ifname); + +#endif /* H_HELPER_H */ diff --git a/mDNSResponder/mDNSMacOSX/helpermsg-types.h b/mDNSResponder/mDNSMacOSX/helpermsg-types.h new file mode 100644 index 00000000..ca5b140a --- /dev/null +++ b/mDNSResponder/mDNSMacOSX/helpermsg-types.h @@ -0,0 +1,30 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2007 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. + */ + +#ifndef H_HELPERMSG_TYPES_H +#define H_HELPERMSG_TYPES_H + +#include <stdint.h> +typedef uint8_t v4addr_t [ 4]; +typedef uint8_t ethaddr_t[ 6]; +typedef uint8_t v6addr_t [16]; +typedef const char *string_t; + +#define PFPortArraySize 16 +typedef uint16_t pfArray_t [PFPortArraySize]; + +#endif /* H_HELPERMSG_TYPES_H */ diff --git a/mDNSResponder/mDNSMacOSX/helpermsg.defs b/mDNSResponder/mDNSMacOSX/helpermsg.defs new file mode 100644 index 00000000..58363082 --- /dev/null +++ b/mDNSResponder/mDNSMacOSX/helpermsg.defs @@ -0,0 +1,143 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * 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/std_types.defs> +#include <mach/mach_types.defs> + +import "helpermsg-types.h"; + +type v4addr_t = array [ 4] of uint8_t; +type ethaddr_t = array [ 6] of uint8_t; +type v6addr_t = array [16] of uint8_t; +type string_t = c_string[*:1024]; + +// Mig doesn't generate the output file if I use the constant PFPortArraySize below +type pfArray_t = array [16] of uint16_t; + +subsystem helper 1833193043; +serverprefix do_; +userprefix proxy_; + +simpleroutine mDNSExit( port : mach_port_t; + ServerAuditToken token : audit_token_t); + +simpleroutine mDNSRequestBPF( port : mach_port_t; + ServerAuditToken token : audit_token_t); + +routine mDNSPowerRequest( port : mach_port_t; + key : int; + interval : int; + out err : int; + ServerAuditToken token : audit_token_t); + +routine mDNSSetLocalAddressCacheEntry( + port : mach_port_t; + ifindex : int; + family : int; + ip : v6addr_t; + eth : ethaddr_t; + out err : int; + ServerAuditToken token : audit_token_t); + +simpleroutine mDNSNotify( port : mach_port_t; + title : string_t; + msg : string_t; + ServerAuditToken token : audit_token_t); + +simpleroutine mDNSPreferencesSetName( + port : mach_port_t; + key : int; + old : string_t; + new : string_t; + ServerAuditToken token : audit_token_t); + +routine mDNSKeychainGetSecrets( port : mach_port_t; + out numsecrets : unsigned; + out secrets : pointer_t; + out err : int; + ServerAuditToken token : audit_token_t); + +simpleroutine mDNSConfigureServer( + port : mach_port_t; + updown : int; + id : string_t; + ServerAuditToken token : audit_token_t); + +routine mDNSAutoTunnelSetKeys( port : mach_port_t; + replacedelete : int; + local_inner : v6addr_t; + local_outer : v6addr_t; + local_port : uint16_t; /* Port expressed as a numeric integer value */ + remote_inner : v6addr_t; + remote_outer : v6addr_t; + remote_port : uint16_t; /* Port expressed as a numeric integer value */ + id : string_t; + out err : int; + ServerAuditToken token : audit_token_t); + +simpleroutine mDNSSendWakeupPacket( + port : mach_port_t; + ifid : unsigned; + eth_addr : string_t; + ip_addr : string_t; + iteration : int; + ServerAuditToken token : audit_token_t); + +simpleroutine mDNSPacketFilterControl( + port : mach_port_t; + command : uint32_t; + ifname : string_t; + arraySize : uint32_t; + portArray : pfArray_t; + protocolArray : pfArray_t; + ServerAuditToken token : audit_token_t); + + +simpleroutine mDNSSendKeepalive( port : mach_port_t; + sadd : v6addr_t; + dadd : v6addr_t; + lport : uint16_t; + rport : uint16_t; + seq : unsigned; + ack : unsigned; + win : uint16_t; + ServerAuditToken token : audit_token_t); + +routine mDNSRetrieveTCPInfo( + port : mach_port_t; + family : int; + laddr : v6addr_t; + lport : uint16_t; + raddr : v6addr_t; + rport : uint16_t; + out seq : uint32_t; + out ack : uint32_t; + out win : uint16_t; + out intfid : int32_t; + ServerAuditToken token : audit_token_t); + +routine mDNSGetRemoteMAC( port : mach_port_t; + family : int; + raddr : v6addr_t; + out eth : ethaddr_t; + ServerAuditToken token : audit_token_t); + +simpleroutine mDNSStoreSPSMACAddress( port : mach_port_t; + family : int; + spsaddr : v6addr_t; + ifname : string_t; + ServerAuditToken token : audit_token_t); diff --git a/mDNSResponder/mDNSMacOSX/ipsec_strerror.h b/mDNSResponder/mDNSMacOSX/ipsec_strerror.h new file mode 100644 index 00000000..ecacf3b2 --- /dev/null +++ b/mDNSResponder/mDNSMacOSX/ipsec_strerror.h @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2003-2007 Apple Computer, 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. + */ +/* $FreeBSD: src/lib/libipsec/ipsec_strerror.h,v 1.1.2.2 2001/07/03 11:01:14 ume Exp $ */ +/* $KAME: ipsec_strerror.h,v 1.8 2000/07/30 00:45:12 itojun Exp $ */ + +/* + * Copyright (C) 1995, 1996, 1997, 1998, and 1999 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +extern int __ipsec_errcode; +extern void __ipsec_set_strerror __P((const char *)); + +#define EIPSEC_NO_ERROR 0 /*success*/ +#define EIPSEC_NOT_SUPPORTED 1 /*not supported*/ +#define EIPSEC_INVAL_ARGUMENT 2 /*invalid argument*/ +#define EIPSEC_INVAL_SADBMSG 3 /*invalid sadb message*/ +#define EIPSEC_INVAL_VERSION 4 /*invalid version*/ +#define EIPSEC_INVAL_POLICY 5 /*invalid security policy*/ +#define EIPSEC_INVAL_ADDRESS 6 /*invalid address specification*/ +#define EIPSEC_INVAL_PROTO 7 /*invalid ipsec protocol*/ +#define EIPSEC_INVAL_MODE 8 /*Invalid ipsec mode*/ +#define EIPSEC_INVAL_LEVEL 9 /*invalid ipsec level*/ +#define EIPSEC_INVAL_SATYPE 10 /*invalid SA type*/ +#define EIPSEC_INVAL_MSGTYPE 11 /*invalid message type*/ +#define EIPSEC_INVAL_EXTTYPE 12 /*invalid extension type*/ +#define EIPSEC_INVAL_ALGS 13 /*Invalid algorithm type*/ +#define EIPSEC_INVAL_KEYLEN 14 /*invalid key length*/ +#define EIPSEC_INVAL_FAMILY 15 /*invalid address family*/ +#define EIPSEC_INVAL_PREFIXLEN 16 /*SPI range violation*/ +#define EIPSEC_INVAL_DIR 17 /*Invalid direciton*/ +#define EIPSEC_INVAL_SPI 18 /*invalid prefixlen*/ +#define EIPSEC_NO_PROTO 19 /*no protocol specified*/ +#define EIPSEC_NO_ALGS 20 /*No algorithm specified*/ +#define EIPSEC_NO_BUFS 21 /*no buffers available*/ +#define EIPSEC_DO_GET_SUPP_LIST 22 /*must get supported algorithm first*/ +#define EIPSEC_PROTO_MISMATCH 23 /*protocol mismatch*/ +#define EIPSEC_FAMILY_MISMATCH 24 /*family mismatch*/ +#define EIPSEC_FEW_ARGUMENTS 25 /*Too few arguments*/ +#define EIPSEC_SYSTEM_ERROR 26 /*system error*/ +#define EIPSEC_MAX 27 /*unknown error*/ diff --git a/mDNSResponder/mDNSMacOSX/libpfkey.h b/mDNSResponder/mDNSMacOSX/libpfkey.h new file mode 100644 index 00000000..98d192d8 --- /dev/null +++ b/mDNSResponder/mDNSMacOSX/libpfkey.h @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2003-2007 Apple Computer, 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. + */ +/* $FreeBSD: src/lib/libipsec/libpfkey.h,v 1.1.2.2 2001/07/03 11:01:14 ume Exp $ */ +/* $KAME: libpfkey.h,v 1.6 2001/03/05 18:22:17 thorpej Exp $ */ + +/* + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +struct sadb_msg; +extern void pfkey_sadump __P((struct sadb_msg *)); +extern void pfkey_spdump __P((struct sadb_msg *)); + +struct sockaddr; +struct sadb_alg; +int ipsec_check_keylen __P((u_int, u_int, u_int)); +int ipsec_check_keylen2 __P((u_int, u_int, u_int)); +int ipsec_get_keylen __P((u_int, u_int, struct sadb_alg *)); +u_int pfkey_set_softrate __P((u_int, u_int)); +u_int pfkey_get_softrate __P((u_int)); +int pfkey_send_getspi __P((int, u_int, u_int, struct sockaddr *, + struct sockaddr *, u_int32_t, u_int32_t, u_int32_t, u_int32_t)); +int pfkey_send_update __P((int, u_int, u_int, struct sockaddr *, + struct sockaddr *, u_int32_t, u_int32_t, u_int, + caddr_t, u_int, u_int, u_int, u_int, u_int, u_int32_t, u_int64_t, + u_int64_t, u_int64_t, u_int32_t)); +int pfkey_send_add __P((int, u_int, u_int, struct sockaddr *, + struct sockaddr *, u_int32_t, u_int32_t, u_int, + caddr_t, u_int, u_int, u_int, u_int, u_int, u_int32_t, u_int64_t, + u_int64_t, u_int64_t, u_int32_t)); +int pfkey_send_delete __P((int, u_int, u_int, + struct sockaddr *, struct sockaddr *, u_int32_t)); +int pfkey_send_delete_all __P((int, u_int, u_int, + struct sockaddr *, struct sockaddr *)); +int pfkey_send_get __P((int, u_int, u_int, + struct sockaddr *, struct sockaddr *, u_int32_t)); +int pfkey_send_register __P((int, u_int)); +int pfkey_recv_register __P((int)); +int pfkey_set_supported __P((struct sadb_msg *, int)); +int pfkey_send_flush __P((int, u_int)); +int pfkey_send_dump __P((int, u_int)); +int pfkey_send_promisc_toggle __P((int, int)); +int pfkey_send_spdadd __P((int, struct sockaddr *, u_int, + struct sockaddr *, u_int, u_int, caddr_t, int, u_int32_t)); +int pfkey_send_spdadd2 __P((int, struct sockaddr *, u_int, + struct sockaddr *, u_int, u_int, u_int64_t, u_int64_t, + caddr_t, int, u_int32_t)); +int pfkey_send_spdupdate __P((int, struct sockaddr *, u_int, + struct sockaddr *, u_int, u_int, caddr_t, int, u_int32_t)); +int pfkey_send_spdupdate2 __P((int, struct sockaddr *, u_int, + struct sockaddr *, u_int, u_int, u_int64_t, u_int64_t, + caddr_t, int, u_int32_t)); +int pfkey_send_spddelete __P((int, struct sockaddr *, u_int, + struct sockaddr *, u_int, u_int, caddr_t, int, u_int32_t)); +int pfkey_send_spddelete2 __P((int, u_int32_t)); +int pfkey_send_spdget __P((int, u_int32_t)); +int pfkey_send_spdsetidx __P((int, struct sockaddr *, u_int, + struct sockaddr *, u_int, u_int, caddr_t, int, u_int32_t)); +int pfkey_send_spdflush __P((int)); +int pfkey_send_spddump __P((int)); + +int pfkey_open __P((void)); +void pfkey_close __P((int)); +struct sadb_msg *pfkey_recv __P((int)); +int pfkey_send __P((int, struct sadb_msg *, int)); +int pfkey_align __P((struct sadb_msg *, caddr_t *)); +int pfkey_check __P((caddr_t *)); diff --git a/mDNSResponder/mDNSMacOSX/mDNSMacOSX.c b/mDNSResponder/mDNSMacOSX/mDNSMacOSX.c new file mode 100644 index 00000000..fda595c9 --- /dev/null +++ b/mDNSResponder/mDNSMacOSX/mDNSMacOSX.c @@ -0,0 +1,10442 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2002-2013 Apple Computer, 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. + */ + +// *************************************************************************** +// mDNSMacOSX.c: +// Supporting routines to run mDNS on a CFRunLoop platform +// *************************************************************************** + +// For debugging, set LIST_ALL_INTERFACES to 1 to display all found interfaces, +// including ones that mDNSResponder chooses not to use. +#define LIST_ALL_INTERFACES 0 + +#include "mDNSEmbeddedAPI.h" // Defines the interface provided to the client layer above +#include "DNSCommon.h" +#include "uDNS.h" +#include "mDNSMacOSX.h" // Defines the specific types needed to run mDNS on this platform +#include "dns_sd.h" // For mDNSInterface_LocalOnly etc. +#include "PlatformCommon.h" +#include "uds_daemon.h" +#include "CryptoSupport.h" + +#include <stdio.h> +#include <stdarg.h> // For va_list support +#include <stdlib.h> // For arc4random +#include <net/if.h> +#include <net/if_types.h> // For IFT_ETHER +#include <net/if_dl.h> +#include <net/bpf.h> // For BIOCSETIF etc. +#include <sys/uio.h> +#include <sys/param.h> +#include <sys/socket.h> +#include <sys/sysctl.h> +#include <sys/event.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <time.h> // platform support for UTC time +#include <arpa/inet.h> // for inet_aton +#include <pthread.h> +#include <netdb.h> // for getaddrinfo +#include <sys/sockio.h> // for SIOCGIFEFLAGS +#include <notify.h> +#include <netinet/in.h> // For IP_RECVTTL +#ifndef IP_RECVTTL +#define IP_RECVTTL 24 // bool; receive reception TTL w/dgram +#endif + +#include <netinet/in_systm.h> // For n_long, required by <netinet/ip.h> below +#include <netinet/ip.h> // For IPTOS_LOWDELAY etc. +#include <netinet6/in6_var.h> // For IN6_IFF_NOTREADY etc. +#include <netinet6/nd6.h> // For ND6_INFINITE_LIFETIME etc. + +#include <netinet/tcp.h> + +#include <DebugServices.h> +#include "dnsinfo.h" + +#include <ifaddrs.h> + +#include <IOKit/IOKitLib.h> +#include <IOKit/IOMessage.h> + +#include <IOKit/ps/IOPowerSources.h> +#include <IOKit/ps/IOPowerSourcesPrivate.h> +#include <IOKit/ps/IOPSKeys.h> + +#include <mach/mach_error.h> +#include <mach/mach_port.h> +#include <mach/mach_time.h> +#include "helper.h" +#include "P2PPacketFilter.h" + +#include <asl.h> +#include <SystemConfiguration/SCPrivate.h> + +// Include definition of opaque_presence_indication for KEV_DL_NODE_PRESENCE handling logic. +#include <Kernel/IOKit/apple80211/apple80211_var.h> + +#if APPLE_OSX_mDNSResponder +#include <DeviceToDeviceManager/DeviceToDeviceManager.h> +#include <AWACS.h> +#if !NO_D2D +D2DStatus D2DInitialize(CFRunLoopRef runLoop, D2DServiceCallback serviceCallback, void* userData) __attribute__((weak_import)); +D2DStatus D2DRetain(D2DServiceInstance instanceHandle, D2DTransportType transportType) __attribute__((weak_import)); +D2DStatus D2DStopAdvertisingPairOnTransport(const Byte *key, const size_t keySize, const Byte *value, const size_t valueSize, D2DTransportType transport) __attribute__((weak_import)); +D2DStatus D2DRelease(D2DServiceInstance instanceHandle, D2DTransportType transportType) __attribute__((weak_import)); +D2DStatus D2DStartAdvertisingPairOnTransport(const Byte *key, const size_t keySize, const Byte *value, const size_t valueSize, D2DTransportType transport) __attribute__((weak_import)); +D2DStatus D2DStartBrowsingForKeyOnTransport(const Byte *key, const size_t keySize, D2DTransportType transport) __attribute__((weak_import)); +D2DStatus D2DStopBrowsingForKeyOnTransport(const Byte *key, const size_t keySize, D2DTransportType transport) __attribute__((weak_import)); +void D2DStartResolvingPairOnTransport(const Byte *key, const size_t keySize, const Byte *value, const size_t valueSize, D2DTransportType transport) __attribute__((weak_import)); +void D2DStopResolvingPairOnTransport(const Byte *key, const size_t keySize, const Byte *value, const size_t valueSize, D2DTransportType transport) __attribute__((weak_import)); +D2DStatus D2DTerminate() __attribute__((weak_import)); + +#endif // ! NO_D2D + +#else +#define NO_D2D 1 +#define NO_AWACS 1 +#endif // APPLE_OSX_mDNSResponder + +#if APPLE_OSX_mDNSResponder && !TARGET_OS_EMBEDDED +#include <IOKit/platform/IOPlatformSupportPrivate.h> +#endif // APPLE_OSX_mDNSResponder && !TARGET_OS_EMBEDDED + + +#define kInterfaceSpecificOption "interface=" + +#define mDNS_IOREG_KEY "mDNS_KEY" +#define mDNS_IOREG_VALUE "2009-07-30" +#define mDNS_IOREG_KA_KEY "mDNS_Keepalive" +#define mDNS_USER_CLIENT_CREATE_TYPE 'mDNS' + +// cache the InterfaceID of the AWDL interface +static mDNSInterfaceID AWDLInterfaceID; + +// *************************************************************************** +// Globals + +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - Globals +#endif + +// By default we don't offer sleep proxy service +// If OfferSleepProxyService is set non-zero (typically via command-line switch), +// then we'll offer sleep proxy service on desktop Macs that are set to never sleep. +// We currently do not offer sleep proxy service on laptops, or on machines that are set to go to sleep. +mDNSexport int OfferSleepProxyService = 0; +mDNSexport int DisableSleepProxyClient = 0; +mDNSexport int UseInternalSleepProxy = 1; // Set to non-zero to use internal (in-NIC) Sleep Proxy + +mDNSexport int OSXVers, iOSVers; +mDNSexport int KQueueFD; + +#ifndef NO_SECURITYFRAMEWORK +static CFArrayRef ServerCerts; +OSStatus SSLSetAllowAnonymousCiphers(SSLContextRef context, Boolean enable); +#endif /* NO_SECURITYFRAMEWORK */ + +static CFStringRef NetworkChangedKey_IPv4; +static CFStringRef NetworkChangedKey_IPv6; +static CFStringRef NetworkChangedKey_Hostnames; +static CFStringRef NetworkChangedKey_Computername; +static CFStringRef NetworkChangedKey_DNS; +static CFStringRef NetworkChangedKey_StateInterfacePrefix; +static CFStringRef NetworkChangedKey_DynamicDNS = CFSTR("Setup:/Network/DynamicDNS"); +static CFStringRef NetworkChangedKey_BackToMyMac = CFSTR("Setup:/Network/BackToMyMac"); +static CFStringRef NetworkChangedKey_BTMMConnectivity = CFSTR("State:/Network/Connectivity"); +static CFStringRef NetworkChangedKey_PowerSettings = CFSTR("State:/IOKit/PowerManagement/CurrentSettings"); + +static char HINFO_HWstring_buffer[32]; +static char *HINFO_HWstring = "Device"; +static int HINFO_HWstring_prefixlen = 6; + +mDNSexport int WatchDogReportingThreshold = 250; + +dispatch_queue_t SSLqueue; + +//To prevent blocking the main queue, all writes to DynamicStore happen on the DynamicStoreQueue +static dispatch_queue_t DynamicStoreQueue; + +#if TARGET_OS_EMBEDDED +#define kmDNSResponderManagedPrefsID CFSTR("/Library/Managed Preferences/mobile/com.apple.mDNSResponder.plist") +#endif + +#if APPLE_OSX_mDNSResponder +static mDNSu8 SPMetricPortability = 99; +static mDNSu8 SPMetricMarginalPower = 99; +static mDNSu8 SPMetricTotalPower = 99; +static mDNSu8 SPMetricFeatures = 1; /* The current version supports TCP Keep Alive Feature */ +mDNSexport domainname ActiveDirectoryPrimaryDomain; +mDNSexport int ActiveDirectoryPrimaryDomainLabelCount; +mDNSexport mDNSAddr ActiveDirectoryPrimaryDomainServer; +#endif // APPLE_OSX_mDNSResponder + +// Don't send triggers too often. We arbitrarily limit it to three minutes. +#define DNS_TRIGGER_INTERVAL (180 * mDNSPlatformOneSecond) + +// Used by AutoTunnel +const char btmmprefix[] = "btmmdns:"; +const char dnsprefix[] = "dns:"; + +// String Array used to write list of private domains to Dynamic Store +static CFArrayRef privateDnsArray = NULL; + +// *************************************************************************** +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - +#pragma mark - D2D Support +#endif + +#if !NO_D2D + +mDNSexport void D2D_start_advertising_interface(NetworkInterfaceInfo *interface) +{ + // AWDL wants the address and reverse address PTR record communicated + // via the D2D interface layer. + if (interface->InterfaceID == AWDLInterfaceID) + { + // only log if we have a valid record to start advertising + if (interface->RR_A.resrec.RecordType || interface->RR_PTR.resrec.RecordType) + LogInfo("D2D_start_advertising_interface: %s", interface->ifname); + + if (interface->RR_A.resrec.RecordType) + external_start_advertising_service(&interface->RR_A.resrec, NULL); + if (interface->RR_PTR.resrec.RecordType) + external_start_advertising_service(&interface->RR_PTR.resrec, NULL); + } +} + +mDNSexport void D2D_stop_advertising_interface(NetworkInterfaceInfo *interface) +{ + if (interface->InterfaceID == AWDLInterfaceID) + { + // only log if we have a valid record to stop advertising + if (interface->RR_A.resrec.RecordType || interface->RR_PTR.resrec.RecordType) + LogInfo("D2D_stop_advertising_interface: %s", interface->ifname); + + if (interface->RR_A.resrec.RecordType) + external_stop_advertising_service(&interface->RR_A.resrec, NULL); + if (interface->RR_PTR.resrec.RecordType) + external_stop_advertising_service(&interface->RR_PTR.resrec, NULL); + } +} + +// Name compression items for fake packet version number 1 +static const mDNSu8 compression_packet_v1 = 0x01; + +static DNSMessage compression_base_msg = { { {{0}}, {{0}}, 2, 0, 0, 0 }, "\x04_tcp\x05local\x00\x00\x0C\x00\x01\x04_udp\xC0\x11\x00\x0C\x00\x01" }; +static mDNSu8 *const compression_limit = (mDNSu8 *) &compression_base_msg + sizeof(DNSMessage); +static mDNSu8 *const compression_lhs = (mDNSu8 *const) compression_base_msg.data + 27; + +mDNSlocal void FreeD2DARElemCallback(mDNS *const m, AuthRecord *const rr, mStatus result); +mDNSlocal void PrintHex(mDNSu8 *data, mDNSu16 len); + +typedef struct D2DRecordListElem +{ + struct D2DRecordListElem *next; + D2DServiceInstance instanceHandle; + D2DTransportType transportType; + AuthRecord ar; // must be last in the structure to accomodate extra space + // allocated for large records. +} D2DRecordListElem; + +static D2DRecordListElem *D2DRecords = NULL; // List of records returned with D2DServiceFound events + +typedef struct D2DBrowseListElem +{ + struct D2DBrowseListElem *next; + domainname name; + mDNSu16 type; + unsigned int refCount; +} D2DBrowseListElem; + +D2DBrowseListElem* D2DBrowseList = NULL; + +mDNSlocal mDNSu8 *putVal16(mDNSu8 *ptr, mDNSu16 val) +{ + ptr[0] = (mDNSu8)((val >> 8 ) & 0xFF); + ptr[1] = (mDNSu8)((val ) & 0xFF); + return ptr + sizeof(mDNSu16); +} + +mDNSlocal mDNSu8 *putVal32(mDNSu8 *ptr, mDNSu32 val) +{ + ptr[0] = (mDNSu8)((val >> 24) & 0xFF); + ptr[1] = (mDNSu8)((val >> 16) & 0xFF); + ptr[2] = (mDNSu8)((val >> 8) & 0xFF); + ptr[3] = (mDNSu8)((val ) & 0xFF); + return ptr + sizeof(mDNSu32); +} + +mDNSlocal void DomainnameToLower(const domainname * const in, domainname * const out) +{ + const mDNSu8 * const start = (const mDNSu8 * const)in; + mDNSu8 *ptr = (mDNSu8*)start; + while(*ptr) + { + mDNSu8 c = *ptr; + out->c[ptr-start] = *ptr; + ptr++; + for (; c; c--,ptr++) out->c[ptr-start] = mDNSIsUpperCase(*ptr) ? (*ptr - 'A' + 'a') : *ptr; + } + out->c[ptr-start] = *ptr; +} + +mDNSlocal mStatus DNSNameCompressionParseBytes(mDNS *const m, const mDNSu8 *const lhs, const mDNSu16 lhs_len, const mDNSu8 *const rhs, const mDNSu16 rhs_len, AuthRecord *rr) +{ + if (mDNS_LoggingEnabled) + { + LogInfo("%s", __func__); + LogInfo(" Static Bytes: (%d bytes)", compression_lhs - (mDNSu8*)&compression_base_msg); + PrintHex((mDNSu8*)&compression_base_msg, compression_lhs - (mDNSu8*)&compression_base_msg); + } + + mDNSu8 *ptr = compression_lhs; // pointer to the end of our fake packet + + // Check to make sure we're not going to go past the end of the DNSMessage data + // 7 = 2 for CLASS (-1 for our version) + 4 for TTL + 2 for RDLENGTH + if (ptr + lhs_len - 7 + rhs_len >= compression_limit) return mStatus_NoMemoryErr; + + // Copy the LHS onto our fake wire packet + mDNSPlatformMemCopy(ptr, lhs, lhs_len); + ptr += lhs_len - 1; + + // Check the 'fake packet' version number, to ensure that we know how to decompress this data + if (*ptr != compression_packet_v1) return mStatus_Incompatible; + + // two bytes of CLASS + ptr = putVal16(ptr, kDNSClass_IN | kDNSClass_UniqueRRSet); + + // four bytes of TTL + ptr = putVal32(ptr, 120); + + // Copy the RHS length into the RDLENGTH of our fake wire packet + ptr = putVal16(ptr, rhs_len); + + // Copy the RHS onto our fake wire packet + mDNSPlatformMemCopy(ptr, rhs, rhs_len); + ptr += rhs_len; + + if (mDNS_LoggingEnabled) + { + LogInfo(" Our Bytes (%d bytes): ", ptr - compression_lhs); + PrintHex(compression_lhs, ptr - compression_lhs); + } + + ptr = (mDNSu8 *) GetLargeResourceRecord(m, &compression_base_msg, compression_lhs, ptr, mDNSInterface_Any, kDNSRecordTypePacketAns, &m->rec); + if (!ptr || m->rec.r.resrec.RecordType == kDNSRecordTypePacketNegative) + { LogMsg("DNSNameCompressionParseBytes: failed to get large RR"); m->rec.r.resrec.RecordType = 0; return mStatus_UnknownErr; } + else LogInfo("DNSNameCompressionParseBytes: got rr: %s", CRDisplayString(m, &m->rec.r)); + + mDNS_SetupResourceRecord(rr, mDNSNULL, mDNSInterface_P2P, m->rec.r.resrec.rrtype, 7200, kDNSRecordTypeShared, AuthRecordP2P, FreeD2DARElemCallback, NULL); + AssignDomainName(&rr->namestorage, &m->rec.namestorage); + rr->resrec.rdlength = m->rec.r.resrec.rdlength; + rr->resrec.rdata->MaxRDLength = m->rec.r.resrec.rdlength; + mDNSPlatformMemCopy(rr->resrec.rdata->u.data, m->rec.r.resrec.rdata->u.data, m->rec.r.resrec.rdlength); + rr->resrec.namehash = DomainNameHashValue(rr->resrec.name); + SetNewRData(&rr->resrec, mDNSNULL, 0); // Sets rr->rdatahash for us + + m->rec.r.resrec.RecordType = 0; // Mark m->rec as no longer in use + + return mStatus_NoError; +} + +mDNSlocal mDNSu8 * DNSNameCompressionBuildLHS(const domainname* typeDomain, DNS_TypeValues qtype) +{ + mDNSu8 *ptr = putDomainNameAsLabels(&compression_base_msg, compression_lhs, compression_limit, typeDomain); + if (!ptr) return ptr; + *ptr = (qtype >> 8) & 0xff; + ptr += 1; + *ptr = qtype & 0xff; + ptr += 1; + *ptr = compression_packet_v1; + return ptr + 1; +} + +mDNSlocal mDNSu8 * DNSNameCompressionBuildRHS(mDNSu8 *start, const ResourceRecord *const resourceRecord) +{ + return putRData(&compression_base_msg, start, compression_limit, resourceRecord); +} + +#define PRINT_DEBUG_BYTES_LIMIT 64 // set limit on number of record bytes printed for debugging + +mDNSlocal void PrintHex(mDNSu8 *data, mDNSu16 len) +{ + mDNSu8 *end; + char buffer[49] = {0}; + char *bufend = buffer + sizeof(buffer); + + if (len > PRINT_DEBUG_BYTES_LIMIT) + { + LogInfo(" (limiting debug output to %d bytes)", PRINT_DEBUG_BYTES_LIMIT); + len = PRINT_DEBUG_BYTES_LIMIT; + } + end = data + len; + + while(data < end) + { + char *ptr = buffer; + for(; data < end && ptr < bufend-1; ptr+=3,data++) + mDNS_snprintf(ptr, bufend - ptr, "%02X ", *data); + LogInfo(" %s", buffer); + } +} + +mDNSlocal void PrintHelper(const char *const tag, mDNSu8 *lhs, mDNSu16 lhs_len, mDNSu8 *rhs, mDNSu16 rhs_len) +{ + if (!mDNS_LoggingEnabled) return; + + LogInfo("%s:", tag); + LogInfo(" LHS: (%d bytes)", lhs_len); + PrintHex(lhs, lhs_len); + + if (!rhs) return; + + LogInfo(" RHS: (%d bytes)", rhs_len); + PrintHex(rhs, rhs_len); +} + +mDNSlocal void FreeD2DARElemCallback(mDNS *const m, AuthRecord *const rr, mStatus result) +{ + (void)m; // unused + if (result == mStatus_MemFree) + { + D2DRecordListElem **ptr = &D2DRecords; + D2DRecordListElem *tmp; + while (*ptr && &(*ptr)->ar != rr) ptr = &(*ptr)->next; + if (!*ptr) { LogMsg("FreeD2DARElemCallback: Could not find in D2DRecords: %s", ARDisplayString(m, rr)); return; } + LogInfo("FreeD2DARElemCallback: Found in D2DRecords: %s", ARDisplayString(m, rr)); + tmp = *ptr; + *ptr = (*ptr)->next; + // Just because we stoppped browsing, doesn't mean we should tear down the PAN connection. + mDNSPlatformMemFree(tmp); + } +} + +mDNSexport void external_connection_release(const domainname *instance) +{ + (void) instance; + D2DRecordListElem *ptr = D2DRecords; + + for ( ; ptr ; ptr = ptr->next) + { + if ((ptr->ar.resrec.rrtype == kDNSServiceType_PTR) && + SameDomainName(&ptr->ar.rdatastorage.u.name, instance)) + { + LogInfo("external_connection_release: Calling D2DRelease(instanceHandle = %p, transportType = %d", + ptr->instanceHandle, ptr->transportType); + if (D2DRelease) D2DRelease(ptr->instanceHandle, ptr->transportType); + } + } +} + +mDNSlocal void xD2DClearCache(const domainname *regType, DNS_TypeValues qtype) +{ + D2DRecordListElem *ptr = D2DRecords; + for ( ; ptr ; ptr = ptr->next) + { + if ((ptr->ar.resrec.rrtype == qtype) && SameDomainName(&ptr->ar.namestorage, regType)) + { + mDNS_Deregister(&mDNSStorage, &ptr->ar); + LogInfo("xD2DClearCache: Clearing cache record and deregistering %s", ARDisplayString(&mDNSStorage, &ptr->ar)); + } + } +} + +mDNSlocal D2DBrowseListElem ** D2DFindInBrowseList(const domainname *const name, mDNSu16 type) +{ + D2DBrowseListElem **ptr = &D2DBrowseList; + + for ( ; *ptr; ptr = &(*ptr)->next) + if ((*ptr)->type == type && SameDomainName(&(*ptr)->name, name)) + break; + + return ptr; +} + +mDNSlocal unsigned int D2DBrowseListRefCount(const domainname *const name, mDNSu16 type) +{ + D2DBrowseListElem **ptr = D2DFindInBrowseList(name, type); + return *ptr ? (*ptr)->refCount : 0; +} + +mDNSlocal void D2DBrowseListRetain(const domainname *const name, mDNSu16 type) +{ + D2DBrowseListElem **ptr = D2DFindInBrowseList(name, type); + + if (!*ptr) + { + *ptr = mDNSPlatformMemAllocate(sizeof(**ptr)); + mDNSPlatformMemZero(*ptr, sizeof(**ptr)); + (*ptr)->type = type; + AssignDomainName(&(*ptr)->name, name); + } + (*ptr)->refCount += 1; + + LogInfo("D2DBrowseListRetain: %##s %s refcount now %u", (*ptr)->name.c, DNSTypeName((*ptr)->type), (*ptr)->refCount); +} + +mDNSlocal void D2DBrowseListRelease(const domainname *const name, mDNSu16 type) +{ + D2DBrowseListElem **ptr = D2DFindInBrowseList(name, type); + + if (!*ptr) { LogMsg("D2DBrowseListRelease: Didn't find %##s %s in list", name->c, DNSTypeName(type)); return; } + + (*ptr)->refCount -= 1; + + LogInfo("D2DBrowseListRelease: %##s %s refcount now %u", (*ptr)->name.c, DNSTypeName((*ptr)->type), (*ptr)->refCount); + + if (!(*ptr)->refCount) + { + D2DBrowseListElem *tmp = *ptr; + *ptr = (*ptr)->next; + mDNSPlatformMemFree(tmp); + } +} + +mDNSlocal mStatus xD2DParse(mDNS *const m, const mDNSu8 * const lhs, const mDNSu16 lhs_len, const mDNSu8 * const rhs, const mDNSu16 rhs_len, AuthRecord *rr) +{ + if (*(lhs + (lhs_len - 1)) == compression_packet_v1) + return DNSNameCompressionParseBytes(m, lhs, lhs_len, rhs, rhs_len, rr); + else + return mStatus_Incompatible; +} + +mDNSlocal void xD2DAddToCache(mDNS *const m, D2DStatus result, D2DServiceInstance instanceHandle, D2DTransportType transportType, const Byte *key, size_t keySize, const Byte *value, size_t valueSize) +{ + if (result == kD2DSuccess) + { + if ( key == NULL || value == NULL || keySize == 0 || valueSize == 0) { LogMsg("xD2DAddToCache: NULL Byte * passed in or length == 0"); return; } + + mStatus err; + D2DRecordListElem *ptr = mDNSPlatformMemAllocate(sizeof(D2DRecordListElem) + (valueSize < sizeof(RData) ? 0 : valueSize - sizeof(RData))); + + if (ptr == NULL) { LogMsg("xD2DAddToCache: memory allocation failure"); return; } + + err = xD2DParse(m, (const mDNSu8 * const)key, (const mDNSu16)keySize, (const mDNSu8 * const)value, (const mDNSu16)valueSize, &ptr->ar); + if (err) + { + LogMsg("xD2DAddToCache: xD2DParse returned error: %d", err); + PrintHelper(__func__, (mDNSu8 *)key, (mDNSu16)keySize, (mDNSu8 *)value, (mDNSu16)valueSize); + mDNSPlatformMemFree(ptr); + return; + } + err = mDNS_Register(m, &ptr->ar); + if (err) + { + LogMsg("xD2DAddToCache: mDNS_Register returned error %d for %s", err, ARDisplayString(m, &ptr->ar)); + mDNSPlatformMemFree(ptr); + return; + } + + LogInfo("xD2DAddToCache: mDNS_Register succeeded for %s", ARDisplayString(m, &ptr->ar)); + ptr->instanceHandle = instanceHandle; + ptr->transportType = transportType; + ptr->next = D2DRecords; + D2DRecords = ptr; + } + else + LogMsg("xD2DAddToCache: Unexpected result %d", result); +} + +mDNSlocal D2DRecordListElem * xD2DFindInList(mDNS *const m, const Byte *const key, const size_t keySize, const Byte *const value, const size_t valueSize) +{ + D2DRecordListElem *ptr = D2DRecords; + D2DRecordListElem *arptr; + + if ( key == NULL || value == NULL || keySize == 0 || valueSize == 0) { LogMsg("xD2DFindInList: NULL Byte * passed in or length == 0"); return NULL; } + + arptr = mDNSPlatformMemAllocate(sizeof(D2DRecordListElem) + (valueSize < sizeof(RData) ? 0 : valueSize - sizeof(RData))); + if (arptr == NULL) { LogMsg("xD2DFindInList: memory allocation failure"); return NULL; } + + if (xD2DParse(m, (const mDNSu8 *const)key, (const mDNSu16)keySize, (const mDNSu8 *const)value, (const mDNSu16)valueSize, &arptr->ar) != mStatus_NoError) + { + LogMsg("xD2DFindInList: xD2DParse failed for key: %p (%u) value: %p (%u)", key, keySize, value, valueSize); + mDNSPlatformMemFree(arptr); + return NULL; + } + + while (ptr) + { + if (IdenticalResourceRecord(&arptr->ar.resrec, &ptr->ar.resrec)) break; + ptr = ptr->next; + } + + if (!ptr) LogMsg("xD2DFindInList: Could not find in D2DRecords: %s", ARDisplayString(m, &arptr->ar)); + mDNSPlatformMemFree(arptr); + return ptr; +} + +mDNSlocal void xD2DRemoveFromCache(mDNS *const m, D2DStatus result, D2DServiceInstance instanceHandle, D2DTransportType transportType, const Byte *key, size_t keySize, const Byte *value, size_t valueSize) +{ + (void)transportType; // We don't care about this, yet. + (void)instanceHandle; // We don't care about this, yet. + + if (result == kD2DSuccess) + { + D2DRecordListElem *ptr = xD2DFindInList(m, key, keySize, value, valueSize); + if (ptr) + { + LogInfo("xD2DRemoveFromCache: Remove from cache: %s", ARDisplayString(m, &ptr->ar)); + mDNS_Deregister(m, &ptr->ar); + } + } + else + LogMsg("xD2DRemoveFromCache: Unexpected result %d", result); +} + +mDNSlocal void xD2DServiceResolved(mDNS *const m, D2DStatus result, D2DServiceInstance instanceHandle, D2DTransportType transportType, const Byte *key, size_t keySize, const Byte *value, size_t valueSize) +{ + (void)m; + (void)key; + (void)keySize; + (void)value; + (void)valueSize; + + if (result == kD2DSuccess) + { + LogInfo("xD2DServiceResolved: Starting up PAN connection for %p", instanceHandle); + if (D2DRetain) D2DRetain(instanceHandle, transportType); + } + else LogMsg("xD2DServiceResolved: Unexpected result %d", result); +} + +mDNSlocal void xD2DRetainHappened(mDNS *const m, D2DStatus result, D2DServiceInstance instanceHandle, D2DTransportType transportType, const Byte *key, size_t keySize, const Byte *value, size_t valueSize) +{ + (void)m; + (void)instanceHandle; + (void)transportType; + (void)key; + (void)keySize; + (void)value; + (void)valueSize; + + if (result == kD2DSuccess) LogInfo("xD2DRetainHappened: Opening up PAN connection for %p", instanceHandle); + else LogMsg("xD2DRetainHappened: Unexpected result %d", result); +} + +mDNSlocal void xD2DReleaseHappened(mDNS *const m, D2DStatus result, D2DServiceInstance instanceHandle, D2DTransportType transportType, const Byte *key, size_t keySize, const Byte *value, size_t valueSize) +{ + (void)m; + (void)instanceHandle; + (void)transportType; + (void)key; + (void)keySize; + (void)value; + (void)valueSize; + + if (result == kD2DSuccess) LogInfo("xD2DReleaseHappened: Closing PAN connection for %p", instanceHandle); + else LogMsg("xD2DReleaseHappened: Unexpected result %d", result); +} + +mDNSlocal void xD2DServiceCallback(D2DServiceEvent event, D2DStatus result, D2DServiceInstance instanceHandle, D2DTransportType transportType, const Byte *key, size_t keySize, const Byte *value, size_t valueSize, void *userData) +{ + mDNS *m = (mDNS *) userData; + const char *eventString = "unknown"; + + KQueueLock(m); + + if (keySize > 0xFFFF) LogMsg("xD2DServiceCallback: keySize too large: %u", keySize); + if (valueSize > 0xFFFF) LogMsg("xD2DServiceCallback: valueSize too large: %u", valueSize); + + switch (event) + { + case D2DServiceFound: + eventString = "D2DServiceFound"; + break; + case D2DServiceLost: + eventString = "D2DServiceLost"; + break; + case D2DServiceResolved: + eventString = "D2DServiceResolved"; + break; + case D2DServiceRetained: + eventString = "D2DServiceRetained"; + break; + case D2DServiceReleased: + eventString = "D2DServiceReleased"; + break; + default: + break; + } + + LogInfo("xD2DServiceCallback: event=%s result=%d instanceHandle=%p transportType=%d LHS=%p (%u) RHS=%p (%u) userData=%p", eventString, result, instanceHandle, transportType, key, keySize, value, valueSize, userData); + PrintHelper(__func__, (mDNSu8 *)key, (mDNSu16)keySize, (mDNSu8 *)value, (mDNSu16)valueSize); + + switch (event) + { + case D2DServiceFound: + xD2DAddToCache(m, result, instanceHandle, transportType, key, keySize, value, valueSize); + break; + case D2DServiceLost: + xD2DRemoveFromCache(m, result, instanceHandle, transportType, key, keySize, value, valueSize); + break; + case D2DServiceResolved: + xD2DServiceResolved(m, result, instanceHandle, transportType, key, keySize, value, valueSize); + break; + case D2DServiceRetained: + xD2DRetainHappened(m, result, instanceHandle, transportType, key, keySize, value, valueSize); + break; + case D2DServiceReleased: + xD2DReleaseHappened(m, result, instanceHandle, transportType, key, keySize, value, valueSize); + break; + default: + break; + } + + // Need to tickle the main kqueue loop to potentially handle records we removed or added. + KQueueUnlock(m, "xD2DServiceCallback"); +} + +// Map interface index and flags to a specific D2D transport type or D2DTransportMax if all plugins +// should be called. +// When D2DTransportMax is returned, if a specific transport should not be called, *excludedTransportType +// will be set to the excluded transport value, otherwise, it will be set to D2DTransportMax. +// If the return value is not D2DTransportMax, excludedTransportType is undefined. + +mDNSlocal D2DTransportType xD2DInterfaceToTransportType(mDNSInterfaceID InterfaceID, DNSServiceFlags flags, D2DTransportType * excludedTransportType) +{ + NetworkInterfaceInfoOSX *info; + + // Default exludes the D2DAWDLTransport when D2DTransportMax is returned. + *excludedTransportType = D2DAWDLTransport; + + // Call all D2D plugins when both kDNSServiceFlagsIncludeP2P and kDNSServiceFlagsIncludeAWDL are set. + if ((flags & kDNSServiceFlagsIncludeP2P) && (flags & kDNSServiceFlagsIncludeAWDL)) + { + LogInfo("xD2DInterfaceToTransportType: returning D2DTransportMax (including AWDL) since both kDNSServiceFlagsIncludeP2P and kDNSServiceFlagsIncludeAWDL are set"); + *excludedTransportType = D2DTransportMax; + return D2DTransportMax; + } + // Call all D2D plugins (exlcluding AWDL) when only kDNSServiceFlagsIncludeP2P is set. + else if (flags & kDNSServiceFlagsIncludeP2P) + { + LogInfo("xD2DInterfaceToTransportType: returning D2DTransportMax (excluding AWDL) since only kDNSServiceFlagsIncludeP2P is set"); + return D2DTransportMax; + } + // Call AWDL D2D plugin when only kDNSServiceFlagsIncludeAWDL is set. + else if (flags & kDNSServiceFlagsIncludeAWDL) + { + LogInfo("xD2DInterfaceToTransportType: returning D2DAWDLTransport since only kDNSServiceFlagsIncludeAWDL is set"); + return D2DAWDLTransport; + } + + if (InterfaceID == mDNSInterface_P2P) + { + LogInfo("xD2DInterfaceToTransportType: returning D2DTransportMax (excluding AWDL) for interface index mDNSInterface_P2P"); + return D2DTransportMax; + } + + // Compare to cached AWDL interface ID. + if (AWDLInterfaceID && (InterfaceID == AWDLInterfaceID)) + { + LogInfo("xD2DInterfaceToTransportType: returning D2DAWDLTransport for interface index %d", InterfaceID); + return D2DAWDLTransport; + } + + info = IfindexToInterfaceInfoOSX(&mDNSStorage, InterfaceID); + if (info == NULL) + { + LogInfo("xD2DInterfaceToTransportType: Invalid interface index %d", InterfaceID); + return D2DTransportMax; + } + + // Recognize AirDrop specific p2p* interface based on interface name. + if (strncmp(info->ifinfo.ifname, "p2p", 3) == 0) + { + LogInfo("xD2DInterfaceToTransportType: returning D2DWifiPeerToPeerTransport for interface index %d", InterfaceID); + return D2DWifiPeerToPeerTransport; + } + + // Currently there is no way to identify Bluetooth interface by name, + // since they use "en*" based name strings. + + LogInfo("xD2DInterfaceToTransportType: returning default D2DTransportMax for interface index %d", InterfaceID); + return D2DTransportMax; +} + +mDNSexport void external_start_browsing_for_service(mDNSInterfaceID InterfaceID, const domainname *const typeDomain, DNS_TypeValues qtype, DNSServiceFlags flags) +{ + domainname lower; + + if (qtype == kDNSServiceType_A || qtype == kDNSServiceType_AAAA) + { + LogInfo("external_start_browsing_for_service: ignoring address record"); + return; + } + + DomainnameToLower(typeDomain, &lower); + + if (!D2DBrowseListRefCount(&lower, qtype)) + { + D2DTransportType transportType, excludedTransport; + + LogInfo("external_start_browsing_for_service: Starting browse for: %##s %s", lower.c, DNSTypeName(qtype)); + mDNSu8 *end = DNSNameCompressionBuildLHS(&lower, qtype); + PrintHelper(__func__, compression_lhs, end - compression_lhs, mDNSNULL, 0); + + transportType = xD2DInterfaceToTransportType(InterfaceID, flags, & excludedTransport); + if (transportType == D2DTransportMax) + { + D2DTransportType i; + for (i = 0; i < D2DTransportMax; i++) + { + if (i == excludedTransport) continue; + if (D2DStartBrowsingForKeyOnTransport) D2DStartBrowsingForKeyOnTransport(compression_lhs, end - compression_lhs, i); + } + } + else + { + if (D2DStartBrowsingForKeyOnTransport) D2DStartBrowsingForKeyOnTransport(compression_lhs, end - compression_lhs, transportType); + } + } + D2DBrowseListRetain(&lower, qtype); +} + +mDNSexport void external_stop_browsing_for_service(mDNSInterfaceID InterfaceID, const domainname *const typeDomain, DNS_TypeValues qtype, DNSServiceFlags flags) +{ + domainname lower; + + if (qtype == kDNSServiceType_A || qtype == kDNSServiceType_AAAA) + { + LogInfo("external_stop_browsing_for_service: ignoring address record"); + return; + } + + DomainnameToLower(typeDomain, &lower); + + D2DBrowseListRelease(&lower, qtype); + if (!D2DBrowseListRefCount(&lower, qtype)) + { + D2DTransportType transportType, excludedTransport; + + LogInfo("external_stop_browsing_for_service: Stopping browse for: %##s %s", lower.c, DNSTypeName(qtype)); + mDNSu8 *end = DNSNameCompressionBuildLHS(&lower, qtype); + PrintHelper(__func__, compression_lhs, end - compression_lhs, mDNSNULL, 0); + + transportType = xD2DInterfaceToTransportType(InterfaceID, flags, & excludedTransport); + if (transportType == D2DTransportMax) + { + D2DTransportType i; + for (i = 0; i < D2DTransportMax; i++) + { + if (i == excludedTransport) continue; + if (D2DStopBrowsingForKeyOnTransport) D2DStopBrowsingForKeyOnTransport(compression_lhs, end - compression_lhs, i); + } + } + else + { + if (D2DStopBrowsingForKeyOnTransport) D2DStopBrowsingForKeyOnTransport(compression_lhs, end - compression_lhs, transportType); + } + + // The D2D driver may not generate the D2DServiceLost event for this key after + // the D2DStopBrowsingForKey*() call above. So, we flush the key from the D2D + // record cache now. + xD2DClearCache(&lower, qtype); + } +} + +mDNSexport void external_start_advertising_service(const ResourceRecord *const resourceRecord, DNSServiceFlags flags) +{ + domainname lower; + mDNSu8 *rhs = NULL; + mDNSu8 *end = NULL; + D2DTransportType transportType, excludedTransport; + DomainnameToLower(resourceRecord->name, &lower); + + LogInfo("external_start_advertising_service: %s", RRDisplayString(&mDNSStorage, resourceRecord)); + // For SRV records, update packet filter if p2p interface already exists, otherwise, + // if will be updated when we get the KEV_DL_IF_ATTACHED event for the interface. + if (resourceRecord->rrtype == kDNSType_SRV) + mDNSUpdatePacketFilter(NULL); + + rhs = DNSNameCompressionBuildLHS(&lower, resourceRecord->rrtype); + end = DNSNameCompressionBuildRHS(rhs, resourceRecord); + PrintHelper(__func__, compression_lhs, rhs - compression_lhs, rhs, end - rhs); + + transportType = xD2DInterfaceToTransportType(resourceRecord->InterfaceID, flags, & excludedTransport); + if (transportType == D2DTransportMax) + { + D2DTransportType i; + for (i = 0; i < D2DTransportMax; i++) + { + if (i == excludedTransport) continue; + if (D2DStartAdvertisingPairOnTransport) D2DStartAdvertisingPairOnTransport(compression_lhs, rhs - compression_lhs, rhs, end - rhs, i); + } + } + else + { + if (D2DStartAdvertisingPairOnTransport) D2DStartAdvertisingPairOnTransport(compression_lhs, rhs - compression_lhs, rhs, end - rhs, transportType); + } +} + +mDNSexport void external_stop_advertising_service(const ResourceRecord *const resourceRecord, DNSServiceFlags flags) +{ + domainname lower; + mDNSu8 *rhs = NULL; + mDNSu8 *end = NULL; + D2DTransportType transportType, excludedTransport; + DomainnameToLower(resourceRecord->name, &lower); + + LogInfo("external_stop_advertising_service: %s", RRDisplayString(&mDNSStorage, resourceRecord)); + + // For SRV records, update packet filter to to remove this port from list + if (resourceRecord->rrtype == kDNSType_SRV) + mDNSUpdatePacketFilter(resourceRecord); + + rhs = DNSNameCompressionBuildLHS(&lower, resourceRecord->rrtype); + end = DNSNameCompressionBuildRHS(rhs, resourceRecord); + PrintHelper(__func__, compression_lhs, rhs - compression_lhs, rhs, end - rhs); + + transportType = xD2DInterfaceToTransportType(resourceRecord->InterfaceID, flags, & excludedTransport); + if (transportType == D2DTransportMax) + { + D2DTransportType i; + for (i = 0; i < D2DTransportMax; i++) + { + if (i == excludedTransport) continue; + if (D2DStopAdvertisingPairOnTransport) D2DStopAdvertisingPairOnTransport(compression_lhs, rhs - compression_lhs, rhs, end - rhs, i); + } + } + else + { + if (D2DStopAdvertisingPairOnTransport) D2DStopAdvertisingPairOnTransport(compression_lhs, rhs - compression_lhs, rhs, end - rhs, transportType); + } +} + +mDNSexport void external_start_resolving_service(mDNSInterfaceID InterfaceID, const domainname *const fqdn, DNSServiceFlags flags) +{ + domainname lower; + mDNSu8 *rhs = NULL; + mDNSu8 *end = NULL; + mDNSBool AWDL_used = false; // whether AWDL was used for this resolve + D2DTransportType transportType, excludedTransport; + DomainnameToLower(SkipLeadingLabels(fqdn, 1), &lower); + + LogInfo("external_start_resolving_service: %##s", fqdn->c); + rhs = DNSNameCompressionBuildLHS(&lower, kDNSType_PTR); + end = putDomainNameAsLabels(&compression_base_msg, rhs, compression_limit, fqdn); + PrintHelper(__func__, compression_lhs, rhs - compression_lhs, rhs, end - rhs); + + transportType = xD2DInterfaceToTransportType(InterfaceID, flags, & excludedTransport); + if (transportType == D2DTransportMax) + { + // Resolving over all the transports, except for excludedTransport if set. + D2DTransportType i; + for (i = 0; i < D2DTransportMax; i++) + { + if (i == excludedTransport) continue; + if (D2DStartResolvingPairOnTransport) D2DStartResolvingPairOnTransport(compression_lhs, rhs - compression_lhs, rhs, end - rhs, i); + + if (i == D2DAWDLTransport) + AWDL_used = true; + } + } + else + { + // Resolving over one specific transport. + if (D2DStartResolvingPairOnTransport) D2DStartResolvingPairOnTransport(compression_lhs, rhs - compression_lhs, rhs, end - rhs, transportType); + + if (transportType == D2DAWDLTransport) + AWDL_used = true; + } + + // AWDL wants the SRV and TXT record queries communicated over the D2D interface. + // We only want these records going to AWDL, so use AWDLInterfaceID as the + // interface and don't set any other flags. + if (AWDL_used && AWDLInterfaceID) + { + LogInfo("external_start_resolving_service: browse for TXT and SRV over AWDL"); + external_start_browsing_for_service(AWDLInterfaceID, fqdn, kDNSType_TXT, NULL); + external_start_browsing_for_service(AWDLInterfaceID, fqdn, kDNSType_SRV, NULL); + } +} + +mDNSexport void external_stop_resolving_service(mDNSInterfaceID InterfaceID, const domainname *const fqdn, DNSServiceFlags flags) +{ + domainname lower; + mDNSu8 *rhs = NULL; + mDNSu8 *end = NULL; + mDNSBool AWDL_used = false; // whether AWDL was used for this resolve + D2DTransportType transportType, excludedTransport; + DomainnameToLower(SkipLeadingLabels(fqdn, 1), &lower); + + LogInfo("external_stop_resolving_service: %##s", fqdn->c); + rhs = DNSNameCompressionBuildLHS(&lower, kDNSType_PTR); + end = putDomainNameAsLabels(&compression_base_msg, rhs, compression_limit, fqdn); + PrintHelper(__func__, compression_lhs, rhs - compression_lhs, rhs, end - rhs); + + transportType = xD2DInterfaceToTransportType(InterfaceID, flags, & excludedTransport); + if (transportType == D2DTransportMax) + { + D2DTransportType i; + for (i = 0; i < D2DTransportMax; i++) + { + if (i == excludedTransport) continue; + if (D2DStopResolvingPairOnTransport) D2DStopResolvingPairOnTransport(compression_lhs, rhs - compression_lhs, rhs, end - rhs, i); + + if (i == D2DAWDLTransport) + AWDL_used = true; + } + } + else + { + if (D2DStopResolvingPairOnTransport) D2DStopResolvingPairOnTransport(compression_lhs, rhs - compression_lhs, rhs, end - rhs, transportType); + + if (transportType == D2DAWDLTransport) + AWDL_used = true; + } + + // AWDL wants the SRV and TXT record queries communicated over the D2D interface. + // We only want these records going to AWDL, so use AWDLInterfaceID as the + // interface and don't set any other flags. + if (AWDL_used && AWDLInterfaceID) + { + LogInfo("external_stop_resolving_service: stop browse for TXT and SRV on AWDL"); + external_stop_browsing_for_service(AWDLInterfaceID, fqdn, kDNSType_TXT, NULL); + external_stop_browsing_for_service(AWDLInterfaceID, fqdn, kDNSType_SRV, NULL); + } +} + +#elif APPLE_OSX_mDNSResponder + +mDNSexport void external_start_browsing_for_service(mDNS *const m, const domainname *const type, DNS_TypeValues qtype, DNSServiceFlags flags) { (void)m; (void)type; (void)qtype; (void)flags;} +mDNSexport void external_stop_browsing_for_service(mDNS *const m, const domainname *const type, DNS_TypeValues qtype, DNSServiceFlags flags) { (void)m; (void)type; (void)qtype; (void)flags;} +mDNSexport void external_start_advertising_service(const ResourceRecord *const resourceRecord, DNSServiceFlags flags) { (void)resourceRecord; (void)flags;} +mDNSexport void external_stop_advertising_service(const ResourceRecord *const resourceRecord, DNSServiceFlags flags) { (void)resourceRecord; (void)flags;} +mDNSexport void external_start_resolving_service(const domainname *const fqdn, DNSServiceFlags flags) { (void)fqdn; (void)flags;} +mDNSexport void external_stop_resolving_service(const domainname *const fqdn, DNSServiceFlags flags) { (void)fqdn; (void)flags;} + +#endif // ! NO_D2D + +// *************************************************************************** +// Functions + +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - +#pragma mark - Utility Functions +#endif + +// We only attempt to send and receive multicast packets on interfaces that are +// (a) flagged as multicast-capable +// (b) *not* flagged as point-to-point (e.g. modem) +// Typically point-to-point interfaces are modems (including mobile-phone pseudo-modems), and we don't want +// to run up the user's bill sending multicast traffic over a link where there's only a single device at the +// other end, and that device (e.g. a modem bank) is probably not answering Multicast DNS queries anyway. +#define MulticastInterface(i) (((i)->ifa_flags & IFF_MULTICAST) && !((i)->ifa_flags & IFF_POINTOPOINT)) + +mDNSexport void NotifyOfElusiveBug(const char *title, const char *msg) // Both strings are UTF-8 text +{ + static int notifyCount = 0; + if (notifyCount) return; + + // If we display our alert early in the boot process, then it vanishes once the desktop appears. + // To avoid this, we don't try to display alerts in the first three minutes after boot. + if ((mDNSu32)(mDNSPlatformRawTime()) < (mDNSu32)(mDNSPlatformOneSecond * 180)) return; + + // Unless ForceAlerts is defined, we only show these bug report alerts on machines that have a 17.x.x.x address + #if !ForceAlerts + { + // Determine if we're at Apple (17.*.*.*) + NetworkInterfaceInfoOSX *i; + for (i = mDNSStorage.p->InterfaceList; i; i = i->next) + if (i->ifinfo.ip.type == mDNSAddrType_IPv4 && i->ifinfo.ip.ip.v4.b[0] == 17) + break; + if (!i) return; // If not at Apple, don't show the alert + } + #endif + + LogMsg("%s", title); + LogMsg("%s", msg); + // Display a notification to the user + notifyCount++; + +#ifndef NO_CFUSERNOTIFICATION + mDNSNotify(title, msg); +#endif /* NO_CFUSERNOTIFICATION */ +} + +// Returns true if it is an AppleTV based hardware running iOS, false otherwise +mDNSlocal mDNSBool IsAppleTV(void) +{ +#if TARGET_OS_EMBEDDED + static mDNSBool sInitialized = mDNSfalse; + static mDNSBool sIsAppleTV = mDNSfalse; + CFStringRef deviceClass = NULL; + + if(!sInitialized) + { + deviceClass = (CFStringRef) MGCopyAnswer(kMGQDeviceClass, NULL); + if(deviceClass) + { + if(CFEqual(deviceClass, kMGDeviceClassAppleTV)) + sIsAppleTV = mDNStrue; + CFRelease(deviceClass); + } + sInitialized = mDNStrue; + } + return(sIsAppleTV); +#else + return mDNSfalse; +#endif // TARGET_OS_EMBEDDED +} + +mDNSlocal struct ifaddrs *myGetIfAddrs(int refresh) +{ + static struct ifaddrs *ifa = NULL; + + if (refresh && ifa) + { + freeifaddrs(ifa); + ifa = NULL; + } + + if (ifa == NULL) + getifaddrs(&ifa); + return ifa; +} + +mDNSlocal void DynamicStoreWrite(int key, const char* subkey, uintptr_t value, signed long valueCnt) +{ + CFStringRef sckey = NULL; + Boolean release_sckey = FALSE; + CFDataRef bytes = NULL; + CFPropertyListRef plist = NULL; + SCDynamicStoreRef store = NULL; + + switch ((enum mDNSDynamicStoreSetConfigKey)key) + { + case kmDNSMulticastConfig: + sckey = CFSTR("State:/Network/" kDNSServiceCompMulticastDNS); + break; + case kmDNSDynamicConfig: + sckey = CFSTR("State:/Network/DynamicDNS"); + break; + case kmDNSPrivateConfig: + sckey = CFSTR("State:/Network/" kDNSServiceCompPrivateDNS); + break; + case kmDNSBackToMyMacConfig: + sckey = CFSTR("State:/Network/BackToMyMac"); + break; + case kmDNSSleepProxyServersState: + { + CFMutableStringRef tmp = CFStringCreateMutable(kCFAllocatorDefault, 0); + CFStringAppend(tmp, CFSTR("State:/Network/Interface/")); + CFStringAppendCString(tmp, subkey, kCFStringEncodingUTF8); + CFStringAppend(tmp, CFSTR("/SleepProxyServers")); + sckey = CFStringCreateCopy(kCFAllocatorDefault, tmp); + release_sckey = TRUE; + CFRelease(tmp); + break; + } + case kmDNSDebugState: + sckey = CFSTR("State:/Network/mDNSResponder/DebugState"); + break; + default: + LogMsg("unrecognized key %d", key); + goto fin; + } + if (NULL == (bytes = CFDataCreateWithBytesNoCopy(NULL, (void *)value, + valueCnt, kCFAllocatorNull))) + { + LogMsg("CFDataCreateWithBytesNoCopy of value failed"); + goto fin; + } + if (NULL == (plist = CFPropertyListCreateFromXMLData(NULL, bytes, + kCFPropertyListImmutable, NULL))) + { + LogMsg("CFPropertyListCreateFromXMLData of bytes failed"); + goto fin; + } + CFRelease(bytes); + bytes = NULL; + if (NULL == (store = SCDynamicStoreCreate(NULL, + CFSTR(kmDNSResponderServName), NULL, NULL))) + { + LogMsg("SCDynamicStoreCreate failed: %s", SCErrorString(SCError())); + goto fin; + } + SCDynamicStoreSetValue(store, sckey, plist); + +fin: + if (NULL != bytes) + CFRelease(bytes); + if (NULL != plist) + CFRelease(plist); + if (NULL != store) + CFRelease(store); + if (release_sckey && sckey) + CFRelease(sckey); +} + +mDNSexport void mDNSDynamicStoreSetConfig(int key, const char *subkey, CFPropertyListRef value) +{ + CFPropertyListRef valueCopy; + char *subkeyCopy = NULL; + if (!value) + return; + + // We need to copy the key and value before we dispatch off the block below as the + // caller will free the memory once we return from this function. + valueCopy = CFPropertyListCreateDeepCopy(NULL, value, kCFPropertyListImmutable); + if (!valueCopy) + { + LogMsg("mDNSDynamicStoreSetConfig: ERROR valueCopy NULL"); + return; + } + if (subkey) + { + int len = strlen(subkey); + subkeyCopy = mDNSPlatformMemAllocate(len + 1); + if (!subkeyCopy) + { + LogMsg("mDNSDynamicStoreSetConfig: ERROR subkeyCopy NULL"); + return; + } + mDNSPlatformMemCopy(subkeyCopy, subkey, len); + subkeyCopy[len] = 0; + } + + dispatch_async(DynamicStoreQueue, ^{ + CFWriteStreamRef stream = NULL; + CFDataRef bytes = NULL; + CFStringRef error; + CFIndex ret; + + if (NULL == (stream = CFWriteStreamCreateWithAllocatedBuffers(NULL, NULL))) + { + LogMsg("mDNSDynamicStoreSetConfig : CFWriteStreamCreateWithAllocatedBuffers failed (Object creation failed)"); + goto END; + } + CFWriteStreamOpen(stream); + ret = CFPropertyListWriteToStream(valueCopy, stream, kCFPropertyListBinaryFormat_v1_0, &error); + if (ret == 0) + { + LogMsg("mDNSDynamicStoreSetConfig : CFPropertyListWriteToStream failed (Could not write property list to stream)"); + goto END; + } + if (NULL == (bytes = CFWriteStreamCopyProperty(stream, kCFStreamPropertyDataWritten))) + { + LogMsg("mDNSDynamicStoreSetConfig : CFWriteStreamCopyProperty failed (Object creation failed) "); + goto END; + } + CFWriteStreamClose(stream); + CFRelease(stream); + stream = NULL; + LogInfo("mDNSDynamicStoreSetConfig: key %d subkey %s", key, subkeyCopy); + DynamicStoreWrite(key, subkeyCopy ? subkeyCopy : "", (uintptr_t)CFDataGetBytePtr(bytes), CFDataGetLength(bytes)); + + END: + CFRelease(valueCopy); + if (NULL != stream) + { + CFWriteStreamClose(stream); + CFRelease(stream); + } + if (NULL != bytes) + CFRelease(bytes); + if (subkeyCopy) + mDNSPlatformMemFree(subkeyCopy); + }); +} + +// To match *either* a v4 or v6 instance of this interface name, pass AF_UNSPEC for type +mDNSlocal NetworkInterfaceInfoOSX *SearchForInterfaceByName(mDNS *const m, const char *ifname, int type) +{ + NetworkInterfaceInfoOSX *i; + for (i = m->p->InterfaceList; i; i = i->next) + if (i->Exists && !strcmp(i->ifinfo.ifname, ifname) && + ((type == AF_UNSPEC ) || + (type == AF_INET && i->ifinfo.ip.type == mDNSAddrType_IPv4) || + (type == AF_INET6 && i->ifinfo.ip.type == mDNSAddrType_IPv6))) return(i); + return(NULL); +} + +#if TARGET_OS_EMBEDDED +mDNSlocal SCPreferencesRef mDNSManagedPrefsGet(void) +{ + SCPreferencesRef smDNSManagedPrefs = NULL; + smDNSManagedPrefs = SCPreferencesCreate(kCFAllocatorDefault, CFSTR("mDNSManagedPrefs"), kmDNSResponderManagedPrefsID); + + return (smDNSManagedPrefs); +} + +mDNSlocal mDNSBool GetmDNSManagedPrefKeyVal(SCPreferencesRef prefs, CFStringRef key) +{ + mDNSBool val = mDNSfalse; + CFBooleanRef val_cf = NULL; + + if (prefs != NULL) + { + val_cf = SCPreferencesGetValue(prefs, key); + if (isA_CFBoolean(val_cf) != NULL) + val = CFBooleanGetValue(val_cf); //When mDNSResponder-Debug-profile is Installed + else + val = mDNSfalse; //When mDNSResponder-Debug-profile is Uninstalled + } + else + { + LogMsg("GetmDNSManagedPrefKeyVal: mDNSManagedPrefs are NULL!"); + val = mDNSfalse; + } + if (val_cf) + CFRelease(val_cf); + return (val); +} + +mDNSexport mDNSBool GetmDNSManagedPref(CFStringRef key) +{ + SCPreferencesRef managed = NULL; + mDNSBool ret_value; + + managed = mDNSManagedPrefsGet(); + ret_value = GetmDNSManagedPrefKeyVal(managed, key); + + if (managed) + CFRelease(managed); + return (ret_value); +} +#endif //TARGET_OS_EMBEDDED + +mDNSlocal int myIfIndexToName(u_short ifindex, char *name) +{ + struct ifaddrs *ifa; + for (ifa = myGetIfAddrs(0); ifa; ifa = ifa->ifa_next) + if (ifa->ifa_addr->sa_family == AF_LINK) + if (((struct sockaddr_dl*)ifa->ifa_addr)->sdl_index == ifindex) + { strlcpy(name, ifa->ifa_name, IF_NAMESIZE); return 0; } + return -1; +} + +mDNSexport NetworkInterfaceInfoOSX *IfindexToInterfaceInfoOSX(const mDNS *const m, mDNSInterfaceID ifindex) +{ + mDNSu32 scope_id = (mDNSu32)(uintptr_t)ifindex; + NetworkInterfaceInfoOSX *i; + + // Don't get tricked by inactive interfaces + for (i = m->p->InterfaceList; i; i = i->next) + if (i->Registered && i->scope_id == scope_id) return(i); + + return mDNSNULL; +} + +mDNSexport mDNSInterfaceID mDNSPlatformInterfaceIDfromInterfaceIndex(mDNS *const m, mDNSu32 ifindex) +{ + if (ifindex == kDNSServiceInterfaceIndexLocalOnly) return(mDNSInterface_LocalOnly); + if (ifindex == kDNSServiceInterfaceIndexP2P ) return(mDNSInterface_P2P); + if (ifindex == kDNSServiceInterfaceIndexAny ) return(mDNSNULL); + + NetworkInterfaceInfoOSX* ifi = IfindexToInterfaceInfoOSX(m, (mDNSInterfaceID)(uintptr_t)ifindex); + if (!ifi) + { + // Not found. Make sure our interface list is up to date, then try again. + LogInfo("mDNSPlatformInterfaceIDfromInterfaceIndex: InterfaceID for interface index %d not found; Updating interface list", ifindex); + mDNSMacOSXNetworkChanged(m); + ifi = IfindexToInterfaceInfoOSX(m, (mDNSInterfaceID)(uintptr_t)ifindex); + } + + if (!ifi) return(mDNSNULL); + + return(ifi->ifinfo.InterfaceID); +} + + +mDNSexport mDNSu32 mDNSPlatformInterfaceIndexfromInterfaceID(mDNS *const m, mDNSInterfaceID id, mDNSBool suppressNetworkChange) +{ + NetworkInterfaceInfoOSX *i; + if (id == mDNSInterface_LocalOnly) return(kDNSServiceInterfaceIndexLocalOnly); + if (id == mDNSInterface_P2P ) return(kDNSServiceInterfaceIndexP2P); + if (id == mDNSInterface_Any ) return(0); + + mDNSu32 scope_id = (mDNSu32)(uintptr_t)id; + + // Don't use i->Registered here, because we DO want to find inactive interfaces, which have no Registered set + for (i = m->p->InterfaceList; i; i = i->next) + if (i->scope_id == scope_id) return(i->scope_id); + + // If we are supposed to suppress network change, return "id" back + if (suppressNetworkChange) return scope_id; + + // Not found. Make sure our interface list is up to date, then try again. + LogInfo("Interface index for InterfaceID %p not found; Updating interface list", id); + mDNSMacOSXNetworkChanged(m); + for (i = m->p->InterfaceList; i; i = i->next) + if (i->scope_id == scope_id) return(i->scope_id); + + return(0); +} + +#if APPLE_OSX_mDNSResponder +mDNSexport void mDNSASLLog(uuid_t *uuid, const char *subdomain, const char *result, const char *signature, const char *fmt, ...) +{ + if (iOSVers) + return; // No ASL on iOS + + static char buffer[512]; + aslmsg asl_msg = asl_new(ASL_TYPE_MSG); + + if (!asl_msg) { LogMsg("mDNSASLLog: asl_new failed"); return; } + if (uuid) + { + char uuidStr[37]; + uuid_unparse(*uuid, uuidStr); + asl_set (asl_msg, "com.apple.message.uuid", uuidStr); + } + + static char domainBase[] = "com.apple.mDNSResponder.%s"; + mDNS_snprintf (buffer, sizeof(buffer), domainBase, subdomain); + asl_set (asl_msg, "com.apple.message.domain", buffer); + + if (result) asl_set(asl_msg, "com.apple.message.result", result); + if (signature) asl_set(asl_msg, "com.apple.message.signature", signature); + + va_list ptr; + va_start(ptr,fmt); + mDNS_vsnprintf(buffer, sizeof(buffer), fmt, ptr); + va_end(ptr); + + int old_filter = asl_set_filter(NULL,ASL_FILTER_MASK_UPTO(ASL_LEVEL_DEBUG)); + asl_log(NULL, asl_msg, ASL_LEVEL_DEBUG, "%s", buffer); + asl_set_filter(NULL, old_filter); + asl_free(asl_msg); +} + + +mDNSlocal void mDNSLogDNSSECStatistics(mDNS *const m) +{ + char buffer[16]; + + aslmsg aslmsg = asl_new(ASL_TYPE_MSG); + + // If we failed to allocate an aslmsg structure, keep accumulating + // the statistics and try again at the next log interval. + if (!aslmsg) + { + LogMsg("mDNSLogDNSSECStatistics: asl_new() failed!"); + return; + } + + asl_set(aslmsg,"com.apple.message.domain", "com.apple.mDNSResponder.DNSSECstatistics"); + + if (m->rrcache_totalused_unicast) + { + mDNS_snprintf(buffer, sizeof(buffer), "%u", (mDNSu32) ((unsigned long)(m->DNSSECStats.TotalMemUsed * 100))/m->rrcache_totalused_unicast); + } + else + { + LogMsg("mDNSLogDNSSECStatistics: unicast is zero"); + buffer[0] = 0; + } + asl_set(aslmsg,"com.apple.message.MemUsage", buffer); + + mDNS_snprintf(buffer, sizeof(buffer), "%u", m->DNSSECStats.Latency0); + asl_set(aslmsg,"com.apple.message.Latency0", buffer); + mDNS_snprintf(buffer, sizeof(buffer), "%u", m->DNSSECStats.Latency10); + asl_set(aslmsg,"com.apple.message.Latency10", buffer); + mDNS_snprintf(buffer, sizeof(buffer), "%u", m->DNSSECStats.Latency20); + asl_set(aslmsg,"com.apple.message.Latency20", buffer); + mDNS_snprintf(buffer, sizeof(buffer), "%u", m->DNSSECStats.Latency50); + asl_set(aslmsg,"com.apple.message.Latency50", buffer); + mDNS_snprintf(buffer, sizeof(buffer), "%u", m->DNSSECStats.Latency100); + asl_set(aslmsg,"com.apple.message.Latency100", buffer); + + mDNS_snprintf(buffer, sizeof(buffer), "%u", m->DNSSECStats.ExtraPackets0); + asl_set(aslmsg,"com.apple.message.ExtraPackets0", buffer); + mDNS_snprintf(buffer, sizeof(buffer), "%u", m->DNSSECStats.ExtraPackets3); + asl_set(aslmsg,"com.apple.message.ExtraPackets3", buffer); + mDNS_snprintf(buffer, sizeof(buffer), "%u", m->DNSSECStats.ExtraPackets7); + asl_set(aslmsg,"com.apple.message.ExtraPackets7", buffer); + mDNS_snprintf(buffer, sizeof(buffer), "%u", m->DNSSECStats.ExtraPackets10); + asl_set(aslmsg,"com.apple.message.ExtraPackets10", buffer); + + // Ignore IndeterminateStatus as we don't log them + mDNS_snprintf(buffer, sizeof(buffer), "%u", m->DNSSECStats.SecureStatus); + asl_set(aslmsg,"com.apple.message.SecureStatus", buffer); + mDNS_snprintf(buffer, sizeof(buffer), "%u", m->DNSSECStats.InsecureStatus); + asl_set(aslmsg,"com.apple.message.InsecureStatus", buffer); + mDNS_snprintf(buffer, sizeof(buffer), "%u", m->DNSSECStats.BogusStatus); + asl_set(aslmsg,"com.apple.message.BogusStatus", buffer); + mDNS_snprintf(buffer, sizeof(buffer), "%u", m->DNSSECStats.NoResponseStatus); + asl_set(aslmsg,"com.apple.message.NoResponseStatus", buffer); + + mDNS_snprintf(buffer, sizeof(buffer), "%u", m->DNSSECStats.NumProbesSent); + asl_set(aslmsg,"com.apple.message.NumProbesSent", buffer); + mDNS_snprintf(buffer, sizeof(buffer), "%u", m->DNSSECStats.MsgSize0); + asl_set(aslmsg,"com.apple.message.MsgSize0", buffer); + mDNS_snprintf(buffer, sizeof(buffer), "%u", m->DNSSECStats.MsgSize1); + asl_set(aslmsg,"com.apple.message.MsgSize1", buffer); + mDNS_snprintf(buffer, sizeof(buffer), "%u", m->DNSSECStats.MsgSize2); + asl_set(aslmsg,"com.apple.message.MsgSize2", buffer); + + asl_log(NULL, aslmsg, ASL_LEVEL_NOTICE, ""); + asl_free(aslmsg); +} + +// Calculate packets per hour given total packet count and interval in seconds. +// Cast one term of multiplication to (long) to use 64-bit arithmetic +// and avoid a potential 32-bit overflow prior to the division. +#define ONE_HOUR 3600 +#define PACKET_RATE(PACKETS, INTERVAL) (int)(((long) (PACKETS) * ONE_HOUR)/(INTERVAL)) + +// Put packet rate data in discrete buckets. +mDNSlocal int mDNSBucketData(int inputData, int interval) +{ + if (!interval) + { + LogMsg("mDNSBucketData: interval is zero!"); + return 0; + } + + int ratePerHour = PACKET_RATE(inputData, interval); + int bucket; + + if (ratePerHour == 0) + bucket = 0; + else if (ratePerHour <= 10) + bucket = 10; + else if (ratePerHour <= 100) + bucket = 100; + else if (ratePerHour <= 1000) + bucket = 1000; + else if (ratePerHour <= 5000) + bucket = 5000; + else if (ratePerHour <= 10000) + bucket = 10000; + else if (ratePerHour <= 50000) + bucket = 50000; + else if (ratePerHour <= 100000) + bucket = 100000; + else if (ratePerHour <= 250000) + bucket = 250000; + else if (ratePerHour <= 500000) + bucket = 500000; + else + bucket = 1000000; + + return bucket; +} + +mDNSlocal void mDNSLogBonjourStatistics(mDNS *const m) +{ + static mDNSs32 last_PktNum, last_MPktNum; + static mDNSs32 last_UnicastPacketsSent, last_MulticastPacketsSent; + static mDNSs32 last_RemoteSubnet; + + mDNSs32 interval; + char buffer[16]; + mDNSs32 inMulticast = m->MPktNum - last_MPktNum; + mDNSs32 inUnicast = m->PktNum - last_PktNum - inMulticast; + mDNSs32 outUnicast = m->UnicastPacketsSent - last_UnicastPacketsSent; + mDNSs32 outMulticast = m->MulticastPacketsSent - last_MulticastPacketsSent; + mDNSs32 remoteSubnet = m->RemoteSubnet - last_RemoteSubnet; + + + // save starting values for new interval + last_PktNum = m->PktNum; + last_MPktNum = m->MPktNum; + last_UnicastPacketsSent = m->UnicastPacketsSent; + last_MulticastPacketsSent = m->MulticastPacketsSent; + last_RemoteSubnet = m->RemoteSubnet; + + // Need a non-zero active time interval. + if (!m->ActiveStatTime) + return; + + // Round interval time to nearest hour boundary. Less then 30 minutes rounds to zero. + interval = (m->ActiveStatTime + ONE_HOUR/2)/ONE_HOUR; + + // Use a minimum of 30 minutes of awake time to calculate average packet rates. + // The rounded awake interval should not be greater than the rounded reporting + // interval. + if ((interval == 0) || (interval > (kDefaultNextStatsticsLogTime + ONE_HOUR/2)/ONE_HOUR)) + return; + + aslmsg aslmsg = asl_new(ASL_TYPE_MSG); + + if (!aslmsg) + { + LogMsg("mDNSLogBonjourStatistics: asl_new() failed!"); + return; + } + // log in MessageTracer format + asl_set(aslmsg,"com.apple.message.domain", "com.apple.mDNSResponder.statistics"); + + snprintf(buffer, sizeof(buffer), "%d", interval); + asl_set(aslmsg,"com.apple.message.interval", buffer); + + // log the packet rates as packets per hour + snprintf(buffer, sizeof(buffer), "%d", + mDNSBucketData(inUnicast, m->ActiveStatTime)); + asl_set(aslmsg,"com.apple.message.UnicastIn", buffer); + + snprintf(buffer, sizeof(buffer), "%d", + mDNSBucketData(inMulticast, m->ActiveStatTime)); + asl_set(aslmsg,"com.apple.message.MulticastIn", buffer); + + snprintf(buffer, sizeof(buffer), "%d", + mDNSBucketData(outUnicast, m->ActiveStatTime)); + asl_set(aslmsg,"com.apple.message.UnicastOut", buffer); + + snprintf(buffer, sizeof(buffer), "%d", + mDNSBucketData(outMulticast, m->ActiveStatTime)); + asl_set(aslmsg,"com.apple.message.MulticastOut", buffer); + + snprintf(buffer, sizeof(buffer), "%d", + mDNSBucketData(remoteSubnet, m->ActiveStatTime)); + asl_set(aslmsg,"com.apple.message.RemoteSubnet", buffer); + + asl_log(NULL, aslmsg, ASL_LEVEL_NOTICE, ""); + + asl_free(aslmsg); +} + +// Log multicast and unicast traffic statistics to MessageTracer on OSX +mDNSexport void mDNSLogStatistics(mDNS *const m) +{ + // MessageTracer only available on OSX + if (iOSVers) + return; + + mDNSs32 currentUTC = mDNSPlatformUTC(); + + // log runtime statistics + if ((currentUTC - m->NextStatLogTime) >= 0) + { + m->NextStatLogTime = currentUTC + kDefaultNextStatsticsLogTime; + // If StatStartTime is zero, it hasn't been reinitialized yet + // in the wakeup code path. + if (m->StatStartTime) + { + m->ActiveStatTime += currentUTC - m->StatStartTime; + } + + // Only log statistics if we have recorded some active time during + // this statistics interval. + if (m->ActiveStatTime) + { + mDNSLogBonjourStatistics(m); + mDNSLogDNSSECStatistics(m); + } + + // Start a new statistics gathering interval. + m->StatStartTime = currentUTC; + m->ActiveStatTime = 0; + } +} + +#endif // APPLE_OSX_mDNSResponder + +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - +#pragma mark - UDP & TCP send & receive +#endif + +mDNSlocal mDNSBool AddrRequiresPPPConnection(const struct sockaddr *addr) +{ + mDNSBool result = mDNSfalse; + SCNetworkConnectionFlags flags; + CFDataRef remote_addr; + CFMutableDictionaryRef options; + SCNetworkReachabilityRef ReachRef = NULL; + + options = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + remote_addr = CFDataCreate(NULL, (const UInt8 *)addr, addr->sa_len); + CFDictionarySetValue(options, kSCNetworkReachabilityOptionRemoteAddress, remote_addr); + CFDictionarySetValue(options, kSCNetworkReachabilityOptionServerBypass, kCFBooleanTrue); + ReachRef = SCNetworkReachabilityCreateWithOptions(kCFAllocatorDefault, options); + CFRelease(options); + CFRelease(remote_addr); + + if (!ReachRef) + { + LogMsg("ERROR: RequiresConnection - SCNetworkReachabilityCreateWithOptions"); + goto end; + } + if (!SCNetworkReachabilityGetFlags(ReachRef, &flags)) + { + LogMsg("ERROR: AddrRequiresPPPConnection - SCNetworkReachabilityGetFlags"); + goto end; + } + result = flags & kSCNetworkFlagsConnectionRequired; + +end: + if (ReachRef) + CFRelease(ReachRef); + return result; +} + +// Set traffic class for socket +mDNSlocal void setTrafficClass(int socketfd, mDNSBool useBackgroundTrafficClass) +{ + int traffic_class; + + if (useBackgroundTrafficClass) + traffic_class = SO_TC_BK_SYS; + else + traffic_class = SO_TC_CTL; + + (void) setsockopt(socketfd, SOL_SOCKET, SO_TRAFFIC_CLASS, (void *)&traffic_class, sizeof(traffic_class)); +} + +mDNSexport void mDNSPlatformSetDelegatePID(UDPSocket *src, const mDNSAddr *dst, DNSQuestion *q) +{ + if (src) + { + int s; + + if (dst->type == mDNSAddrType_IPv4) + { + s = src->ss.sktv4; + } + else + { + s = src->ss.sktv6; + } + + if (q->pid) + { + if (setsockopt(s, SOL_SOCKET, SO_DELEGATED, &q->pid, sizeof(q->pid)) == -1) + { + LogInfo("mDNSPlatformSetDelegatePID: Delegate PID failed %s for PID %d", strerror(errno), q->pid); + } + } + else + { + if (setsockopt(s, SOL_SOCKET, SO_DELEGATED_UUID, &q->uuid, sizeof(q->uuid)) == -1) + { + LogInfo("mDNSPlatformSetDelegatePID: Delegate UUID failed %s", strerror(errno)); + } + } + } +} + +// Note: If InterfaceID is NULL, it means, "send this packet through our anonymous unicast socket" +// Note: If InterfaceID is non-NULL it means, "send this packet through our port 5353 socket on the specified interface" +// OR send via our primary v4 unicast socket +// UPDATE: The UDPSocket *src parameter now allows the caller to specify the source socket +mDNSexport mStatus mDNSPlatformSendUDP(const mDNS *const m, const void *const msg, const mDNSu8 *const end, + mDNSInterfaceID InterfaceID, UDPSocket *src, const mDNSAddr *dst, + mDNSIPPort dstPort, mDNSBool useBackgroundTrafficClass) +{ + NetworkInterfaceInfoOSX *info = mDNSNULL; + struct sockaddr_storage to; + int s = -1, err; + mStatus result = mStatus_NoError; + + if (InterfaceID) + { + info = IfindexToInterfaceInfoOSX(m, InterfaceID); + if (info == NULL) + { + // We may not have registered interfaces with the "core" as we may not have + // seen any interface notifications yet. This typically happens during wakeup + // where we might try to send DNS requests (non-SuppressUnusable questions internal + // to mDNSResponder) before we receive network notifications. + LogInfo("mDNSPlatformSendUDP: Invalid interface index %p", InterfaceID); + return mStatus_BadParamErr; + } + } + + char *ifa_name = InterfaceID ? info->ifinfo.ifname : "unicast"; + + if (dst->type == mDNSAddrType_IPv4) + { + struct sockaddr_in *sin_to = (struct sockaddr_in*)&to; + sin_to->sin_len = sizeof(*sin_to); + sin_to->sin_family = AF_INET; + sin_to->sin_port = dstPort.NotAnInteger; + sin_to->sin_addr.s_addr = dst->ip.v4.NotAnInteger; + s = (src ? src->ss : m->p->permanentsockets).sktv4; + + if (info) // Specify outgoing interface + { + if (!mDNSAddrIsDNSMulticast(dst)) + { + #ifdef IP_BOUND_IF + if (info->scope_id == 0) + LogInfo("IP_BOUND_IF socket option not set -- info %p (%s) scope_id is zero", info, ifa_name); + else + setsockopt(s, IPPROTO_IP, IP_BOUND_IF, &info->scope_id, sizeof(info->scope_id)); + #else + { + static int displayed = 0; + if (displayed < 1000) + { + displayed++; + LogInfo("IP_BOUND_IF socket option not defined -- cannot specify interface for unicast packets"); + } + } + #endif + } + else + #ifdef IP_MULTICAST_IFINDEX + { + err = setsockopt(s, IPPROTO_IP, IP_MULTICAST_IFINDEX, &info->scope_id, sizeof(info->scope_id)); + // We get an error when we compile on a machine that supports this option and run the binary on + // a different machine that does not support it + if (err < 0) + { + if (errno != ENOPROTOOPT) LogInfo("mDNSPlatformSendUDP: setsockopt: IP_MUTLTICAST_IFINDEX returned %d", errno); + err = setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, &info->ifa_v4addr, sizeof(info->ifa_v4addr)); + if (err < 0 && !m->p->NetworkChanged) + LogMsg("setsockopt - IP_MULTICAST_IF error %.4a %d errno %d (%s)", &info->ifa_v4addr, err, errno, strerror(errno)); + } + } + #else + { + err = setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, &info->ifa_v4addr, sizeof(info->ifa_v4addr)); + if (err < 0 && !m->p->NetworkChanged) + LogMsg("setsockopt - IP_MULTICAST_IF error %.4a %d errno %d (%s)", &info->ifa_v4addr, err, errno, strerror(errno)); + + } + #endif + } + } + + else if (dst->type == mDNSAddrType_IPv6) + { + struct sockaddr_in6 *sin6_to = (struct sockaddr_in6*)&to; + sin6_to->sin6_len = sizeof(*sin6_to); + sin6_to->sin6_family = AF_INET6; + sin6_to->sin6_port = dstPort.NotAnInteger; + sin6_to->sin6_flowinfo = 0; + sin6_to->sin6_addr = *(struct in6_addr*)&dst->ip.v6; + sin6_to->sin6_scope_id = info ? info->scope_id : 0; + s = (src ? src->ss : m->p->permanentsockets).sktv6; + if (info && mDNSAddrIsDNSMulticast(dst)) // Specify outgoing interface + { + err = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_IF, &info->scope_id, sizeof(info->scope_id)); + if (err < 0) + { + char name[IFNAMSIZ]; + if (if_indextoname(info->scope_id, name) != NULL) + LogMsg("setsockopt - IPV6_MULTICAST_IF error %d errno %d (%s)", err, errno, strerror(errno)); + else + LogInfo("setsockopt - IPV6_MUTLICAST_IF scopeid %d, not a valid interface", info->scope_id); + } + } + } + + else + { + LogMsg("mDNSPlatformSendUDP: dst is not an IPv4 or IPv6 address!"); +#if ForceAlerts + *(long*)0 = 0; +#endif + return mStatus_BadParamErr; + } + + if (s >= 0) + verbosedebugf("mDNSPlatformSendUDP: sending on InterfaceID %p %5s/%ld to %#a:%d skt %d", + InterfaceID, ifa_name, dst->type, dst, mDNSVal16(dstPort), s); + else + verbosedebugf("mDNSPlatformSendUDP: NOT sending on InterfaceID %p %5s/%ld (socket of this type not available)", + InterfaceID, ifa_name, dst->type, dst, mDNSVal16(dstPort)); + + // Note: When sending, mDNSCore may often ask us to send both a v4 multicast packet and then a v6 multicast packet + // If we don't have the corresponding type of socket available, then return mStatus_Invalid + if (s < 0) return(mStatus_Invalid); + + // switch to background traffic class for this message if requested + if (useBackgroundTrafficClass) + setTrafficClass(s, useBackgroundTrafficClass); + + err = sendto(s, msg, (UInt8*)end - (UInt8*)msg, 0, (struct sockaddr *)&to, to.ss_len); + + // set traffic class back to default value + if (useBackgroundTrafficClass) + setTrafficClass(s, mDNSfalse); + + if (err < 0) + { + static int MessageCount = 0; + // Don't report EHOSTDOWN (i.e. ARP failure), ENETDOWN, or no route to host for unicast destinations + if (!mDNSAddressIsAllDNSLinkGroup(dst)) + if (errno == EHOSTDOWN || errno == ENETDOWN || errno == EHOSTUNREACH || errno == ENETUNREACH) return(mStatus_TransientErr); + // Don't report EHOSTUNREACH in the first three minutes after boot + // This is because mDNSResponder intentionally starts up early in the boot process (See <rdar://problem/3409090>) + // but this means that sometimes it starts before configd has finished setting up the multicast routing entries. + if (errno == EHOSTUNREACH && (mDNSu32)(mDNSPlatformRawTime()) < (mDNSu32)(mDNSPlatformOneSecond * 180)) return(mStatus_TransientErr); + // Don't report EADDRNOTAVAIL ("Can't assign requested address") if we're in the middle of a network configuration change + if (errno == EADDRNOTAVAIL && m->p->NetworkChanged) return(mStatus_TransientErr); + if (errno == EHOSTUNREACH || errno == EADDRNOTAVAIL || errno == ENETDOWN) + LogInfo("mDNSPlatformSendUDP sendto(%d) failed to send packet on InterfaceID %p %5s/%d to %#a:%d skt %d error %d errno %d (%s) %lu", + s, InterfaceID, ifa_name, dst->type, dst, mDNSVal16(dstPort), s, err, errno, strerror(errno), (mDNSu32)(m->timenow)); + else + { + MessageCount++; + if (MessageCount < 50) // Cap and ensure NO spamming of LogMsgs + LogMsg("mDNSPlatformSendUDP: sendto(%d) failed to send packet on InterfaceID %p %5s/%d to %#a:%d skt %d error %d errno %d (%s) %lu MessageCount is %d", + s, InterfaceID, ifa_name, dst->type, dst, mDNSVal16(dstPort), s, err, errno, strerror(errno), (mDNSu32)(m->timenow), MessageCount); + else // If logging is enabled, remove the cap and log aggressively + LogInfo("mDNSPlatformSendUDP: sendto(%d) failed to send packet on InterfaceID %p %5s/%d to %#a:%d skt %d error %d errno %d (%s) %lu MessageCount is %d", + s, InterfaceID, ifa_name, dst->type, dst, mDNSVal16(dstPort), s, err, errno, strerror(errno), (mDNSu32)(m->timenow), MessageCount); + } + + result = mStatus_UnknownErr; + } + +#ifdef IP_BOUND_IF + if (dst->type == mDNSAddrType_IPv4 && info && !mDNSAddrIsDNSMulticast(dst)) + { + static const mDNSu32 ifindex = 0; + setsockopt(s, IPPROTO_IP, IP_BOUND_IF, &ifindex, sizeof(ifindex)); + } +#endif + + return(result); +} + +mDNSexport ssize_t myrecvfrom(const int s, void *const buffer, const size_t max, + struct sockaddr *const from, size_t *const fromlen, mDNSAddr *dstaddr, char ifname[IF_NAMESIZE], mDNSu8 *ttl) +{ + static unsigned int numLogMessages = 0; + struct iovec databuffers = { (char *)buffer, max }; + struct msghdr msg; + ssize_t n; + struct cmsghdr *cmPtr; + char ancillary[1024]; + + *ttl = 255; // If kernel fails to provide TTL data (e.g. Jaguar doesn't) then assume the TTL was 255 as it should be + + // Set up the message + msg.msg_name = (caddr_t)from; + msg.msg_namelen = *fromlen; + msg.msg_iov = &databuffers; + msg.msg_iovlen = 1; + msg.msg_control = (caddr_t)&ancillary; + msg.msg_controllen = sizeof(ancillary); + msg.msg_flags = 0; + + // Receive the data + n = recvmsg(s, &msg, 0); + if (n<0) + { + if (errno != EWOULDBLOCK && numLogMessages++ < 100) LogMsg("mDNSMacOSX.c: recvmsg(%d) returned error %d errno %d", s, n, errno); + return(-1); + } + if (msg.msg_controllen < (int)sizeof(struct cmsghdr)) + { + if (numLogMessages++ < 100) LogMsg("mDNSMacOSX.c: recvmsg(%d) returned %d msg.msg_controllen %d < sizeof(struct cmsghdr) %lu, errno %d", + s, n, msg.msg_controllen, sizeof(struct cmsghdr), errno); + return(-1); + } + if (msg.msg_flags & MSG_CTRUNC) + { + if (numLogMessages++ < 100) LogMsg("mDNSMacOSX.c: recvmsg(%d) msg.msg_flags & MSG_CTRUNC", s); + return(-1); + } + + *fromlen = msg.msg_namelen; + + // Parse each option out of the ancillary data. + for (cmPtr = CMSG_FIRSTHDR(&msg); cmPtr; cmPtr = CMSG_NXTHDR(&msg, cmPtr)) + { + // debugf("myrecvfrom cmsg_level %d cmsg_type %d", cmPtr->cmsg_level, cmPtr->cmsg_type); + if (cmPtr->cmsg_level == IPPROTO_IP && cmPtr->cmsg_type == IP_RECVDSTADDR) + { + dstaddr->type = mDNSAddrType_IPv4; + dstaddr->ip.v4 = *(mDNSv4Addr*)CMSG_DATA(cmPtr); + //LogMsg("mDNSMacOSX.c: recvmsg IP_RECVDSTADDR %.4a", &dstaddr->ip.v4); + } + if (cmPtr->cmsg_level == IPPROTO_IP && cmPtr->cmsg_type == IP_RECVIF) + { + struct sockaddr_dl *sdl = (struct sockaddr_dl *)CMSG_DATA(cmPtr); + if (sdl->sdl_nlen < IF_NAMESIZE) + { + mDNSPlatformMemCopy(ifname, sdl->sdl_data, sdl->sdl_nlen); + ifname[sdl->sdl_nlen] = 0; + // debugf("IP_RECVIF sdl_index %d, sdl_data %s len %d", sdl->sdl_index, ifname, sdl->sdl_nlen); + } + } + if (cmPtr->cmsg_level == IPPROTO_IP && cmPtr->cmsg_type == IP_RECVTTL) + *ttl = *(u_char*)CMSG_DATA(cmPtr); + if (cmPtr->cmsg_level == IPPROTO_IPV6 && cmPtr->cmsg_type == IPV6_PKTINFO) + { + struct in6_pktinfo *ip6_info = (struct in6_pktinfo*)CMSG_DATA(cmPtr); + dstaddr->type = mDNSAddrType_IPv6; + dstaddr->ip.v6 = *(mDNSv6Addr*)&ip6_info->ipi6_addr; + myIfIndexToName(ip6_info->ipi6_ifindex, ifname); + } + if (cmPtr->cmsg_level == IPPROTO_IPV6 && cmPtr->cmsg_type == IPV6_HOPLIMIT) + *ttl = *(int*)CMSG_DATA(cmPtr); + } + + return(n); +} + +mDNSlocal mDNSInterfaceID FindMyInterface(mDNS *const m, const mDNSAddr *addr) +{ + NetworkInterfaceInfo *intf; + + if (addr->type == mDNSAddrType_IPv4) + { + for (intf = m->HostInterfaces; intf; intf = intf->next) + { + if (intf->ip.type == addr->type && intf->McastTxRx) + { + if ((intf->ip.ip.v4.NotAnInteger ^ addr->ip.v4.NotAnInteger) == 0) + { + return(intf->InterfaceID); + } + } + } + } + + if (addr->type == mDNSAddrType_IPv6) + { + for (intf = m->HostInterfaces; intf; intf = intf->next) + { + if (intf->ip.type == addr->type && intf->McastTxRx) + { + if (((intf->ip.ip.v6.l[0] ^ addr->ip.v6.l[0]) == 0) && + ((intf->ip.ip.v6.l[1] ^ addr->ip.v6.l[1]) == 0) && + ((intf->ip.ip.v6.l[2] ^ addr->ip.v6.l[2]) == 0) && + (((intf->ip.ip.v6.l[3] ^ addr->ip.v6.l[3]) == 0))) + { + return(intf->InterfaceID); + } + } + } + } + return(mDNSInterface_Any); +} + +mDNSexport mDNSBool mDNSPlatformPeekUDP(mDNS *const m, UDPSocket *src) +{ + // We should have a DNSMessage header followed by the question and an answer + // which also includes a CNAME (that's when this function is called). To keep it + // simple, we expect at least the size of DNSMessage header(12) and size of "A" + // record (14 bytes). + char buffer[26]; + int ret; + + (void) m; + + if (!src) + return mDNSfalse; + + ret = recv(src->ss.sktv4, buffer, sizeof(buffer), MSG_PEEK); + if (ret > 0) + return mDNStrue; + else + return mDNSfalse; +} + +mDNSexport void myKQSocketCallBack(int s1, short filter, void *context) +{ + KQSocketSet *const ss = (KQSocketSet *)context; + mDNS *const m = ss->m; + int err = 0, count = 0, closed = 0; + + if (filter != EVFILT_READ) + LogMsg("myKQSocketCallBack: Why is filter %d not EVFILT_READ (%d)?", filter, EVFILT_READ); + + if (s1 != ss->sktv4 && s1 != ss->sktv6) + { + LogMsg("myKQSocketCallBack: native socket %d", s1); + LogMsg("myKQSocketCallBack: sktv4 %d sktv6 %d", ss->sktv4, ss->sktv6); + } + + while (!closed) + { + mDNSAddr senderAddr, destAddr; + mDNSIPPort senderPort; + struct sockaddr_storage from; + size_t fromlen = sizeof(from); + char packetifname[IF_NAMESIZE] = ""; + mDNSu8 ttl; + err = myrecvfrom(s1, &m->imsg, sizeof(m->imsg), (struct sockaddr *)&from, &fromlen, &destAddr, packetifname, &ttl); + if (err < 0) break; + + count++; + if (from.ss_family == AF_INET) + { + struct sockaddr_in *s = (struct sockaddr_in*)&from; + senderAddr.type = mDNSAddrType_IPv4; + senderAddr.ip.v4.NotAnInteger = s->sin_addr.s_addr; + senderPort.NotAnInteger = s->sin_port; + //LogInfo("myKQSocketCallBack received IPv4 packet from %#-15a to %#-15a on skt %d %s", &senderAddr, &destAddr, s1, packetifname); + } + else if (from.ss_family == AF_INET6) + { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)&from; + senderAddr.type = mDNSAddrType_IPv6; + senderAddr.ip.v6 = *(mDNSv6Addr*)&sin6->sin6_addr; + senderPort.NotAnInteger = sin6->sin6_port; + //LogInfo("myKQSocketCallBack received IPv6 packet from %#-15a to %#-15a on skt %d %s", &senderAddr, &destAddr, s1, packetifname); + } + else + { + LogMsg("myKQSocketCallBack from is unknown address family %d", from.ss_family); + return; + } + + // Note: When handling multiple packets in a batch, MUST reset InterfaceID before handling each packet + mDNSInterfaceID InterfaceID = mDNSNULL; + NetworkInterfaceInfoOSX *intf = m->p->InterfaceList; + while (intf) + { + if (intf->Exists && !strcmp(intf->ifinfo.ifname, packetifname)) + break; + intf = intf->next; + } + + // When going to sleep we deregister all our interfaces, but if the machine + // takes a few seconds to sleep we may continue to receive multicasts + // during that time, which would confuse mDNSCoreReceive, because as far + // as it's concerned, we should have no active interfaces any more. + // Hence we ignore multicasts for which we can find no matching InterfaceID. + if (intf) + InterfaceID = intf->ifinfo.InterfaceID; + else if (mDNSAddrIsDNSMulticast(&destAddr)) + continue; + + if (!InterfaceID) + { + InterfaceID = FindMyInterface(m, &destAddr); + } + +// LogMsg("myKQSocketCallBack got packet from %#a to %#a on interface %#a/%s", +// &senderAddr, &destAddr, &ss->info->ifinfo.ip, ss->info->ifinfo.ifname); + + // mDNSCoreReceive may close the socket we're reading from. We must break out of our + // loop when that happens, or we may try to read from an invalid FD. We do this by + // setting the closeFlag pointer in the socketset, so CloseSocketSet can inform us + // if it closes the socketset. + ss->closeFlag = &closed; + + if (ss->proxy) + { + m->p->UDPProxyCallback(m, &m->p->UDPProxy, (unsigned char *)&m->imsg, (unsigned char*)&m->imsg + err, &senderAddr, + senderPort, &destAddr, ss->port, InterfaceID, NULL); + } + else + { + mDNSCoreReceive(m, &m->imsg, (unsigned char*)&m->imsg + err, &senderAddr, senderPort, &destAddr, ss->port, InterfaceID); + } + + // if we didn't close, we can safely dereference the socketset, and should to + // reset the closeFlag, since it points to something on the stack + if (!closed) ss->closeFlag = mDNSNULL; + } + + if (err < 0 && (errno != EWOULDBLOCK || count == 0)) + { + // Something is busted here. + // kqueue says there is a packet, but myrecvfrom says there is not. + // Try calling select() to get another opinion. + // Find out about other socket parameter that can help understand why select() says the socket is ready for read + // All of this is racy, as data may have arrived after the call to select() + static unsigned int numLogMessages = 0; + int save_errno = errno; + int so_error = -1; + int so_nread = -1; + int fionread = -1; + socklen_t solen = sizeof(int); + fd_set readfds; + struct timeval timeout; + int selectresult; + FD_ZERO(&readfds); + FD_SET(s1, &readfds); + timeout.tv_sec = 0; + timeout.tv_usec = 0; + selectresult = select(s1+1, &readfds, NULL, NULL, &timeout); + if (getsockopt(s1, SOL_SOCKET, SO_ERROR, &so_error, &solen) == -1) + LogMsg("myKQSocketCallBack getsockopt(SO_ERROR) error %d", errno); + if (getsockopt(s1, SOL_SOCKET, SO_NREAD, &so_nread, &solen) == -1) + LogMsg("myKQSocketCallBack getsockopt(SO_NREAD) error %d", errno); + if (ioctl(s1, FIONREAD, &fionread) == -1) + LogMsg("myKQSocketCallBack ioctl(FIONREAD) error %d", errno); + if (numLogMessages++ < 100) + LogMsg("myKQSocketCallBack recvfrom skt %d error %d errno %d (%s) select %d (%spackets waiting) so_error %d so_nread %d fionread %d count %d", + s1, err, save_errno, strerror(save_errno), selectresult, FD_ISSET(s1, &readfds) ? "" : "*NO* ", so_error, so_nread, fionread, count); + if (numLogMessages > 5) + NotifyOfElusiveBug("Flaw in Kernel (select/recvfrom mismatch)", + "Congratulations, you've reproduced an elusive bug.\r" + "Please contact the current assignee of <rdar://problem/3375328>.\r" + "Alternatively, you can send email to radar-3387020@group.apple.com. (Note number is different.)\r" + "If possible, please leave your machine undisturbed so that someone can come to investigate the problem."); + + sleep(1); // After logging this error, rate limit so we don't flood syslog + } +} + +mDNSlocal void doTcpSocketCallback(TCPSocket *sock) +{ + mDNSBool c = !sock->connected; + sock->connected = mDNStrue; + sock->callback(sock, sock->context, c, sock->err); + // Note: the callback may call CloseConnection here, which frees the context structure! +} + +#ifndef NO_SECURITYFRAMEWORK + +mDNSlocal OSStatus tlsWriteSock(SSLConnectionRef connection, const void *data, size_t *dataLength) +{ + int ret = send(((TCPSocket *)connection)->fd, data, *dataLength, 0); + if (ret >= 0 && (size_t)ret < *dataLength) { *dataLength = ret; return(errSSLWouldBlock); } + if (ret >= 0) { *dataLength = ret; return(noErr); } + *dataLength = 0; + if (errno == EAGAIN ) return(errSSLWouldBlock); + if (errno == ENOENT ) return(errSSLClosedGraceful); + if (errno == EPIPE || errno == ECONNRESET) return(errSSLClosedAbort); + LogMsg("ERROR: tlsWriteSock: %d error %d (%s)\n", ((TCPSocket *)connection)->fd, errno, strerror(errno)); + return(errSSLClosedAbort); +} + +mDNSlocal OSStatus tlsReadSock(SSLConnectionRef connection, void *data, size_t *dataLength) +{ + int ret = recv(((TCPSocket *)connection)->fd, data, *dataLength, 0); + if (ret > 0 && (size_t)ret < *dataLength) { *dataLength = ret; return(errSSLWouldBlock); } + if (ret > 0) { *dataLength = ret; return(noErr); } + *dataLength = 0; + if (ret == 0 || errno == ENOENT ) return(errSSLClosedGraceful); + if ( errno == EAGAIN ) return(errSSLWouldBlock); + if ( errno == ECONNRESET) return(errSSLClosedAbort); + LogMsg("ERROR: tlsSockRead: error %d (%s)\n", errno, strerror(errno)); + return(errSSLClosedAbort); +} + +mDNSlocal OSStatus tlsSetupSock(TCPSocket *sock, SSLProtocolSide pside, SSLConnectionType ctype) +{ + char domname_cstr[MAX_ESCAPED_DOMAIN_NAME]; + + sock->tlsContext = SSLCreateContext(kCFAllocatorDefault, pside, ctype); + if (!sock->tlsContext) + { + LogMsg("ERROR: tlsSetupSock: SSLCreateContext failed"); + return(mStatus_UnknownErr); + } + + mStatus err = SSLSetIOFuncs(sock->tlsContext, tlsReadSock, tlsWriteSock); + if (err) + { + LogMsg("ERROR: tlsSetupSock: SSLSetIOFuncs failed with error code: %d", err); + goto fail; + } + + err = SSLSetConnection(sock->tlsContext, (SSLConnectionRef) sock); + if (err) + { + LogMsg("ERROR: tlsSetupSock: SSLSetConnection failed with error code: %d", err); + goto fail; + } + + // Instead of listing all the acceptable ciphers, we just disable the bad ciphers. It does not disable + // all the bad ciphers like RC4_MD5, but it assumes that the servers don't offer them. + err = SSLSetAllowAnonymousCiphers(sock->tlsContext, 0); + if (err) + { + LogMsg("ERROR: tlsSetupSock: SSLSetAllowAnonymousCiphers failed with error code: %d", err); + goto fail; + } + + // We already checked for NULL in hostname and this should never happen. Hence, returning -1 + // (error not in OSStatus space) is okay. + if (!sock->hostname.c[0]) + { + LogMsg("ERROR: tlsSetupSock: hostname NULL"); + err = -1; + goto fail; + } + + ConvertDomainNameToCString(&sock->hostname, domname_cstr); + err = SSLSetPeerDomainName(sock->tlsContext, domname_cstr, strlen(domname_cstr)); + if (err) + { + LogMsg("ERROR: tlsSetupSock: SSLSetPeerDomainname: %s failed with error code: %d", domname_cstr, err); + goto fail; + } + + return(err); + +fail: + if (sock->tlsContext) + CFRelease(sock->tlsContext); + return(err); +} + +#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM +mDNSlocal void doSSLHandshake(TCPSocket *sock) +{ + mStatus err = SSLHandshake(sock->tlsContext); + + //Can't have multiple threads in mDNS core. When MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM is + //defined, KQueueLock is a noop. Hence we need to serialize here + // + //NOTE: We just can't serialize doTcpSocketCallback alone on the main queue. + //We need the rest of the logic also. Otherwise, we can enable the READ + //events below, dispatch a doTcpSocketCallback on the main queue. Assume it is + //ConnFailed which means we are going to free the tcpInfo. While it + //is waiting to be dispatched, another read event can come into tcpKQSocketCallback + //and potentially call doTCPCallback with error which can close the fd and free the + //tcpInfo. Later when the thread gets dispatched it will crash because the tcpInfo + //is already freed. + + dispatch_async(dispatch_get_main_queue(), ^{ + + LogInfo("doSSLHandshake %p: got lock", sock); // Log *after* we get the lock + + if (sock->handshake == handshake_to_be_closed) + { + LogInfo("SSLHandshake completed after close"); + mDNSPlatformTCPCloseConnection(sock); + } + else + { + if (sock->fd != -1) KQueueSet(sock->fd, EV_ADD, EVFILT_READ, sock->kqEntry); + else LogMsg("doSSLHandshake: sock->fd is -1"); + + if (err == errSSLWouldBlock) + sock->handshake = handshake_required; + else + { + if (err) + { + LogMsg("SSLHandshake failed: %d%s", err, err == errSSLPeerInternalError ? " (server busy)" : ""); + CFRelease(sock->tlsContext); + sock->tlsContext = NULL; + } + + sock->err = err ? mStatus_ConnFailed : 0; + sock->handshake = handshake_completed; + + LogInfo("doSSLHandshake: %p calling doTcpSocketCallback fd %d", sock, sock->fd); + doTcpSocketCallback(sock); + } + } + + LogInfo("SSLHandshake %p: dropping lock for fd %d", sock, sock->fd); + return; + }); +} +#else // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM +mDNSlocal void *doSSLHandshake(TCPSocket *sock) +{ + // Warning: Touching sock without the kqueue lock! + // We're protected because sock->handshake == handshake_in_progress + mDNS * const m = sock->m; // Get m now, as we may free sock if marked to be closed while we're waiting on SSLHandshake + mStatus err = SSLHandshake(sock->tlsContext); + + KQueueLock(m); + debugf("doSSLHandshake %p: got lock", sock); // Log *after* we get the lock + + if (sock->handshake == handshake_to_be_closed) + { + LogInfo("SSLHandshake completed after close"); + mDNSPlatformTCPCloseConnection(sock); + } + else + { + if (sock->fd != -1) KQueueSet(sock->fd, EV_ADD, EVFILT_READ, sock->kqEntry); + else LogMsg("doSSLHandshake: sock->fd is -1"); + + if (err == errSSLWouldBlock) + sock->handshake = handshake_required; + else + { + if (err) + { + LogMsg("SSLHandshake failed: %d%s", err, err == errSSLPeerInternalError ? " (server busy)" : ""); + CFRelease(sock->tlsContext); + sock->tlsContext = NULL; + } + + sock->err = err ? mStatus_ConnFailed : 0; + sock->handshake = handshake_completed; + + debugf("doSSLHandshake: %p calling doTcpSocketCallback fd %d", sock, sock->fd); + doTcpSocketCallback(sock); + } + } + + debugf("SSLHandshake %p: dropping lock for fd %d", sock, sock->fd); + KQueueUnlock(m, "doSSLHandshake"); + return NULL; +} +#endif // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM + +mDNSlocal void spawnSSLHandshake(TCPSocket* sock) +{ + debugf("spawnSSLHandshake %p: entry", sock); + + if (sock->handshake != handshake_required) LogMsg("spawnSSLHandshake: handshake status not required: %d", sock->handshake); + sock->handshake = handshake_in_progress; + KQueueSet(sock->fd, EV_DELETE, EVFILT_READ, sock->kqEntry); + + // Dispatch it on a separate queue to help avoid blocking other threads/queues, and + // to limit the number of threads used for SSLHandshake + dispatch_async(SSLqueue, ^{doSSLHandshake(sock);}); + + debugf("spawnSSLHandshake %p: done for %d", sock, sock->fd); +} + +#endif /* NO_SECURITYFRAMEWORK */ + +mDNSlocal void tcpKQSocketCallback(__unused int fd, short filter, void *context) +{ + TCPSocket *sock = context; + sock->err = mStatus_NoError; + + //if (filter == EVFILT_READ ) LogMsg("myKQSocketCallBack: tcpKQSocketCallback %d is EVFILT_READ", filter); + //if (filter == EVFILT_WRITE) LogMsg("myKQSocketCallBack: tcpKQSocketCallback %d is EVFILT_WRITE", filter); + // EV_ONESHOT doesn't seem to work, so we add the filter with EV_ADD, and explicitly delete it here with EV_DELETE + if (filter == EVFILT_WRITE) + KQueueSet(sock->fd, EV_DELETE, EVFILT_WRITE, sock->kqEntry); + + if (sock->flags & kTCPSocketFlags_UseTLS) + { +#ifndef NO_SECURITYFRAMEWORK + if (!sock->setup) + { + sock->setup = mDNStrue; + sock->err = tlsSetupSock(sock, kSSLClientSide, kSSLStreamType); + if (sock->err) + { + LogMsg("ERROR: tcpKQSocketCallback: tlsSetupSock failed with error code: %d", sock->err); + return; + } + } + if (sock->handshake == handshake_required) + { + spawnSSLHandshake(sock); + return; + } + else if (sock->handshake == handshake_in_progress || sock->handshake == handshake_to_be_closed) + { + return; + } + else if (sock->handshake != handshake_completed) + { + if (!sock->err) + sock->err = mStatus_UnknownErr; + LogMsg("tcpKQSocketCallback called with unexpected SSLHandshake status: %d", sock->handshake); + } +#else /* NO_SECURITYFRAMEWORK */ + sock->err = mStatus_UnsupportedErr; +#endif /* NO_SECURITYFRAMEWORK */ + } + + doTcpSocketCallback(sock); +} + +#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM +mDNSexport int KQueueSet(int fd, u_short flags, short filter, KQueueEntry *const entryRef) +{ + dispatch_queue_t queue = dispatch_get_main_queue(); + dispatch_source_t source; + if (flags == EV_DELETE) + { + if (filter == EVFILT_READ) + { + dispatch_source_cancel(entryRef->readSource); + dispatch_release(entryRef->readSource); + entryRef->readSource = mDNSNULL; + debugf("KQueueSet: source cancel for read %p, %p", entryRef->readSource, entryRef->writeSource); + } + else if (filter == EVFILT_WRITE) + { + dispatch_source_cancel(entryRef->writeSource); + dispatch_release(entryRef->writeSource); + entryRef->writeSource = mDNSNULL; + debugf("KQueueSet: source cancel for write %p, %p", entryRef->readSource, entryRef->writeSource); + } + else + LogMsg("KQueueSet: ERROR: Wrong filter value %d for EV_DELETE", filter); + return 0; + } + if (flags != EV_ADD) LogMsg("KQueueSet: Invalid flags %d", flags); + + if (filter == EVFILT_READ) + { + source = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, fd, 0, queue); + } + else if (filter == EVFILT_WRITE) + { + source = dispatch_source_create(DISPATCH_SOURCE_TYPE_WRITE, fd, 0, queue); + } + else + { + LogMsg("KQueueSet: ERROR: Wrong filter value %d for EV_ADD", filter); + return -1; + } + if (!source) return -1; + dispatch_source_set_event_handler(source, ^{ + + mDNSs32 stime = mDNSPlatformRawTime(); + entryRef->KQcallback(fd, filter, entryRef->KQcontext); + mDNSs32 etime = mDNSPlatformRawTime(); + if (etime - stime >= WatchDogReportingThreshold) + LogInfo("KQEntryCallback Block: WARNING: took %dms to complete", etime - stime); + + // Trigger the event delivery to the application. Even though we trigger the + // event completion after handling every event source, these all will hopefully + // get merged + TriggerEventCompletion(); + + }); + dispatch_source_set_cancel_handler(source, ^{ + if (entryRef->fdClosed) + { + //LogMsg("CancelHandler: closing fd %d", fd); + close(fd); + } + }); + dispatch_resume(source); + if (filter == EVFILT_READ) + entryRef->readSource = source; + else + entryRef->writeSource = source; + + return 0; +} + +mDNSexport void KQueueLock(mDNS *const m) +{ + (void)m; //unused +} +mDNSexport void KQueueUnlock(mDNS *const m, const char const *task) +{ + (void)m; //unused + (void)task; //unused +} +#else +mDNSexport int KQueueSet(int fd, u_short flags, short filter, const KQueueEntry *const entryRef) +{ + struct kevent new_event; + EV_SET(&new_event, fd, filter, flags, 0, 0, (void*)entryRef); + return (kevent(KQueueFD, &new_event, 1, NULL, 0, NULL) < 0) ? errno : 0; +} + +mDNSexport void KQueueLock(mDNS *const m) +{ + pthread_mutex_lock(&m->p->BigMutex); + m->p->BigMutexStartTime = mDNSPlatformRawTime(); +} + +mDNSexport void KQueueUnlock(mDNS *const m, const char* task) +{ + mDNSs32 end = mDNSPlatformRawTime(); + (void)task; + if (end - m->p->BigMutexStartTime >= WatchDogReportingThreshold) + LogInfo("WARNING: %s took %dms to complete", task, end - m->p->BigMutexStartTime); + + pthread_mutex_unlock(&m->p->BigMutex); + + char wake = 1; + if (send(m->p->WakeKQueueLoopFD, &wake, sizeof(wake), 0) == -1) + LogMsg("ERROR: KQueueWake: send failed with error code: %d (%s)", errno, strerror(errno)); +} +#endif + +mDNSexport void mDNSPlatformCloseFD(KQueueEntry *kq, int fd) +{ +#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM + (void) fd; //unused + if (kq->readSource) + { + dispatch_source_cancel(kq->readSource); + kq->readSource = mDNSNULL; + } + if (kq->writeSource) + { + dispatch_source_cancel(kq->writeSource); + kq->writeSource = mDNSNULL; + } + // Close happens in the cancellation handler + debugf("mDNSPlatformCloseFD: resetting sources for %d", fd); + kq->fdClosed = mDNStrue; +#else + (void)kq; //unused + close(fd); +#endif +} + +mDNSlocal mStatus SetupTCPSocket(TCPSocket *sock, u_short sa_family, mDNSIPPort *port, mDNSBool useBackgroundTrafficClass) +{ + KQSocketSet *cp = &sock->ss; + int *s = (sa_family == AF_INET) ? &cp->sktv4 : &cp->sktv6; + KQueueEntry *k = (sa_family == AF_INET) ? &cp->kqsv4 : &cp->kqsv6; + const int on = 1; // "on" for setsockopt + mStatus err; + + int skt = socket(sa_family, SOCK_STREAM, IPPROTO_TCP); + if (skt < 3) { if (errno != EAFNOSUPPORT) LogMsg("SetupTCPSocket: socket error %d errno %d (%s)", skt, errno, strerror(errno));return(skt); } + + // for TCP sockets, the traffic class is set once and not changed + setTrafficClass(skt, useBackgroundTrafficClass); + + if (sa_family == AF_INET) + { + // Bind it + struct sockaddr_in addr; + mDNSPlatformMemZero(&addr, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = port->NotAnInteger; + err = bind(skt, (struct sockaddr*) &addr, sizeof(addr)); + if (err < 0) { LogMsg("ERROR: bind %s", strerror(errno)); return err; } + + // Receive interface identifiers + err = setsockopt(skt, IPPROTO_IP, IP_RECVIF, &on, sizeof(on)); + if (err < 0) { LogMsg("setsockopt IP_RECVIF - %s", strerror(errno)); return err; } + + mDNSPlatformMemZero(&addr, sizeof(addr)); + socklen_t len = sizeof(addr); + err = getsockname(skt, (struct sockaddr*) &addr, &len); + if (err < 0) { LogMsg("getsockname - %s", strerror(errno)); return err; } + + port->NotAnInteger = addr.sin_port; + } + else + { + // Bind it + struct sockaddr_in6 addr6; + mDNSPlatformMemZero(&addr6, sizeof(addr6)); + addr6.sin6_family = AF_INET6; + addr6.sin6_port = port->NotAnInteger; + err = bind(skt, (struct sockaddr*) &addr6, sizeof(addr6)); + if (err < 0) { LogMsg("ERROR: bind6 %s", strerror(errno)); return err; } + + // We want to receive destination addresses and receive interface identifiers + err = setsockopt(skt, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, sizeof(on)); + if (err < 0) { LogMsg("ERROR: setsockopt IPV6_RECVPKTINFO %s", strerror(errno)); return err; } + + mDNSPlatformMemZero(&addr6, sizeof(addr6)); + socklen_t len = sizeof(addr6); + err = getsockname(skt, (struct sockaddr *) &addr6, &len); + if (err < 0) { LogMsg("getsockname6 - %s", strerror(errno)); return err; } + + port->NotAnInteger = addr6.sin6_port; + + } + *s = skt; + k->KQcallback = tcpKQSocketCallback; + k->KQcontext = sock; + k->KQtask = "mDNSPlatformTCPSocket"; +#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM + k->readSource = mDNSNULL; + k->writeSource = mDNSNULL; + k->fdClosed = mDNSfalse; +#endif + return mStatus_NoError; +} + +mDNSexport TCPSocket *mDNSPlatformTCPSocket(mDNS *const m, TCPSocketFlags flags, mDNSIPPort *port, mDNSBool useBackgroundTrafficClass) +{ + mStatus err; + (void) m; + + TCPSocket *sock = mallocL("TCPSocket/mDNSPlatformTCPSocket", sizeof(TCPSocket)); + if (!sock) { LogMsg("mDNSPlatformTCPSocket: memory allocation failure"); return(mDNSNULL); } + + mDNSPlatformMemZero(sock, sizeof(TCPSocket)); + + sock->ss.m = m; + sock->ss.sktv4 = -1; + sock->ss.sktv6 = -1; + err = SetupTCPSocket(sock, AF_INET, port, useBackgroundTrafficClass); + + if (!err) + { + err = SetupTCPSocket(sock, AF_INET6, port, useBackgroundTrafficClass); + if (err) { mDNSPlatformCloseFD(&sock->ss.kqsv4, sock->ss.sktv4); sock->ss.sktv4 = -1; } + } + if (err) + { + LogMsg("mDNSPlatformTCPSocket: socket error %d errno %d (%s)", sock->fd, errno, strerror(errno)); + freeL("TCPSocket/mDNSPlatformTCPSocket", sock); + return(mDNSNULL); + } + // sock->fd is used as the default fd if the caller does not call mDNSPlatformTCPConnect + sock->fd = sock->ss.sktv4; + sock->callback = mDNSNULL; + sock->flags = flags; + sock->context = mDNSNULL; + sock->setup = mDNSfalse; + sock->connected = mDNSfalse; + sock->handshake = handshake_required; + sock->m = m; + sock->err = mStatus_NoError; + + return sock; +} + +mDNSexport mStatus mDNSPlatformTCPConnect(TCPSocket *sock, const mDNSAddr *dst, mDNSOpaque16 dstport, domainname *hostname, mDNSInterfaceID InterfaceID, TCPConnectionCallback callback, void *context) +{ + KQSocketSet *cp = &sock->ss; + int *s = (dst->type == mDNSAddrType_IPv4) ? &cp->sktv4 : &cp->sktv6; + KQueueEntry *k = (dst->type == mDNSAddrType_IPv4) ? &cp->kqsv4 : &cp->kqsv6; + mStatus err = mStatus_NoError; + struct sockaddr_storage ss; + + sock->callback = callback; + sock->context = context; + sock->setup = mDNSfalse; + sock->connected = mDNSfalse; + sock->handshake = handshake_required; + sock->err = mStatus_NoError; + + if (hostname) { debugf("mDNSPlatformTCPConnect: hostname %##s", hostname->c); AssignDomainName(&sock->hostname, hostname); } + + if (dst->type == mDNSAddrType_IPv4) + { + struct sockaddr_in *saddr = (struct sockaddr_in *)&ss; + mDNSPlatformMemZero(saddr, sizeof(*saddr)); + saddr->sin_family = AF_INET; + saddr->sin_port = dstport.NotAnInteger; + saddr->sin_len = sizeof(*saddr); + saddr->sin_addr.s_addr = dst->ip.v4.NotAnInteger; + } + else + { + struct sockaddr_in6 *saddr6 = (struct sockaddr_in6 *)&ss; + mDNSPlatformMemZero(saddr6, sizeof(*saddr6)); + saddr6->sin6_family = AF_INET6; + saddr6->sin6_port = dstport.NotAnInteger; + saddr6->sin6_len = sizeof(*saddr6); + saddr6->sin6_addr = *(struct in6_addr *)&dst->ip.v6; + } + + // Watch for connect complete (write is ready) + // EV_ONESHOT doesn't seem to work, so we add the filter with EV_ADD, and explicitly delete it in tcpKQSocketCallback using EV_DELETE + if (KQueueSet(*s, EV_ADD /* | EV_ONESHOT */, EVFILT_WRITE, k)) + { + LogMsg("ERROR: mDNSPlatformTCPConnect - KQueueSet failed"); + return errno; + } + + // Watch for incoming data + if (KQueueSet(*s, EV_ADD, EVFILT_READ, k)) + { + LogMsg("ERROR: mDNSPlatformTCPConnect - KQueueSet failed"); + return errno; + } + + if (fcntl(*s, F_SETFL, fcntl(*s, F_GETFL, 0) | O_NONBLOCK) < 0) // set non-blocking + { + LogMsg("ERROR: setsockopt O_NONBLOCK - %s", strerror(errno)); + return mStatus_UnknownErr; + } + + // We bind to the interface and all subsequent packets including the SYN will be sent out + // on this interface + // + // Note: If we are in Active Directory domain, we may try TCP (if the response can't fit in + // UDP). mDNSInterface_Unicast indicates this case and not a valid interface. + if (InterfaceID && InterfaceID != mDNSInterface_Unicast) + { + NetworkInterfaceInfoOSX *info = IfindexToInterfaceInfoOSX(&mDNSStorage, InterfaceID); + if (dst->type == mDNSAddrType_IPv4) + { + #ifdef IP_BOUND_IF + if (info) setsockopt(*s, IPPROTO_IP, IP_BOUND_IF, &info->scope_id, sizeof(info->scope_id)); + else { LogMsg("mDNSPlatformTCPConnect: Invalid interface index %p", InterfaceID); return mStatus_BadParamErr; } + #else + (void)InterfaceID; // Unused + (void)info; // Unused + #endif + } + else + { + #ifdef IPV6_BOUND_IF + if (info) setsockopt(*s, IPPROTO_IPV6, IPV6_BOUND_IF, &info->scope_id, sizeof(info->scope_id)); + else { LogMsg("mDNSPlatformTCPConnect: Invalid interface index %p", InterfaceID); return mStatus_BadParamErr; } + #else + (void)InterfaceID; // Unused + (void)info; // Unused + #endif + } + } + + // mDNSPlatformReadTCP/WriteTCP (unlike the UDP counterpart) does not provide the destination address + // from which we can infer the destination address family. Hence we need to remember that here. + // Instead of remembering the address family, we remember the right fd. + sock->fd = *s; + sock->kqEntry = k; + // initiate connection wth peer + if (connect(*s, (struct sockaddr *)&ss, ss.ss_len) < 0) + { + if (errno == EINPROGRESS) return mStatus_ConnPending; + if (errno == EHOSTUNREACH || errno == EADDRNOTAVAIL || errno == ENETDOWN) + LogInfo("ERROR: mDNSPlatformTCPConnect - connect failed: socket %d: Error %d (%s)", sock->fd, errno, strerror(errno)); + else + LogMsg("ERROR: mDNSPlatformTCPConnect - connect failed: socket %d: Error %d (%s) length %d", sock->fd, errno, strerror(errno), ss.ss_len); + return mStatus_ConnFailed; + } + + LogMsg("NOTE: mDNSPlatformTCPConnect completed synchronously"); + // kQueue should notify us, but this LogMsg is to help track down if it doesn't + return err; +} + +// Why doesn't mDNSPlatformTCPAccept actually call accept() ? +mDNSexport TCPSocket *mDNSPlatformTCPAccept(TCPSocketFlags flags, int fd) +{ + mStatus err = mStatus_NoError; + + TCPSocket *sock = mallocL("TCPSocket/mDNSPlatformTCPAccept", sizeof(TCPSocket)); + if (!sock) return(mDNSNULL); + + mDNSPlatformMemZero(sock, sizeof(*sock)); + sock->fd = fd; + sock->flags = flags; + + if (flags & kTCPSocketFlags_UseTLS) + { +#ifndef NO_SECURITYFRAMEWORK + if (!ServerCerts) { LogMsg("ERROR: mDNSPlatformTCPAccept: unable to find TLS certificates"); err = mStatus_UnknownErr; goto exit; } + + err = tlsSetupSock(sock, kSSLServerSide, kSSLStreamType); + if (err) { LogMsg("ERROR: mDNSPlatformTCPAccept: tlsSetupSock failed with error code: %d", err); goto exit; } + + err = SSLSetCertificate(sock->tlsContext, ServerCerts); + if (err) { LogMsg("ERROR: mDNSPlatformTCPAccept: SSLSetCertificate failed with error code: %d", err); goto exit; } +#else + err = mStatus_UnsupportedErr; +#endif /* NO_SECURITYFRAMEWORK */ + } +#ifndef NO_SECURITYFRAMEWORK +exit: +#endif + + if (err) { freeL("TCPSocket/mDNSPlatformTCPAccept", sock); return(mDNSNULL); } + return(sock); +} + +mDNSexport mDNSu16 mDNSPlatformGetUDPPort(UDPSocket *sock) +{ + mDNSu16 port; + + port = -1; + if (sock) + { + port = sock->ss.port.NotAnInteger; + } + return port; +} + +mDNSlocal void CloseSocketSet(KQSocketSet *ss) +{ + if (ss->sktv4 != -1) + { + mDNSPlatformCloseFD(&ss->kqsv4, ss->sktv4); + ss->sktv4 = -1; + } + if (ss->sktv6 != -1) + { + mDNSPlatformCloseFD(&ss->kqsv6, ss->sktv6); + ss->sktv6 = -1; + } + if (ss->closeFlag) *ss->closeFlag = 1; +} + +mDNSexport void mDNSPlatformTCPCloseConnection(TCPSocket *sock) +{ + if (sock) + { +#ifndef NO_SECURITYFRAMEWORK + if (sock->tlsContext) + { + if (sock->handshake == handshake_in_progress) // SSLHandshake thread using this sock (esp. tlsContext) + { + LogInfo("mDNSPlatformTCPCloseConnection: called while handshake in progress"); + // When we come back from SSLHandshake, we will notice that a close was here and + // call this function again which will do the cleanup then. + sock->handshake = handshake_to_be_closed; + return; + } + + SSLClose(sock->tlsContext); + CFRelease(sock->tlsContext); + sock->tlsContext = NULL; + } +#endif /* NO_SECURITYFRAMEWORK */ + if (sock->ss.sktv4 != -1) + shutdown(sock->ss.sktv4, 2); + if (sock->ss.sktv6 != -1) + shutdown(sock->ss.sktv6, 2); + CloseSocketSet(&sock->ss); + sock->fd = -1; + + freeL("TCPSocket/mDNSPlatformTCPCloseConnection", sock); + } +} + +mDNSexport long mDNSPlatformReadTCP(TCPSocket *sock, void *buf, unsigned long buflen, mDNSBool *closed) +{ + ssize_t nread = 0; + *closed = mDNSfalse; + + if (sock->flags & kTCPSocketFlags_UseTLS) + { +#ifndef NO_SECURITYFRAMEWORK + if (sock->handshake == handshake_required) { LogMsg("mDNSPlatformReadTCP called while handshake required"); return 0; } + else if (sock->handshake == handshake_in_progress) return 0; + else if (sock->handshake != handshake_completed) LogMsg("mDNSPlatformReadTCP called with unexpected SSLHandshake status: %d", sock->handshake); + + //LogMsg("Starting SSLRead %d %X", sock->fd, fcntl(sock->fd, F_GETFL, 0)); + mStatus err = SSLRead(sock->tlsContext, buf, buflen, (size_t *)&nread); + //LogMsg("SSLRead returned %d (%d) nread %d buflen %d", err, errSSLWouldBlock, nread, buflen); + if (err == errSSLClosedGraceful) { nread = 0; *closed = mDNStrue; } + else if (err && err != errSSLWouldBlock) + { LogMsg("ERROR: mDNSPlatformReadTCP - SSLRead: %d", err); nread = -1; *closed = mDNStrue; } +#else + nread = -1; + *closed = mDNStrue; +#endif /* NO_SECURITYFRAMEWORK */ + } + else + { + static int CLOSEDcount = 0; + static int EAGAINcount = 0; + nread = recv(sock->fd, buf, buflen, 0); + + if (nread > 0) + { + CLOSEDcount = 0; + EAGAINcount = 0; + } // On success, clear our error counters + else if (nread == 0) + { + *closed = mDNStrue; + if ((++CLOSEDcount % 1000) == 0) + { + LogMsg("ERROR: mDNSPlatformReadTCP - recv %d got CLOSED %d times", sock->fd, CLOSEDcount); + assert(CLOSEDcount < 1000); + // Recovery Mechanism to bail mDNSResponder out of trouble: Instead of logging the same error msg multiple times, + // crash mDNSResponder using assert() and restart fresh. See advantages below: + // 1.Better User Experience + // 2.CrashLogs frequency can be monitored + // 3.StackTrace can be used for more info + } + } + // else nread is negative -- see what kind of error we got + else if (errno == ECONNRESET) { nread = 0; *closed = mDNStrue; } + else if (errno != EAGAIN) { LogMsg("ERROR: mDNSPlatformReadTCP - recv: %d (%s)", errno, strerror(errno)); nread = -1; } + else // errno is EAGAIN (EWOULDBLOCK) -- no data available + { + nread = 0; + if ((++EAGAINcount % 1000) == 0) { LogMsg("ERROR: mDNSPlatformReadTCP - recv %d got EAGAIN %d times", sock->fd, EAGAINcount); sleep(1); } + } + } + + return nread; +} + +mDNSexport long mDNSPlatformWriteTCP(TCPSocket *sock, const char *msg, unsigned long len) +{ + int nsent; + + if (sock->flags & kTCPSocketFlags_UseTLS) + { +#ifndef NO_SECURITYFRAMEWORK + size_t processed; + if (sock->handshake == handshake_required) { LogMsg("mDNSPlatformWriteTCP called while handshake required"); return 0; } + if (sock->handshake == handshake_in_progress) return 0; + else if (sock->handshake != handshake_completed) LogMsg("mDNSPlatformWriteTCP called with unexpected SSLHandshake status: %d", sock->handshake); + + mStatus err = SSLWrite(sock->tlsContext, msg, len, &processed); + + if (!err) nsent = (int) processed; + else if (err == errSSLWouldBlock) nsent = 0; + else { LogMsg("ERROR: mDNSPlatformWriteTCP - SSLWrite returned %d", err); nsent = -1; } +#else + nsent = -1; +#endif /* NO_SECURITYFRAMEWORK */ + } + else + { + nsent = send(sock->fd, msg, len, 0); + if (nsent < 0) + { + if (errno == EAGAIN) nsent = 0; + else { LogMsg("ERROR: mDNSPlatformWriteTCP - send %s", strerror(errno)); nsent = -1; } + } + } + + return nsent; +} + +mDNSexport int mDNSPlatformTCPGetFD(TCPSocket *sock) +{ + return sock->fd; +} + +// If mDNSIPPort port is non-zero, then it's a multicast socket on the specified interface +// If mDNSIPPort port is zero, then it's a randomly assigned port number, used for sending unicast queries +mDNSlocal mStatus SetupSocket(KQSocketSet *cp, const mDNSIPPort port, u_short sa_family, mDNSIPPort *const outport) +{ + int *s = (sa_family == AF_INET) ? &cp->sktv4 : &cp->sktv6; + KQueueEntry *k = (sa_family == AF_INET) ? &cp->kqsv4 : &cp->kqsv6; + const int on = 1; + const int twofivefive = 255; + mStatus err = mStatus_NoError; + char *errstr = mDNSNULL; + const int mtu = 0; + + cp->closeFlag = mDNSNULL; + + int skt = socket(sa_family, SOCK_DGRAM, IPPROTO_UDP); + if (skt < 3) { if (errno != EAFNOSUPPORT) LogMsg("SetupSocket: socket error %d errno %d (%s)", skt, errno, strerror(errno));return(skt); } + + // set default traffic class + setTrafficClass(skt, mDNSfalse); + +#ifdef SO_RECV_ANYIF + // Enable inbound packets on IFEF_AWDL interface. + // Only done for multicast sockets, since we don't expect unicast socket operations + // on the IFEF_AWDL interface. Operation is a no-op for other interface types. + if (mDNSSameIPPort(port, MulticastDNSPort)) + { + err = setsockopt(skt, SOL_SOCKET, SO_RECV_ANYIF, &on, sizeof(on)); + if (err < 0) { errstr = "setsockopt - SO_RECV_ANYIF"; goto fail; } + } +#endif // SO_RECV_ANYIF + + // ... with a shared UDP port, if it's for multicast receiving + if (mDNSSameIPPort(port, MulticastDNSPort) || mDNSSameIPPort(port, NATPMPAnnouncementPort)) err = setsockopt(skt, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(on)); + if (err < 0) { errstr = "setsockopt - SO_REUSEPORT"; goto fail; } + + if (sa_family == AF_INET) + { + // We want to receive destination addresses + err = setsockopt(skt, IPPROTO_IP, IP_RECVDSTADDR, &on, sizeof(on)); + if (err < 0) { errstr = "setsockopt - IP_RECVDSTADDR"; goto fail; } + + // We want to receive interface identifiers + err = setsockopt(skt, IPPROTO_IP, IP_RECVIF, &on, sizeof(on)); + if (err < 0) { errstr = "setsockopt - IP_RECVIF"; goto fail; } + + // We want to receive packet TTL value so we can check it + err = setsockopt(skt, IPPROTO_IP, IP_RECVTTL, &on, sizeof(on)); + // We ignore errors here -- we already know Jaguar doesn't support this, but we can get by without it + + // Send unicast packets with TTL 255 + err = setsockopt(skt, IPPROTO_IP, IP_TTL, &twofivefive, sizeof(twofivefive)); + if (err < 0) { errstr = "setsockopt - IP_TTL"; goto fail; } + + // And multicast packets with TTL 255 too + err = setsockopt(skt, IPPROTO_IP, IP_MULTICAST_TTL, &twofivefive, sizeof(twofivefive)); + if (err < 0) { errstr = "setsockopt - IP_MULTICAST_TTL"; goto fail; } + + // And start listening for packets + struct sockaddr_in listening_sockaddr; + listening_sockaddr.sin_family = AF_INET; + listening_sockaddr.sin_port = port.NotAnInteger; // Pass in opaque ID without any byte swapping + listening_sockaddr.sin_addr.s_addr = mDNSSameIPPort(port, NATPMPAnnouncementPort) ? AllHosts_v4.NotAnInteger : 0; + err = bind(skt, (struct sockaddr *) &listening_sockaddr, sizeof(listening_sockaddr)); + if (err) { errstr = "bind"; goto fail; } + if (outport) outport->NotAnInteger = listening_sockaddr.sin_port; + } + else if (sa_family == AF_INET6) + { + // NAT-PMP Announcements make no sense on IPv6, and we don't support IPv6 for PCP, so bail early w/o error + if (mDNSSameIPPort(port, NATPMPAnnouncementPort)) { if (outport) *outport = zeroIPPort;return mStatus_NoError; } + + // We want to receive destination addresses and receive interface identifiers + err = setsockopt(skt, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, sizeof(on)); + if (err < 0) { errstr = "setsockopt - IPV6_RECVPKTINFO"; goto fail; } + + // We want to receive packet hop count value so we can check it + err = setsockopt(skt, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &on, sizeof(on)); + if (err < 0) { errstr = "setsockopt - IPV6_RECVHOPLIMIT"; goto fail; } + + // We want to receive only IPv6 packets. Without this option we get IPv4 packets too, + // with mapped addresses of the form 0:0:0:0:0:FFFF:xxxx:xxxx, where xxxx:xxxx is the IPv4 address + err = setsockopt(skt, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)); + if (err < 0) { errstr = "setsockopt - IPV6_V6ONLY"; goto fail; } + + // Send unicast packets with TTL 255 + err = setsockopt(skt, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &twofivefive, sizeof(twofivefive)); + if (err < 0) { errstr = "setsockopt - IPV6_UNICAST_HOPS"; goto fail; } + + // And multicast packets with TTL 255 too + err = setsockopt(skt, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &twofivefive, sizeof(twofivefive)); + if (err < 0) { errstr = "setsockopt - IPV6_MULTICAST_HOPS"; goto fail; } + + // Want to receive our own packets + err = setsockopt(skt, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &on, sizeof(on)); + if (err < 0) { errstr = "setsockopt - IPV6_MULTICAST_LOOP"; goto fail; } + + // Disable default option to send mDNSv6 packets at min IPv6 MTU: RFC 3542, Sec 11 + err = setsockopt(skt, IPPROTO_IPV6, IPV6_USE_MIN_MTU, &mtu, sizeof(mtu)); + if (err < 0) // Since it is an optimization if we fail just log the err, no need to close the skt + LogMsg("SetupSocket: setsockopt - IPV6_USE_MIN_MTU: IP6PO_MINMTU_DISABLE socket %d err %d errno %d (%s)", + skt, err, errno, strerror(errno)); + + // And start listening for packets + struct sockaddr_in6 listening_sockaddr6; + mDNSPlatformMemZero(&listening_sockaddr6, sizeof(listening_sockaddr6)); + listening_sockaddr6.sin6_len = sizeof(listening_sockaddr6); + listening_sockaddr6.sin6_family = AF_INET6; + listening_sockaddr6.sin6_port = port.NotAnInteger; // Pass in opaque ID without any byte swapping + listening_sockaddr6.sin6_flowinfo = 0; + listening_sockaddr6.sin6_addr = in6addr_any; // Want to receive multicasts AND unicasts on this socket + listening_sockaddr6.sin6_scope_id = 0; + err = bind(skt, (struct sockaddr *) &listening_sockaddr6, sizeof(listening_sockaddr6)); + if (err) { errstr = "bind"; goto fail; } + if (outport) outport->NotAnInteger = listening_sockaddr6.sin6_port; + } + + fcntl(skt, F_SETFL, fcntl(skt, F_GETFL, 0) | O_NONBLOCK); // set non-blocking + fcntl(skt, F_SETFD, 1); // set close-on-exec + *s = skt; + k->KQcallback = myKQSocketCallBack; + k->KQcontext = cp; + k->KQtask = "UDP packet reception"; +#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM + k->readSource = mDNSNULL; + k->writeSource = mDNSNULL; + k->fdClosed = mDNSfalse; +#endif + KQueueSet(*s, EV_ADD, EVFILT_READ, k); + + return(err); + +fail: + // For "bind" failures, only write log messages for our shared mDNS port, or for binding to zero + if (strcmp(errstr, "bind") || mDNSSameIPPort(port, MulticastDNSPort) || mDNSIPPortIsZero(port)) + LogMsg("%s skt %d port %d error %d errno %d (%s)", errstr, skt, mDNSVal16(port), err, errno, strerror(errno)); + + // If we got a "bind" failure of EADDRINUSE, inform the caller as it might need to try another random port + if (!strcmp(errstr, "bind") && errno == EADDRINUSE) + { + err = EADDRINUSE; + if (mDNSSameIPPort(port, MulticastDNSPort)) + NotifyOfElusiveBug("Setsockopt SO_REUSEPORT failed", + "Congratulations, you've reproduced an elusive bug.\r" + "Please contact the current assignee of <rdar://problem/3814904>.\r" + "Alternatively, you can send email to radar-3387020@group.apple.com. (Note number is different.)\r" + "If possible, please leave your machine undisturbed so that someone can come to investigate the problem."); + } + + mDNSPlatformCloseFD(k, skt); + return(err); +} + +mDNSexport UDPSocket *mDNSPlatformUDPSocket(mDNS *const m, const mDNSIPPort requestedport) +{ + mStatus err; + mDNSIPPort port = requestedport; + mDNSBool randomizePort = mDNSIPPortIsZero(requestedport); + int i = 10000; // Try at most 10000 times to get a unique random port + UDPSocket *p = mallocL("UDPSocket", sizeof(UDPSocket)); + if (!p) { LogMsg("mDNSPlatformUDPSocket: memory exhausted"); return(mDNSNULL); } + mDNSPlatformMemZero(p, sizeof(UDPSocket)); + p->ss.port = zeroIPPort; + p->ss.m = m; + p->ss.sktv4 = -1; + p->ss.sktv6 = -1; + p->ss.proxy = mDNSfalse; + + do + { + // The kernel doesn't do cryptographically strong random port allocation, so we do it ourselves here + if (randomizePort) port = mDNSOpaque16fromIntVal(0xC000 + mDNSRandom(0x3FFF)); + err = SetupSocket(&p->ss, port, AF_INET, &p->ss.port); + if (!err) + { + err = SetupSocket(&p->ss, port, AF_INET6, &p->ss.port); + if (err) { mDNSPlatformCloseFD(&p->ss.kqsv4, p->ss.sktv4); p->ss.sktv4 = -1; } + } + i--; + } while (err == EADDRINUSE && randomizePort && i); + + if (err) + { + // In customer builds we don't want to log failures with port 5351, because this is a known issue + // of failing to bind to this port when Internet Sharing has already bound to it + // We also don't want to log about port 5350, due to a known bug when some other + // process is bound to it. + if (mDNSSameIPPort(requestedport, NATPMPPort) || mDNSSameIPPort(requestedport, NATPMPAnnouncementPort)) + LogInfo("mDNSPlatformUDPSocket: SetupSocket %d failed error %d errno %d (%s)", mDNSVal16(requestedport), err, errno, strerror(errno)); + else LogMsg("mDNSPlatformUDPSocket: SetupSocket %d failed error %d errno %d (%s)", mDNSVal16(requestedport), err, errno, strerror(errno)); + freeL("UDPSocket", p); + return(mDNSNULL); + } + return(p); +} + +mDNSexport void mDNSPlatformUDPClose(UDPSocket *sock) +{ + CloseSocketSet(&sock->ss); + freeL("UDPSocket", sock); +} + +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - +#pragma mark - BPF Raw packet sending/receiving +#endif + +#if APPLE_OSX_mDNSResponder + +mDNSexport void mDNSPlatformSendRawPacket(const void *const msg, const mDNSu8 *const end, mDNSInterfaceID InterfaceID) +{ + if (!InterfaceID) { LogMsg("mDNSPlatformSendRawPacket: No InterfaceID specified"); return; } + NetworkInterfaceInfoOSX *info; + + info = IfindexToInterfaceInfoOSX(&mDNSStorage, InterfaceID); + if (info == NULL) + { + LogMsg("mDNSPlatformSendUDP: Invalid interface index %p", InterfaceID); + return; + } + if (info->BPF_fd < 0) + LogMsg("mDNSPlatformSendRawPacket: %s BPF_fd %d not ready", info->ifinfo.ifname, info->BPF_fd); + else + { + //LogMsg("mDNSPlatformSendRawPacket %d bytes on %s", end - (mDNSu8 *)msg, info->ifinfo.ifname); + if (write(info->BPF_fd, msg, end - (mDNSu8 *)msg) < 0) + LogMsg("mDNSPlatformSendRawPacket: BPF write(%d) failed %d (%s)", info->BPF_fd, errno, strerror(errno)); + } +} + +mDNSexport void mDNSPlatformSetLocalAddressCacheEntry(mDNS *const m, const mDNSAddr *const tpa, const mDNSEthAddr *const tha, mDNSInterfaceID InterfaceID) +{ + if (!InterfaceID) { LogMsg("mDNSPlatformSetLocalAddressCacheEntry: No InterfaceID specified"); return; } + NetworkInterfaceInfoOSX *info; + info = IfindexToInterfaceInfoOSX(m, InterfaceID); + if (info == NULL) { LogMsg("mDNSPlatformSetLocalAddressCacheEntry: Invalid interface index %p", InterfaceID); return; } + // Manually inject an entry into our local ARP cache. + // (We can't do this by sending an ARP broadcast, because the kernel only pays attention to incoming ARP packets, not outgoing.) + if (!mDNS_AddressIsLocalSubnet(m, InterfaceID, tpa, mDNSNULL)) + LogSPS("Don't need address cache entry for %s %#a %.6a", info->ifinfo.ifname, tpa, tha); + else + { + int result = mDNSSetLocalAddressCacheEntry(info->scope_id, tpa->type, tpa->ip.v6.b, tha->b); + if (result) LogMsg("Set local address cache entry for %s %#a %.6a failed: %d", info->ifinfo.ifname, tpa, tha, result); + else LogSPS("Set local address cache entry for %s %#a %.6a", info->ifinfo.ifname, tpa, tha); + } +} + +mDNSlocal void CloseBPF(NetworkInterfaceInfoOSX *const i) +{ + LogSPS("%s closing BPF fd %d", i->ifinfo.ifname, i->BPF_fd); +#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM + // close will happen in the cancel handler + dispatch_source_cancel(i->BPF_source); +#else + + // Note: MUST NOT close() the underlying native BSD sockets. + // CFSocketInvalidate() will do that for us, in its own good time, which may not necessarily be immediately, because + // it first has to unhook the sockets from its select() call on its other thread, before it can safely close them. + CFRunLoopRemoveSource(i->m->p->CFRunLoop, i->BPF_rls, kCFRunLoopDefaultMode); + CFRelease(i->BPF_rls); + CFSocketInvalidate(i->BPF_cfs); + CFRelease(i->BPF_cfs); +#endif + i->BPF_fd = -1; + if (i->BPF_mcfd >= 0) { close(i->BPF_mcfd); i->BPF_mcfd = -1; } +} + +mDNSlocal void bpf_callback_common(NetworkInterfaceInfoOSX *info) +{ + KQueueLock(info->m); + + // Now we've got the lock, make sure the kqueue thread didn't close the fd out from under us (will not be a problem once the OS X + // kernel has a mechanism for dispatching all events to a single thread, but for now we have to guard against this race condition). + if (info->BPF_fd < 0) goto exit; + + ssize_t n = read(info->BPF_fd, &info->m->imsg, info->BPF_len); + const mDNSu8 *ptr = (const mDNSu8 *)&info->m->imsg; + const mDNSu8 *end = (const mDNSu8 *)&info->m->imsg + n; + debugf("%3d: bpf_callback got %d bytes on %s", info->BPF_fd, n, info->ifinfo.ifname); + + if (n<0) + { + /* <rdar://problem/10287386> + * sometimes there can be a race condition btw when the bpf socket + * gets data and the callback get scheduled and when we call BIOCSETF (which + * clears the socket). this can cause the read to hang for a really long time + * and effectively prevent us from responding to requests for long periods of time. + * to prevent this make the socket non blocking and just bail if we dont get anything + */ + if (errno == EAGAIN) + { + LogMsg("bpf_callback got EAGAIN bailing"); + goto exit; + } + LogMsg("Closing %s BPF fd %d due to error %d (%s)", info->ifinfo.ifname, info->BPF_fd, errno, strerror(errno)); + CloseBPF(info); + goto exit; + } + + while (ptr < end) + { + const struct bpf_hdr *const bh = (const struct bpf_hdr *)ptr; + debugf("%3d: bpf_callback ptr %p bh_hdrlen %d data %p bh_caplen %4d bh_datalen %4d next %p remaining %4d", + info->BPF_fd, ptr, bh->bh_hdrlen, ptr + bh->bh_hdrlen, bh->bh_caplen, bh->bh_datalen, + ptr + BPF_WORDALIGN(bh->bh_hdrlen + bh->bh_caplen), end - (ptr + BPF_WORDALIGN(bh->bh_hdrlen + bh->bh_caplen))); + // Note that BPF guarantees that the NETWORK LAYER header will be word aligned, not the link-layer header. + // Given that An Ethernet header is 14 bytes, this means that if the network layer header (e.g. IP header, + // ARP message, etc.) is 4-byte aligned, then necessarily the Ethernet header will be NOT be 4-byte aligned. + mDNSCoreReceiveRawPacket(info->m, ptr + bh->bh_hdrlen, ptr + bh->bh_hdrlen + bh->bh_caplen, info->ifinfo.InterfaceID); + ptr += BPF_WORDALIGN(bh->bh_hdrlen + bh->bh_caplen); + } +exit: + KQueueUnlock(info->m, "bpf_callback"); +} +#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM +mDNSlocal void bpf_callback_dispatch(NetworkInterfaceInfoOSX *const info) +{ + bpf_callback_common(info); +} +#else +mDNSlocal void bpf_callback(const CFSocketRef cfs, const CFSocketCallBackType CallBackType, const CFDataRef address, const void *const data, void *const context) +{ + (void)cfs; + (void)CallBackType; + (void)address; + (void)data; + bpf_callback_common((NetworkInterfaceInfoOSX *)context); +} +#endif + +mDNSexport void mDNSPlatformSendKeepalive(mDNSAddr *sadd, mDNSAddr *dadd, mDNSIPPort *lport, mDNSIPPort *rport, mDNSu32 seq, mDNSu32 ack, mDNSu16 win) +{ + LogMsg("mDNSPlatformSendKeepalive called\n"); + mDNSSendKeepalive(sadd->ip.v6.b, dadd->ip.v6.b, lport->NotAnInteger, rport->NotAnInteger, seq, ack, win); +} + +mDNSexport mStatus mDNSPlatformClearSPSMACAddr(void) +{ + SCDynamicStoreRef store = NULL; + CFStringRef entityname = NULL; + + if ((store = SCDynamicStoreCreate(NULL, CFSTR("mDNSResponder:ClearSPSMACAddress"), NULL, NULL))) + { + if ((entityname = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%s%s%s"), "State:/Network/Interface/", "[^/]", "/BonjourSleepProxyAddress"))) + { + if (SCDynamicStoreRemoveValue(store, entityname) == false) + LogMsg("mDNSPlatformClearSPSMACAddr: Unable to remove key"); + } + } + + if (entityname) CFRelease(entityname); + if (store) CFRelease(store); + return KERN_SUCCESS; +} + +mDNSexport mStatus mDNSPlatformStoreSPSMACAddr(mDNSAddr *spsaddr, char *ifname) +{ + int family = (spsaddr->type == mDNSAddrType_IPv4) ? AF_INET : AF_INET6; + LogSPS("mDNSPlatformStoreSPSMACAddr : Storing %#a on interface %s", spsaddr, ifname); + mDNSStoreSPSMACAddress(family, spsaddr->ip.v6.b, ifname); + return KERN_SUCCESS; +} + +mDNSexport mStatus mDNSPlatformGetRemoteMacAddr(mDNS *const m, mDNSAddr *raddr) +{ + int family = (raddr->type == mDNSAddrType_IPv4) ? AF_INET : AF_INET6; + + mDNSGetRemoteMAC(m, family, raddr->ip.v6.b); + return KERN_SUCCESS; +} + +mDNSexport mStatus mDNSPlatformRetrieveTCPInfo(mDNS *const m, mDNSAddr *laddr, mDNSIPPort *lport, mDNSAddr *raddr, mDNSIPPort *rport, mDNSTCPInfo *mti) +{ + mDNSs32 intfid; + mDNSs32 error = 0; + int family = (laddr->type == mDNSAddrType_IPv4) ? AF_INET : AF_INET6; + + error = mDNSRetrieveTCPInfo(family, laddr->ip.v6.b, lport->NotAnInteger, raddr->ip.v6.b, rport->NotAnInteger, (uint32_t *)&(mti->seq), (uint32_t *)&(mti->ack), (uint16_t *)&(mti->window), (int32_t*)&intfid); + if (error != KERN_SUCCESS) + { + LogMsg("%s: mDNSRetrieveTCPInfo returned : %d", __func__, error); + return error; + } + mti->IntfId = mDNSPlatformInterfaceIDfromInterfaceIndex(m, intfid); + return error; +} + +#define BPF_SetOffset(from, cond, to) (from)->cond = (to) - 1 - (from) + +mDNSlocal int CountProxyTargets(mDNS *const m, NetworkInterfaceInfoOSX *x, int *p4, int *p6) +{ + int numv4 = 0, numv6 = 0; + AuthRecord *rr; + + for (rr = m->ResourceRecords; rr; rr=rr->next) + if (rr->resrec.InterfaceID == x->ifinfo.InterfaceID && rr->AddressProxy.type == mDNSAddrType_IPv4) + { + if (p4) LogSPS("CountProxyTargets: fd %d %-7s IP%2d %.4a", x->BPF_fd, x->ifinfo.ifname, numv4, &rr->AddressProxy.ip.v4); + numv4++; + } + + for (rr = m->ResourceRecords; rr; rr=rr->next) + if (rr->resrec.InterfaceID == x->ifinfo.InterfaceID && rr->AddressProxy.type == mDNSAddrType_IPv6) + { + if (p6) LogSPS("CountProxyTargets: fd %d %-7s IP%2d %.16a", x->BPF_fd, x->ifinfo.ifname, numv6, &rr->AddressProxy.ip.v6); + numv6++; + } + + if (p4) *p4 = numv4; + if (p6) *p6 = numv6; + return(numv4 + numv6); +} + +mDNSexport void mDNSPlatformUpdateProxyList(mDNS *const m, const mDNSInterfaceID InterfaceID) +{ + NetworkInterfaceInfoOSX *x; + + // Note: We can't use IfIndexToInterfaceInfoOSX because that looks for Registered also. + for (x = m->p->InterfaceList; x; x = x->next) if (x->ifinfo.InterfaceID == InterfaceID) break; + + if (!x) { LogMsg("mDNSPlatformUpdateProxyList: ERROR InterfaceID %p not found", InterfaceID); return; } + + #define MAX_BPF_ADDRS 250 + int numv4 = 0, numv6 = 0; + + if (CountProxyTargets(m, x, &numv4, &numv6) > MAX_BPF_ADDRS) + { + LogMsg("mDNSPlatformUpdateProxyList: ERROR Too many address proxy records v4 %d v6 %d", numv4, numv6); + if (numv4 > MAX_BPF_ADDRS) numv4 = MAX_BPF_ADDRS; + numv6 = MAX_BPF_ADDRS - numv4; + } + + LogSPS("mDNSPlatformUpdateProxyList: fd %d %-7s MAC %.6a %d v4 %d v6", x->BPF_fd, x->ifinfo.ifname, &x->ifinfo.MAC, numv4, numv6); + + // Caution: This is a static structure, so we need to be careful that any modifications we make to it + // are done in such a way that they work correctly when mDNSPlatformUpdateProxyList is called multiple times + static struct bpf_insn filter[17 + MAX_BPF_ADDRS] = + { + BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 12), // 0 Read Ethertype (bytes 12,13) + + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x0806, 0, 1), // 1 If Ethertype == ARP goto next, else 3 + BPF_STMT(BPF_RET + BPF_K, 42), // 2 Return 42-byte ARP + + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x0800, 4, 0), // 3 If Ethertype == IPv4 goto 8 (IPv4 address list check) else next + + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x86DD, 0, 9), // 4 If Ethertype == IPv6 goto next, else exit + BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 20), // 5 Read Protocol and Hop Limit (bytes 20,21) + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x3AFF, 0, 9), // 6 If (Prot,TTL) == (3A,FF) goto next, else IPv6 address list check + BPF_STMT(BPF_RET + BPF_K, 86), // 7 Return 86-byte ND + + // Is IPv4 packet; check if it's addressed to any IPv4 address we're proxying for + BPF_STMT(BPF_LD + BPF_W + BPF_ABS, 30), // 8 Read IPv4 Dst (bytes 30,31,32,33) + }; + + struct bpf_insn *pc = &filter[9]; + struct bpf_insn *chk6 = pc + numv4 + 1; // numv4 address checks, plus a "return 0" + struct bpf_insn *fail = chk6 + 1 + numv6; // Get v6 Dst LSW, plus numv6 address checks + struct bpf_insn *ret4 = fail + 1; + struct bpf_insn *ret6 = ret4 + 4; + + static const struct bpf_insn rf = BPF_STMT(BPF_RET + BPF_K, 0); // No match: Return nothing + + static const struct bpf_insn g6 = BPF_STMT(BPF_LD + BPF_W + BPF_ABS, 50); // Read IPv6 Dst LSW (bytes 50,51,52,53) + + static const struct bpf_insn r4a = BPF_STMT(BPF_LDX + BPF_B + BPF_MSH, 14); // Get IP Header length (normally 20) + static const struct bpf_insn r4b = BPF_STMT(BPF_LD + BPF_IMM, 54); // A = 54 (14-byte Ethernet plus 20-byte TCP + 20 bytes spare) + static const struct bpf_insn r4c = BPF_STMT(BPF_ALU + BPF_ADD + BPF_X, 0); // A += IP Header length + static const struct bpf_insn r4d = BPF_STMT(BPF_RET + BPF_A, 0); // Success: Return Ethernet + IP + TCP + 20 bytes spare (normally 74) + + static const struct bpf_insn r6a = BPF_STMT(BPF_RET + BPF_K, 94); // Success: Return Eth + IPv6 + TCP + 20 bytes spare + + BPF_SetOffset(&filter[4], jf, fail); // If Ethertype not ARP, IPv4, or IPv6, fail + BPF_SetOffset(&filter[6], jf, chk6); // If IPv6 but not ICMPv6, go to IPv6 address list check + + // BPF Byte-Order Note + // The BPF API designers apparently thought that programmers would not be smart enough to use htons + // and htonl correctly to convert numeric values to network byte order on little-endian machines, + // so instead they chose to make the API implicitly byte-swap *ALL* values, even literal byte strings + // that shouldn't be byte-swapped, like ASCII text, Ethernet addresses, IP addresses, etc. + // As a result, if we put Ethernet addresses and IP addresses in the right byte order, the BPF API + // will byte-swap and make them backwards, and then our filter won't work. So, we have to arrange + // that on little-endian machines we deliberately put addresses in memory with the bytes backwards, + // so that when the BPF API goes through and swaps them all, they end up back as they should be. + // In summary, if we byte-swap all the non-numeric fields that shouldn't be swapped, and we *don't* + // swap any of the numeric values that *should* be byte-swapped, then the filter will work correctly. + + // IPSEC capture size notes: + // 8 bytes UDP header + // 4 bytes Non-ESP Marker + // 28 bytes IKE Header + // -- + // 40 Total. Capturing TCP Header + 20 gets us enough bytes to receive the IKE Header in a UDP-encapsulated IKE packet. + + AuthRecord *rr; + for (rr = m->ResourceRecords; rr; rr=rr->next) + if (rr->resrec.InterfaceID == InterfaceID && rr->AddressProxy.type == mDNSAddrType_IPv4) + { + mDNSv4Addr a = rr->AddressProxy.ip.v4; + pc->code = BPF_JMP + BPF_JEQ + BPF_K; + BPF_SetOffset(pc, jt, ret4); + pc->jf = 0; + pc->k = (bpf_u_int32)a.b[0] << 24 | (bpf_u_int32)a.b[1] << 16 | (bpf_u_int32)a.b[2] << 8 | (bpf_u_int32)a.b[3]; + pc++; + } + *pc++ = rf; + + if (pc != chk6) LogMsg("mDNSPlatformUpdateProxyList: pc %p != chk6 %p", pc, chk6); + *pc++ = g6; // chk6 points here + + // First cancel any previous ND group memberships we had, then create a fresh socket + if (x->BPF_mcfd >= 0) close(x->BPF_mcfd); + x->BPF_mcfd = socket(AF_INET6, SOCK_DGRAM, 0); + + for (rr = m->ResourceRecords; rr; rr=rr->next) + if (rr->resrec.InterfaceID == InterfaceID && rr->AddressProxy.type == mDNSAddrType_IPv6) + { + const mDNSv6Addr *const a = &rr->AddressProxy.ip.v6; + pc->code = BPF_JMP + BPF_JEQ + BPF_K; + BPF_SetOffset(pc, jt, ret6); + pc->jf = 0; + pc->k = (bpf_u_int32)a->b[0x0C] << 24 | (bpf_u_int32)a->b[0x0D] << 16 | (bpf_u_int32)a->b[0x0E] << 8 | (bpf_u_int32)a->b[0x0F]; + pc++; + + struct ipv6_mreq i6mr; + i6mr.ipv6mr_interface = x->scope_id; + i6mr.ipv6mr_multiaddr = *(const struct in6_addr*)&NDP_prefix; + i6mr.ipv6mr_multiaddr.s6_addr[0xD] = a->b[0xD]; + i6mr.ipv6mr_multiaddr.s6_addr[0xE] = a->b[0xE]; + i6mr.ipv6mr_multiaddr.s6_addr[0xF] = a->b[0xF]; + + // Do precautionary IPV6_LEAVE_GROUP first, necessary to clear stale kernel state + mStatus err = setsockopt(x->BPF_mcfd, IPPROTO_IPV6, IPV6_LEAVE_GROUP, &i6mr, sizeof(i6mr)); + if (err < 0 && (errno != EADDRNOTAVAIL)) + LogMsg("mDNSPlatformUpdateProxyList: IPV6_LEAVE_GROUP error %d errno %d (%s) group %.16a on %u", err, errno, strerror(errno), &i6mr.ipv6mr_multiaddr, i6mr.ipv6mr_interface); + + err = setsockopt(x->BPF_mcfd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &i6mr, sizeof(i6mr)); + if (err < 0 && (errno != EADDRINUSE)) // Joining same group twice can give "Address already in use" error -- no need to report that + LogMsg("mDNSPlatformUpdateProxyList: IPV6_JOIN_GROUP error %d errno %d (%s) group %.16a on %u", err, errno, strerror(errno), &i6mr.ipv6mr_multiaddr, i6mr.ipv6mr_interface); + + LogSPS("Joined IPv6 ND multicast group %.16a for %.16a", &i6mr.ipv6mr_multiaddr, a); + } + + if (pc != fail) LogMsg("mDNSPlatformUpdateProxyList: pc %p != fail %p", pc, fail); + *pc++ = rf; // fail points here + + if (pc != ret4) LogMsg("mDNSPlatformUpdateProxyList: pc %p != ret4 %p", pc, ret4); + *pc++ = r4a; // ret4 points here + *pc++ = r4b; + *pc++ = r4c; + *pc++ = r4d; + + if (pc != ret6) LogMsg("mDNSPlatformUpdateProxyList: pc %p != ret6 %p", pc, ret6); + *pc++ = r6a; // ret6 points here + + struct bpf_program prog = { pc - filter, filter }; + +#if 0 + // For debugging BPF filter program + unsigned int q; + for (q=0; q<prog.bf_len; q++) + LogSPS("mDNSPlatformUpdateProxyList: %2d { 0x%02x, %d, %d, 0x%08x },", q, prog.bf_insns[q].code, prog.bf_insns[q].jt, prog.bf_insns[q].jf, prog.bf_insns[q].k); +#endif + + if (!numv4 && !numv6) + { + LogSPS("mDNSPlatformUpdateProxyList: No need for filter"); + if (m->timenow == 0) LogMsg("mDNSPlatformUpdateProxyList: m->timenow == 0"); + // Schedule check to see if we can close this BPF_fd now + if (!m->p->NetworkChanged) m->p->NetworkChanged = NonZeroTime(m->timenow + mDNSPlatformOneSecond * 2); + // prog.bf_len = 0; This seems to panic the kernel + if (x->BPF_fd < 0) return; // If we've already closed our BPF_fd, no need to generate an error message below + } + + if (ioctl(x->BPF_fd, BIOCSETFNR, &prog) < 0) LogMsg("mDNSPlatformUpdateProxyList: BIOCSETFNR(%d) failed %d (%s)", prog.bf_len, errno, strerror(errno)); + else LogSPS("mDNSPlatformUpdateProxyList: BIOCSETFNR(%d) successful", prog.bf_len); +} + +mDNSexport void mDNSPlatformReceiveBPF_fd(mDNS *const m, int fd) +{ + mDNS_Lock(m); + + NetworkInterfaceInfoOSX *i; + for (i = m->p->InterfaceList; i; i = i->next) if (i->BPF_fd == -2) break; + if (!i) { LogSPS("mDNSPlatformReceiveBPF_fd: No Interfaces awaiting BPF fd %d; closing", fd); close(fd); } + else + { + LogSPS("%s using BPF fd %d", i->ifinfo.ifname, fd); + + struct bpf_version v; + if (ioctl(fd, BIOCVERSION, &v) < 0) + LogMsg("mDNSPlatformReceiveBPF_fd: %d %s BIOCVERSION failed %d (%s)", fd, i->ifinfo.ifname, errno, strerror(errno)); + else if (BPF_MAJOR_VERSION != v.bv_major || BPF_MINOR_VERSION != v.bv_minor) + LogMsg("mDNSPlatformReceiveBPF_fd: %d %s BIOCVERSION header %d.%d kernel %d.%d", + fd, i->ifinfo.ifname, BPF_MAJOR_VERSION, BPF_MINOR_VERSION, v.bv_major, v.bv_minor); + + if (ioctl(fd, BIOCGBLEN, &i->BPF_len) < 0) + LogMsg("mDNSPlatformReceiveBPF_fd: %d %s BIOCGBLEN failed %d (%s)", fd, i->ifinfo.ifname, errno, strerror(errno)); + + if (i->BPF_len > sizeof(m->imsg)) + { + i->BPF_len = sizeof(m->imsg); + if (ioctl(fd, BIOCSBLEN, &i->BPF_len) < 0) + LogMsg("mDNSPlatformReceiveBPF_fd: %d %s BIOCSBLEN failed %d (%s)", fd, i->ifinfo.ifname, errno, strerror(errno)); + else + LogSPS("mDNSPlatformReceiveBPF_fd: %d %s BIOCSBLEN %d", fd, i->ifinfo.ifname, i->BPF_len); + } + + static const u_int opt_one = 1; + if (ioctl(fd, BIOCIMMEDIATE, &opt_one) < 0) + LogMsg("mDNSPlatformReceiveBPF_fd: %d %s BIOCIMMEDIATE failed %d (%s)", fd, i->ifinfo.ifname, errno, strerror(errno)); + + //if (ioctl(fd, BIOCPROMISC, &opt_one) < 0) + // LogMsg("mDNSPlatformReceiveBPF_fd: %d %s BIOCPROMISC failed %d (%s)", fd, i->ifinfo.ifname, errno, strerror(errno)); + + //if (ioctl(fd, BIOCSHDRCMPLT, &opt_one) < 0) + // LogMsg("mDNSPlatformReceiveBPF_fd: %d %s BIOCSHDRCMPLT failed %d (%s)", fd, i->ifinfo.ifname, errno, strerror(errno)); + + /* <rdar://problem/10287386> + * make socket non blocking see comments in bpf_callback_common for more info + */ + if (fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK) < 0) // set non-blocking + { + LogMsg("mDNSPlatformReceiveBPF_fd: %d %s O_NONBLOCK failed %d (%s)", fd, i->ifinfo.ifname, errno, strerror(errno)); + } + + struct ifreq ifr; + mDNSPlatformMemZero(&ifr, sizeof(ifr)); + strlcpy(ifr.ifr_name, i->ifinfo.ifname, sizeof(ifr.ifr_name)); + if (ioctl(fd, BIOCSETIF, &ifr) < 0) + { LogMsg("mDNSPlatformReceiveBPF_fd: %d %s BIOCSETIF failed %d (%s)", fd, i->ifinfo.ifname, errno, strerror(errno)); i->BPF_fd = -3; } + else + { +#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM + i->BPF_fd = fd; + i->BPF_source = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, fd, 0, dispatch_get_main_queue()); + if (!i->BPF_source) {LogMsg("mDNSPlatformReceiveBPF_fd: dispatch source create failed"); return;} + dispatch_source_set_event_handler(i->BPF_source, ^{bpf_callback_dispatch(i);}); + dispatch_source_set_cancel_handler(i->BPF_source, ^{close(fd);}); + dispatch_resume(i->BPF_source); +#else + CFSocketContext myCFSocketContext = { 0, i, NULL, NULL, NULL }; + i->BPF_fd = fd; + i->BPF_cfs = CFSocketCreateWithNative(kCFAllocatorDefault, fd, kCFSocketReadCallBack, bpf_callback, &myCFSocketContext); + i->BPF_rls = CFSocketCreateRunLoopSource(kCFAllocatorDefault, i->BPF_cfs, 0); + CFRunLoopAddSource(i->m->p->CFRunLoop, i->BPF_rls, kCFRunLoopDefaultMode); +#endif + mDNSPlatformUpdateProxyList(m, i->ifinfo.InterfaceID); + } + } + + mDNS_Unlock(m); +} + +#endif // APPLE_OSX_mDNSResponder + +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - +#pragma mark - Key Management +#endif + +#ifndef NO_SECURITYFRAMEWORK +mDNSlocal CFArrayRef GetCertChain(SecIdentityRef identity) +{ + CFMutableArrayRef certChain = NULL; + if (!identity) { LogMsg("getCertChain: identity is NULL"); return(NULL); } + SecCertificateRef cert; + OSStatus err = SecIdentityCopyCertificate(identity, &cert); + if (err || !cert) LogMsg("getCertChain: SecIdentityCopyCertificate() returned %d", (int) err); + else + { + SecPolicySearchRef searchRef; + err = SecPolicySearchCreate(CSSM_CERT_X_509v3, &CSSMOID_APPLE_X509_BASIC, NULL, &searchRef); + if (err || !searchRef) LogMsg("getCertChain: SecPolicySearchCreate() returned %d", (int) err); + else + { + SecPolicyRef policy; + err = SecPolicySearchCopyNext(searchRef, &policy); + if (err || !policy) LogMsg("getCertChain: SecPolicySearchCopyNext() returned %d", (int) err); + else + { + CFArrayRef wrappedCert = CFArrayCreate(NULL, (const void**) &cert, 1, &kCFTypeArrayCallBacks); + if (!wrappedCert) LogMsg("getCertChain: wrappedCert is NULL"); + else + { + SecTrustRef trust; + err = SecTrustCreateWithCertificates(wrappedCert, policy, &trust); + if (err || !trust) LogMsg("getCertChain: SecTrustCreateWithCertificates() returned %d", (int) err); + else + { + err = SecTrustEvaluate(trust, NULL); + if (err) LogMsg("getCertChain: SecTrustEvaluate() returned %d", (int) err); + else + { + CFArrayRef rawCertChain; + CSSM_TP_APPLE_EVIDENCE_INFO *statusChain = NULL; + err = SecTrustGetResult(trust, NULL, &rawCertChain, &statusChain); + if (err || !rawCertChain || !statusChain) LogMsg("getCertChain: SecTrustGetResult() returned %d", (int) err); + else + { + certChain = CFArrayCreateMutableCopy(NULL, 0, rawCertChain); + if (!certChain) LogMsg("getCertChain: certChain is NULL"); + else + { + // Replace the SecCertificateRef at certChain[0] with a SecIdentityRef per documentation for SSLSetCertificate: + // <http://devworld.apple.com/documentation/Security/Reference/secureTransportRef/index.html> + CFArraySetValueAtIndex(certChain, 0, identity); + // Remove root from cert chain, but keep any and all intermediate certificates that have been signed by the root certificate + if (CFArrayGetCount(certChain) > 1) CFArrayRemoveValueAtIndex(certChain, CFArrayGetCount(certChain) - 1); + } + CFRelease(rawCertChain); + // Do not free statusChain: + // <http://developer.apple.com/documentation/Security/Reference/certifkeytrustservices/Reference/reference.html> says: + // certChain: Call the CFRelease function to release this object when you are finished with it. + // statusChain: Do not attempt to free this pointer; it remains valid until the trust management object is released... + } + } + CFRelease(trust); + } + CFRelease(wrappedCert); + } + CFRelease(policy); + } + CFRelease(searchRef); + } + CFRelease(cert); + } + return certChain; +} +#endif /* NO_SECURITYFRAMEWORK */ + +mDNSexport mStatus mDNSPlatformTLSSetupCerts(void) +{ +#ifdef NO_SECURITYFRAMEWORK + return mStatus_UnsupportedErr; +#else + SecIdentityRef identity = nil; + SecIdentitySearchRef srchRef = nil; + OSStatus err; + + // search for "any" identity matching specified key use + // In this app, we expect there to be exactly one + err = SecIdentitySearchCreate(NULL, CSSM_KEYUSE_DECRYPT, &srchRef); + if (err) { LogMsg("ERROR: mDNSPlatformTLSSetupCerts: SecIdentitySearchCreate returned %d", (int) err); return err; } + + err = SecIdentitySearchCopyNext(srchRef, &identity); + if (err) { LogMsg("ERROR: mDNSPlatformTLSSetupCerts: SecIdentitySearchCopyNext returned %d", (int) err); return err; } + + if (CFGetTypeID(identity) != SecIdentityGetTypeID()) + { LogMsg("ERROR: mDNSPlatformTLSSetupCerts: SecIdentitySearchCopyNext CFTypeID failure"); return mStatus_UnknownErr; } + + // Found one. Call getCertChain to create the correct certificate chain. + ServerCerts = GetCertChain(identity); + if (ServerCerts == nil) { LogMsg("ERROR: mDNSPlatformTLSSetupCerts: getCertChain error"); return mStatus_UnknownErr; } + + return mStatus_NoError; +#endif /* NO_SECURITYFRAMEWORK */ +} + +mDNSexport void mDNSPlatformTLSTearDownCerts(void) +{ +#ifndef NO_SECURITYFRAMEWORK + if (ServerCerts) { CFRelease(ServerCerts); ServerCerts = NULL; } +#endif /* NO_SECURITYFRAMEWORK */ +} + +// This gets the text of the field currently labelled "Computer Name" in the Sharing Prefs Control Panel +mDNSlocal void GetUserSpecifiedFriendlyComputerName(domainlabel *const namelabel) +{ + CFStringEncoding encoding = kCFStringEncodingUTF8; + CFStringRef cfs = SCDynamicStoreCopyComputerName(NULL, &encoding); + if (cfs) + { + CFStringGetPascalString(cfs, namelabel->c, sizeof(*namelabel), kCFStringEncodingUTF8); + CFRelease(cfs); + } +} + +// This gets the text of the field currently labelled "Local Hostname" in the Sharing Prefs Control Panel +mDNSlocal void GetUserSpecifiedLocalHostName(domainlabel *const namelabel) +{ + CFStringRef cfs = SCDynamicStoreCopyLocalHostName(NULL); + if (cfs) + { + CFStringGetPascalString(cfs, namelabel->c, sizeof(*namelabel), kCFStringEncodingUTF8); + CFRelease(cfs); + } +} + +mDNSexport mDNSBool DictionaryIsEnabled(CFDictionaryRef dict) +{ + mDNSs32 val; + CFNumberRef state = (CFNumberRef)CFDictionaryGetValue(dict, CFSTR("Enabled")); + if (!state) return mDNSfalse; + if (!CFNumberGetValue(state, kCFNumberSInt32Type, &val)) + { LogMsg("ERROR: DictionaryIsEnabled - CFNumberGetValue"); return mDNSfalse; } + return val ? mDNStrue : mDNSfalse; +} + +mDNSlocal mStatus SetupAddr(mDNSAddr *ip, const struct sockaddr *const sa) +{ + if (!sa) { LogMsg("SetupAddr ERROR: NULL sockaddr"); return(mStatus_Invalid); } + + if (sa->sa_family == AF_INET) + { + struct sockaddr_in *ifa_addr = (struct sockaddr_in *)sa; + ip->type = mDNSAddrType_IPv4; + ip->ip.v4.NotAnInteger = ifa_addr->sin_addr.s_addr; + return(mStatus_NoError); + } + + if (sa->sa_family == AF_INET6) + { + struct sockaddr_in6 *ifa_addr = (struct sockaddr_in6 *)sa; + // Inside the BSD kernel they use a hack where they stuff the sin6->sin6_scope_id + // value into the second word of the IPv6 link-local address, so they can just + // pass around IPv6 address structures instead of full sockaddr_in6 structures. + // Those hacked IPv6 addresses aren't supposed to escape the kernel in that form, but they do. + // To work around this we always whack the second word of any IPv6 link-local address back to zero. + if (IN6_IS_ADDR_LINKLOCAL(&ifa_addr->sin6_addr)) ifa_addr->sin6_addr.__u6_addr.__u6_addr16[1] = 0; + ip->type = mDNSAddrType_IPv6; + ip->ip.v6 = *(mDNSv6Addr*)&ifa_addr->sin6_addr; + return(mStatus_NoError); + } + + LogMsg("SetupAddr invalid sa_family %d", sa->sa_family); + return(mStatus_Invalid); +} + +mDNSlocal mDNSEthAddr GetBSSID(char *ifa_name) +{ + mDNSEthAddr eth = zeroEthAddr; + SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("mDNSResponder:GetBSSID"), NULL, NULL); + if (!store) + LogMsg("GetBSSID: SCDynamicStoreCreate failed: %s", SCErrorString(SCError())); + else + { + CFStringRef entityname = CFStringCreateWithFormat(NULL, NULL, CFSTR("State:/Network/Interface/%s/AirPort"), ifa_name); + if (entityname) + { + CFDictionaryRef dict = SCDynamicStoreCopyValue(store, entityname); + if (dict) + { + CFRange range = { 0, 6 }; // Offset, length + CFDataRef data = CFDictionaryGetValue(dict, CFSTR("BSSID")); + if (data && CFDataGetLength(data) == 6) CFDataGetBytes(data, range, eth.b); + CFRelease(dict); + } + CFRelease(entityname); + } + CFRelease(store); + } + return(eth); +} + +mDNSlocal int GetMAC(mDNSEthAddr *eth, u_short ifindex) +{ + struct ifaddrs *ifa; + for (ifa = myGetIfAddrs(0); ifa; ifa = ifa->ifa_next) + if (ifa->ifa_addr->sa_family == AF_LINK) + { + const struct sockaddr_dl *const sdl = (const struct sockaddr_dl *)ifa->ifa_addr; + if (sdl->sdl_index == ifindex) + { mDNSPlatformMemCopy(eth->b, sdl->sdl_data + sdl->sdl_nlen, 6); return 0; } + } + *eth = zeroEthAddr; + return -1; +} + +#ifndef SIOCGIFWAKEFLAGS +#define SIOCGIFWAKEFLAGS _IOWR('i', 136, struct ifreq) /* get interface wake property flags */ +#endif + +#ifndef IF_WAKE_ON_MAGIC_PACKET +#define IF_WAKE_ON_MAGIC_PACKET 0x01 +#endif + +#ifndef ifr_wake_flags +#define ifr_wake_flags ifr_ifru.ifru_intval +#endif + +mDNSlocal mDNSBool CheckInterfaceSupport(NetworkInterfaceInfo *const intf, const char *key) +{ + io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, IOBSDNameMatching(kIOMasterPortDefault, 0, intf->ifname)); + if (!service) + { + LogSPS("CheckInterfaceSupport: No service for interface %s", intf->ifname); + return mDNSfalse; + } + + io_name_t n1, n2; + IOObjectGetClass(service, n1); + io_object_t parent; + mDNSBool ret = mDNSfalse; + kern_return_t kr = IORegistryEntryGetParentEntry(service, kIOServicePlane, &parent); + if (kr == KERN_SUCCESS) + { + CFStringRef keystr = CFStringCreateWithCString(NULL, key, kCFStringEncodingUTF8); + IOObjectGetClass(parent, n2); + LogSPS("CheckInterfaceSupport: Interface %s service %s parent %s", intf->ifname, n1, n2); + const CFTypeRef ref = IORegistryEntryCreateCFProperty(parent, keystr, kCFAllocatorDefault, mDNSNULL); + if (!ref) + { + LogSPS("CheckInterfaceSupport: No mDNS_IOREG_KEY for interface %s/%s/%s", intf->ifname, n1, n2); + ret = mDNSfalse; + } + else + { + ret = mDNStrue; + CFRelease(ref); + } + IOObjectRelease(parent); + CFRelease(keystr); + } + else + { + LogSPS("CheckInterfaceSupport: IORegistryEntryGetParentEntry for %s/%s failed %d", intf->ifname, n1, kr); + ret = mDNSfalse; + } + IOObjectRelease(service); + return ret; +} + + +mDNSlocal mDNSBool InterfaceSupportsKeepAlive(NetworkInterfaceInfo *const intf) +{ + return CheckInterfaceSupport(intf, mDNS_IOREG_KA_KEY); +} + +mDNSlocal mDNSBool NetWakeInterface(NetworkInterfaceInfoOSX *i) +{ + if (!MulticastInterface(i) ) return(mDNSfalse); // We only use Sleep Proxy Service on multicast-capable interfaces + if (i->ifa_flags & IFF_LOOPBACK) return(mDNSfalse); // except loopback + + // If the interface supports TCPKeepalive, it is capable of waking up for a magic packet + // This check is needed since the SIOCGIFWAKEFLAGS ioctl returns wrong values for WOMP capability + // when the power source is not AC Power. + if (InterfaceSupportsKeepAlive(&i->ifinfo)) + { + LogSPS("NetWakeInterface: %s supports TCP Keepalive returning true", i->ifinfo.ifname); + return mDNStrue; + } + + int s = socket(AF_INET, SOCK_DGRAM, 0); + if (s < 0) { LogMsg("NetWakeInterface socket failed %s error %d errno %d (%s)", i->ifinfo.ifname, s, errno, strerror(errno)); return(mDNSfalse); } + + struct ifreq ifr; + strlcpy(ifr.ifr_name, i->ifinfo.ifname, sizeof(ifr.ifr_name)); + if (ioctl(s, SIOCGIFWAKEFLAGS, &ifr) < 0) + { + // For some strange reason, in /usr/include/sys/errno.h, EOPNOTSUPP is defined to be + // 102 when compiling kernel code, and 45 when compiling user-level code. Since this + // error code is being returned from the kernel, we need to use the kernel version. + #define KERNEL_EOPNOTSUPP 102 + if (errno != KERNEL_EOPNOTSUPP) // "Operation not supported on socket", the expected result on Leopard and earlier + LogMsg("NetWakeInterface SIOCGIFWAKEFLAGS %s errno %d (%s)", i->ifinfo.ifname, errno, strerror(errno)); + // If on Leopard or earlier, we get EOPNOTSUPP, so in that case + // we enable WOL if this interface is not AirPort and "Wake for Network access" is turned on. + ifr.ifr_wake_flags = (errno == KERNEL_EOPNOTSUPP && !(i)->BSSID.l[0] && i->m->SystemWakeOnLANEnabled) ? IF_WAKE_ON_MAGIC_PACKET : 0; + } + + close(s); + + // ifr.ifr_wake_flags = IF_WAKE_ON_MAGIC_PACKET; // For testing with MacBook Air, using a USB dongle that doesn't actually support Wake-On-LAN + + LogSPS("%-6s %#-14a %s WOMP", i->ifinfo.ifname, &i->ifinfo.ip, (ifr.ifr_wake_flags & IF_WAKE_ON_MAGIC_PACKET) ? "supports" : "no"); + + return((ifr.ifr_wake_flags & IF_WAKE_ON_MAGIC_PACKET) != 0); +} + +mDNSlocal u_int64_t getExtendedFlags(char * ifa_name) +{ + int sockFD; + struct ifreq ifr; + + sockFD = socket(AF_INET, SOCK_DGRAM, 0); + if (sockFD < 0) + { + LogMsg("getExtendedFlags: socket() call failed, errno = %d (%s)", errno, strerror(errno)); + return 0; + } + + ifr.ifr_addr.sa_family = AF_INET; + strlcpy(ifr.ifr_name, ifa_name, sizeof(ifr.ifr_name)); + + if (ioctl(sockFD, SIOCGIFEFLAGS, (caddr_t)&ifr) == -1) + { + LogMsg("getExtendedFlags: SIOCGIFEFLAGS failed, errno = %d (%s)", errno, strerror(errno)); + ifr.ifr_eflags = 0; + } + LogInfo("getExtendedFlags: %s ifr_eflags = 0x%x", ifa_name, ifr.ifr_eflags); + + close(sockFD); + return ifr.ifr_eflags; +} + +// Returns pointer to newly created NetworkInterfaceInfoOSX object, or +// pointer to already-existing NetworkInterfaceInfoOSX object found in list, or +// may return NULL if out of memory (unlikely) or parameters are invalid for some reason +// (e.g. sa_family not AF_INET or AF_INET6) +mDNSlocal NetworkInterfaceInfoOSX *AddInterfaceToList(mDNS *const m, struct ifaddrs *ifa, mDNSs32 utc) +{ + mDNSu32 scope_id = if_nametoindex(ifa->ifa_name); + mDNSEthAddr bssid = GetBSSID(ifa->ifa_name); + u_int64_t eflags = getExtendedFlags(ifa->ifa_name); + + mDNSAddr ip, mask; + if (SetupAddr(&ip, ifa->ifa_addr ) != mStatus_NoError) return(NULL); + if (SetupAddr(&mask, ifa->ifa_netmask) != mStatus_NoError) return(NULL); + + NetworkInterfaceInfoOSX **p; + for (p = &m->p->InterfaceList; *p; p = &(*p)->next) + if (scope_id == (*p)->scope_id && + mDNSSameAddress(&ip, &(*p)->ifinfo.ip) && + mDNSSameEthAddress(&bssid, &(*p)->BSSID)) + { + debugf("AddInterfaceToList: Found existing interface %lu %.6a with address %#a at %p, ifname before %s, after %s", scope_id, &bssid, &ip, *p, (*p)->ifinfo.ifname, ifa->ifa_name); + // The name should be updated to the new name so that we don't report a wrong name in our SIGINFO output. + // When interfaces are created with same MAC address, kernel resurrects the old interface. + // Even though the interface index is the same (which should be sufficient), when we receive a UDP packet + // we get the corresponding name for the interface index on which the packet was received and check against + // the InterfaceList for a matching name. So, keep the name in sync + strlcpy((*p)->ifinfo.ifname, ifa->ifa_name, sizeof((*p)->ifinfo.ifname)); + (*p)->Exists = mDNStrue; + // If interface was not in getifaddrs list last time we looked, but it is now, update 'AppearanceTime' for this record + if ((*p)->LastSeen != utc) (*p)->AppearanceTime = utc; + + // If Wake-on-LAN capability of this interface has changed (e.g. because power cable on laptop has been disconnected) + // we may need to start or stop or sleep proxy browse operation + const mDNSBool NetWake = NetWakeInterface(*p); + if ((*p)->ifinfo.NetWake != NetWake) + { + (*p)->ifinfo.NetWake = NetWake; + // If this interface is already registered with mDNSCore, then we need to start or stop its NetWake browse on-the-fly. + // If this interface is not already registered (i.e. it's a dormant interface we had in our list + // from when we previously saw it) then we mustn't do that, because mDNSCore doesn't know about it yet. + // In this case, the mDNS_RegisterInterface() call will take care of starting the NetWake browse if necessary. + if ((*p)->Registered) + { + mDNS_Lock(m); + if (NetWake) mDNS_ActivateNetWake_internal (m, &(*p)->ifinfo); + else mDNS_DeactivateNetWake_internal(m, &(*p)->ifinfo); + mDNS_Unlock(m); + } + } + // Reset the flag if it has changed this time. + (*p)->ifinfo.IgnoreIPv4LL = ((eflags & IFEF_ARPLL) != 0) ? mDNSfalse : mDNStrue; + + return(*p); + } + + NetworkInterfaceInfoOSX *i = (NetworkInterfaceInfoOSX *)mallocL("NetworkInterfaceInfoOSX", sizeof(*i)); + debugf("AddInterfaceToList: Making new interface %lu %.6a with address %#a at %p", scope_id, &bssid, &ip, i); + if (!i) return(mDNSNULL); + mDNSPlatformMemZero(i, sizeof(NetworkInterfaceInfoOSX)); + i->ifinfo.InterfaceID = (mDNSInterfaceID)(uintptr_t)scope_id; + i->ifinfo.ip = ip; + i->ifinfo.mask = mask; + strlcpy(i->ifinfo.ifname, ifa->ifa_name, sizeof(i->ifinfo.ifname)); + i->ifinfo.ifname[sizeof(i->ifinfo.ifname)-1] = 0; + // We can be configured to disable multicast advertisement, but we want to to support + // local-only services, which need a loopback address record. + i->ifinfo.Advertise = m->DivertMulticastAdvertisements ? ((ifa->ifa_flags & IFF_LOOPBACK) ? mDNStrue : mDNSfalse) : m->AdvertiseLocalAddresses; + i->ifinfo.McastTxRx = mDNSfalse; // For now; will be set up later at the end of UpdateInterfaceList + i->ifinfo.Loopback = ((ifa->ifa_flags & IFF_LOOPBACK) != 0) ? mDNStrue : mDNSfalse; + i->ifinfo.IgnoreIPv4LL = ((eflags & IFEF_ARPLL) != 0) ? mDNSfalse : mDNStrue; + i->ifinfo.DirectLink = (eflags & IFEF_DIRECTLINK) ? mDNStrue: mDNSfalse; + + i->next = mDNSNULL; + i->m = m; + i->Exists = mDNStrue; + i->Flashing = mDNSfalse; + i->Occulting = mDNSfalse; + i->D2DInterface = (eflags & IFEF_LOCALNET_PRIVATE) ? mDNStrue: mDNSfalse; + if (eflags & IFEF_AWDL) + { + AWDLInterfaceID = i->ifinfo.InterfaceID; + LogInfo("AddInterfaceToList: AWDLInterfaceID = %d", (int) AWDLInterfaceID); + } + i->AppearanceTime = utc; // Brand new interface; AppearanceTime is now + i->LastSeen = utc; + i->ifa_flags = ifa->ifa_flags; + i->scope_id = scope_id; + i->BSSID = bssid; + i->sa_family = ifa->ifa_addr->sa_family; + i->BPF_fd = -1; + i->BPF_mcfd = -1; + i->BPF_len = 0; + i->Registered = mDNSNULL; + + // Do this AFTER i->BSSID has been set up + i->ifinfo.NetWake = NetWakeInterface(i); + GetMAC(&i->ifinfo.MAC, scope_id); + if (i->ifinfo.NetWake && !i->ifinfo.MAC.l[0]) + LogMsg("AddInterfaceToList: Bad MAC address %.6a for %d %s %#a", &i->ifinfo.MAC, scope_id, i->ifinfo.ifname, &ip); + + *p = i; + return(i); +} + +#if APPLE_OSX_mDNSResponder + +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - +#pragma mark - AutoTunnel +#endif + +#define kRacoonPort 4500 + +static DomainAuthInfo* AnonymousRacoonConfig = mDNSNULL; + +#ifndef NO_SECURITYFRAMEWORK + +static CFMutableDictionaryRef domainStatusDict = NULL; + +mDNSlocal mStatus CheckQuestionForStatus(const DNSQuestion *const q) +{ + if (q->LongLived) + { + if (q->servAddr.type == mDNSAddrType_IPv4 && mDNSIPv4AddressIsOnes(q->servAddr.ip.v4)) + return mStatus_NoSuchRecord; + else if (q->state == LLQ_Poll) + return mStatus_PollingMode; + else if (q->state != LLQ_Established && !q->DuplicateOf) + return mStatus_TransientErr; + } + + return mStatus_NoError; +} + +mDNSlocal mStatus UpdateLLQStatus(const mDNS *const m, char *buffer, int bufsz, const DomainAuthInfo *const info) +{ + mStatus status = mStatus_NoError; + DNSQuestion* q, *worst_q = mDNSNULL; + for (q = m->Questions; q; q=q->next) + if (q->AuthInfo == info) + { + mStatus newStatus = CheckQuestionForStatus(q); + if (newStatus == mStatus_NoSuchRecord) { status = newStatus; worst_q = q; break; } + else if (newStatus == mStatus_PollingMode) { status = newStatus; worst_q = q; } + else if (newStatus == mStatus_TransientErr && status == mStatus_NoError) { status = newStatus; worst_q = q; } + } + + if (status == mStatus_NoError) mDNS_snprintf(buffer, bufsz, "Success"); + else if (status == mStatus_NoSuchRecord) mDNS_snprintf(buffer, bufsz, "GetZoneData %s: %##s", worst_q->nta ? "not yet complete" : "failed", worst_q->qname.c); + else if (status == mStatus_PollingMode) mDNS_snprintf(buffer, bufsz, "Query polling %##s", worst_q->qname.c); + else if (status == mStatus_TransientErr) mDNS_snprintf(buffer, bufsz, "Query not yet established %##s", worst_q->qname.c); + return status; +} + +mDNSlocal mStatus UpdateRRStatus(const mDNS *const m, char *buffer, int bufsz, const DomainAuthInfo *const info) +{ + AuthRecord *r; + + if (info->deltime) return mStatus_NoError; + for (r = m->ResourceRecords; r; r = r->next) + { + // This function is called from UpdateAutoTunnelDomainStatus which in turn may be called from + // a callback e.g., CheckNATMappings. GetAuthInfoFor_internal does not like that (reentrancy being 1), + // hence we inline the code here. We just need the lock to walk the list of AuthInfos which the caller + // has already checked + const domainname *n = r->resrec.name; + while (n->c[0]) + { + DomainAuthInfo *ptr; + for (ptr = m->AuthInfoList; ptr; ptr = ptr->next) + if (SameDomainName(&ptr->domain, n)) + { + if (ptr == info && (r->updateError == mStatus_BadSig || r->updateError == mStatus_BadKey || r->updateError == mStatus_BadTime)) + { + mDNS_snprintf(buffer, bufsz, "Resource record update failed for %##s", r->resrec.name); + return r->updateError; + } + } + n = (const domainname *)(n->c + 1 + n->c[0]); + } + } + return mStatus_NoError; +} + +#endif // ndef NO_SECURITYFRAMEWORK + +// MUST be called with lock held +mDNSlocal void UpdateAutoTunnelDomainStatus(const mDNS *const m, const DomainAuthInfo *const info) +{ +#ifdef NO_SECURITYFRAMEWORK + (void) m; + (void)info; +#else + // Note that in the LLQNAT, the clientCallback being non-zero means it's in use, + // whereas in the AutoTunnelNAT, the clientContext being non-zero means it's in use + const NATTraversalInfo *const llq = m->LLQNAT.clientCallback ? &m->LLQNAT : mDNSNULL; + const NATTraversalInfo *const tun = m->AutoTunnelNAT.clientContext ? &m->AutoTunnelNAT : mDNSNULL; + char buffer[1024]; + mDNSu32 buflen = 0; + CFMutableDictionaryRef dict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + CFStringRef domain = NULL; + CFStringRef tmp = NULL; + CFNumberRef num = NULL; + mStatus status = mStatus_NoError; + mStatus llqStatus = mStatus_NoError; + char llqBuffer[1024]; + + mDNS_CheckLock(m); + + if (!domainStatusDict) + { + domainStatusDict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + if (!domainStatusDict) { LogMsg("UpdateAutoTunnelDomainStatus: Could not create CFDictionary domainStatusDict"); return; } + } + + if (!dict) { LogMsg("UpdateAutoTunnelDomainStatus: Could not create CFDictionary dict"); return; } + + buflen = mDNS_snprintf(buffer, sizeof(buffer), "%##s", info->domain.c); + domain = CFStringCreateWithCString(NULL, buffer, kCFStringEncodingUTF8); + if (!domain) { LogMsg("UpdateAutoTunnelDomainStatus: Could not create CFString domain"); return; } + + if (info->deltime) + { + if (CFDictionaryContainsKey(domainStatusDict, domain)) + { + CFDictionaryRemoveValue(domainStatusDict, domain); + if (!m->ShutdownTime) mDNSDynamicStoreSetConfig(kmDNSBackToMyMacConfig, mDNSNULL, domainStatusDict); + } + CFRelease(domain); + CFRelease(dict); + + return; + } + + mDNS_snprintf(buffer, sizeof(buffer), "%#a", &m->Router); + tmp = CFStringCreateWithCString(NULL, buffer, kCFStringEncodingUTF8); + if (!tmp) + LogMsg("UpdateAutoTunnelDomainStatus: Could not create CFString RouterAddress"); + else + { + CFDictionarySetValue(dict, CFSTR("RouterAddress"), tmp); + CFRelease(tmp); + } + + if (llq) + { + mDNSu32 port = mDNSVal16(llq->ExternalPort); + + num = CFNumberCreate(NULL, kCFNumberSInt32Type, &port); + if (!num) + LogMsg("UpdateAutoTunnelDomainStatus: Could not create CFNumber LLQExternalPort"); + else + { + CFDictionarySetValue(dict, CFSTR("LLQExternalPort"), num); + CFRelease(num); + } + + if (llq->Result) + { + num = CFNumberCreate(NULL, kCFNumberSInt32Type, &llq->Result); + if (!num) + LogMsg("UpdateAutoTunnelDomainStatus: Could not create CFNumber LLQNPMStatus"); + else + { + CFDictionarySetValue(dict, CFSTR("LLQNPMStatus"), num); + CFRelease(num); + } + } + } + + if (tun) + { + mDNSu32 port = mDNSVal16(tun->ExternalPort); + + num = CFNumberCreate(NULL, kCFNumberSInt32Type, &port); + if (!num) + LogMsg("UpdateAutoTunnelDomainStatus: Could not create CFNumber AutoTunnelExternalPort"); + else + { + CFDictionarySetValue(dict, CFSTR("AutoTunnelExternalPort"), num); + CFRelease(num); + } + + mDNS_snprintf(buffer, sizeof(buffer), "%.4a", &tun->ExternalAddress); + tmp = CFStringCreateWithCString(NULL, buffer, kCFStringEncodingUTF8); + if (!tmp) + LogMsg("UpdateAutoTunnelDomainStatus: Could not create CFString ExternalAddress"); + else + { + CFDictionarySetValue(dict, CFSTR("ExternalAddress"), tmp); + CFRelease(tmp); + } + + if (tun->Result) + { + num = CFNumberCreate(NULL, kCFNumberSInt32Type, &tun->Result); + if (!num) + LogMsg("UpdateAutoTunnelDomainStatus: Could not create CFNumber AutoTunnelNPMStatus"); + else + { + CFDictionarySetValue(dict, CFSTR("AutoTunnelNPMStatus"), num); + CFRelease(num); + } + } + } + if (tun || llq) + { + mDNSu32 code = m->LastNATMapResultCode; + + num = CFNumberCreate(NULL, kCFNumberSInt32Type, &code); + if (!num) + LogMsg("UpdateAutoTunnelDomainStatus: Could not create CFNumber LastNATMapResultCode"); + else + { + CFDictionarySetValue(dict, CFSTR("LastNATMapResultCode"), num); + CFRelease(num); + } + } + + mDNS_snprintf(buffer, sizeof(buffer), "Success"); + llqStatus = UpdateLLQStatus(m, llqBuffer, sizeof(llqBuffer), info); + status = UpdateRRStatus(m, buffer, sizeof(buffer), info); + + // If we have a bad signature error updating a RR, it overrides any error as it needs to be + // reported so that it can be fixed automatically (or the user needs to be notified) + if (status != mStatus_NoError) + { + LogInfo("UpdateAutoTunnelDomainStatus: RR Status %d, %s", status, buffer); + } + else if (m->Router.type == mDNSAddrType_None) + { + status = mStatus_NoRouter; + mDNS_snprintf(buffer, sizeof(buffer), "No network connection - none"); + } + else if (m->Router.type == mDNSAddrType_IPv4 && mDNSIPv4AddressIsZero(m->Router.ip.v4)) + { + status = mStatus_NoRouter; + mDNS_snprintf(buffer, sizeof(buffer), "No network connection - v4 zero"); + } + else if (mDNSIPv6AddressIsZero(info->AutoTunnelInnerAddress)) + { + status = mStatus_ServiceNotRunning; + mDNS_snprintf(buffer, sizeof(buffer), "No inner address"); + } + else if (!llq && !tun) + { + status = mStatus_NotInitializedErr; + mDNS_snprintf(buffer, sizeof(buffer), "Neither LLQ nor AutoTunnel NAT port mapping is currently active"); + } + else if (llqStatus == mStatus_NoSuchRecord) + { + status = llqStatus; + mDNS_snprintf(buffer, sizeof(buffer), llqBuffer); + } + else if ((llq && llq->Result == mStatus_DoubleNAT) || (tun && tun->Result == mStatus_DoubleNAT)) + { + status = mStatus_DoubleNAT; + mDNS_snprintf(buffer, sizeof(buffer), "Double NAT: Router is reporting a private address"); + } + else if ((llq && llq->Result == mStatus_NATPortMappingDisabled) || + (tun && tun->Result == mStatus_NATPortMappingDisabled) || + (m->LastNATMapResultCode == NATErr_Refused && ((llq && !llq->Result && mDNSIPPortIsZero(llq->ExternalPort)) || (tun && !tun->Result && mDNSIPPortIsZero(tun->ExternalPort))))) + { + status = mStatus_NATPortMappingDisabled; + mDNS_snprintf(buffer, sizeof(buffer), "PCP/NAT-PMP is disabled on the router"); + } + else if ((llq && llq->Result) || (tun && tun->Result)) + { + status = mStatus_NATTraversal; + mDNS_snprintf(buffer, sizeof(buffer), "Error obtaining NAT port mapping from router"); + } + else if ((llq && mDNSIPPortIsZero(llq->ExternalPort)) || (tun && mDNSIPPortIsZero(tun->ExternalPort))) + { + status = mStatus_NATTraversal; + mDNS_snprintf(buffer, sizeof(buffer), "Unable to obtain NAT port mapping from router"); + } + else + { + status = llqStatus; + mDNS_snprintf(buffer, sizeof(buffer), llqBuffer); + LogInfo("UpdateAutoTunnelDomainStatus: LLQ Status %d, %s", status, buffer); + } + + num = CFNumberCreate(NULL, kCFNumberSInt32Type, &status); + if (!num) + LogMsg("UpdateAutoTunnelDomainStatus: Could not create CFNumber StatusCode"); + else + { + CFDictionarySetValue(dict, CFSTR("StatusCode"), num); + CFRelease(num); + } + + tmp = CFStringCreateWithCString(NULL, buffer, kCFStringEncodingUTF8); + if (!tmp) + LogMsg("UpdateAutoTunnelDomainStatus: Could not create CFString StatusMessage"); + else + { + CFDictionarySetValue(dict, CFSTR("StatusMessage"), tmp); + CFRelease(tmp); + } + + if (!CFDictionaryContainsKey(domainStatusDict, domain) || + !CFEqual(dict, (CFMutableDictionaryRef)CFDictionaryGetValue(domainStatusDict, domain))) + { + CFDictionarySetValue(domainStatusDict, domain, dict); + if (!m->ShutdownTime) + { + static char statusBuf[16]; + mDNS_snprintf(statusBuf, sizeof(statusBuf), "%d", (int)status); + mDNSASLLog((uuid_t *)&m->asl_uuid, "autotunnel.domainstatus", status ? "failure" : "success", statusBuf, ""); + mDNSDynamicStoreSetConfig(kmDNSBackToMyMacConfig, mDNSNULL, domainStatusDict); + } + } + + CFRelease(domain); + CFRelease(dict); + + debugf("UpdateAutoTunnelDomainStatus: %s", buffer); +#endif // def NO_SECURITYFRAMEWORK +} + +// MUST be called with lock held +mDNSexport void UpdateAutoTunnelDomainStatuses(const mDNS *const m) +{ +#ifdef NO_SECURITYFRAMEWORK + (void) m; +#else + mDNS_CheckLock(m); + DomainAuthInfo* info; + for (info = m->AuthInfoList; info; info = info->next) + if (info->AutoTunnel && !info->deltime) + UpdateAutoTunnelDomainStatus(m, info); +#endif // def NO_SECURITYFRAMEWORK +} + +mDNSlocal void UpdateAnonymousRacoonConfig(mDNS *m) // Determine whether we need racoon to accept incoming connections +{ + DomainAuthInfo *info; + + for (info = m->AuthInfoList; info; info = info->next) + if (info->AutoTunnel && !info->deltime && (!mDNSIPPortIsZero(m->AutoTunnelNAT.ExternalPort) || !mDNSIPv6AddressIsZero(m->AutoTunnelRelayAddr))) + break; + + if (info != AnonymousRacoonConfig) + { + AnonymousRacoonConfig = info; + // Create or revert configuration file, and start (or SIGHUP) Racoon + (void)mDNSConfigureServer(AnonymousRacoonConfig ? kmDNSUp : kmDNSDown, AnonymousRacoonConfig ? btmmprefix : mDNSNULL, AnonymousRacoonConfig ? &AnonymousRacoonConfig->domain : mDNSNULL); + } +} + +mDNSlocal void AutoTunnelRecordCallback(mDNS *const m, AuthRecord *const rr, mStatus result); + +// Caller must hold the lock +mDNSlocal mDNSBool DeregisterAutoTunnelRecord(mDNS *m, DomainAuthInfo *info, AuthRecord* record) +{ + mDNS_CheckLock(m); + + LogInfo("DeregisterAutoTunnelRecord %##s %##s", &info->domain.c, record->namestorage.c); + + if (record->resrec.RecordType > kDNSRecordTypeDeregistering) + { + mStatus err = mDNS_Deregister_internal(m, record, mDNS_Dereg_normal); + if (err) + { + record->resrec.RecordType = kDNSRecordTypeUnregistered; + LogMsg("DeregisterAutoTunnelRecord error %d deregistering %##s %##s", err, info->domain.c, record->namestorage.c); + return mDNSfalse; + } + else LogInfo("DeregisterAutoTunnelRecord: Deregistered"); + } + else LogInfo("DeregisterAutoTunnelRecord: Not deregistering, state:%d", record->resrec.RecordType); + + return mDNStrue; +} + +// Caller must hold the lock +mDNSlocal void DeregisterAutoTunnelHostRecord(mDNS *m, DomainAuthInfo *info) +{ + if (!DeregisterAutoTunnelRecord(m, info, &info->AutoTunnelHostRecord)) + { + info->AutoTunnelHostRecord.namestorage.c[0] = 0; + m->NextSRVUpdate = NonZeroTime(m->timenow); + } +} + +// Caller must hold the lock +mDNSlocal void UpdateAutoTunnelHostRecord(mDNS *m, DomainAuthInfo *info) +{ + mStatus err; + mDNSBool NATProblem = mDNSIPPortIsZero(m->AutoTunnelNAT.ExternalPort) || m->AutoTunnelNAT.Result; + + mDNS_CheckLock(m); + + if (!info->AutoTunnelServiceStarted || info->deltime || m->ShutdownTime || mDNSIPv6AddressIsZero(info->AutoTunnelInnerAddress) || (m->SleepState != SleepState_Awake && NATProblem)) + { + LogInfo("UpdateAutoTunnelHostRecord: Dereg %##s : AutoTunnelServiceStarted(%d) deltime(%d) address(%.16a) sleepstate(%d)", + info->domain.c, info->AutoTunnelServiceStarted, info->deltime, &info->AutoTunnelInnerAddress, m->SleepState); + DeregisterAutoTunnelHostRecord(m, info); + } + else if (info->AutoTunnelHostRecord.resrec.RecordType == kDNSRecordTypeUnregistered) + { + mDNS_SetupResourceRecord(&info->AutoTunnelHostRecord, mDNSNULL, mDNSInterface_Any, kDNSType_AAAA, kHostNameTTL, + kDNSRecordTypeUnregistered, AuthRecordAny, AutoTunnelRecordCallback, info); + info->AutoTunnelHostRecord.namestorage.c[0] = 0; + AppendDomainLabel(&info->AutoTunnelHostRecord.namestorage, &m->hostlabel); + AppendDomainName (&info->AutoTunnelHostRecord.namestorage, &info->domain); + info->AutoTunnelHostRecord.resrec.rdata->u.ipv6 = info->AutoTunnelInnerAddress; + info->AutoTunnelHostRecord.resrec.RecordType = kDNSRecordTypeKnownUnique; + + err = mDNS_Register_internal(m, &info->AutoTunnelHostRecord); + if (err) LogMsg("UpdateAutoTunnelHostRecord error %d registering %##s", err, info->AutoTunnelHostRecord.namestorage.c); + else + { + // Make sure we trigger the registration of all SRV records in regState_NoTarget again + m->NextSRVUpdate = NonZeroTime(m->timenow); + LogInfo("UpdateAutoTunnelHostRecord registering %##s", info->AutoTunnelHostRecord.namestorage.c); + } + } + else LogInfo("UpdateAutoTunnelHostRecord: Type %d", info->AutoTunnelHostRecord.resrec.RecordType); +} + +// Caller must hold the lock +mDNSlocal void DeregisterAutoTunnelServiceRecords(mDNS *m, DomainAuthInfo *info) +{ + LogInfo("DeregisterAutoTunnelServiceRecords %##s", info->domain.c); + + DeregisterAutoTunnelRecord(m, info, &info->AutoTunnelTarget); + DeregisterAutoTunnelRecord(m, info, &info->AutoTunnelService); + UpdateAutoTunnelHostRecord(m, info); +} + +// Caller must hold the lock +mDNSlocal void UpdateAutoTunnelServiceRecords(mDNS *m, DomainAuthInfo *info) +{ + mDNS_CheckLock(m); + + if (!info->AutoTunnelServiceStarted || info->deltime || m->ShutdownTime || mDNSIPPortIsZero(m->AutoTunnelNAT.ExternalPort) || m->AutoTunnelNAT.Result) + { + LogInfo("UpdateAutoTunnelServiceRecords: Dereg %##s : AutoTunnelServiceStarted(%d) deltime(%d) ExtPort(%d) NATResult(%d)", info->domain.c, info->AutoTunnelServiceStarted, info->deltime, mDNSVal16(m->AutoTunnelNAT.ExternalPort), m->AutoTunnelNAT.Result); + DeregisterAutoTunnelServiceRecords(m, info); + } + else + { + if (info->AutoTunnelTarget.resrec.RecordType == kDNSRecordTypeUnregistered) + { + // 1. Set up our address record for the external tunnel address + // (Constructed name, not generally user-visible, used as target in IKE tunnel's SRV record) + mDNS_SetupResourceRecord(&info->AutoTunnelTarget, mDNSNULL, mDNSInterface_Any, kDNSType_A, kHostNameTTL, + kDNSRecordTypeUnregistered, AuthRecordAny, AutoTunnelRecordCallback, info); + AssignDomainName (&info->AutoTunnelTarget.namestorage, (const domainname*) "\x0B" "_autotunnel"); + AppendDomainLabel(&info->AutoTunnelTarget.namestorage, &m->hostlabel); + AppendDomainName (&info->AutoTunnelTarget.namestorage, &info->domain); + info->AutoTunnelTarget.resrec.rdata->u.ipv4 = m->AutoTunnelNAT.ExternalAddress; + info->AutoTunnelTarget.resrec.RecordType = kDNSRecordTypeKnownUnique; + + mStatus err = mDNS_Register_internal(m, &info->AutoTunnelTarget); + if (err) LogMsg("UpdateAutoTunnelServiceRecords error %d registering %##s", err, info->AutoTunnelTarget.namestorage.c); + else LogInfo("UpdateAutoTunnelServiceRecords registering %##s", info->AutoTunnelTarget.namestorage.c); + } + else LogInfo("UpdateAutoTunnelServiceRecords: NOOP Target state(%d)", info->AutoTunnelTarget.resrec.RecordType); + + if (info->AutoTunnelService.resrec.RecordType == kDNSRecordTypeUnregistered) + { + // 2. Set up IKE tunnel's SRV record: _autotunnel._udp.AutoTunnelHost SRV 0 0 port AutoTunnelTarget + mDNS_SetupResourceRecord(&info->AutoTunnelService, mDNSNULL, mDNSInterface_Any, kDNSType_SRV, kHostNameTTL, + kDNSRecordTypeUnregistered, AuthRecordAny, AutoTunnelRecordCallback, info); + AssignDomainName (&info->AutoTunnelService.namestorage, (const domainname*) "\x0B" "_autotunnel" "\x04" "_udp"); + AppendDomainLabel(&info->AutoTunnelService.namestorage, &m->hostlabel); + AppendDomainName (&info->AutoTunnelService.namestorage, &info->domain); + info->AutoTunnelService.resrec.rdata->u.srv.priority = 0; + info->AutoTunnelService.resrec.rdata->u.srv.weight = 0; + info->AutoTunnelService.resrec.rdata->u.srv.port = m->AutoTunnelNAT.ExternalPort; + AssignDomainName(&info->AutoTunnelService.resrec.rdata->u.srv.target, &info->AutoTunnelTarget.namestorage); + info->AutoTunnelService.resrec.RecordType = kDNSRecordTypeKnownUnique; + + mStatus err = mDNS_Register_internal(m, &info->AutoTunnelService); + if (err) LogMsg("UpdateAutoTunnelServiceRecords error %d registering %##s", err, info->AutoTunnelService.namestorage.c); + else LogInfo("UpdateAutoTunnelServiceRecords registering %##s", info->AutoTunnelService.namestorage.c); + } + else LogInfo("UpdateAutoTunnelServiceRecords: NOOP Service state(%d)", info->AutoTunnelService.resrec.RecordType); + + UpdateAutoTunnelHostRecord(m, info); + + LogInfo("AutoTunnel server listening for connections on %##s[%.4a]:%d:%##s[%.16a]", + info->AutoTunnelTarget.namestorage.c, &m->AdvertisedV4.ip.v4, mDNSVal16(m->AutoTunnelNAT.IntPort), + info->AutoTunnelHostRecord.namestorage.c, &info->AutoTunnelInnerAddress); + + } +} + +// Caller must hold the lock +mDNSlocal void DeregisterAutoTunnelDeviceInfoRecord(mDNS *m, DomainAuthInfo *info) +{ + DeregisterAutoTunnelRecord(m, info, &info->AutoTunnelDeviceInfo); +} + +// Caller must hold the lock +mDNSlocal void UpdateAutoTunnelDeviceInfoRecord(mDNS *m, DomainAuthInfo *info) +{ + mDNS_CheckLock(m); + + if (!info->AutoTunnelServiceStarted || info->deltime || m->ShutdownTime) + DeregisterAutoTunnelDeviceInfoRecord(m, info); + else if (info->AutoTunnelDeviceInfo.resrec.RecordType == kDNSRecordTypeUnregistered) + { + mDNS_SetupResourceRecord(&info->AutoTunnelDeviceInfo, mDNSNULL, mDNSInterface_Any, kDNSType_TXT, kStandardTTL, kDNSRecordTypeUnregistered, AuthRecordAny, AutoTunnelRecordCallback, info); + ConstructServiceName(&info->AutoTunnelDeviceInfo.namestorage, &m->nicelabel, &DeviceInfoName, &info->domain); + + info->AutoTunnelDeviceInfo.resrec.rdlength = initializeDeviceInfoTXT(m, info->AutoTunnelDeviceInfo.resrec.rdata->u.data); + info->AutoTunnelDeviceInfo.resrec.RecordType = kDNSRecordTypeKnownUnique; + + mStatus err = mDNS_Register_internal(m, &info->AutoTunnelDeviceInfo); + if (err) LogMsg("UpdateAutoTunnelDeviceInfoRecord error %d registering %##s", err, info->AutoTunnelDeviceInfo.namestorage.c); + else LogInfo("UpdateAutoTunnelDeviceInfoRecord registering %##s", info->AutoTunnelDeviceInfo.namestorage.c); + } + else + LogInfo("UpdateAutoTunnelDeviceInfoRecord: not in Unregistered state: %d",info->AutoTunnelDeviceInfo.resrec.RecordType); +} + +// Caller must hold the lock +mDNSlocal void DeregisterAutoTunnel6Record(mDNS *m, DomainAuthInfo *info) +{ + LogInfo("DeregisterAutoTunnel6Record %##s", info->domain.c); + + DeregisterAutoTunnelRecord(m, info, &info->AutoTunnel6Record); + UpdateAutoTunnelHostRecord(m, info); + UpdateAutoTunnelDomainStatus(m, info); +} + +// Caller must hold the lock +mDNSlocal void UpdateAutoTunnel6Record(mDNS *m, DomainAuthInfo *info) +{ + mDNS_CheckLock(m); + + if (!info->AutoTunnelServiceStarted || info->deltime || m->ShutdownTime || mDNSIPv6AddressIsZero(m->AutoTunnelRelayAddr) || m->SleepState != SleepState_Awake) + DeregisterAutoTunnel6Record(m, info); + else if (info->AutoTunnel6Record.resrec.RecordType == kDNSRecordTypeUnregistered) + { + mDNS_SetupResourceRecord(&info->AutoTunnel6Record, mDNSNULL, mDNSInterface_Any, kDNSType_AAAA, kHostNameTTL, + kDNSRecordTypeUnregistered, AuthRecordAny, AutoTunnelRecordCallback, info); + AssignDomainName (&info->AutoTunnel6Record.namestorage, (const domainname*) "\x0C" "_autotunnel6"); + AppendDomainLabel(&info->AutoTunnel6Record.namestorage, &m->hostlabel); + AppendDomainName (&info->AutoTunnel6Record.namestorage, &info->domain); + info->AutoTunnel6Record.resrec.rdata->u.ipv6 = m->AutoTunnelRelayAddr; + info->AutoTunnel6Record.resrec.RecordType = kDNSRecordTypeKnownUnique; + + mStatus err = mDNS_Register_internal(m, &info->AutoTunnel6Record); + if (err) LogMsg("UpdateAutoTunnel6Record error %d registering %##s", err, info->AutoTunnel6Record.namestorage.c); + else LogInfo("UpdateAutoTunnel6Record registering %##s", info->AutoTunnel6Record.namestorage.c); + + UpdateAutoTunnelHostRecord(m, info); + + LogInfo("AutoTunnel6 server listening for connections on %##s[%.16a] :%##s[%.16a]", + info->AutoTunnel6Record.namestorage.c, &m->AutoTunnelRelayAddr, + info->AutoTunnelHostRecord.namestorage.c, &info->AutoTunnelInnerAddress); + + } + else LogInfo("UpdateAutoTunnel6Record NOOP state(%d)",info->AutoTunnel6Record.resrec.RecordType); +} + +mDNSlocal void AutoTunnelRecordCallback(mDNS *const m, AuthRecord *const rr, mStatus result) +{ + DomainAuthInfo *info = (DomainAuthInfo *)rr->RecordContext; + if (result == mStatus_MemFree) + { + LogInfo("AutoTunnelRecordCallback MemFree %s", ARDisplayString(m, rr)); + + mDNS_Lock(m); + + // Reset the host record namestorage to force high-level PTR/SRV/TXT to deregister + if (rr == &info->AutoTunnelHostRecord) + { + rr->namestorage.c[0] = 0; + m->NextSRVUpdate = NonZeroTime(m->timenow); + LogInfo("AutoTunnelRecordCallback: NextSRVUpdate in %d %d", m->NextSRVUpdate - m->timenow, m->timenow); + } + if (m->ShutdownTime) + { + LogInfo("AutoTunnelRecordCallback: Shutdown, returning"); + mDNS_Unlock(m); + return; + } + if (rr == &info->AutoTunnelHostRecord) + { + LogInfo("AutoTunnelRecordCallback: calling UpdateAutoTunnelHostRecord"); + UpdateAutoTunnelHostRecord(m,info); + } + else if (rr == &info->AutoTunnelDeviceInfo) + { + LogInfo("AutoTunnelRecordCallback: Calling UpdateAutoTunnelDeviceInfoRecord"); + UpdateAutoTunnelDeviceInfoRecord(m,info); + } + else if (rr == &info->AutoTunnelService || rr == &info->AutoTunnelTarget) + { + LogInfo("AutoTunnelRecordCallback: Calling UpdateAutoTunnelServiceRecords"); + UpdateAutoTunnelServiceRecords(m,info); + } + else if (rr == &info->AutoTunnel6Record) + { + LogInfo("AutoTunnelRecordCallback: Calling UpdateAutoTunnel6Record"); + UpdateAutoTunnel6Record(m,info); + } + + mDNS_Unlock(m); + } +} + +mDNSlocal void AutoTunnelNATCallback(mDNS *m, NATTraversalInfo *n) +{ + DomainAuthInfo *info; + + LogInfo("AutoTunnelNATCallback Result %d %.4a Internal %d External %d", + n->Result, &n->ExternalAddress, mDNSVal16(n->IntPort), mDNSVal16(n->ExternalPort)); + + mDNS_Lock(m); + + m->NextSRVUpdate = NonZeroTime(m->timenow); + LogInfo("AutoTunnelNATCallback: NextSRVUpdate in %d %d", m->NextSRVUpdate - m->timenow, m->timenow); + + for (info = m->AuthInfoList; info; info = info->next) + if (info->AutoTunnel) + UpdateAutoTunnelServiceRecords(m, info); + + UpdateAnonymousRacoonConfig(m); // Determine whether we need racoon to accept incoming connections + + UpdateAutoTunnelDomainStatuses(m); + + mDNS_Unlock(m); +} + +mDNSlocal void AutoTunnelHostNameChanged(mDNS *m, DomainAuthInfo *info) +{ + LogInfo("AutoTunnelHostNameChanged %#s.%##s", m->hostlabel.c, info->domain.c); + + mDNS_Lock(m); + // We forcibly deregister the records that are based on the hostname. + // When deregistration of each completes, the MemFree callback will make the + // appropriate Update* call to use the new name to reregister. + DeregisterAutoTunnelHostRecord(m, info); + DeregisterAutoTunnelDeviceInfoRecord(m, info); + DeregisterAutoTunnelServiceRecords(m, info); + DeregisterAutoTunnel6Record(m, info); + m->NextSRVUpdate = NonZeroTime(m->timenow); + mDNS_Unlock(m); +} + +// Must be called with the lock held +mDNSexport void StartServerTunnel(mDNS *const m, DomainAuthInfo *const info) +{ + if (info->deltime) return; + + if (info->AutoTunnelServiceStarted) + { + // On wake from sleep, this function will be called when determining SRV targets, + // and needs to re-register the host record for the target to be set correctly + UpdateAutoTunnelHostRecord(m, info); + return; + } + + info->AutoTunnelServiceStarted = mDNStrue; + + // Now that we have a service in this domain, we need to try to register the + // AutoTunnel records, because the relay connection & NAT-T may have already been + // started for another domain. If the relay connection is not up or the NAT-T has not + // yet succeeded, the Update* functions are smart enough to not register the records. + // Note: This should be done after we set AutoTunnelServiceStarted, as that variable is used to + // decide whether to register the AutoTunnel records in the calls below. + UpdateAutoTunnelServiceRecords(m, info); + UpdateAutoTunnel6Record(m, info); + UpdateAutoTunnelDeviceInfoRecord(m, info); + UpdateAutoTunnelHostRecord(m, info); + + // If the global AutoTunnel NAT-T is not yet started, start it. + if (!m->AutoTunnelNAT.clientContext) + { + m->AutoTunnelNAT.clientCallback = AutoTunnelNATCallback; + m->AutoTunnelNAT.clientContext = (void*)1; // Means AutoTunnelNAT Traversal is active; + m->AutoTunnelNAT.Protocol = NATOp_MapUDP; + m->AutoTunnelNAT.IntPort = IPSECPort; + m->AutoTunnelNAT.RequestedPort = IPSECPort; + m->AutoTunnelNAT.NATLease = 0; + mStatus err = mDNS_StartNATOperation_internal(m, &m->AutoTunnelNAT); + if (err) LogMsg("StartServerTunnel: error %d starting NAT mapping", err); + } +} + +mDNSlocal mStatus AutoTunnelSetKeys(ClientTunnel *tun, mDNSBool AddNew) +{ + mDNSv6Addr loc_outer6; + mDNSv6Addr rmt_outer6; + + // When we are tunneling over IPv6 Relay address, the port number is zero + if (mDNSIPPortIsZero(tun->rmt_outer_port)) + { + loc_outer6 = tun->loc_outer6; + rmt_outer6 = tun->rmt_outer6; + } + else + { + loc_outer6 = zerov6Addr; + loc_outer6.b[0] = tun->loc_outer.b[0]; + loc_outer6.b[1] = tun->loc_outer.b[1]; + loc_outer6.b[2] = tun->loc_outer.b[2]; + loc_outer6.b[3] = tun->loc_outer.b[3]; + + rmt_outer6 = zerov6Addr; + rmt_outer6.b[0] = tun->rmt_outer.b[0]; + rmt_outer6.b[1] = tun->rmt_outer.b[1]; + rmt_outer6.b[2] = tun->rmt_outer.b[2]; + rmt_outer6.b[3] = tun->rmt_outer.b[3]; + } + + return(mDNSAutoTunnelSetKeys(AddNew ? kmDNSAutoTunnelSetKeysReplace : kmDNSAutoTunnelSetKeysDelete, tun->loc_inner.b, loc_outer6.b, kRacoonPort, tun->rmt_inner.b, rmt_outer6.b, mDNSVal16(tun->rmt_outer_port), btmmprefix, SkipLeadingLabels(&tun->dstname, 1))); +} + +// If the EUI-64 part of the IPv6 ULA matches, then that means the two addresses point to the same machine +#define mDNSSameClientTunnel(A,B) ((A)->l[2] == (B)->l[2] && (A)->l[3] == (B)->l[3]) + +mDNSlocal void ReissueBlockedQuestionWithType(mDNS *const m, domainname *d, mDNSBool success, mDNSu16 qtype) +{ + DNSQuestion *q = m->Questions; + while (q) + { + if (q->NoAnswer == NoAnswer_Suspended && q->qtype == qtype && q->AuthInfo && q->AuthInfo->AutoTunnel && SameDomainName(&q->qname, d)) + { + LogInfo("Restart %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); + mDNSQuestionCallback *tmp = q->QuestionCallback; + q->QuestionCallback = AutoTunnelCallback; // Set QuestionCallback to suppress another call back to AddNewClientTunnel + mDNS_StopQuery(m, q); + mDNS_StartQuery(m, q); + q->QuestionCallback = tmp; // Restore QuestionCallback back to the real value + if (!success) q->NoAnswer = NoAnswer_Fail; + // When we call mDNS_StopQuery, it's possible for other subordinate questions like the GetZoneData query to be cancelled too. + // In general we have to assume that the question list might have changed in arbitrary ways. + // This code is itself called from a question callback, so the m->CurrentQuestion mechanism is + // already in use. The safest solution is just to go back to the start of the list and start again. + // In principle this sounds like an n^2 algorithm, but in practice we almost always activate + // just one suspended question, so it's really a 2n algorithm. + q = m->Questions; + } + else + q = q->next; + } +} + +mDNSlocal void ReissueBlockedQuestions(mDNS *const m, domainname *d, mDNSBool success) +{ + // 1. We deliberately restart AAAA queries before A queries, because in the common case where a BTTM host has + // a v6 address but no v4 address, we prefer the caller to get the positive AAAA response before the A NXDOMAIN. + // 2. In the case of AAAA queries, if our tunnel setup failed, then we return a deliberate failure indication to the caller -- + // even if the name does have a valid AAAA record, we don't want clients trying to connect to it without a properly encrypted tunnel. + // 3. For A queries we never fabricate failures -- if a BTTM service is really using raw IPv4, then it doesn't need the IPv6 tunnel. + ReissueBlockedQuestionWithType(m, d, success, kDNSType_AAAA); + ReissueBlockedQuestionWithType(m, d, mDNStrue, kDNSType_A); +} + +mDNSlocal void UnlinkAndReissueBlockedQuestions(mDNS *const m, ClientTunnel *tun, mDNSBool success) +{ + ClientTunnel **p = &m->TunnelClients; + while (*p != tun && *p) p = &(*p)->next; + if (*p) *p = tun->next; + ReissueBlockedQuestions(m, &tun->dstname, success); + LogInfo("UnlinkAndReissueBlockedQuestions: Disposing ClientTunnel %p", tun); + freeL("ClientTunnel", tun); +} + +mDNSlocal mDNSBool TunnelClientDeleteMatching(mDNS *const m, ClientTunnel *tun, mDNSBool v6Tunnel) +{ + ClientTunnel **p; + mDNSBool needSetKeys = mDNStrue; + + p = &tun->next; + while (*p) + { + // Is this a tunnel to the same host that we are trying to setup now? + if (!mDNSSameClientTunnel(&(*p)->rmt_inner, &tun->rmt_inner)) p = &(*p)->next; + else + { + ClientTunnel *old = *p; + if (v6Tunnel) + { + if (!mDNSIPPortIsZero(old->rmt_outer_port)) { p = &old->next; continue; } + LogInfo("TunnelClientDeleteMatching: Found existing IPv6 AutoTunnel for %##s %.16a", old->dstname.c, &old->rmt_inner); + if (old->q.ThisQInterval >= 0) + { + LogInfo("TunnelClientDeleteMatching: Stopping query on IPv6 AutoTunnel for %##s %.16a", old->dstname.c, &old->rmt_inner); + mDNS_StopQuery(m, &old->q); + } + else if (!mDNSSameIPv6Address((*p)->rmt_inner, tun->rmt_inner) || + !mDNSSameIPv6Address(old->loc_inner, tun->loc_inner) || + !mDNSSameIPv6Address(old->loc_outer6, tun->loc_outer6) || + !mDNSSameIPv6Address(old->rmt_outer6, tun->rmt_outer6)) + { + // Delete the old tunnel if the current tunnel to the same host does not have the same ULA or + // the other parameters of the tunnel are different + LogInfo("TunnelClientDeleteMatching: Deleting existing IPv6 AutoTunnel for %##s %.16a", old->dstname.c, &old->rmt_inner); + AutoTunnelSetKeys(old, mDNSfalse); + } + else + { + // Reusing the existing tunnel means that we reuse the IPsec SAs and the policies. We delete the old + // as "tun" and "old" are identical + LogInfo("TunnelClientDeleteMatching: Reusing the existing IPv6 AutoTunnel for %##s %.16a", old->dstname.c, + &old->rmt_inner); + needSetKeys = mDNSfalse; + } + } + else + { + if (mDNSIPPortIsZero(old->rmt_outer_port)) { p = &old->next; continue; } + LogInfo("TunnelClientDeleteMatching: Found existing IPv4 AutoTunnel for %##s %.16a", old->dstname.c, &old->rmt_inner); + if (old->q.ThisQInterval >= 0) + { + LogInfo("TunnelClientDeleteMatching: Stopping query on IPv4 AutoTunnel for %##s %.16a", old->dstname.c, &old->rmt_inner); + mDNS_StopQuery(m, &old->q); + } + else if (!mDNSSameIPv6Address((*p)->rmt_inner, tun->rmt_inner) || + !mDNSSameIPv6Address(old->loc_inner, tun->loc_inner) || + !mDNSSameIPv4Address(old->loc_outer, tun->loc_outer) || + !mDNSSameIPv4Address(old->rmt_outer, tun->rmt_outer) || + !mDNSSameIPPort(old->rmt_outer_port, tun->rmt_outer_port)) + { + // Delete the old tunnel if the current tunnel to the same host does not have the same ULA or + // the other parameters of the tunnel are different + LogInfo("TunnelClientDeleteMatching: Deleting existing IPv4 AutoTunnel for %##s %.16a", old->dstname.c, &old->rmt_inner); + AutoTunnelSetKeys(old, mDNSfalse); + } + else + { + // Reusing the existing tunnel means that we reuse the IPsec SAs and the policies. We delete the old + // as "tun" and "old" are identical + LogInfo("TunnelClientDeleteMatching: Reusing the existing IPv4 AutoTunnel for %##s %.16a", old->dstname.c, + &old->rmt_inner); + needSetKeys = mDNSfalse; + } + } + + *p = old->next; + LogInfo("TunnelClientDeleteMatching: Disposing ClientTunnel %p", old); + freeL("ClientTunnel", old); + } + } + return needSetKeys; +} + +// v6Tunnel indicates whether to delete a tunnel whose outer header is IPv6. If false, outer IPv4 +// tunnel will be deleted +mDNSlocal void TunnelClientDeleteAny(mDNS *const m, ClientTunnel *tun, mDNSBool v6Tunnel) +{ + ClientTunnel **p; + + p = &tun->next; + while (*p) + { + // If there is more than one client tunnel to the same host, delete all of them. + // We do this by just checking against the EUI64 rather than the full address + if (!mDNSSameClientTunnel(&(*p)->rmt_inner, &tun->rmt_inner)) p = &(*p)->next; + else + { + ClientTunnel *old = *p; + if (v6Tunnel) + { + if (!mDNSIPPortIsZero(old->rmt_outer_port)) { p = &old->next; continue;} + LogInfo("TunnelClientDeleteAny: Found existing IPv6 AutoTunnel for %##s %.16a", old->dstname.c, &old->rmt_inner); + } + else + { + if (mDNSIPPortIsZero(old->rmt_outer_port)) { p = &old->next; continue;} + LogInfo("TunnelClientDeleteAny: Found existing IPv4 AutoTunnel for %##s %.16a", old->dstname.c, &old->rmt_inner); + } + if (old->q.ThisQInterval >= 0) + { + LogInfo("TunnelClientDeleteAny: Stopping query on AutoTunnel for %##s %.16a", old->dstname.c, &old->rmt_inner); + mDNS_StopQuery(m, &old->q); + } + else + { + LogInfo("TunnelClientDeleteAny: Deleting existing AutoTunnel for %##s %.16a", old->dstname.c, &old->rmt_inner); + AutoTunnelSetKeys(old, mDNSfalse); + } + *p = old->next; + LogInfo("TunnelClientDeleteAny: Disposing ClientTunnel %p", old); + freeL("ClientTunnel", old); + } + } +} + +mDNSlocal void TunnelClientFinish(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer) +{ + mDNSBool needSetKeys = mDNStrue; + ClientTunnel *tun = (ClientTunnel *)question->QuestionContext; + mDNSBool v6Tunnel = mDNSfalse; + DomainAuthInfo *info; + + // If the port is zero, then we have a relay address of the peer + if (mDNSIPPortIsZero(tun->rmt_outer_port)) + v6Tunnel = mDNStrue; + + if (v6Tunnel) + { + LogInfo("TunnelClientFinish: Relay address %.16a", &answer->rdata->u.ipv6); + tun->rmt_outer6 = answer->rdata->u.ipv6; + tun->loc_outer6 = m->AutoTunnelRelayAddr; + } + else + { + LogInfo("TunnelClientFinish: SRV target address %.4a", &answer->rdata->u.ipv4); + tun->rmt_outer = answer->rdata->u.ipv4; + mDNSAddr tmpDst = { mDNSAddrType_IPv4, {{{0}}} }; + tmpDst.ip.v4 = tun->rmt_outer; + mDNSAddr tmpSrc = zeroAddr; + mDNSPlatformSourceAddrForDest(&tmpSrc, &tmpDst); + if (tmpSrc.type == mDNSAddrType_IPv4) tun->loc_outer = tmpSrc.ip.v4; + else tun->loc_outer = m->AdvertisedV4.ip.v4; + } + + question->ThisQInterval = -1; // So we know this tunnel setup has completed + + info = GetAuthInfoForName(m, &tun->dstname); + if (!info) + { + LogMsg("TunnelClientFinish: Could not get AuthInfo for %##s", tun->dstname.c); + ReissueBlockedQuestions(m, &tun->dstname, mDNSfalse); + return; + } + + tun->loc_inner = info->AutoTunnelInnerAddress; + + // If we found a v6Relay address for our peer, delete all the v4Tunnels for our peer and + // look for existing tunnels to see whether they have the same information for our peer. + // If not, delete them and need to create a new tunnel. If they are same, just use the + // same tunnel. Do the similar thing if we found a v4Tunnel end point for our peer. + TunnelClientDeleteAny(m, tun, !v6Tunnel); + needSetKeys = TunnelClientDeleteMatching(m, tun, v6Tunnel); + + if (needSetKeys) LogInfo("TunnelClientFinish: New %s AutoTunnel for %##s %.16a", (v6Tunnel ? "IPv6" : "IPv4"), tun->dstname.c, &tun->rmt_inner); + else LogInfo("TunnelClientFinish: Reusing exiting %s AutoTunnel for %##s %.16a", (v6Tunnel ? "IPv6" : "IPv4"), tun->dstname.c, &tun->rmt_inner); + + mStatus result = needSetKeys ? AutoTunnelSetKeys(tun, mDNStrue) : mStatus_NoError; + static char msgbuf[32]; + mDNS_snprintf(msgbuf, sizeof(msgbuf), "Tunnel setup - %d", result); + mDNSASLLog((uuid_t *)&m->asl_uuid, "autotunnel.config", result ? "failure" : "success", msgbuf, ""); + // Kick off any questions that were held pending this tunnel setup + ReissueBlockedQuestions(m, &tun->dstname, (result == mStatus_NoError) ? mDNStrue : mDNSfalse); +} + +mDNSexport void AutoTunnelCallback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord) +{ + ClientTunnel *tun = (ClientTunnel *)question->QuestionContext; + DomainAuthInfo *info; + + LogInfo("AutoTunnelCallback tun %p AddRecord %d rdlength %d qtype %d", tun, AddRecord, answer->rdlength, question->qtype); + + if (!AddRecord) return; + mDNS_StopQuery(m, question); + + // If we are looking up the AAAA record for _autotunnel6, don't consider it as failure. + // The code below will look for _autotunnel._udp SRV record followed by A record + if (tun->tc_state != TC_STATE_AAAA_PEER_RELAY && !answer->rdlength) + { + LogInfo("AutoTunnelCallback NXDOMAIN %##s (%s)", question->qname.c, DNSTypeName(question->qtype)); + static char msgbuf[16]; + mDNS_snprintf(msgbuf, sizeof(msgbuf), "%s lookup", DNSTypeName(question->qtype)); + mDNSASLLog((uuid_t *)&m->asl_uuid, "autotunnel.config", "failure", msgbuf, ""); + UnlinkAndReissueBlockedQuestions(m, tun, mDNSfalse); + return; + } + + switch (tun->tc_state) + { + case TC_STATE_AAAA_PEER: + if (question->qtype != kDNSType_AAAA) + { + LogMsg("AutoTunnelCallback: Bad question type %d in TC_STATE_AAAA_PEER", question->qtype); + } + info = GetAuthInfoForName(m, &tun->dstname); + if (!info) + { + LogMsg("AutoTunnelCallback: Could not get AuthInfo for %##s", tun->dstname.c); + UnlinkAndReissueBlockedQuestions(m, tun, mDNStrue); + return; + } + if (mDNSSameIPv6Address(answer->rdata->u.ipv6, info->AutoTunnelInnerAddress)) + { + LogInfo("AutoTunnelCallback: suppressing tunnel to self %.16a", &answer->rdata->u.ipv6); + UnlinkAndReissueBlockedQuestions(m, tun, mDNStrue); + return; + } + if (info && mDNSSameIPv6NetworkPart(answer->rdata->u.ipv6, info->AutoTunnelInnerAddress)) + { + LogInfo("AutoTunnelCallback: suppressing tunnel to peer %.16a", &answer->rdata->u.ipv6); + UnlinkAndReissueBlockedQuestions(m, tun, mDNStrue); + return; + } + tun->rmt_inner = answer->rdata->u.ipv6; + LogInfo("AutoTunnelCallback:TC_STATE_AAAA_PEER: dst host %.16a", &tun->rmt_inner); + if (!mDNSIPv6AddressIsZero(m->AutoTunnelRelayAddr)) + { + LogInfo("AutoTunnelCallback: Looking up _autotunnel6 AAAA"); + tun->tc_state = TC_STATE_AAAA_PEER_RELAY; + question->qtype = kDNSType_AAAA; + AssignDomainName(&question->qname, (const domainname*) "\x0C" "_autotunnel6"); + } + else + { + LogInfo("AutoTunnelCallback: Looking up _autotunnel._udp SRV"); + tun->tc_state = TC_STATE_SRV_PEER; + question->qtype = kDNSType_SRV; + AssignDomainName(&question->qname, (const domainname*) "\x0B" "_autotunnel" "\x04" "_udp"); + } + AppendDomainName(&question->qname, &tun->dstname); + mDNS_StartQuery(m, &tun->q); + return; + case TC_STATE_AAAA_PEER_RELAY: + if (question->qtype != kDNSType_AAAA) + { + LogMsg("AutoTunnelCallback: Bad question type %d in TC_STATE_AAAA_PEER_RELAY", question->qtype); + } + // If it failed, look for the SRV record. + if (!answer->rdlength) + { + LogInfo("AutoTunnelCallback: Looking up _autotunnel6 AAAA failed, trying SRV"); + tun->tc_state = TC_STATE_SRV_PEER; + AssignDomainName(&question->qname, (const domainname*) "\x0B" "_autotunnel" "\x04" "_udp"); + AppendDomainName(&question->qname, &tun->dstname); + question->qtype = kDNSType_SRV; + mDNS_StartQuery(m, &tun->q); + return; + } + TunnelClientFinish(m, question, answer); + return; + case TC_STATE_SRV_PEER: + if (question->qtype != kDNSType_SRV) + { + LogMsg("AutoTunnelCallback: Bad question type %d in TC_STATE_SRV_PEER", question->qtype); + } + LogInfo("AutoTunnelCallback: SRV target name %##s", answer->rdata->u.srv.target.c); + tun->tc_state = TC_STATE_ADDR_PEER; + AssignDomainName(&tun->q.qname, &answer->rdata->u.srv.target); + tun->rmt_outer_port = answer->rdata->u.srv.port; + question->qtype = kDNSType_A; + mDNS_StartQuery(m, &tun->q); + return; + case TC_STATE_ADDR_PEER: + if (question->qtype != kDNSType_A) + { + LogMsg("AutoTunnelCallback: Bad question type %d in TC_STATE_ADDR_PEER", question->qtype); + } + TunnelClientFinish(m, question, answer); + return; + default: + LogMsg("AutoTunnelCallback: Unknown question %p", question); + } +} + +// Must be called with the lock held +mDNSexport void AddNewClientTunnel(mDNS *const m, DNSQuestion *const q) +{ + ClientTunnel *p = mallocL("ClientTunnel", sizeof(ClientTunnel)); + if (!p) return; + AssignDomainName(&p->dstname, &q->qname); + p->MarkedForDeletion = mDNSfalse; + p->loc_inner = zerov6Addr; + p->loc_outer = zerov4Addr; + p->loc_outer6 = zerov6Addr; + p->rmt_inner = zerov6Addr; + p->rmt_outer = zerov4Addr; + p->rmt_outer6 = zerov6Addr; + p->rmt_outer_port = zeroIPPort; + p->tc_state = TC_STATE_AAAA_PEER; + p->next = m->TunnelClients; + m->TunnelClients = p; // We intentionally build list in reverse order + + p->q.InterfaceID = mDNSInterface_Any; + p->q.flags = 0; + p->q.Target = zeroAddr; + AssignDomainName(&p->q.qname, &q->qname); + p->q.qtype = kDNSType_AAAA; + p->q.qclass = kDNSClass_IN; + p->q.LongLived = mDNSfalse; + p->q.ExpectUnique = mDNStrue; + p->q.ForceMCast = mDNSfalse; + p->q.ReturnIntermed = mDNStrue; + p->q.SuppressUnusable = mDNSfalse; + p->q.SearchListIndex = 0; + p->q.AppendSearchDomains = 0; + p->q.RetryWithSearchDomains = mDNSfalse; + p->q.TimeoutQuestion = 0; + p->q.WakeOnResolve = 0; + p->q.UseBackgroundTrafficClass = mDNSfalse; + p->q.ValidationRequired = 0; + p->q.ValidatingResponse = 0; + p->q.ProxyQuestion = 0; + p->q.qnameOrig = mDNSNULL; + p->q.AnonInfo = mDNSNULL; + p->q.pid = mDNSPlatformGetPID(); + p->q.QuestionCallback = AutoTunnelCallback; + p->q.QuestionContext = p; + + LogInfo("AddNewClientTunnel start tun %p %##s (%s)%s", p, &q->qname.c, DNSTypeName(q->qtype), q->LongLived ? " LongLived" : ""); + mDNS_StartQuery_internal(m, &p->q); +} + +#endif // APPLE_OSX_mDNSResponder + +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - +#pragma mark - Power State & Configuration Change Management +#endif + +mDNSlocal mStatus UpdateInterfaceList(mDNS *const m, mDNSs32 utc) +{ + mDNSBool foundav4 = mDNSfalse; + mDNSBool foundav6 = mDNSfalse; + struct ifaddrs *ifa = myGetIfAddrs(1); + struct ifaddrs *v4Loopback = NULL; + struct ifaddrs *v6Loopback = NULL; + char defaultname[64]; + int InfoSocket = socket(AF_INET6, SOCK_DGRAM, 0); + if (InfoSocket < 3 && errno != EAFNOSUPPORT) + LogMsg("UpdateInterfaceList: InfoSocket error %d errno %d (%s)", InfoSocket, errno, strerror(errno)); + + while (ifa) + { +#if LIST_ALL_INTERFACES + if (ifa->ifa_addr->sa_family == AF_APPLETALK) + LogMsg("UpdateInterfaceList: %5s(%d) Flags %04X Family %2d is AF_APPLETALK", + ifa->ifa_name, if_nametoindex(ifa->ifa_name), ifa->ifa_flags, ifa->ifa_addr->sa_family); + else if (ifa->ifa_addr->sa_family == AF_LINK) + LogMsg("UpdateInterfaceList: %5s(%d) Flags %04X Family %2d is AF_LINK", + ifa->ifa_name, if_nametoindex(ifa->ifa_name), ifa->ifa_flags, ifa->ifa_addr->sa_family); + else if (ifa->ifa_addr->sa_family != AF_INET && ifa->ifa_addr->sa_family != AF_INET6) + LogMsg("UpdateInterfaceList: %5s(%d) Flags %04X Family %2d not AF_INET (2) or AF_INET6 (30)", + ifa->ifa_name, if_nametoindex(ifa->ifa_name), ifa->ifa_flags, ifa->ifa_addr->sa_family); + if (!(ifa->ifa_flags & IFF_UP)) + LogMsg("UpdateInterfaceList: %5s(%d) Flags %04X Family %2d Interface not IFF_UP", + ifa->ifa_name, if_nametoindex(ifa->ifa_name), ifa->ifa_flags, ifa->ifa_addr->sa_family); + if (!(ifa->ifa_flags & IFF_MULTICAST)) + LogMsg("UpdateInterfaceList: %5s(%d) Flags %04X Family %2d Interface not IFF_MULTICAST", + ifa->ifa_name, if_nametoindex(ifa->ifa_name), ifa->ifa_flags, ifa->ifa_addr->sa_family); + if (ifa->ifa_flags & IFF_POINTOPOINT) + LogMsg("UpdateInterfaceList: %5s(%d) Flags %04X Family %2d Interface IFF_POINTOPOINT", + ifa->ifa_name, if_nametoindex(ifa->ifa_name), ifa->ifa_flags, ifa->ifa_addr->sa_family); + if (ifa->ifa_flags & IFF_LOOPBACK) + LogMsg("UpdateInterfaceList: %5s(%d) Flags %04X Family %2d Interface IFF_LOOPBACK", + ifa->ifa_name, if_nametoindex(ifa->ifa_name), ifa->ifa_flags, ifa->ifa_addr->sa_family); +#endif + + if (ifa->ifa_addr->sa_family == AF_LINK) + { + struct sockaddr_dl *sdl = (struct sockaddr_dl *)ifa->ifa_addr; + if (sdl->sdl_type == IFT_ETHER && sdl->sdl_alen == sizeof(m->PrimaryMAC) && mDNSSameEthAddress(&m->PrimaryMAC, &zeroEthAddr)) + mDNSPlatformMemCopy(m->PrimaryMAC.b, sdl->sdl_data + sdl->sdl_nlen, 6); + } + + if (ifa->ifa_flags & IFF_UP && ifa->ifa_addr) + if (ifa->ifa_addr->sa_family == AF_INET || ifa->ifa_addr->sa_family == AF_INET6) + { + if (!ifa->ifa_netmask) + { + mDNSAddr ip; + SetupAddr(&ip, ifa->ifa_addr); + LogMsg("getifaddrs: ifa_netmask is NULL for %5s(%d) Flags %04X Family %2d %#a", + ifa->ifa_name, if_nametoindex(ifa->ifa_name), ifa->ifa_flags, ifa->ifa_addr->sa_family, &ip); + } + // Apparently it's normal for the sa_family of an ifa_netmask to sometimes be zero, so we don't complain about that + // <rdar://problem/5492035> getifaddrs is returning invalid netmask family for fw0 and vmnet + else if (ifa->ifa_netmask->sa_family != ifa->ifa_addr->sa_family && ifa->ifa_netmask->sa_family != 0) + { + mDNSAddr ip; + SetupAddr(&ip, ifa->ifa_addr); + LogMsg("getifaddrs ifa_netmask for %5s(%d) Flags %04X Family %2d %#a has different family: %d", + ifa->ifa_name, if_nametoindex(ifa->ifa_name), ifa->ifa_flags, ifa->ifa_addr->sa_family, &ip, ifa->ifa_netmask->sa_family); + } + // Currently we use a few internal ones like mDNSInterfaceID_LocalOnly etc. that are negative values (0, -1, -2). + else if ((int)if_nametoindex(ifa->ifa_name) <= 0) + { + LogMsg("UpdateInterfaceList: if_nametoindex returned zero/negative value for %5s(%d)", ifa->ifa_name, if_nametoindex(ifa->ifa_name)); + } + else + { + // Make sure ifa_netmask->sa_family is set correctly + // <rdar://problem/5492035> getifaddrs is returning invalid netmask family for fw0 and vmnet + ifa->ifa_netmask->sa_family = ifa->ifa_addr->sa_family; + int ifru_flags6 = 0; + + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)ifa->ifa_addr; + if (ifa->ifa_addr->sa_family == AF_INET6 && InfoSocket >= 0) + { + struct in6_ifreq ifr6; + mDNSPlatformMemZero((char *)&ifr6, sizeof(ifr6)); + strlcpy(ifr6.ifr_name, ifa->ifa_name, sizeof(ifr6.ifr_name)); + ifr6.ifr_addr = *sin6; + if (ioctl(InfoSocket, SIOCGIFAFLAG_IN6, &ifr6) != -1) + ifru_flags6 = ifr6.ifr_ifru.ifru_flags6; + verbosedebugf("%s %.16a %04X %04X", ifa->ifa_name, &sin6->sin6_addr, ifa->ifa_flags, ifru_flags6); + } + + if (!(ifru_flags6 & (IN6_IFF_NOTREADY | IN6_IFF_DETACHED | IN6_IFF_DEPRECATED | IN6_IFF_TEMPORARY))) + { + if (ifa->ifa_flags & IFF_LOOPBACK) + { + if (ifa->ifa_addr->sa_family == AF_INET) + v4Loopback = ifa; + else if (sin6->sin6_addr.s6_addr[0] != 0xFD) + v6Loopback = ifa; + } + else + { + NetworkInterfaceInfoOSX *i = AddInterfaceToList(m, ifa, utc); + if (i && MulticastInterface(i) && i->ifinfo.Advertise) + { + if (ifa->ifa_addr->sa_family == AF_INET) + foundav4 = mDNStrue; + else + foundav6 = mDNStrue; + } + } + } + } + } + ifa = ifa->ifa_next; + } + + // For efficiency, we don't register a loopback interface when other interfaces of that family are available and advertising + if (!foundav4 && v4Loopback) AddInterfaceToList(m, v4Loopback, utc); + if (!foundav6 && v6Loopback) AddInterfaceToList(m, v6Loopback, utc); + + // Now the list is complete, set the McastTxRx setting for each interface. + NetworkInterfaceInfoOSX *i; + for (i = m->p->InterfaceList; i; i = i->next) + if (i->Exists) + { + mDNSBool txrx = MulticastInterface(i); + if (i->ifinfo.McastTxRx != txrx) + { + i->ifinfo.McastTxRx = txrx; + i->Exists = 2; // State change; need to deregister and reregister this interface + } + } + + if (InfoSocket >= 0) + close(InfoSocket); + + mDNS_snprintf(defaultname, sizeof(defaultname), "%.*s-%02X%02X%02X%02X%02X%02X", HINFO_HWstring_prefixlen, HINFO_HWstring, + m->PrimaryMAC.b[0], m->PrimaryMAC.b[1], m->PrimaryMAC.b[2], m->PrimaryMAC.b[3], m->PrimaryMAC.b[4], m->PrimaryMAC.b[5]); + + // Set up the nice label + domainlabel nicelabel; + nicelabel.c[0] = 0; + GetUserSpecifiedFriendlyComputerName(&nicelabel); + if (nicelabel.c[0] == 0) + { + debugf("Couldn’t read user-specified Computer Name; using default “%s” instead", defaultname); + MakeDomainLabelFromLiteralString(&nicelabel, defaultname); + } + + // Set up the RFC 1034-compliant label + domainlabel hostlabel; + hostlabel.c[0] = 0; + GetUserSpecifiedLocalHostName(&hostlabel); + if (hostlabel.c[0] == 0) + { + debugf("Couldn’t read user-specified Local Hostname; using default “%s.local” instead", defaultname); + MakeDomainLabelFromLiteralString(&hostlabel, defaultname); + } + + mDNSBool namechange = mDNSfalse; + + // We use a case-sensitive comparison here because even though changing the capitalization + // of the name alone is not significant to DNS, it's still a change from the user's point of view + if (SameDomainLabelCS(m->p->usernicelabel.c, nicelabel.c)) + debugf("Usernicelabel (%#s) unchanged since last time; not changing m->nicelabel (%#s)", m->p->usernicelabel.c, m->nicelabel.c); + else + { + if (m->p->usernicelabel.c[0]) // Don't show message first time through, when we first read name from prefs on boot + LogMsg("User updated Computer Name from “%#s” to “%#s”", m->p->usernicelabel.c, nicelabel.c); + m->p->usernicelabel = m->nicelabel = nicelabel; + namechange = mDNStrue; + } + + if (SameDomainLabelCS(m->p->userhostlabel.c, hostlabel.c)) + debugf("Userhostlabel (%#s) unchanged since last time; not changing m->hostlabel (%#s)", m->p->userhostlabel.c, m->hostlabel.c); + else + { + if (m->p->userhostlabel.c[0]) // Don't show message first time through, when we first read name from prefs on boot + LogMsg("User updated Local Hostname from “%#s” to “%#s”", m->p->userhostlabel.c, hostlabel.c); + m->p->userhostlabel = m->hostlabel = hostlabel; + mDNS_SetFQDN(m); + namechange = mDNStrue; + } + +#if APPLE_OSX_mDNSResponder + if (namechange) // If either name has changed, we need to tickle our AutoTunnel state machine to update its registered records + { + DomainAuthInfo *info; + for (info = m->AuthInfoList; info; info = info->next) + if (info->AutoTunnel) AutoTunnelHostNameChanged(m, info); + } +#endif // APPLE_OSX_mDNSResponder + + return(mStatus_NoError); +} + +// Returns number of leading one-bits in mask: 0-32 for IPv4, 0-128 for IPv6 +// Returns -1 if all the one-bits are not contiguous +mDNSlocal int CountMaskBits(mDNSAddr *mask) +{ + int i = 0, bits = 0; + int bytes = mask->type == mDNSAddrType_IPv4 ? 4 : mask->type == mDNSAddrType_IPv6 ? 16 : 0; + while (i < bytes) + { + mDNSu8 b = mask->ip.v6.b[i++]; + while (b & 0x80) { bits++; b <<= 1; } + if (b) return(-1); + } + while (i < bytes) if (mask->ip.v6.b[i++]) return(-1); + return(bits); +} + +// returns count of non-link local V4 addresses registered +mDNSlocal int SetupActiveInterfaces(mDNS *const m, mDNSs32 utc) +{ + NetworkInterfaceInfoOSX *i; + int count = 0; + for (i = m->p->InterfaceList; i; i = i->next) + if (i->Exists) + { + NetworkInterfaceInfo *const n = &i->ifinfo; + NetworkInterfaceInfoOSX *primary = SearchForInterfaceByName(m, i->ifinfo.ifname, AF_UNSPEC); + if (!primary) LogMsg("SetupActiveInterfaces ERROR! SearchForInterfaceByName didn't find %s", i->ifinfo.ifname); + + if (i->Registered && i->Registered != primary) // Sanity check + { + LogMsg("SetupActiveInterfaces ERROR! n->Registered %p != primary %p", i->Registered, primary); + i->Registered = mDNSNULL; + } + + if (!i->Registered) + { + // Note: If i->Registered is set, that means we've called mDNS_RegisterInterface() for this interface, + // so we need to make sure we call mDNS_DeregisterInterface() before disposing it. + // If i->Registered is NOT set, then we haven't registered it and we should not try to deregister it + // + + i->Registered = primary; + + // If i->LastSeen == utc, then this is a brand-new interface, just created, or an interface that never went away. + // If i->LastSeen != utc, then this is an old interface, previously seen, that went away for (utc - i->LastSeen) seconds. + // If the interface is an old one that went away and came back in less than a minute, then we're in a flapping scenario. + i->Occulting = !(i->ifa_flags & IFF_LOOPBACK) && (utc - i->LastSeen > 0 && utc - i->LastSeen < 60); + + // Temporary fix to handle P2P flapping. P2P reuses the scope-id, mac address and the IP address + // everytime it creates a new interface. We think it is a duplicate and hence consider it + // as flashing and occulting, that is, flapping. If an interface is marked as flapping, + // mDNS_RegisterInterface() changes the probe delay from 1/2 second to 5 seconds and + // logs a warning message to system.log noting frequent interface transitions. + // Same logic applies when IFEF_DIRECTLINK flag is set on the interface. + if ((strncmp(i->ifinfo.ifname, "p2p", 3) == 0) || i->ifinfo.DirectLink) + { + LogInfo("SetupActiveInterfaces: %s interface registering %s %s", i->ifinfo.ifname, + i->Flashing ? " (Flashing)" : "", + i->Occulting ? " (Occulting)" : ""); + mDNS_RegisterInterface(m, n, 0); + } + else + { + mDNS_RegisterInterface(m, n, i->Flashing && i->Occulting); + } + + if (!mDNSAddressIsLinkLocal(&n->ip)) count++; + LogInfo("SetupActiveInterfaces: Registered %5s(%lu) %.6a InterfaceID %p(%p), primary %p, %#a/%d%s%s%s", + i->ifinfo.ifname, i->scope_id, &i->BSSID, i->ifinfo.InterfaceID, i, primary, &n->ip, CountMaskBits(&n->mask), + i->Flashing ? " (Flashing)" : "", + i->Occulting ? " (Occulting)" : "", + n->InterfaceActive ? " (Primary)" : ""); + + if (!n->McastTxRx) + debugf("SetupActiveInterfaces: No Tx/Rx on %5s(%lu) %.6a InterfaceID %p %#a", i->ifinfo.ifname, i->scope_id, &i->BSSID, i->ifinfo.InterfaceID, &n->ip); + else + { + if (i->sa_family == AF_INET) + { + struct ip_mreq imr; + primary->ifa_v4addr.s_addr = n->ip.ip.v4.NotAnInteger; + imr.imr_multiaddr.s_addr = AllDNSLinkGroup_v4.ip.v4.NotAnInteger; + imr.imr_interface = primary->ifa_v4addr; + + // If this is our *first* IPv4 instance for this interface name, we need to do a IP_DROP_MEMBERSHIP first, + // before trying to join the group, to clear out stale kernel state which may be lingering. + // In particular, this happens with removable network interfaces like USB Ethernet adapters -- the kernel has stale state + // from the last time the USB Ethernet adapter was connected, and part of the kernel thinks we've already joined the group + // on that interface (so we get EADDRINUSE when we try to join again) but a different part of the kernel thinks we haven't + // joined the group (so we receive no multicasts). Doing an IP_DROP_MEMBERSHIP before joining seems to flush the stale state. + // Also, trying to make the code leave the group when the adapter is removed doesn't work either, + // because by the time we get the configuration change notification, the interface is already gone, + // so attempts to unsubscribe fail with EADDRNOTAVAIL (errno 49 "Can't assign requested address"). + // <rdar://problem/5585972> IP_ADD_MEMBERSHIP fails for previously-connected removable interfaces + if (SearchForInterfaceByName(m, i->ifinfo.ifname, AF_INET) == i) + { + LogInfo("SetupActiveInterfaces: %5s(%lu) Doing precautionary IP_DROP_MEMBERSHIP for %.4a on %.4a", i->ifinfo.ifname, i->scope_id, &imr.imr_multiaddr, &imr.imr_interface); + mStatus err = setsockopt(m->p->permanentsockets.sktv4, IPPROTO_IP, IP_DROP_MEMBERSHIP, &imr, sizeof(imr)); + if (err < 0 && (errno != EADDRNOTAVAIL)) + LogMsg("setsockopt - IP_DROP_MEMBERSHIP error %d errno %d (%s)", err, errno, strerror(errno)); + } + + LogInfo("SetupActiveInterfaces: %5s(%lu) joining IPv4 mcast group %.4a on %.4a", i->ifinfo.ifname, i->scope_id, &imr.imr_multiaddr, &imr.imr_interface); + mStatus err = setsockopt(m->p->permanentsockets.sktv4, IPPROTO_IP, IP_ADD_MEMBERSHIP, &imr, sizeof(imr)); + // Joining same group twice can give "Address already in use" error -- no need to report that + if (err < 0 && (errno != EADDRINUSE)) + LogMsg("setsockopt - IP_ADD_MEMBERSHIP error %d errno %d (%s) group %.4a on %.4a", err, errno, strerror(errno), &imr.imr_multiaddr, &imr.imr_interface); + } + if (i->sa_family == AF_INET6) + { + struct ipv6_mreq i6mr; + i6mr.ipv6mr_interface = primary->scope_id; + i6mr.ipv6mr_multiaddr = *(struct in6_addr*)&AllDNSLinkGroup_v6.ip.v6; + + if (SearchForInterfaceByName(m, i->ifinfo.ifname, AF_INET6) == i) + { + LogInfo("SetupActiveInterfaces: %5s(%lu) Doing precautionary IPV6_LEAVE_GROUP for %.16a on %u", i->ifinfo.ifname, i->scope_id, &i6mr.ipv6mr_multiaddr, i6mr.ipv6mr_interface); + mStatus err = setsockopt(m->p->permanentsockets.sktv6, IPPROTO_IPV6, IPV6_LEAVE_GROUP, &i6mr, sizeof(i6mr)); + if (err < 0 && (errno != EADDRNOTAVAIL)) + LogMsg("setsockopt - IPV6_LEAVE_GROUP error %d errno %d (%s) group %.16a on %u", err, errno, strerror(errno), &i6mr.ipv6mr_multiaddr, i6mr.ipv6mr_interface); + } + + LogInfo("SetupActiveInterfaces: %5s(%lu) joining IPv6 mcast group %.16a on %u", i->ifinfo.ifname, i->scope_id, &i6mr.ipv6mr_multiaddr, i6mr.ipv6mr_interface); + mStatus err = setsockopt(m->p->permanentsockets.sktv6, IPPROTO_IPV6, IPV6_JOIN_GROUP, &i6mr, sizeof(i6mr)); + // Joining same group twice can give "Address already in use" error -- no need to report that + if (err < 0 && (errno != EADDRINUSE)) + LogMsg("setsockopt - IPV6_JOIN_GROUP error %d errno %d (%s) group %.16a on %u", err, errno, strerror(errno), &i6mr.ipv6mr_multiaddr, i6mr.ipv6mr_interface); + } + } + } + } + + return count; +} + +mDNSlocal void MarkAllInterfacesInactive(mDNS *const m, mDNSs32 utc) +{ + NetworkInterfaceInfoOSX *i; + for (i = m->p->InterfaceList; i; i = i->next) + { + if (i->Exists) i->LastSeen = utc; + i->Exists = mDNSfalse; + } +} + +// returns count of non-link local V4 addresses deregistered +mDNSlocal int ClearInactiveInterfaces(mDNS *const m, mDNSs32 utc) +{ + // First pass: + // If an interface is going away, then deregister this from the mDNSCore. + // We also have to deregister it if the primary interface that it's using for its InterfaceID is going away. + // We have to do this because mDNSCore will use that InterfaceID when sending packets, and if the memory + // it refers to has gone away we'll crash. + NetworkInterfaceInfoOSX *i; + int count = 0; + for (i = m->p->InterfaceList; i; i = i->next) + { + // If this interface is no longer active, or its InterfaceID is changing, deregister it + NetworkInterfaceInfoOSX *primary = SearchForInterfaceByName(m, i->ifinfo.ifname, AF_UNSPEC); + if (i->Registered) + if (i->Exists == 0 || i->Exists == 2 || i->Registered != primary) + { + i->Flashing = !(i->ifa_flags & IFF_LOOPBACK) && (utc - i->AppearanceTime < 60); + LogInfo("ClearInactiveInterfaces: Deregistering %5s(%lu) %.6a InterfaceID %p(%p), primary %p, %#a/%d%s%s%s", + i->ifinfo.ifname, i->scope_id, &i->BSSID, i->ifinfo.InterfaceID, i, primary, + &i->ifinfo.ip, CountMaskBits(&i->ifinfo.mask), + i->Flashing ? " (Flashing)" : "", + i->Occulting ? " (Occulting)" : "", + i->ifinfo.InterfaceActive ? " (Primary)" : ""); + + // Temporary fix to handle P2P flapping. P2P reuses the scope-id, mac address and the IP address + // everytime it creates a new interface. We think it is a duplicate and hence consider it + // as flashing and occulting. The "core" does not flush the cache for this case. This leads to + // stale data returned to the application even after the interface is removed. The application + // then starts to send data but the new interface is not yet created. + // Same logic applies when IFEF_DIRECTLINK flag is set on the interface. + if ((strncmp(i->ifinfo.ifname, "p2p", 3) == 0) || i->ifinfo.DirectLink) + { + LogInfo("ClearInactiveInterfaces: %s interface deregistering %s %s", i->ifinfo.ifname, + i->Flashing ? " (Flashing)" : "", + i->Occulting ? " (Occulting)" : ""); + mDNS_DeregisterInterface(m, &i->ifinfo, 0); + } + else + { + mDNS_DeregisterInterface(m, &i->ifinfo, i->Flashing && i->Occulting); + } + if (!mDNSAddressIsLinkLocal(&i->ifinfo.ip)) count++; + i->Registered = mDNSNULL; + // Note: If i->Registered is set, that means we've called mDNS_RegisterInterface() for this interface, + // so we need to make sure we call mDNS_DeregisterInterface() before disposing it. + // If i->Registered is NOT set, then it's not registered and we should not call mDNS_DeregisterInterface() on it. + + // Caution: If we ever decide to add code here to leave the multicast group, we need to make sure that this + // is the LAST representative of this physical interface, or we'll unsubscribe from the group prematurely. + } + } + + // Second pass: + // Now that everything that's going to deregister has done so, we can clean up and free the memory + NetworkInterfaceInfoOSX **p = &m->p->InterfaceList; + while (*p) + { + i = *p; + // If no longer active, delete interface from list and free memory + if (!i->Exists) + { + if (i->LastSeen == utc) i->LastSeen = utc - 1; + mDNSBool delete = (NumCacheRecordsForInterfaceID(m, i->ifinfo.InterfaceID) == 0) && (utc - i->LastSeen >= 60); + LogInfo("ClearInactiveInterfaces: %-13s %5s(%lu) %.6a InterfaceID %p(%p) %#a/%d Age %d%s", delete ? "Deleting" : "Holding", + i->ifinfo.ifname, i->scope_id, &i->BSSID, i->ifinfo.InterfaceID, i, + &i->ifinfo.ip, CountMaskBits(&i->ifinfo.mask), utc - i->LastSeen, + i->ifinfo.InterfaceActive ? " (Primary)" : ""); +#if APPLE_OSX_mDNSResponder + if (i->BPF_fd >= 0) CloseBPF(i); +#endif // APPLE_OSX_mDNSResponder + if (delete) + { + *p = i->next; + freeL("NetworkInterfaceInfoOSX", i); + continue; // After deleting this object, don't want to do the "p = &i->next;" thing at the end of the loop + } + } + p = &i->next; + } + return count; +} + +mDNSlocal void AppendDNameListElem(DNameListElem ***List, mDNSu32 uid, domainname *name) +{ + DNameListElem *dnle = (DNameListElem*) mallocL("DNameListElem/AppendDNameListElem", sizeof(DNameListElem)); + if (!dnle) LogMsg("ERROR: AppendDNameListElem: memory exhausted"); + else + { + dnle->next = mDNSNULL; + dnle->uid = uid; + AssignDomainName(&dnle->name, name); + **List = dnle; + *List = &dnle->next; + } +} + +mDNSlocal int compare_dns_configs(const void *aa, const void *bb) +{ + dns_resolver_t *a = *(dns_resolver_t**)aa; + dns_resolver_t *b = *(dns_resolver_t**)bb; + + return (a->search_order < b->search_order) ? -1 : (a->search_order == b->search_order) ? 0 : 1; +} + +mDNSlocal void UpdateSearchDomainHash(mDNS *const m, MD5_CTX *sdc, char *domain, mDNSInterfaceID InterfaceID) +{ + char *buf = "."; + mDNSu32 scopeid = 0; + char ifid_buf[16]; + + if (domain) + buf = domain; + // + // Hash the search domain name followed by the InterfaceID. + // As we have scoped search domains, we also included InterfaceID. If either of them change, + // we will detect it. Even if the order of them change, we will detect it. + // + // Note: We have to handle a few of these tricky cases. + // + // 1) Current: com, apple.com Changing to: comapple.com + // 2) Current: a.com,b.com Changing to a.comb.com + // 3) Current: a.com,b.com (ifid 8), Changing to a.com8b.com (ifid 8) + // 4) Current: a.com (ifid 12), Changing to a.com1 (ifid: 2) + // + // There are more variants of the above. The key thing is if we include the null in each case + // at the end of name and the InterfaceID, it will prevent a new name (which can't include + // NULL as part of the name) to be mistakenly thought of as a old name. + + scopeid = mDNSPlatformInterfaceIndexfromInterfaceID(m, InterfaceID, mDNStrue); + // mDNS_snprintf always null terminates + if (mDNS_snprintf(ifid_buf, sizeof(ifid_buf), "%u", scopeid) >= sizeof(ifid_buf)) + LogMsg("UpdateSearchDomainHash: mDNS_snprintf failed for scopeid %u", scopeid); + + LogInfo("UpdateSearchDomainHash: buf %s, ifid_buf %s", buf, ifid_buf); + MD5_Update(sdc, buf, strlen(buf) + 1); + MD5_Update(sdc, ifid_buf, strlen(ifid_buf) + 1); +} + +mDNSlocal void FinalizeSearchDomainHash(mDNS *const m, MD5_CTX *sdc) +{ + mDNSu8 md5_hash[MD5_LEN]; + + MD5_Final(md5_hash, sdc); + + if (memcmp(md5_hash, m->SearchDomainsHash, MD5_LEN)) + { + // If the hash is different, either the search domains have changed or + // the ordering between them has changed. Restart the questions that + // would be affected by this. + LogInfo("FinalizeSearchDomains: The hash is different"); + memcpy(m->SearchDomainsHash, md5_hash, MD5_LEN); + RetrySearchDomainQuestions(m); + } + else { LogInfo("FinalizeSearchDomains: The hash is same"); } +} + +mDNSexport const char *DNSScopeToString(mDNSu32 scope) +{ + switch (scope) + { + case kScopeNone: + return "Unscoped"; + case kScopeInterfaceID: + return "InterfaceScoped"; + case kScopeServiceID: + return "ServiceScoped"; + default: + return "Unknown"; + } +} + +mDNSlocal void ConfigSearchDomains(mDNS *const m, dns_resolver_t *resolver, mDNSInterfaceID interface, mDNSu32 scope, MD5_CTX *sdc) +{ + const char *scopeString = DNSScopeToString(scope); + int j; + + if (scope != kScopeNone) + { + LogInfo("ConfigSearchDomains: (%s) Ignoring search domain for Interface %p", scopeString, interface); + return; + } + for (j = 0; j < resolver->n_search; j++) + { + LogInfo("ConfigSearchDomains: (%s) configuring search list %s", scopeString, resolver->search[j]); + UpdateSearchDomainHash(m, sdc, resolver->search[j], NULL); + mDNS_AddSearchDomain_CString(resolver->search[j], NULL); + } +} + +mDNSlocal mDNSInterfaceID ConfigParseInterfaceID(mDNS *const m, mDNSu32 ifindex) +{ + NetworkInterfaceInfoOSX *ni; + mDNSInterfaceID interface; + + for (ni = m->p->InterfaceList; ni; ni = ni->next) + { + if (ni->ifinfo.InterfaceID && ni->scope_id == ifindex) + break; + } + if (ni != NULL) + { + interface = ni->ifinfo.InterfaceID; + } + else + { + // In rare circumstances, we could potentially hit this case where we cannot parse the InterfaceID + // (see <rdar://problem/13214785>). At this point, we still accept the DNS Config from configd + // Note: We currently ack the whole dns configuration and not individual resolvers or DNS servers. + // As the caller is going to ack the configuration always, we have to add all the DNS servers + // in the configuration. Otherwise, we won't have any DNS servers up until the network change. + + LogMsg("ConfigParseInterfaceID: interface specific index %d not found (interface may not be UP)",ifindex); + + // Set the correct interface from configd before passing this to mDNS_AddDNSServer() below + interface = (mDNSInterfaceID)(unsigned long)ifindex; + } + return interface; +} + +mDNSlocal void ConfigNonUnicastResolver(mDNS *const m, dns_resolver_t *r) +{ + char *opt = r->options; + domainname d; + + if (opt && !strncmp(opt, "mdns", strlen(opt))) + { + if (!MakeDomainNameFromDNSNameString(&d, r->domain)) + { + LogMsg("ConfigNonUnicastResolver: config->resolver bad domain %s", r->domain); + return; + } + mDNS_AddMcastResolver(m, &d, mDNSInterface_Any, r->timeout); + } +} + +mDNSlocal void ConfigDNSServers(mDNS *const m, dns_resolver_t *r, mDNSInterfaceID interface, mDNSu32 scope, mDNSu16 resGroupID) +{ + int n; + domainname d; + int serviceID = 0; + mDNSBool cellIntf = mDNSfalse; + mDNSBool scopedDNS = mDNSfalse; + mDNSBool reqA, reqAAAA; + + if (!r->domain || !*r->domain) + { + d.c[0] = 0; + } + else if (!MakeDomainNameFromDNSNameString(&d, r->domain)) + { + LogMsg("ConfigDNSServers: bad domain %s", r->domain); + return; + } + // Parse the resolver specific attributes that affects all the DNS servers. + if (scope == kScopeInterfaceID) + { + scopedDNS = mDNStrue; + } + else if (scope == kScopeServiceID) + { + serviceID = r->service_identifier; + } + +#if TARGET_OS_IPHONE + cellIntf = (r->reach_flags & kSCNetworkReachabilityFlagsIsWWAN) ? mDNStrue : mDNSfalse; +#endif + reqA = (r->flags & DNS_RESOLVER_FLAGS_REQUEST_A_RECORDS ? mDNStrue : mDNSfalse); + reqAAAA = (r->flags & DNS_RESOLVER_FLAGS_REQUEST_AAAA_RECORDS ? mDNStrue : mDNSfalse); + + for (n = 0; n < r->n_nameserver; n++) + { + mDNSAddr saddr; + DNSServer *s; + + if (r->nameserver[n]->sa_family != AF_INET && r->nameserver[n]->sa_family != AF_INET6) + continue; + + if (SetupAddr(&saddr, r->nameserver[n])) + { + LogMsg("ConfigDNSServers: Bad address"); + continue; + } + + // The timeout value is for all the DNS servers in a given resolver, hence we pass + // the timeout value only for the first DNSServer. If we don't have a value in the + // resolver, then use the core's default value + // + // Note: this assumes that when the core picks a list of DNSServers for a question, + // it takes the sum of all the timeout values for all DNS servers. By doing this, it + // tries all the DNS servers in a specified timeout + s = mDNS_AddDNSServer(m, &d, interface, serviceID, &saddr, r->port ? mDNSOpaque16fromIntVal(r->port) : UnicastDNSPort, scope, + (n == 0 ? (r->timeout ? r->timeout : DEFAULT_UDNS_TIMEOUT) : 0), cellIntf, resGroupID, reqA, reqAAAA, mDNStrue); + if (s) + { + LogInfo("ConfigDNSServers(%s): DNS server %#a:%d for domain %##s", DNSScopeToString(scope), &s->addr, mDNSVal16(s->port), d.c); + } + } +} + +// ConfigResolvers is called for different types of resolvers: Unscoped resolver, Interface scope resolver and +// Service scope resolvers. This is indicated by the scope argument. +// +// "resolver" has entries that should only be used for unscoped questions. +// +// "scoped_resolver" has entries that should only be used for Interface scoped question i.e., questions that specify an +// interface index (q->InterfaceID) +// +// "service_specific_resolver" has entries that should be used for Service scoped question i.e., questions that specify +// a service identifier (q->ServiceID) +// +mDNSlocal void ConfigResolvers(mDNS *const m, dns_config_t *config, mDNSu32 scope, mDNSBool setsearch, mDNSBool setservers, MD5_CTX *sdc, mDNSu16 resGroupID) +{ + int i; + dns_resolver_t **resolver; + int nresolvers; + const char *scopeString = DNSScopeToString(scope); + mDNSInterfaceID interface; + + switch (scope) + { + case kScopeNone: + resolver = config->resolver; + nresolvers = config->n_resolver; + break; + case kScopeInterfaceID: + resolver = config->scoped_resolver; + nresolvers = config->n_scoped_resolver; + break; + case kScopeServiceID: + resolver = config->service_specific_resolver; + nresolvers = config->n_service_specific_resolver; + break; + default: + return; + } + qsort(resolver, nresolvers, sizeof(dns_resolver_t*), compare_dns_configs); + + for (i = 0; i < nresolvers; i++) + { + dns_resolver_t *r = resolver[i]; + + LogInfo("ConfigResolvers: %s resolver[%d] domain %s n_nameserver %d", scopeString, i, r->domain, r->n_nameserver); + + interface = mDNSInterface_Any; + + // Parse the interface index + if (r->if_index != 0) + { + interface = ConfigParseInterfaceID(m, r->if_index); + } + + if (setsearch) + { + ConfigSearchDomains(m, resolver[i], interface, scope, sdc); + // Parse other scoped resolvers for search lists + if (!setservers) + continue; + } + + if (r->port == 5353 || r->n_nameserver == 0) + { + ConfigNonUnicastResolver(m, r); + } + else + { + // Each scoped resolver gets its own ID (i.e., they are in their own group) so that responses from the + // scoped resolver are not used by other non-scoped or scoped resolvers. + if (scope != kScopeNone) + resGroupID++; + + ConfigDNSServers(m, r, interface, scope, resGroupID); + } + } +} + +#if APPLE_OSX_mDNSResponder +mDNSlocal mDNSBool QuestionValidForDNSTrigger(DNSQuestion *q) +{ + if (QuerySuppressed(q)) + { + debugf("QuestionValidForDNSTrigger: Suppressed: %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); + return mDNSfalse; + } + if (mDNSOpaque16IsZero(q->TargetQID)) + { + debugf("QuestionValidForDNSTrigger: Multicast: %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); + return mDNSfalse; + } + // If we answered using LocalOnly records e.g., /etc/hosts, don't consider that a valid response + // for trigger. + if (q->LOAddressAnswers) + { + debugf("QuestionValidForDNSTrigger: LocalOnly answers: %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); + return mDNSfalse; + } + return mDNStrue; +} +#endif + +// This function is called if we are not delivering unicast answers to "A" or "AAAA" questions. +// We set our state appropriately so that if we start receiving answers, trigger the +// upper layer to retry DNS questions. +#if APPLE_OSX_mDNSResponder +mDNSexport void mDNSPlatformUpdateDNSStatus(mDNS *const m, DNSQuestion *q) +{ + if (!QuestionValidForDNSTrigger(q)) + return; + + // Ignore applications that start and stop queries for no reason before we ever talk + // to any DNS server. + if (!q->triedAllServersOnce) + { + LogInfo("QuestionValidForDNSTrigger: question %##s (%s) stopped too soon", q->qname.c, DNSTypeName(q->qtype)); + return; + } + if (q->qtype == kDNSType_A) + m->p->v4answers = 0; + if (q->qtype == kDNSType_AAAA) + m->p->v6answers = 0; + if (!m->p->v4answers || !m->p->v6answers) + { + LogInfo("mDNSPlatformUpdateDNSStatus: Trigger needed v4 %d, v6 %d, quesiton %##s (%s)", m->p->v4answers, m->p->v6answers, q->qname.c, + DNSTypeName(q->qtype)); + } +} +#endif + +mDNSlocal void AckConfigd(mDNS *const m, dns_config_t *config) +{ + mDNS_CheckLock(m); + + // Acking the configuration triggers configd to reissue the reachability queries + m->p->DNSTrigger = NonZeroTime(m->timenow); + _dns_configuration_ack(config, "com.apple.mDNSResponder"); +} + +// If v4q is non-NULL, it means we have received some answers for "A" type questions +// If v6q is non-NULL, it means we have received some answers for "AAAA" type questions +#if APPLE_OSX_mDNSResponder +mDNSexport void mDNSPlatformTriggerDNSRetry(mDNS *const m, DNSQuestion *v4q, DNSQuestion *v6q) +{ + mDNSBool trigger = mDNSfalse; + mDNSs32 timenow; + + // Don't send triggers too often. + // If we have started delivering answers to questions, we should send a trigger + // if the time permits. If we are delivering answers, we should set the state + // of v4answers/v6answers to 1 and avoid sending a trigger. But, we don't know + // whether the answers that are being delivered currently is for configd or some + // other application. If we set the v4answers/v6answers to 1 and not deliver a trigger, + // then we won't deliver the trigger later when it is okay to send one as the + // "answers" are already set to 1. Hence, don't affect the state of v4answers and + // v6answers if we are not delivering triggers. + mDNS_Lock(m); + timenow = m->timenow; + if (m->p->DNSTrigger && (timenow - m->p->DNSTrigger) < DNS_TRIGGER_INTERVAL) + { + if (!m->p->v4answers || !m->p->v6answers) + { + debugf("mDNSPlatformTriggerDNSRetry: not triggering, time since last trigger %d ms, v4ans %d, v6ans %d", + (timenow - m->p->DNSTrigger), m->p->v4answers, m->p->v6answers); + } + mDNS_Unlock(m); + return; + } + mDNS_Unlock(m); + if (v4q != NULL && QuestionValidForDNSTrigger(v4q)) + { + int old = m->p->v4answers; + + m->p->v4answers = 1; + + // If there are IPv4 answers now and previously we did not have + // any answers, trigger a DNS change so that reachability + // can retry the queries again. + if (!old) + { + LogInfo("mDNSPlatformTriggerDNSRetry: Triggering because of IPv4, last trigger %d ms, %##s (%s)", (timenow - m->p->DNSTrigger), + v4q->qname.c, DNSTypeName(v4q->qtype)); + trigger = mDNStrue; + } + } + if (v6q != NULL && QuestionValidForDNSTrigger(v6q)) + { + int old = m->p->v6answers; + + m->p->v6answers = 1; + // If there are IPv6 answers now and previously we did not have + // any answers, trigger a DNS change so that reachability + // can retry the queries again. + if (!old) + { + LogInfo("mDNSPlatformTriggerDNSRetry: Triggering because of IPv6, last trigger %d ms, %##s (%s)", (timenow - m->p->DNSTrigger), + v6q->qname.c, DNSTypeName(v6q->qtype)); + trigger = mDNStrue; + } + } + if (trigger) + { + dns_config_t *config = dns_configuration_copy(); + if (config) + { + mDNS_Lock(m); + AckConfigd(m, config); + mDNS_Unlock(m); + dns_configuration_free(config); + } + else + { + LogMsg("mDNSPlatformTriggerDNSRetry: ERROR!! configd did not return config"); + } + } +} + +mDNSlocal void SetupActiveDirectoryDomain(dns_config_t *config) +{ + // Record the so-called "primary" domain, which we use as a hint to tell if the user is on a network set up + // by someone using Microsoft Active Directory using "local" as a private internal top-level domain + if (config->n_resolver && config->resolver[0]->domain && config->resolver[0]->n_nameserver && + config->resolver[0]->nameserver[0]) + { + MakeDomainNameFromDNSNameString(&ActiveDirectoryPrimaryDomain, config->resolver[0]->domain); + } + else + { + ActiveDirectoryPrimaryDomain.c[0] = 0; + } + + //MakeDomainNameFromDNSNameString(&ActiveDirectoryPrimaryDomain, "test.local"); + ActiveDirectoryPrimaryDomainLabelCount = CountLabels(&ActiveDirectoryPrimaryDomain); + if (config->n_resolver && config->resolver[0]->n_nameserver && + SameDomainName(SkipLeadingLabels(&ActiveDirectoryPrimaryDomain, ActiveDirectoryPrimaryDomainLabelCount - 1), &localdomain)) + { + SetupAddr(&ActiveDirectoryPrimaryDomainServer, config->resolver[0]->nameserver[0]); + } + else + { + AssignDomainName(&ActiveDirectoryPrimaryDomain, (const domainname *)""); + ActiveDirectoryPrimaryDomainLabelCount = 0; + ActiveDirectoryPrimaryDomainServer = zeroAddr; + } +} +#endif + +mDNSlocal void SetupDDNSDomains(domainname *const fqdn, DNameListElem **RegDomains, DNameListElem **BrowseDomains) +{ + int i; + char buf[MAX_ESCAPED_DOMAIN_NAME]; // Max legal C-string name, including terminating NUL + domainname d; + + SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("mDNSResponder:mDNSPlatformSetDNSConfig"), NULL, NULL); + if (!store) + { + LogMsg("SetupDDNSDomains: SCDynamicStoreCreate failed: %s", SCErrorString(SCError())); + } + else + { + CFDictionaryRef ddnsdict = SCDynamicStoreCopyValue(store, NetworkChangedKey_DynamicDNS); + if (ddnsdict) + { + if (fqdn) + { + CFArrayRef fqdnArray = CFDictionaryGetValue(ddnsdict, CFSTR("HostNames")); + if (fqdnArray && CFArrayGetCount(fqdnArray) > 0) + { + // for now, we only look at the first array element. if we ever support multiple configurations, we will walk the list + CFDictionaryRef fqdnDict = CFArrayGetValueAtIndex(fqdnArray, 0); + if (fqdnDict && DictionaryIsEnabled(fqdnDict)) + { + CFStringRef name = CFDictionaryGetValue(fqdnDict, CFSTR("Domain")); + if (name) + { + if (!CFStringGetCString(name, buf, sizeof(buf), kCFStringEncodingUTF8) || + !MakeDomainNameFromDNSNameString(fqdn, buf) || !fqdn->c[0]) + LogMsg("GetUserSpecifiedDDNSConfig SCDynamicStore bad DDNS host name: %s", buf[0] ? buf : "(unknown)"); + else debugf("GetUserSpecifiedDDNSConfig SCDynamicStore DDNS host name: %s", buf); + } + } + } + } + + if (RegDomains) + { + CFArrayRef regArray = CFDictionaryGetValue(ddnsdict, CFSTR("RegistrationDomains")); + if (regArray && CFArrayGetCount(regArray) > 0) + { + CFDictionaryRef regDict = CFArrayGetValueAtIndex(regArray, 0); + if (regDict && DictionaryIsEnabled(regDict)) + { + CFStringRef name = CFDictionaryGetValue(regDict, CFSTR("Domain")); + if (name) + { + if (!CFStringGetCString(name, buf, sizeof(buf), kCFStringEncodingUTF8) || + !MakeDomainNameFromDNSNameString(&d, buf) || !d.c[0]) + LogMsg("GetUserSpecifiedDDNSConfig SCDynamicStore bad DDNS registration domain: %s", buf[0] ? buf : "(unknown)"); + else + { + debugf("GetUserSpecifiedDDNSConfig SCDynamicStore DDNS registration domain: %s", buf); + AppendDNameListElem(&RegDomains, 0, &d); + } + } + } + } + } + + if (BrowseDomains) + { + CFArrayRef browseArray = CFDictionaryGetValue(ddnsdict, CFSTR("BrowseDomains")); + if (browseArray) + { + for (i = 0; i < CFArrayGetCount(browseArray); i++) + { + CFDictionaryRef browseDict = CFArrayGetValueAtIndex(browseArray, i); + if (browseDict && DictionaryIsEnabled(browseDict)) + { + CFStringRef name = CFDictionaryGetValue(browseDict, CFSTR("Domain")); + if (name) + { + if (!CFStringGetCString(name, buf, sizeof(buf), kCFStringEncodingUTF8) || + !MakeDomainNameFromDNSNameString(&d, buf) || !d.c[0]) + LogMsg("GetUserSpecifiedDDNSConfig SCDynamicStore bad DDNS browsing domain: %s", buf[0] ? buf : "(unknown)"); + else + { + debugf("GetUserSpecifiedDDNSConfig SCDynamicStore DDNS browsing domain: %s", buf); + AppendDNameListElem(&BrowseDomains, 0, &d); + } + } + } + } + } + } + CFRelease(ddnsdict); + } + + if (RegDomains) + { + CFDictionaryRef btmm = SCDynamicStoreCopyValue(store, NetworkChangedKey_BackToMyMac); + if (btmm) + { + CFIndex size = CFDictionaryGetCount(btmm); + const void *key[size]; + const void *val[size]; + CFDictionaryGetKeysAndValues(btmm, key, val); + for (i = 0; i < size; i++) + { + LogInfo("BackToMyMac %d", i); + if (!CFStringGetCString(key[i], buf, sizeof(buf), kCFStringEncodingUTF8)) + LogMsg("Can't read BackToMyMac %d key %s", i, buf); + else + { + mDNSu32 uid = atoi(buf); + if (!CFStringGetCString(val[i], buf, sizeof(buf), kCFStringEncodingUTF8)) + LogMsg("Can't read BackToMyMac %d val %s", i, buf); + else if (MakeDomainNameFromDNSNameString(&d, buf) && d.c[0]) + { + LogInfo("BackToMyMac %d %d %##s", i, uid, d.c); + AppendDNameListElem(&RegDomains, uid, &d); + } + } + } + CFRelease(btmm); + } + } + CFRelease(store); + } +} + +// Returns mDNSfalse, if it does not set the configuration i.e., if the DNS configuration did not change +mDNSexport mDNSBool mDNSPlatformSetDNSConfig(mDNS *const m, mDNSBool setservers, mDNSBool setsearch, domainname *const fqdn, + DNameListElem **RegDomains, DNameListElem **BrowseDomains, mDNSBool ackConfig) +{ + MD5_CTX sdc; // search domain context + static mDNSu16 resolverGroupID = 0; + + // Need to set these here because we need to do this even if SCDynamicStoreCreate() or SCDynamicStoreCopyValue() below don't succeed + if (fqdn) fqdn->c[0] = 0; + if (RegDomains ) *RegDomains = NULL; + if (BrowseDomains) *BrowseDomains = NULL; + + LogInfo("mDNSPlatformSetDNSConfig:%s%s%s%s%s", + setservers ? " setservers" : "", + setsearch ? " setsearch" : "", + fqdn ? " fqdn" : "", + RegDomains ? " RegDomains" : "", + BrowseDomains ? " BrowseDomains" : ""); + + if (setsearch) MD5_Init(&sdc); + + // Add the inferred address-based configuration discovery domains + // (should really be in core code I think, not platform-specific) + if (setsearch) + { + struct ifaddrs *ifa = mDNSNULL; + struct sockaddr_in saddr; + mDNSPlatformMemZero(&saddr, sizeof(saddr)); + saddr.sin_len = sizeof(saddr); + saddr.sin_family = AF_INET; + saddr.sin_port = 0; + saddr.sin_addr.s_addr = *(in_addr_t *)&m->Router.ip.v4; + + // Don't add any reverse-IP search domains if doing the WAB bootstrap queries would cause dial-on-demand connection initiation + if (!AddrRequiresPPPConnection((struct sockaddr *)&saddr)) ifa = myGetIfAddrs(1); + + while (ifa) + { + mDNSAddr a, n; + char buf[64]; + + if (ifa->ifa_addr->sa_family == AF_INET && + ifa->ifa_netmask && + !(ifa->ifa_flags & IFF_LOOPBACK) && + !SetupAddr(&a, ifa->ifa_addr) && + !mDNSv4AddressIsLinkLocal(&a.ip.v4) ) + { + // Apparently it's normal for the sa_family of an ifa_netmask to sometimes be incorrect, so we explicitly fix it here before calling SetupAddr + // <rdar://problem/5492035> getifaddrs is returning invalid netmask family for fw0 and vmnet + ifa->ifa_netmask->sa_family = ifa->ifa_addr->sa_family; // Make sure ifa_netmask->sa_family is set correctly + SetupAddr(&n, ifa->ifa_netmask); + // Note: This is reverse order compared to a normal dotted-decimal IP address, so we can't use our customary "%.4a" format code + mDNS_snprintf(buf, sizeof(buf), "%d.%d.%d.%d.in-addr.arpa.", a.ip.v4.b[3] & n.ip.v4.b[3], + a.ip.v4.b[2] & n.ip.v4.b[2], + a.ip.v4.b[1] & n.ip.v4.b[1], + a.ip.v4.b[0] & n.ip.v4.b[0]); + UpdateSearchDomainHash(m, &sdc, buf, NULL); + mDNS_AddSearchDomain_CString(buf, mDNSNULL); + } + ifa = ifa->ifa_next; + } + } + +#ifndef MDNS_NO_DNSINFO + if (setservers || setsearch) + { + dns_config_t *config = dns_configuration_copy(); + if (!config) + { + // When running on 10.3 (build 7xxx) and earlier, we don't expect dns_configuration_copy() to succeed + // On 10.4, calls to dns_configuration_copy() early in the boot process often fail. + // Apparently this is expected behaviour -- "not a bug". + // Accordingly, we suppress syslog messages for the first three minutes after boot. + // If we are still getting failures after three minutes, then we log them. + if ((mDNSu32)mDNSPlatformRawTime() > (mDNSu32)(mDNSPlatformOneSecond * 180)) + LogMsg("mDNSPlatformSetDNSConfig: Error: dns_configuration_copy returned NULL"); + } + else + { + LogInfo("mDNSPlatformSetDNSConfig: config->n_resolver = %d, generation %llu", config->n_resolver, config->generation); + if (m->p->LastConfigGeneration == config->generation) + { + LogInfo("mDNSPlatformSetDNSConfig: generation number %llu same, not processing", config->generation); + dns_configuration_free(config); + SetupDDNSDomains(fqdn, RegDomains, BrowseDomains); + return mDNSfalse; + } +#if APPLE_OSX_mDNSResponder + SetupActiveDirectoryDomain(config); +#endif + + // With scoped DNS, we don't want to answer a non-scoped question using a scoped cache entry + // and vice-versa. As we compare resolverGroupID for matching cache entry with question, we need + // to make sure that they don't match. We ensure this by always bumping up resolverGroupID between + // the two calls to ConfigResolvers DNSServers for scoped and non-scoped can never have the + // same resolverGroupID. + // + // All non-scoped resolvers use the same resolverGroupID i.e, we treat them all equally. + ConfigResolvers(m, config, kScopeNone, setsearch, setservers, &sdc, ++resolverGroupID); + resolverGroupID += config->n_resolver; + + ConfigResolvers(m, config, kScopeInterfaceID, setsearch, setservers, &sdc, resolverGroupID); + resolverGroupID += config->n_scoped_resolver; + + ConfigResolvers(m, config, kScopeServiceID, setsearch, setservers, &sdc, resolverGroupID); + + // Acking provides a hint that we processed this current configuration and + // we will use that from now on, assuming we don't get another one immediately + // after we return from here. + if (ackConfig) + { + // Note: We have to set the generation number here when we are acking. + // For every DNS configuration change, we do the following: + // + // 1) Copy dns configuration, handle search domains change + // 2) Copy dns configuration, handle dns server change + // + // If we update the generation number at step (1), we won't process the + // DNS servers the second time because generation number would be the same. + // As we ack only when we process dns servers, we set the generation number + // during acking. + m->p->LastConfigGeneration = config->generation; + LogInfo("mDNSPlatformSetDNSConfig: Acking configuration setservers %d, setsearch %d", setservers, setsearch); + AckConfigd(m, config); + } + dns_configuration_free(config); + if (setsearch) FinalizeSearchDomainHash(m, &sdc); + setservers = mDNSfalse; // Done these now -- no need to fetch the same data from SCDynamicStore + setsearch = mDNSfalse; + } + } +#endif // MDNS_NO_DNSINFO + SetupDDNSDomains(fqdn, RegDomains, BrowseDomains); + return mDNStrue; +} + + +mDNSexport mStatus mDNSPlatformGetPrimaryInterface(mDNS *const m, mDNSAddr *v4, mDNSAddr *v6, mDNSAddr *r) +{ + char buf[256]; + (void)m; // Unused + + SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("mDNSResponder:mDNSPlatformGetPrimaryInterface"), NULL, NULL); + if (!store) + LogMsg("mDNSPlatformGetPrimaryInterface: SCDynamicStoreCreate failed: %s", SCErrorString(SCError())); + else + { + CFDictionaryRef dict = SCDynamicStoreCopyValue(store, NetworkChangedKey_IPv4); + if (dict) + { + r->type = mDNSAddrType_IPv4; + r->ip.v4 = zerov4Addr; + CFStringRef string = CFDictionaryGetValue(dict, kSCPropNetIPv4Router); + if (string) + { + if (!CFStringGetCString(string, buf, 256, kCFStringEncodingUTF8)) + LogMsg("Could not convert router to CString"); + else + { + struct sockaddr_in saddr; + saddr.sin_len = sizeof(saddr); + saddr.sin_family = AF_INET; + saddr.sin_port = 0; + inet_aton(buf, &saddr.sin_addr); + + *(in_addr_t *)&r->ip.v4 = saddr.sin_addr.s_addr; + } + } + + string = CFDictionaryGetValue(dict, kSCDynamicStorePropNetPrimaryInterface); + if (string) + { + mDNSBool HavePrimaryGlobalv6 = mDNSfalse; // does the primary interface have a global v6 address? + struct ifaddrs *ifa = myGetIfAddrs(1); + + *v4 = *v6 = zeroAddr; + + if (!CFStringGetCString(string, buf, 256, kCFStringEncodingUTF8)) { LogMsg("Could not convert router to CString"); goto exit; } + + // find primary interface in list + while (ifa && (mDNSIPv4AddressIsZero(v4->ip.v4) || mDNSv4AddressIsLinkLocal(&v4->ip.v4) || !HavePrimaryGlobalv6)) + { + mDNSAddr tmp6 = zeroAddr; + if (!strcmp(buf, ifa->ifa_name)) + { + if (ifa->ifa_addr->sa_family == AF_INET) + { + if (mDNSIPv4AddressIsZero(v4->ip.v4) || mDNSv4AddressIsLinkLocal(&v4->ip.v4)) SetupAddr(v4, ifa->ifa_addr); + } + else if (ifa->ifa_addr->sa_family == AF_INET6) + { + SetupAddr(&tmp6, ifa->ifa_addr); + if (tmp6.ip.v6.b[0] >> 5 == 1) // global prefix: 001 + { HavePrimaryGlobalv6 = mDNStrue; *v6 = tmp6; } + } + } + else + { + // We'll take a V6 address from the non-primary interface if the primary interface doesn't have a global V6 address + if (!HavePrimaryGlobalv6 && ifa->ifa_addr->sa_family == AF_INET6 && !v6->ip.v6.b[0]) + { + SetupAddr(&tmp6, ifa->ifa_addr); + if (tmp6.ip.v6.b[0] >> 5 == 1) *v6 = tmp6; + } + } + ifa = ifa->ifa_next; + } + + // Note that while we advertise v6, we still require v4 (possibly NAT'd, but not link-local) because we must use + // V4 to communicate w/ our DNS server + } + +exit: + CFRelease(dict); + } + CFRelease(store); + } + return mStatus_NoError; +} + +mDNSexport void mDNSPlatformDynDNSHostNameStatusChanged(const domainname *const dname, const mStatus status) +{ + LogInfo("mDNSPlatformDynDNSHostNameStatusChanged %d %##s", status, dname->c); + char uname[MAX_ESCAPED_DOMAIN_NAME]; // Max legal C-string name, including terminating NUL + ConvertDomainNameToCString(dname, uname); + + char *p = uname; + while (*p) + { + *p = tolower(*p); + if (!(*(p+1)) && *p == '.') *p = 0; // if last character, strip trailing dot + p++; + } + + // We need to make a CFDictionary called "State:/Network/DynamicDNS" containing (at present) a single entity. + // That single entity is a CFDictionary with name "HostNames". + // The "HostNames" CFDictionary contains a set of name/value pairs, where the each name is the FQDN + // in question, and the corresponding value is a CFDictionary giving the state for that FQDN. + // (At present we only support a single FQDN, so this dictionary holds just a single name/value pair.) + // The CFDictionary for each FQDN holds (at present) a single name/value pair, + // where the name is "Status" and the value is a CFNumber giving an errror code (with zero meaning success). + + const CFStringRef StateKeys [1] = { CFSTR("HostNames") }; + const CFStringRef HostKeys [1] = { CFStringCreateWithCString(NULL, uname, kCFStringEncodingUTF8) }; + const CFStringRef StatusKeys[1] = { CFSTR("Status") }; + if (!HostKeys[0]) LogMsg("SetDDNSNameStatus: CFStringCreateWithCString(%s) failed", uname); + else + { + const CFNumberRef StatusVals[1] = { CFNumberCreate(NULL, kCFNumberSInt32Type, &status) }; + if (!StatusVals[0]) LogMsg("SetDDNSNameStatus: CFNumberCreate(%d) failed", status); + else + { + const CFDictionaryRef HostVals[1] = { CFDictionaryCreate(NULL, (void*)StatusKeys, (void*)StatusVals, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks) }; + if (HostVals[0]) + { + const CFDictionaryRef StateVals[1] = { CFDictionaryCreate(NULL, (void*)HostKeys, (void*)HostVals, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks) }; + if (StateVals[0]) + { + CFDictionaryRef StateDict = CFDictionaryCreate(NULL, (void*)StateKeys, (void*)StateVals, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + if (StateDict) + { + mDNSDynamicStoreSetConfig(kmDNSDynamicConfig, mDNSNULL, StateDict); + CFRelease(StateDict); + } + CFRelease(StateVals[0]); + } + CFRelease(HostVals[0]); + } + CFRelease(StatusVals[0]); + } + CFRelease(HostKeys[0]); + } +} + +#if APPLE_OSX_mDNSResponder +#if !NO_AWACS + +// checks whether a domain is present in Setup:/Network/BackToMyMac. Just because there is a key in the +// keychain for a domain, it does not become a valid BTMM domain. If things get inconsistent, this will +// help catch it +mDNSlocal mDNSBool IsBTMMDomain(domainname *d) +{ + SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("mDNSResponder:IsBTMMDomain"), NULL, NULL); + if (!store) + { + LogMsg("IsBTMMDomain: SCDynamicStoreCreate failed: %s", SCErrorString(SCError())); + return mDNSfalse; + } + CFDictionaryRef btmm = SCDynamicStoreCopyValue(store, NetworkChangedKey_BackToMyMac); + if (btmm) + { + CFIndex size = CFDictionaryGetCount(btmm); + char buf[MAX_ESCAPED_DOMAIN_NAME]; // Max legal C-string name, including terminating NUL + const void *key[size]; + const void *val[size]; + domainname dom; + int i; + CFDictionaryGetKeysAndValues(btmm, key, val); + for (i = 0; i < size; i++) + { + LogInfo("BackToMyMac %d", i); + if (!CFStringGetCString(key[i], buf, sizeof(buf), kCFStringEncodingUTF8)) + LogMsg("IsBTMMDomain: ERROR!! Can't read BackToMyMac %d key %s", i, buf); + else + { + mDNSu32 uid = atoi(buf); + if (!CFStringGetCString(val[i], buf, sizeof(buf), kCFStringEncodingUTF8)) + LogMsg("IsBTMMDomain: Can't read BackToMyMac %d val %s", i, buf); + else if (MakeDomainNameFromDNSNameString(&dom, buf) && dom.c[0]) + { + if (SameDomainName(&dom, d)) + { + LogInfo("IsBTMMDomain: Domain %##s is a btmm domain, uid %u", d->c, uid); + CFRelease(btmm); + CFRelease(store); + return mDNStrue; + } + } + } + } + CFRelease(btmm); + } + CFRelease(store); + LogInfo("IsBTMMDomain: Domain %##s not a btmm domain", d->c); + return mDNSfalse; +} + +// Appends data to the buffer +mDNSlocal int AddOneItem(char *buf, int bufsz, char *data, int *currlen) +{ + int len; + + len = strlcpy(buf + *currlen, data, bufsz - *currlen); + if (len >= (bufsz - *currlen)) + { + // if we have exceeded the space in buf, it has already been NULL terminated + // and we have nothing more to do. Set currlen to the last byte so that the caller + // knows to do the right thing + LogMsg("AddOneItem: Exceeded the max buffer size currlen %d, len %d", *currlen, len); + *currlen = bufsz - 1; + return -1; + } + else { (*currlen) += len; } + + buf[*currlen] = ','; + if (*currlen >= bufsz) + { + LogMsg("AddOneItem: ERROR!! How can currlen be %d", *currlen); + *currlen = bufsz - 1; + buf[*currlen] = 0; + return -1; + } + // if we have filled up the buffer exactly, then there is no more work to do + if (*currlen == bufsz - 1) { buf[*currlen] = 0; return -1; } + (*currlen)++; + return *currlen; +} + +// If we have at least one BTMM domain, then trigger the connection to the relay. If we have no +// BTMM domains, then bring down the connection to the relay. +mDNSlocal void UpdateBTMMRelayConnection(mDNS *const m) +{ + DomainAuthInfo *BTMMDomain = mDNSNULL; + DomainAuthInfo *FoundInList; + static mDNSBool AWACSDConnected = mDNSfalse; + char AllUsers[1024]; // maximum size of mach message + char AllPass[1024]; // maximum size of mach message + char username[MAX_DOMAIN_LABEL + 1]; + int currulen = 0; + int currplen = 0; + + // if a domain is being deleted, we want to send a disconnect. If we send a disconnect now, + // we may not be able to send the dns queries over the relay connection which may be needed + // for sending the deregistrations. Hence, we need to delay sending the disconnect. But we + // need to make sure that we send the disconnect before attempting the next connect as the + // awacs connections are redirected based on usernames. + // + // For now we send a disconnect immediately. When we start sending dns queries over the relay + // connection, we will need to fix this. + + for (FoundInList = m->AuthInfoList; FoundInList; FoundInList = FoundInList->next) + if (!FoundInList->deltime && FoundInList->AutoTunnel && IsBTMMDomain(&FoundInList->domain)) + { + // We need the passwd from the first domain. + BTMMDomain = FoundInList; + ConvertDomainLabelToCString_unescaped((domainlabel *)BTMMDomain->domain.c, username); + LogInfo("UpdateBTMMRelayConnection: user %s for domain %##s", username, BTMMDomain->domain.c); + if (AddOneItem(AllUsers, sizeof(AllUsers), username, &currulen) == -1) break; + if (AddOneItem(AllPass, sizeof(AllPass), BTMMDomain->b64keydata, &currplen) == -1) break; + } + + if (BTMMDomain) + { + // In the normal case (where we neither exceed the buffer size nor write bytes that + // fit exactly into the buffer), currulen/currplen should be a different size than + // (AllUsers - 1) / (AllPass - 1). In that case, we need to override the "," with a NULL byte. + + if (currulen != (int)(sizeof(AllUsers) - 1)) AllUsers[currulen - 1] = 0; + if (currplen != (int)(sizeof(AllPass) - 1)) AllPass[currplen - 1] = 0; + + LogInfo("UpdateBTMMRelayConnection: AWS_Connect for user %s", AllUsers); + AWACS_Connect(AllUsers, AllPass, "hello.connectivity.me.com"); + AWACSDConnected = mDNStrue; + } + else + { + // Disconnect only if we connected previously + if (AWACSDConnected) + { + LogInfo("UpdateBTMMRelayConnection: AWS_Disconnect"); + AWACS_Disconnect(); + AWACSDConnected = mDNSfalse; + } + else LogInfo("UpdateBTMMRelayConnection: Not calling AWS_Disconnect"); + } +} +#else +mDNSlocal void UpdateBTMMRelayConnection(mDNS *const m) +{ + (void) m; // Unused + LogInfo("UpdateBTMMRelayConnection: AWACS connection not started, no AWACS library"); +} +#endif // ! NO_AWACS + +mDNSlocal void ProcessConndConfigChanges(mDNS *const m); + +#endif // APPLE_OSX_mDNSResponder + +// MUST be called holding the lock +mDNSexport void SetDomainSecrets(mDNS *m) +{ +#ifdef NO_SECURITYFRAMEWORK + (void) m; + LogMsg("Note: SetDomainSecrets: no keychain support"); +#else + mDNSBool haveAutoTunnels = mDNSfalse; + + LogInfo("SetDomainSecrets"); + + // Rather than immediately deleting all keys now, we mark them for deletion in ten seconds. + // In the case where the user simultaneously removes their DDNS host name and the key + // for it, this gives mDNSResponder ten seconds to gracefully delete the name from the + // server before it loses access to the necessary key. Otherwise, we'd leave orphaned + // address records behind that we no longer have permission to delete. + DomainAuthInfo *ptr; + for (ptr = m->AuthInfoList; ptr; ptr = ptr->next) + ptr->deltime = NonZeroTime(m->timenow + mDNSPlatformOneSecond*10); + +#if APPLE_OSX_mDNSResponder + { + // Mark all TunnelClients for deletion + ClientTunnel *client; + for (client = m->TunnelClients; client; client = client->next) + { + LogInfo("SetDomainSecrets: tunnel to %##s marked for deletion", client->dstname.c); + client->MarkedForDeletion = mDNStrue; + } + } +#endif // APPLE_OSX_mDNSResponder + + // String Array used to write list of private domains to Dynamic Store + CFMutableArrayRef sa = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); + if (!sa) { LogMsg("SetDomainSecrets: CFArrayCreateMutable failed"); return; } + CFIndex i; + CFDataRef data = NULL; + const int itemsPerEntry = 4; // domain name, key name, key value, Name value + CFArrayRef secrets = NULL; + int err = mDNSKeychainGetSecrets(&secrets); + if (err || !secrets) + LogMsg("SetDomainSecrets: mDNSKeychainGetSecrets failed error %d CFArrayRef %p", err, secrets); + else + { + CFIndex ArrayCount = CFArrayGetCount(secrets); + // Iterate through the secrets + for (i = 0; i < ArrayCount; ++i) + { + mDNSBool AutoTunnel; + int j, offset; + CFArrayRef entry = CFArrayGetValueAtIndex(secrets, i); + if (CFArrayGetTypeID() != CFGetTypeID(entry) || itemsPerEntry != CFArrayGetCount(entry)) + { LogMsg("SetDomainSecrets: malformed entry %d, itemsPerEntry %d", i, itemsPerEntry); continue; } + for (j = 0; j < CFArrayGetCount(entry); ++j) + if (CFDataGetTypeID() != CFGetTypeID(CFArrayGetValueAtIndex(entry, j))) + { LogMsg("SetDomainSecrets: malformed entry item %d", j); continue; } + + // The names have already been vetted by the helper, but checking them again here helps humans and automated tools verify correctness + + // Max legal domainname as C-string, including space for btmmprefix and terminating NUL + // Get DNS domain this key is for (kmDNSKcWhere) + char stringbuf[MAX_ESCAPED_DOMAIN_NAME + sizeof(btmmprefix)]; + data = CFArrayGetValueAtIndex(entry, kmDNSKcWhere); + if (CFDataGetLength(data) >= (int)sizeof(stringbuf)) + { LogMsg("SetDomainSecrets: Bad kSecServiceItemAttr length %d", CFDataGetLength(data)); continue; } + CFDataGetBytes(data, CFRangeMake(0, CFDataGetLength(data)), (UInt8 *)stringbuf); + stringbuf[CFDataGetLength(data)] = '\0'; + + AutoTunnel = mDNSfalse; + offset = 0; + if (!strncmp(stringbuf, dnsprefix, strlen(dnsprefix))) + offset = strlen(dnsprefix); + else if (!strncmp(stringbuf, btmmprefix, strlen(btmmprefix))) + { + AutoTunnel = mDNStrue; + offset = strlen(btmmprefix); + } + domainname domain; + if (!MakeDomainNameFromDNSNameString(&domain, stringbuf + offset)) { LogMsg("SetDomainSecrets: bad key domain %s", stringbuf); continue; } + + // Get key name (kmDNSKcAccount) + data = CFArrayGetValueAtIndex(entry, kmDNSKcAccount); + if (CFDataGetLength(data) >= (int)sizeof(stringbuf)) + { LogMsg("SetDomainSecrets: Bad kSecAccountItemAttr length %d", CFDataGetLength(data)); continue; } + CFDataGetBytes(data, CFRangeMake(0,CFDataGetLength(data)), (UInt8 *)stringbuf); + stringbuf[CFDataGetLength(data)] = '\0'; + + domainname keyname; + if (!MakeDomainNameFromDNSNameString(&keyname, stringbuf)) { LogMsg("SetDomainSecrets: bad key name %s", stringbuf); continue; } + + // Get key data (kmDNSKcKey) + data = CFArrayGetValueAtIndex(entry, kmDNSKcKey); + if (CFDataGetLength(data) >= (int)sizeof(stringbuf)) + { + LogMsg("SetDomainSecrets: Shared secret too long: %d", CFDataGetLength(data)); + continue; + } + CFDataGetBytes(data, CFRangeMake(0, CFDataGetLength(data)), (UInt8 *)stringbuf); + stringbuf[CFDataGetLength(data)] = '\0'; // mDNS_SetSecretForDomain requires NULL-terminated C string for key + + // Get the Name of the keychain entry (kmDNSKcName) host or host:port + // The hostname also has the port number and ":". It should take a maximum of 6 bytes. + char hostbuf[MAX_ESCAPED_DOMAIN_NAME + 6]; // Max legal domainname as C-string, including terminating NUL + data = CFArrayGetValueAtIndex(entry, kmDNSKcName); + if (CFDataGetLength(data) >= (int)sizeof(hostbuf)) + { + LogMsg("SetDomainSecrets: host:port data too long: %d", CFDataGetLength(data)); + continue; + } + CFDataGetBytes(data, CFRangeMake(0,CFDataGetLength(data)), (UInt8 *)hostbuf); + hostbuf[CFDataGetLength(data)] = '\0'; + + domainname hostname; + mDNSIPPort port; + char *hptr; + hptr = strchr(hostbuf, ':'); + + port.NotAnInteger = 0; + if (hptr) + { + mDNSu8 *p; + mDNSu16 val = 0; + + *hptr++ = '\0'; + while(hptr && *hptr != 0) + { + if (*hptr < '0' || *hptr > '9') + { LogMsg("SetDomainSecrets: Malformed Port number %d, val %d", *hptr, val); val = 0; break;} + val = val * 10 + *hptr - '0'; + hptr++; + } + if (!val) continue; + p = (mDNSu8 *)&val; + port.NotAnInteger = p[0] << 8 | p[1]; + } + // The hostbuf is of the format dsid@hostname:port. We don't care about the dsid. + hptr = strchr(hostbuf, '@'); + if (hptr) + hptr++; + else + hptr = hostbuf; + if (!MakeDomainNameFromDNSNameString(&hostname, hptr)) { LogMsg("SetDomainSecrets: bad host name %s", hptr); continue; } + + DomainAuthInfo *FoundInList; + for (FoundInList = m->AuthInfoList; FoundInList; FoundInList = FoundInList->next) + if (SameDomainName(&FoundInList->domain, &domain)) break; + +#if APPLE_OSX_mDNSResponder + if (FoundInList) + { + // If any client tunnel destination is in this domain, set deletion flag to false + ClientTunnel *client; + for (client = m->TunnelClients; client; client = client->next) + if (FoundInList == GetAuthInfoForName_internal(m, &client->dstname)) + { + LogInfo("SetDomainSecrets: tunnel to %##s no longer marked for deletion", client->dstname.c); + client->MarkedForDeletion = mDNSfalse; + } + } + +#endif // APPLE_OSX_mDNSResponder + + // Uncomment the line below to view the keys as they're read out of the system keychain + // DO NOT SHIP CODE THIS WAY OR YOU'LL LEAK SECRET DATA INTO A PUBLICLY READABLE FILE! + //LogInfo("SetDomainSecrets: domain %##s keyname %##s key %s hostname %##s port %d", &domain.c, &keyname.c, stringbuf, hostname.c, (port.b[0] << 8 | port.b[1])); + LogInfo("SetDomainSecrets: domain %##s keyname %##s hostname %##s port %d", &domain.c, &keyname.c, hostname.c, (port.b[0] << 8 | port.b[1])); + + // If didn't find desired domain in the list, make a new entry + ptr = FoundInList; + if (FoundInList && FoundInList->AutoTunnel && haveAutoTunnels == mDNSfalse) haveAutoTunnels = mDNStrue; + if (!FoundInList) + { + ptr = (DomainAuthInfo*)mallocL("DomainAuthInfo", sizeof(*ptr)); + if (!ptr) { LogMsg("SetDomainSecrets: No memory"); continue; } + } + + //LogInfo("SetDomainSecrets: %d of %d %##s", i, ArrayCount, &domain); + + // It is an AutoTunnel if the keychains tells us so (with btmm prefix) or if it is a TunnelModeDomain + if (mDNS_SetSecretForDomain(m, ptr, &domain, &keyname, stringbuf, &hostname, &port, AutoTunnel) == mStatus_BadParamErr) + { + if (!FoundInList) mDNSPlatformMemFree(ptr); // If we made a new DomainAuthInfo here, and it turned out bad, dispose it immediately + continue; + } + + ConvertDomainNameToCString(&domain, stringbuf); + CFStringRef cfs = CFStringCreateWithCString(NULL, stringbuf, kCFStringEncodingUTF8); + if (cfs) { CFArrayAppendValue(sa, cfs); CFRelease(cfs); } + } + CFRelease(secrets); + } + + if (!privateDnsArray || !CFEqual(privateDnsArray, sa)) + { + if (privateDnsArray) + CFRelease(privateDnsArray); + + privateDnsArray = sa; + CFRetain(privateDnsArray); + mDNSDynamicStoreSetConfig(kmDNSPrivateConfig, mDNSNULL, privateDnsArray); + } + CFRelease(sa); + +#if APPLE_OSX_mDNSResponder + { + // clean up ClientTunnels + ClientTunnel **pp = &m->TunnelClients; + while (*pp) + { + if ((*pp)->MarkedForDeletion) + { + ClientTunnel *cur = *pp; + LogInfo("SetDomainSecrets: removing client %p %##s from list", cur, cur->dstname.c); + if (cur->q.ThisQInterval >= 0) mDNS_StopQuery(m, &cur->q); + AutoTunnelSetKeys(cur, mDNSfalse); + *pp = cur->next; + freeL("ClientTunnel", cur); + } + else + pp = &(*pp)->next; + } + + mDNSBool needAutoTunnelNAT = mDNSfalse; + DomainAuthInfo *info; + for (info = m->AuthInfoList; info; info = info->next) + { + if (info->AutoTunnel) + { + UpdateAutoTunnelDeviceInfoRecord(m, info); + UpdateAutoTunnelHostRecord(m, info); + UpdateAutoTunnelServiceRecords(m, info); + UpdateAutoTunnel6Record(m, info); + if (info->deltime) + { + if (info->AutoTunnelServiceStarted) info->AutoTunnelServiceStarted = mDNSfalse; + } + else if (info->AutoTunnelServiceStarted) + needAutoTunnelNAT = true; + + UpdateAutoTunnelDomainStatus(m, info); + } + } + + // If the AutoTunnel NAT-T is no longer needed (& is currently running), stop it + if (!needAutoTunnelNAT && m->AutoTunnelNAT.clientContext) + { + // stop the NAT operation, reset port, cleanup state + mDNS_StopNATOperation_internal(m, &m->AutoTunnelNAT); + m->AutoTunnelNAT.ExternalAddress = zerov4Addr; + m->AutoTunnelNAT.NewAddress = zerov4Addr; + m->AutoTunnelNAT.ExternalPort = zeroIPPort; + m->AutoTunnelNAT.RequestedPort = zeroIPPort; + m->AutoTunnelNAT.Lifetime = 0; + m->AutoTunnelNAT.Result = mStatus_NoError; + m->AutoTunnelNAT.clientContext = mDNSNULL; + } + + UpdateAnonymousRacoonConfig(m); // Determine whether we need racoon to accept incoming connections + ProcessConndConfigChanges(m); // Update AutoTunnelInnerAddress values and default ipsec policies as necessary + } +#endif // APPLE_OSX_mDNSResponder + + CheckSuppressUnusableQuestions(m); + +#endif /* NO_SECURITYFRAMEWORK */ +} + +mDNSlocal void SetLocalDomains(void) +{ + CFMutableArrayRef sa = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); + if (!sa) { LogMsg("SetLocalDomains: CFArrayCreateMutable failed"); return; } + + CFArrayAppendValue(sa, CFSTR("local")); + CFArrayAppendValue(sa, CFSTR("254.169.in-addr.arpa")); + CFArrayAppendValue(sa, CFSTR("8.e.f.ip6.arpa")); + CFArrayAppendValue(sa, CFSTR("9.e.f.ip6.arpa")); + CFArrayAppendValue(sa, CFSTR("a.e.f.ip6.arpa")); + CFArrayAppendValue(sa, CFSTR("b.e.f.ip6.arpa")); + + mDNSDynamicStoreSetConfig(kmDNSMulticastConfig, mDNSNULL, sa); + CFRelease(sa); +} + +mDNSlocal void GetCurrentPMSetting(const CFStringRef name, mDNSs32 *val) +{ +#if USE_IOPMCOPYACTIVEPMPREFERENCES + CFTypeRef blob = NULL; + CFStringRef str = NULL; + CFDictionaryRef odict = NULL; + CFDictionaryRef idict = NULL; + CFNumberRef number = NULL; + + blob = IOPSCopyPowerSourcesInfo(); + if (!blob) { LogMsg("GetCurrentPMSetting: IOPSCopyPowerSourcesInfo failed!"); goto end; } + + odict = IOPMCopyActivePMPreferences(); + if (!odict) { LogMsg("GetCurrentPMSetting: IOPMCopyActivePMPreferences failed!"); goto end; } + + str = IOPSGetProvidingPowerSourceType(blob); + if (!str) { LogMsg("GetCurrentPMSetting: IOPSGetProvidingPowerSourceType failed!"); goto end; } + + idict = CFDictionaryGetValue(odict, str); + if (!idict) + { + char buf[256]; + if (!CFStringGetCString(str, buf, sizeof(buf), kCFStringEncodingUTF8)) buf[0] = 0; + LogMsg("GetCurrentPMSetting: CFDictionaryGetValue (%s) failed!", buf); + goto end; + } + + number = CFDictionaryGetValue(idict, name); + if (!number || CFGetTypeID(number) != CFNumberGetTypeID() || !CFNumberGetValue(number, kCFNumberSInt32Type, val)) + *val = 0; +end: + if (blob) CFRelease(blob); + if (odict) CFRelease(odict); + +#else + + SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("mDNSResponder:GetCurrentPMSetting"), NULL, NULL); + if (!store) LogMsg("GetCurrentPMSetting: SCDynamicStoreCreate failed: %s", SCErrorString(SCError())); + else + { + CFDictionaryRef dict = SCDynamicStoreCopyValue(store, NetworkChangedKey_PowerSettings); + if (!dict) LogSPS("GetCurrentPMSetting: Could not get IOPM CurrentSettings dict"); + else + { + CFNumberRef number = CFDictionaryGetValue(dict, name); + if (!number || CFGetTypeID(number) != CFNumberGetTypeID() || !CFNumberGetValue(number, kCFNumberSInt32Type, val)) + *val = 0; + CFRelease(dict); + } + CFRelease(store); + } + +#endif +} + +#if APPLE_OSX_mDNSResponder + +static CFMutableDictionaryRef spsStatusDict = NULL; +static const CFStringRef kMetricRef = CFSTR("Metric"); + +mDNSlocal void SPSStatusPutNumber(CFMutableDictionaryRef dict, const mDNSu8* const ptr, CFStringRef key) +{ + mDNSu8 tmp = (ptr[0] - '0') * 10 + ptr[1] - '0'; + CFNumberRef num = CFNumberCreate(NULL, kCFNumberSInt8Type, &tmp); + if (!num) + LogMsg("SPSStatusPutNumber: Could not create CFNumber"); + else + { + CFDictionarySetValue(dict, key, num); + CFRelease(num); + } +} + +mDNSlocal CFMutableDictionaryRef SPSCreateDict(const mDNSu8* const ptr) +{ + CFMutableDictionaryRef dict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + if (!dict) { LogMsg("SPSCreateDict: Could not create CFDictionary dict"); return dict; } + + char buffer[1024]; + buffer[mDNS_snprintf(buffer, sizeof(buffer), "%##s", ptr) - 1] = 0; + CFStringRef spsname = CFStringCreateWithCString(NULL, buffer, kCFStringEncodingUTF8); + if (!spsname) { LogMsg("SPSCreateDict: Could not create CFString spsname full"); CFRelease(dict); return NULL; } + CFDictionarySetValue(dict, CFSTR("FullName"), spsname); + CFRelease(spsname); + + if (ptr[0] >= 2) SPSStatusPutNumber(dict, ptr + 1, CFSTR("Type")); + if (ptr[0] >= 5) SPSStatusPutNumber(dict, ptr + 4, CFSTR("Portability")); + if (ptr[0] >= 8) SPSStatusPutNumber(dict, ptr + 7, CFSTR("MarginalPower")); + if (ptr[0] >= 11) SPSStatusPutNumber(dict, ptr +10, CFSTR("TotalPower")); + + mDNSu32 tmp = SPSMetric(ptr); + CFNumberRef num = CFNumberCreate(NULL, kCFNumberSInt32Type, &tmp); + if (!num) + LogMsg("SPSCreateDict: Could not create CFNumber"); + else + { + CFDictionarySetValue(dict, kMetricRef, num); + CFRelease(num); + } + + if (ptr[0] >= 12) + { + memcpy(buffer, ptr + 13, ptr[0] - 12); + buffer[ptr[0] - 12] = 0; + spsname = CFStringCreateWithCString(NULL, buffer, kCFStringEncodingUTF8); + if (!spsname) { LogMsg("SPSCreateDict: Could not create CFString spsname"); CFRelease(dict); return NULL; } + else + { + CFDictionarySetValue(dict, CFSTR("PrettyName"), spsname); + CFRelease(spsname); + } + } + + return dict; +} + +mDNSlocal CFComparisonResult CompareSPSEntries(const void *val1, const void *val2, void *context) +{ + (void)context; + return CFNumberCompare((CFNumberRef)CFDictionaryGetValue((CFDictionaryRef)val1, kMetricRef), + (CFNumberRef)CFDictionaryGetValue((CFDictionaryRef)val2, kMetricRef), + NULL); +} + +mDNSlocal void UpdateSPSStatus(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord) +{ + NetworkInterfaceInfo* info = (NetworkInterfaceInfo*)question->QuestionContext; + debugf("UpdateSPSStatus: %s %##s %s %s", info->ifname, question->qname.c, AddRecord ? "Add" : "Rmv", answer ? RRDisplayString(m, answer) : "<null>"); + + mDNS_Lock(m); + mDNS_UpdateAllowSleep(m); + mDNS_Unlock(m); + + if (answer && SPSMetric(answer->rdata->u.name.c) > 999999) return; // Ignore instances with invalid names + + if (!spsStatusDict) + { + spsStatusDict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + if (!spsStatusDict) { LogMsg("UpdateSPSStatus: Could not create CFDictionary spsStatusDict"); return; } + } + + CFStringRef ifname = CFStringCreateWithCString(NULL, info->ifname, kCFStringEncodingUTF8); + if (!ifname) { LogMsg("UpdateSPSStatus: Could not create CFString ifname"); return; } + + CFMutableArrayRef array = NULL; + + if (!CFDictionaryGetValueIfPresent(spsStatusDict, ifname, (const void**) &array)) + { + array = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); + if (!array) { LogMsg("UpdateSPSStatus: Could not create CFMutableArray"); CFRelease(ifname); return; } + CFDictionarySetValue(spsStatusDict, ifname, array); + CFRelease(array); // let go of our reference, now that the dict has one + } + else + if (!array) { LogMsg("UpdateSPSStatus: Could not get CFMutableArray for %s", info->ifname); CFRelease(ifname); return; } + + if (!answer) // special call that means the question has been stopped (because the interface is going away) + CFArrayRemoveAllValues(array); + else + { + CFMutableDictionaryRef dict = SPSCreateDict(answer->rdata->u.name.c); + if (!dict) { CFRelease(ifname); return; } + + if (AddRecord) + { + if (!CFArrayContainsValue(array, CFRangeMake(0, CFArrayGetCount(array)), dict)) + { + int i=0; + for (i=0; i<CFArrayGetCount(array); i++) + if (CompareSPSEntries(CFArrayGetValueAtIndex(array, i), dict, NULL) != kCFCompareLessThan) + break; + CFArrayInsertValueAtIndex(array, i, dict); + } + else LogMsg("UpdateSPSStatus: %s array already contains %##s", info->ifname, answer->rdata->u.name.c); + } + else + { + CFIndex i = CFArrayGetFirstIndexOfValue(array, CFRangeMake(0, CFArrayGetCount(array)), dict); + if (i != -1) CFArrayRemoveValueAtIndex(array, i); + else LogMsg("UpdateSPSStatus: %s array does not contain %##s", info->ifname, answer->rdata->u.name.c); + } + + CFRelease(dict); + } + + if (!m->ShutdownTime) mDNSDynamicStoreSetConfig(kmDNSSleepProxyServersState, info->ifname, array); + + CFRelease(ifname); +} + +mDNSlocal mDNSs32 GetSystemSleepTimerSetting(void) +{ + mDNSs32 val = -1; + SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("mDNSResponder:GetSystemSleepTimerSetting"), NULL, NULL); + if (!store) + LogMsg("GetSystemSleepTimerSetting: SCDynamicStoreCreate failed: %s", SCErrorString(SCError())); + else + { + CFDictionaryRef dict = SCDynamicStoreCopyValue(store, NetworkChangedKey_PowerSettings); + if (dict) + { + CFNumberRef number = CFDictionaryGetValue(dict, CFSTR("System Sleep Timer")); + if (number) CFNumberGetValue(number, kCFNumberSInt32Type, &val); + CFRelease(dict); + } + CFRelease(store); + } + return val; +} + +mDNSlocal void SetSPS(mDNS *const m) +{ + + // If we ever want to know InternetSharing status in the future, use DNSXEnableProxy() + mDNSu8 sps = (OfferSleepProxyService && GetSystemSleepTimerSetting() == 0) ? mDNSSleepProxyMetric_IncidentalSoftware : 0; + + // For devices that are not running NAT, but are set to never sleep, we may choose to act + // as a Sleep Proxy, but only for non-portable Macs (Portability > 35 means nominal weight < 3kg) + //if (sps > mDNSSleepProxyMetric_PrimarySoftware && SPMetricPortability > 35) sps = 0; + + // If we decide to let laptops act as Sleep Proxy, we should do it only when running on AC power, not on battery + + // For devices that are unable to sleep at all to save power, or save 1W or less by sleeping, + // it makes sense for them to offer low-priority Sleep Proxy service on the network. + // We rate such a device as metric 70 ("Incidentally Available Hardware") + if (SPMetricMarginalPower <= 60 && !sps) sps = mDNSSleepProxyMetric_IncidentalHardware; + + // If the launchd plist specifies an explicit value for the Intent Metric, then use that instead of the + // computed value (currently 40 "Primary Network Infrastructure Software" or 80 "Incidentally Available Software") + if (sps && OfferSleepProxyService && OfferSleepProxyService < 100) sps = OfferSleepProxyService; + +#ifdef NO_APPLETV_SLEEP_PROXY_ON_WIFI + // AppleTVs are not reliable sleep proxy servers on WiFi. Do not offer to be a BSP if the WiFi interface is active. + if (IsAppleTV()) + { + NetworkInterfaceInfo *intf = mDNSNULL; + mDNSEthAddr bssid = zeroEthAddr; + for (intf = GetFirstActiveInterface(m->HostInterfaces); intf; intf = GetFirstActiveInterface(intf->next)) + { + bssid = GetBSSID(intf->ifname); + if (!mDNSSameEthAddress(&bssid, &zeroEthAddr)) + { + LogMsg("SetSPS: AppleTV on WiFi - not advertising BSP services"); + sps = 0; + break; + } + } + } +#endif // NO_APPLETV_SLEEP_PROXY_ON_WIFI + + mDNSCoreBeSleepProxyServer(m, sps, SPMetricPortability, SPMetricMarginalPower, SPMetricTotalPower, SPMetricFeatures); +} + +// The definitions below should eventually come from some externally-supplied header file. +// However, since these definitions can't really be changed without breaking binary compatibility, +// they should never change, so in practice it should not be a big problem to have them defined here. + +enum +{ // commands from the daemon to the driver + cmd_mDNSOffloadRR = 21, // give the mdns update buffer to the driver +}; + +typedef union { void *ptr; mDNSOpaque64 sixtyfourbits; } FatPtr; + +typedef struct +{ // cmd_mDNSOffloadRR structure + uint32_t command; // set to OffloadRR + uint32_t rrBufferSize; // number of bytes of RR records + uint32_t numUDPPorts; // number of SRV UDP ports + uint32_t numTCPPorts; // number of SRV TCP ports + uint32_t numRRRecords; // number of RR records + uint32_t compression; // rrRecords - compression is base for compressed strings + FatPtr rrRecords; // address of array of pointers to the rr records + FatPtr udpPorts; // address of udp port list (SRV) + FatPtr tcpPorts; // address of tcp port list (SRV) +} mDNSOffloadCmd; + +#include <IOKit/IOKitLib.h> +#include <dns_util.h> + +mDNSlocal mDNSu16 GetPortArray(mDNS *const m, int trans, mDNSIPPort *portarray) +{ + const domainlabel *const tp = (trans == mDNSTransport_UDP) ? (const domainlabel *)"\x4_udp" : (const domainlabel *)"\x4_tcp"; + int count = 0; + AuthRecord *rr; + for (rr = m->ResourceRecords; rr; rr=rr->next) + if (rr->resrec.rrtype == kDNSType_SRV && SameDomainLabel(ThirdLabel(rr->resrec.name)->c, tp->c)) + { + if (portarray) portarray[count] = rr->resrec.rdata->u.srv.port; + count++; + } + + // If Back to My Mac is on, also wake for packets to the IPSEC UDP port (4500) + if (trans == mDNSTransport_UDP && m->AutoTunnelNAT.clientContext) + { + LogSPS("GetPortArray Back to My Mac at %d", count); + if (portarray) portarray[count] = IPSECPort; + count++; + } + return(count); +} + +#if APPLE_OSX_mDNSResponder && !TARGET_OS_EMBEDDED +mDNSlocal mDNSBool SupportsTCPKeepAlive() +{ + IOReturn ret = kIOReturnSuccess; + CFTypeRef obj = NULL; + mDNSBool supports = mDNSfalse; + + ret = IOPlatformCopyFeatureActive(CFSTR("TCPKeepAliveDuringSleep"), &obj); + if ((kIOReturnSuccess == ret) && (obj != NULL)) + { + supports = (obj == kCFBooleanTrue)? mDNStrue : mDNSfalse; + CFRelease(obj); + } + LogSPS("%s: The hardware %s TCP Keep Alive", __func__, (supports ? "supports" : "does not support")); + return supports; +} + +mDNSlocal mDNSBool OnBattery(void) +{ + CFTypeRef powerInfo = IOPSCopyPowerSourcesInfo(); + CFTypeRef powerSrc = IOPSGetProvidingPowerSourceType(powerInfo); + mDNSBool result = mDNSfalse; + + if (powerInfo != NULL) + { + result = CFEqual(CFSTR(kIOPSBatteryPowerValue), powerSrc); + CFRelease(powerInfo); + } + LogSPS("%s: The system is on %s", __func__, (result)? "Battery" : "AC Power"); + return result; +} + +#endif // !TARGET_OS_EMBEDDED + +#define TfrRecordToNIC(RR) \ + ((!(RR)->resrec.InterfaceID && ((RR)->ForceMCast || IsLocalDomain((RR)->resrec.name)))) + +mDNSlocal mDNSu32 CountProxyRecords(mDNS *const m, uint32_t *const numbytes, NetworkInterfaceInfo *const intf, mDNSBool TCPKAOnly, mDNSBool supportsTCPKA) +{ + *numbytes = 0; + int count = 0; + + AuthRecord *rr; + + for (rr = m->ResourceRecords; rr; rr=rr->next) + { + if (!(rr->AuthFlags & AuthFlagsWakeOnly) && rr->resrec.RecordType > kDNSRecordTypeDeregistering) + { +#if APPLE_OSX_mDNSResponder && !TARGET_OS_EMBEDDED + mDNSBool isKeepAliveRecord = mDNS_KeepaliveRecord(&rr->resrec); + // Skip over all other records if we are registering TCP KeepAlive records only + // Skip over TCP KeepAlive records if the policy prohibits it or if the interface does not support TCP Keepalive. + if ((TCPKAOnly && !isKeepAliveRecord) || (isKeepAliveRecord && !supportsTCPKA)) + continue; + + // Update the record before calculating the number of bytes required + // We offload the TCP Keepalive record even if the update fails. When the driver gets the record, it will + // attempt to update the record again. + if (isKeepAliveRecord && (UpdateKeepaliveRData(m, rr, intf, mDNSfalse, mDNSNULL) != mStatus_NoError)) + LogSPS("CountProxyRecords: Failed to update keepalive record - %s", ARDisplayString(m, rr)); +#else + (void) TCPKAOnly; // unused + (void) supportsTCPKA; // unused + (void) intf; // unused +#endif // APPLE_OSX_mDNSResponder + if (TfrRecordToNIC(rr)) + { + *numbytes += DomainNameLength(rr->resrec.name) + 10 + rr->resrec.rdestimate; + LogSPS("CountProxyRecords: %3d size %5d total %5d %s", + count, DomainNameLength(rr->resrec.name) + 10 + rr->resrec.rdestimate, *numbytes, ARDisplayString(m,rr)); + count++; + } + } + } + return(count); +} + +mDNSlocal void GetProxyRecords(mDNS *const m, DNSMessage *const msg, uint32_t *const numbytes, FatPtr *const records, mDNSBool TCPKAOnly, mDNSBool supportsTCPKA) +{ + mDNSu8 *p = msg->data; + const mDNSu8 *const limit = p + *numbytes; + InitializeDNSMessage(&msg->h, zeroID, zeroID); + + int count = 0; + AuthRecord *rr; + + for (rr = m->ResourceRecords; rr; rr=rr->next) + { + if (!(rr->AuthFlags & AuthFlagsWakeOnly) && rr->resrec.RecordType > kDNSRecordTypeDeregistering) + { +#if APPLE_OSX_mDNSResponder && !TARGET_OS_EMBEDDED + mDNSBool isKeepAliveRecord = mDNS_KeepaliveRecord(&rr->resrec); + + // Skip over all other records if we are registering TCP KeepAlive records only + // Skip over TCP KeepAlive records if the policy prohibits it or if the interface does not support TCP Keepalive + if ((TCPKAOnly && !isKeepAliveRecord) || (isKeepAliveRecord && !supportsTCPKA)) + continue; +#else + (void) TCPKAOnly; // unused + (void) supportsTCPKA; // unused +#endif // APPLE_OSX_mDNSResponder + + if (TfrRecordToNIC(rr)) + { + records[count].sixtyfourbits = zeroOpaque64; + records[count].ptr = p; + if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask) + rr->resrec.rrclass |= kDNSClass_UniqueRRSet; // Temporarily set the 'unique' bit so PutResourceRecord will set it + p = PutResourceRecordTTLWithLimit(msg, p, &msg->h.mDNS_numUpdates, &rr->resrec, rr->resrec.rroriginalttl, limit); + rr->resrec.rrclass &= ~kDNSClass_UniqueRRSet; // Make sure to clear 'unique' bit back to normal state + LogSPS("GetProxyRecords: %3d start %p end %p size %5d total %5d %s", + count, records[count].ptr, p, p - (mDNSu8 *)records[count].ptr, p - msg->data, ARDisplayString(m,rr)); + count++; + } + } + } + *numbytes = p - msg->data; +} + +// If compiling with old headers and libraries (pre 10.5) that don't include IOConnectCallStructMethod +// then we declare a dummy version here so that the code at least compiles +#ifndef AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER +static kern_return_t +IOConnectCallStructMethod( + mach_port_t connection, // In + uint32_t selector, // In + const void *inputStruct, // In + size_t inputStructCnt, // In + void *outputStruct, // Out + size_t *outputStructCnt) // In/Out +{ + (void)connection; + (void)selector; + (void)inputStruct; + (void)inputStructCnt; + (void)outputStruct; + (void)outputStructCnt; + LogMsg("Compiled without IOConnectCallStructMethod"); + return(KERN_FAILURE); +} +#endif + +mDNSexport mDNSBool SupportsInNICProxy(NetworkInterfaceInfo *const intf) +{ + if(!UseInternalSleepProxy) + { + LogSPS("SupportsInNICProxy: Internal Sleep Proxy is disabled"); + return mDNSfalse; + } + return CheckInterfaceSupport(intf, mDNS_IOREG_KEY); +} + +mDNSexport mStatus ActivateLocalProxy(mDNS *const m, NetworkInterfaceInfo *const intf) // Called with the lock held +{ + mStatus result = mStatus_UnknownErr; + mDNSBool TCPKAOnly = mDNSfalse; + mDNSBool supportsTCPKA = mDNSfalse; + mDNSBool onbattery = mDNSfalse; + io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, IOBSDNameMatching(kIOMasterPortDefault, 0, intf->ifname)); + +#if APPLE_OSX_mDNSResponder && !TARGET_OS_EMBEDDED + onbattery = OnBattery(); + // Check if the interface supports TCP Keepalives and the system policy says it is ok to offload TCP Keepalive records + supportsTCPKA = (InterfaceSupportsKeepAlive(intf) && SupportsTCPKeepAlive()); + + // Only TCP Keepalive records are to be offloaded if + // - The system is on battery + // - OR wake for network access is not set but powernap is enabled + TCPKAOnly = supportsTCPKA && ((m->SystemWakeOnLANEnabled == mDNS_WakeOnBattery) || onbattery); +#else + (void) onbattery; // unused; +#endif + if (!service) { LogMsg("ActivateLocalProxy: No service for interface %s", intf->ifname); return(mStatus_UnknownErr); } + + io_name_t n1, n2; + IOObjectGetClass(service, n1); + io_object_t parent; + kern_return_t kr = IORegistryEntryGetParentEntry(service, kIOServicePlane, &parent); + if (kr != KERN_SUCCESS) LogMsg("ActivateLocalProxy: IORegistryEntryGetParentEntry for %s/%s failed %d", intf->ifname, n1, kr); + else + { + IOObjectGetClass(parent, n2); + LogSPS("ActivateLocalProxy: Interface %s service %s parent %s", intf->ifname, n1, n2); + const CFTypeRef ref = IORegistryEntryCreateCFProperty(parent, CFSTR(mDNS_IOREG_KEY), kCFAllocatorDefault, mDNSNULL); + if (!ref) LogSPS("ActivateLocalProxy: No mDNS_IOREG_KEY for interface %s/%s/%s", intf->ifname, n1, n2); + else + { + if (CFGetTypeID(ref) != CFStringGetTypeID() || !CFEqual(ref, CFSTR(mDNS_IOREG_VALUE))) + LogMsg("ActivateLocalProxy: mDNS_IOREG_KEY for interface %s/%s/%s value %s != %s", + intf->ifname, n1, n2, CFStringGetCStringPtr(ref, mDNSNULL), mDNS_IOREG_VALUE); + else if (!UseInternalSleepProxy) + LogSPS("ActivateLocalProxy: Not using internal (NIC) sleep proxy for interface %s", intf->ifname); + else + { + io_connect_t conObj; + kr = IOServiceOpen(parent, mach_task_self(), mDNS_USER_CLIENT_CREATE_TYPE, &conObj); + if (kr != KERN_SUCCESS) LogMsg("ActivateLocalProxy: IOServiceOpen for %s/%s/%s failed %d", intf->ifname, n1, n2, kr); + else + { + mDNSOffloadCmd cmd; + mDNSPlatformMemZero(&cmd, sizeof(cmd)); // When compiling 32-bit, make sure top 32 bits of 64-bit pointers get initialized to zero + cmd.command = cmd_mDNSOffloadRR; + cmd.numUDPPorts = GetPortArray(m, mDNSTransport_UDP, mDNSNULL); + cmd.numTCPPorts = GetPortArray(m, mDNSTransport_TCP, mDNSNULL); + cmd.numRRRecords = CountProxyRecords(m, &cmd.rrBufferSize, intf, TCPKAOnly, supportsTCPKA); + cmd.compression = sizeof(DNSMessageHeader); + + DNSMessage *msg = (DNSMessage *)mallocL("mDNSOffloadCmd msg", sizeof(DNSMessageHeader) + cmd.rrBufferSize); + cmd.rrRecords.ptr = mallocL("mDNSOffloadCmd rrRecords", cmd.numRRRecords * sizeof(FatPtr)); + cmd.udpPorts.ptr = mallocL("mDNSOffloadCmd udpPorts", cmd.numUDPPorts * sizeof(mDNSIPPort)); + cmd.tcpPorts.ptr = mallocL("mDNSOffloadCmd tcpPorts", cmd.numTCPPorts * sizeof(mDNSIPPort)); + + LogSPS("ActivateLocalProxy: msg %p %d RR %p %d, UDP %p %d, TCP %p %d", + msg, cmd.rrBufferSize, + cmd.rrRecords.ptr, cmd.numRRRecords, + cmd.udpPorts.ptr, cmd.numUDPPorts, + cmd.tcpPorts.ptr, cmd.numTCPPorts); + + if (!msg || !cmd.rrRecords.ptr || !cmd.udpPorts.ptr || !cmd.tcpPorts.ptr) + LogMsg("ActivateLocalProxy: Failed to allocate memory: msg %p %d RR %p %d, UDP %p %d, TCP %p %d", + msg, cmd.rrBufferSize, + cmd.rrRecords.ptr, cmd.numRRRecords, + cmd.udpPorts.ptr, cmd.numUDPPorts, + cmd.tcpPorts.ptr, cmd.numTCPPorts); + else + { + GetProxyRecords(m, msg, &cmd.rrBufferSize, cmd.rrRecords.ptr, TCPKAOnly, supportsTCPKA); + GetPortArray(m, mDNSTransport_UDP, cmd.udpPorts.ptr); + GetPortArray(m, mDNSTransport_TCP, cmd.tcpPorts.ptr); + char outputData[2]; + size_t outputDataSize = sizeof(outputData); + kr = IOConnectCallStructMethod(conObj, 0, &cmd, sizeof(cmd), outputData, &outputDataSize); + LogSPS("ActivateLocalProxy: IOConnectCallStructMethod for %s/%s/%s %d", intf->ifname, n1, n2, kr); + if (kr == KERN_SUCCESS) result = mStatus_NoError; + } + + if (cmd.tcpPorts.ptr) freeL("mDNSOffloadCmd udpPorts", cmd.tcpPorts.ptr); + if (cmd.udpPorts.ptr) freeL("mDNSOffloadCmd tcpPorts", cmd.udpPorts.ptr); + if (cmd.rrRecords.ptr) freeL("mDNSOffloadCmd rrRecords", cmd.rrRecords.ptr); + if (msg) freeL("mDNSOffloadCmd msg", msg); + IOServiceClose(conObj); + } + } + CFRelease(ref); + } + IOObjectRelease(parent); + } + IOObjectRelease(service); + return result; +} + +#endif // APPLE_OSX_mDNSResponder + +mDNSlocal mDNSu8 SystemWakeForNetworkAccess(void) +{ + mDNSs32 val = 0; + mDNSu8 ret = (mDNSu8)mDNS_NoWake; + + if (DisableSleepProxyClient) + { + LogSPS("SystemWakeForNetworkAccess: Sleep Proxy Client disabled by command-line option"); + return mDNSfalse; + } + + GetCurrentPMSetting(CFSTR("Wake On LAN"), &val); + + ret = (mDNSu8)(val != 0) ? mDNS_WakeOnAC : mDNS_NoWake; + +#if APPLE_OSX_mDNSResponder && !TARGET_OS_EMBEDDED + // If we have TCP Keepalive support, system is capable of registering for TCP Keepalives. + // Further policy decisions on whether to offload the records is handled during sleep processing. + if ((ret == mDNS_NoWake) && SupportsTCPKeepAlive()) + ret = (mDNSu8)mDNS_WakeOnBattery; +#endif // APPLE_OSX_mDNSResponder + + LogSPS("SystemWakeForNetworkAccess: Wake On LAN: %d", ret); + return ret; +} + +mDNSlocal mDNSBool SystemSleepOnlyIfWakeOnLAN(void) +{ + mDNSs32 val = 0; + GetCurrentPMSetting(CFSTR("PrioritizeNetworkReachabilityOverSleep"), &val); + return val != 0 ? mDNStrue : mDNSfalse; +} + +#if APPLE_OSX_mDNSResponder +// When sleeping, we always ensure that the _autotunnel6 record (if connected to RR relay) +// gets deregistered, so that older peers are forced to connect over direct UDP instead of +// the RR relay. +// +// When sleeping w/o a successful AutoTunnel NAT Mapping, we ensure that all our BTMM +// service records are deregistered, so they do not appear in peers' Finder sidebars. +// We do this by checking for the (non-autotunnel) SRV records, as the PTR and TXT records +// depend on their associated SRV record and therefore will be deregistered together in a +// single update with the SRV record. +// +// Also, the per-zone _kerberos TXT record is always there, including while sleeping, so +// its presence shouldn't delay sleep. +// +// Note that the order of record deregistration is: first _autotunnel6 (if connected to RR +// relay) and host records get deregistered, then SRV (UpdateAllSrvRecords), PTR and TXT. +// +// Also note that returning false here will not delay sleep past the maximum of 10 seconds. +mDNSexport mDNSBool RecordReadyForSleep(mDNS *const m, AuthRecord *rr) +{ + if (!AuthRecord_uDNS(rr)) return mDNStrue; + + if ((rr->resrec.rrtype == kDNSType_AAAA) && SameDomainLabel(rr->namestorage.c, (const mDNSu8 *)"\x0c_autotunnel6")) + { + LogInfo("RecordReadyForSleep: %s not ready for sleep", ARDisplayString(m, rr)); + return mDNSfalse; + } + + if ((mDNSIPPortIsZero(m->AutoTunnelNAT.ExternalPort) || m->AutoTunnelNAT.Result)) + { + if (rr->resrec.rrtype == kDNSType_SRV && rr->state != regState_NoTarget && rr->zone + && !SameDomainLabel(rr->namestorage.c, (const mDNSu8 *)"\x0b_autotunnel")) + { + DomainAuthInfo *info = GetAuthInfoForName_internal(m, rr->zone); + if (info && info->AutoTunnel) + { + LogInfo("RecordReadyForSleep: %s not ready for sleep", ARDisplayString(m, rr)); + return mDNSfalse; + } + } + } + + return mDNStrue; +} + +// Caller must hold the lock +mDNSexport void RemoveAutoTunnel6Record(mDNS *const m) +{ + DomainAuthInfo *info; + // Set the address to zero before calling UpdateAutoTunnel6Record, so that it will + // deregister the record, and the MemFree callback won't re-register. + m->AutoTunnelRelayAddr = zerov6Addr; + for (info = m->AuthInfoList; info; info = info->next) + if (info->AutoTunnel) + UpdateAutoTunnel6Record(m, info); +} + +mDNSlocal mDNSBool IPv6AddressIsOnInterface(mDNSv6Addr ipv6Addr, char *ifname) +{ + struct ifaddrs *ifa; + struct ifaddrs *ifaddrs; + mDNSAddr addr; + + if (if_nametoindex(ifname) == 0) {LogInfo("IPv6AddressIsOnInterface: Invalid name %s", ifname); return mDNSfalse;} + + if (getifaddrs(&ifaddrs) < 0) {LogInfo("IPv6AddressIsOnInterface: getifaddrs failed"); return mDNSfalse;} + + for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next) + { + if (strncmp(ifa->ifa_name, ifname, IFNAMSIZ) != 0) + continue; + if ((ifa->ifa_flags & IFF_UP) == 0 || !ifa->ifa_addr || ifa->ifa_addr->sa_family != AF_INET6) + continue; + if (SetupAddr(&addr, ifa->ifa_addr) != mStatus_NoError) + { + LogInfo("IPv6AddressIsOnInterface: SetupAddr error, continuing to the next address"); + continue; + } + if (mDNSSameIPv6Address(ipv6Addr, *(mDNSv6Addr*)&addr.ip.v6)) + { + LogInfo("IPv6AddressIsOnInterface: found %.16a", &ipv6Addr); + break; + } + } + freeifaddrs(ifaddrs); + return ifa != NULL; +} + +mDNSlocal mDNSv6Addr IPv6AddressFromString(char* buf) +{ + mDNSv6Addr retVal; + struct addrinfo hints; + struct addrinfo *res0; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET6; + hints.ai_flags = AI_NUMERICHOST; + + int err = getaddrinfo(buf, NULL, &hints, &res0); + if (err) + return zerov6Addr; + + retVal = *(mDNSv6Addr*)&((struct sockaddr_in6*)res0->ai_addr)->sin6_addr; + + freeaddrinfo(res0); + + return retVal; +} + +mDNSlocal CFDictionaryRef CopyConnectivityBackToMyMacDict() +{ + SCDynamicStoreRef store = NULL; + CFDictionaryRef connd = NULL; + CFDictionaryRef BTMMDict = NULL; + + store = SCDynamicStoreCreate(NULL, CFSTR("mDNSResponder:CopyConnectivityBackToMyMacDict"), NULL, NULL); + if (!store) + { + LogMsg("CopyConnectivityBackToMyMacDict: SCDynamicStoreCreate failed: %s", SCErrorString(SCError())); + goto end; + } + + connd = SCDynamicStoreCopyValue(store, NetworkChangedKey_BTMMConnectivity); + if (!connd) + { + LogInfo("CopyConnectivityBackToMyMacDict: SCDynamicStoreCopyValue failed: %s", SCErrorString(SCError())); + goto end; + } + + BTMMDict = CFDictionaryGetValue(connd, CFSTR("BackToMyMac")); + if (!BTMMDict) + { + LogInfo("CopyConnectivityBackToMyMacDict: CFDictionaryGetValue: No value for BackToMyMac"); + goto end; + } + + // Non-dictionary is treated as non-existent dictionary + if (CFGetTypeID(BTMMDict) != CFDictionaryGetTypeID()) + { + BTMMDict = NULL; + LogMsg("CopyConnectivityBackToMyMacDict: BackToMyMac not a dictionary"); + goto end; + } + + CFRetain(BTMMDict); + +end: + if (connd) CFRelease(connd); + if (store) CFRelease(store); + + return BTMMDict; +} + +#define MAX_IPV6_TEXTUAL 40 + +mDNSlocal mDNSv6Addr ParseBackToMyMacAddr(CFDictionaryRef BTMMDict, CFStringRef ifKey, CFStringRef addrKey) +{ + mDNSv6Addr retVal = zerov6Addr; + CFTypeRef string = NULL; + char ifname[IFNAMSIZ]; + char address[MAX_IPV6_TEXTUAL]; + + if (!BTMMDict) + return zerov6Addr; + + if (!CFDictionaryGetValueIfPresent(BTMMDict, ifKey, &string)) + { + LogInfo("ParseBackToMyMacAddr: interface key does not exist"); + return zerov6Addr; + } + + if (!CFStringGetCString(string, ifname, IFNAMSIZ, kCFStringEncodingUTF8)) + { + LogMsg("ParseBackToMyMacAddr: Could not convert interface to CString"); + return zerov6Addr; + } + + if (!CFDictionaryGetValueIfPresent(BTMMDict, addrKey, &string)) + { + LogMsg("ParseBackToMyMacAddr: address key does not exist, but interface key does"); + return zerov6Addr; + } + + if (!CFStringGetCString(string, address, sizeof(address), kCFStringEncodingUTF8)) + { + LogMsg("ParseBackToMyMacAddr: Could not convert address to CString"); + return zerov6Addr; + } + + retVal = IPv6AddressFromString(address); + LogInfo("ParseBackToMyMacAddr: %s (%s) %.16a", ifname, address, &retVal); + + if (mDNSIPv6AddressIsZero(retVal)) + return zerov6Addr; + + if (!IPv6AddressIsOnInterface(retVal, ifname)) + { + LogMsg("ParseBackToMyMacAddr: %.16a is not on %s", &retVal, ifname); + return zerov6Addr; + } + + return retVal; +} + +mDNSlocal CFDictionaryRef GetBackToMyMacZones(CFDictionaryRef BTMMDict) +{ + CFTypeRef zones = NULL; + + if (!BTMMDict) + return NULL; + + if (!CFDictionaryGetValueIfPresent(BTMMDict, CFSTR("Zones"), &zones)) + { + LogInfo("CopyBTMMZones: Zones key does not exist"); + return NULL; + } + + return zones; +} + +mDNSlocal mDNSv6Addr ParseBackToMyMacZone(CFDictionaryRef zones, DomainAuthInfo* info) +{ + mDNSv6Addr addr = zerov6Addr; + char buffer[MAX_ESCAPED_DOMAIN_NAME]; + CFStringRef domain = NULL; + CFTypeRef theZone = NULL; + + if (!zones) + return addr; + + ConvertDomainNameToCString(&info->domain, buffer); + domain = CFStringCreateWithCString(NULL, buffer, kCFStringEncodingUTF8); + if (!domain) + return addr; + + if (CFDictionaryGetValueIfPresent(zones, domain, &theZone)) + addr = ParseBackToMyMacAddr(theZone, CFSTR("Interface"), CFSTR("Address")); + + CFRelease(domain); + + return addr; +} + +mDNSlocal void SetupBackToMyMacInnerAddresses(mDNS *const m, CFDictionaryRef BTMMDict) +{ + DomainAuthInfo* info; + CFDictionaryRef zones = GetBackToMyMacZones(BTMMDict); + mDNSv6Addr newAddr; + + for (info = m->AuthInfoList; info; info = info->next) + { + if (!info->AutoTunnel) + continue; + + newAddr = ParseBackToMyMacZone(zones, info); + + if (mDNSSameIPv6Address(newAddr, info->AutoTunnelInnerAddress)) + continue; + + info->AutoTunnelInnerAddress = newAddr; + DeregisterAutoTunnelHostRecord(m, info); + UpdateAutoTunnelHostRecord(m, info); + UpdateAutoTunnelDomainStatus(m, info); + } +} + +// MUST be called holding the lock +mDNSlocal void ProcessConndConfigChanges(mDNS *const m) +{ + CFDictionaryRef dict = CopyConnectivityBackToMyMacDict(); + if (!dict) + LogInfo("ProcessConndConfigChanges: No BTMM dictionary"); + mDNSv6Addr relayAddr = ParseBackToMyMacAddr(dict, CFSTR("RelayInterface"), CFSTR("RelayAddress")); + + LogInfo("ProcessConndConfigChanges: relay %.16a", &relayAddr); + + SetupBackToMyMacInnerAddresses(m, dict); + + if (dict) CFRelease(dict); + + if (!mDNSSameIPv6Address(relayAddr, m->AutoTunnelRelayAddr)) + { + m->AutoTunnelRelayAddr = relayAddr; + + DomainAuthInfo* info; + for (info = m->AuthInfoList; info; info = info->next) + if (info->AutoTunnel) + { + DeregisterAutoTunnel6Record(m, info); + UpdateAutoTunnel6Record(m, info); + UpdateAutoTunnelDomainStatus(m, info); + } + + // Determine whether we need racoon to accept incoming connections + UpdateAnonymousRacoonConfig(m); + } + + // If awacsd crashes or exits for some reason, restart it + UpdateBTMMRelayConnection(m); +} +#endif /* APPLE_OSX_mDNSResponder */ + +mDNSlocal mDNSBool IsAppleNetwork(mDNS *const m) +{ + DNSServer *s; + // Determine if we're on AppleNW based on DNSServer having 17.x.y.z IPv4 addr + for (s = m->DNSServers; s; s = s->next) + { + if (s->addr.ip.v4.b[0] == 17) + { + LogInfo("IsAppleNetwork: Found 17.x.y.z DNSServer concluding that we are on AppleNW: %##s %#a", s->domain.c, &s->addr); + return mDNStrue; + } + } + return mDNSfalse; +} + +mDNSexport void mDNSMacOSXNetworkChanged(mDNS *const m) +{ + LogInfo("*** Network Configuration Change *** (%d)%s", + m->p->NetworkChanged ? mDNS_TimeNow(m) - m->p->NetworkChanged : 0, + m->p->NetworkChanged ? "" : " (no scheduled configuration change)"); + m->p->NetworkChanged = 0; // If we received a network change event and deferred processing, we're now dealing with it + mDNSs32 utc = mDNSPlatformUTC(); + m->SystemWakeOnLANEnabled = SystemWakeForNetworkAccess(); + m->SystemSleepOnlyIfWakeOnLAN = SystemSleepOnlyIfWakeOnLAN(); + MarkAllInterfacesInactive(m, utc); + UpdateInterfaceList(m, utc); + ClearInactiveInterfaces(m, utc); + SetupActiveInterfaces(m, utc); + +#if APPLE_OSX_mDNSResponder + + mDNS_Lock(m); + ProcessConndConfigChanges(m); + mDNS_Unlock(m); + + // Scan to find client tunnels whose questions have completed, + // but whose local inner/outer addresses have changed since the tunnel was set up + ClientTunnel *p; + for (p = m->TunnelClients; p; p = p->next) + if (p->q.ThisQInterval < 0) + { + DomainAuthInfo* info = GetAuthInfoForName(m, &p->dstname); + if (!info) + { + LogMsg("mDNSMacOSXNetworkChanged: Could not get AuthInfo for %##s, removing tunnel keys", p->dstname.c); + AutoTunnelSetKeys(p, mDNSfalse); + } + else + { + mDNSv6Addr inner = info->AutoTunnelInnerAddress; + + if (!mDNSIPPortIsZero(p->rmt_outer_port)) + { + mDNSAddr tmpSrc = zeroAddr; + mDNSAddr tmpDst = { mDNSAddrType_IPv4, {{{0}}} }; + tmpDst.ip.v4 = p->rmt_outer; + mDNSPlatformSourceAddrForDest(&tmpSrc, &tmpDst); + if (!mDNSSameIPv6Address(p->loc_inner, inner) || + !mDNSSameIPv4Address(p->loc_outer, tmpSrc.ip.v4)) + { + AutoTunnelSetKeys(p, mDNSfalse); + p->loc_inner = inner; + p->loc_outer = tmpSrc.ip.v4; + AutoTunnelSetKeys(p, mDNStrue); + } + } + else + { + if (!mDNSSameIPv6Address(p->loc_inner, inner) || + !mDNSSameIPv6Address(p->loc_outer6, m->AutoTunnelRelayAddr)) + { + AutoTunnelSetKeys(p, mDNSfalse); + p->loc_inner = inner; + p->loc_outer6 = m->AutoTunnelRelayAddr; + AutoTunnelSetKeys(p, mDNStrue); + } + } + } + } + + SetSPS(m); + + NetworkInterfaceInfoOSX *i; + for (i = m->p->InterfaceList; i; i = i->next) + { + if (!m->SPSSocket) // Not being Sleep Proxy Server; close any open BPF fds + { + if (i->BPF_fd >= 0 && CountProxyTargets(m, i, mDNSNULL, mDNSNULL) == 0) CloseBPF(i); + } + else // else, we're Sleep Proxy Server; open BPF fds + { + if (i->Exists && i->Registered == i && i->ifinfo.McastTxRx && !(i->ifa_flags & IFF_LOOPBACK) && i->BPF_fd == -1) + { LogSPS("%s requesting BPF", i->ifinfo.ifname); i->BPF_fd = -2; mDNSRequestBPF(); } + } + } + +#endif // APPLE_OSX_mDNSResponder + + uDNS_SetupDNSConfig(m); + mDNS_ConfigChanged(m); + + if (IsAppleNetwork(m) != mDNS_McastTracingEnabled) + { + mDNS_McastTracingEnabled = mDNS_McastTracingEnabled ? mDNSfalse : mDNStrue; + LogMsg("mDNSMacOSXNetworkChanged: Multicast Tracing %s", mDNS_McastTracingEnabled ? "Enabled" : "Disabled"); + UpdateDebugState(); + } + +} + +// Called with KQueueLock & mDNS lock +mDNSlocal void SetNetworkChanged(mDNS *const m, mDNSs32 delay) +{ + if (!m->p->NetworkChanged || m->p->NetworkChanged - NonZeroTime(m->timenow + delay) < 0) + { + m->p->NetworkChanged = NonZeroTime(m->timenow + delay); + LogInfo("SetNetworkChanged: scheduling in %d msec", delay); + } +} + +// Called with KQueueLock & mDNS lock +mDNSlocal void SetKeyChainTimer(mDNS *const m, mDNSs32 delay) +{ + // If it's not set or it needs to happen sooner than when it's currently set + if (!m->p->KeyChainTimer || m->p->KeyChainTimer - NonZeroTime(m->timenow + delay) > 0) + { + m->p->KeyChainTimer = NonZeroTime(m->timenow + delay); + LogInfo("SetKeyChainTimer: %d", delay); + } +} + +// Copy the fourth slash-delimited element from either: +// State:/Network/Interface/<bsdname>/IPv4 +// or +// Setup:/Network/Service/<servicename>/Interface +mDNSlocal CFStringRef CopyNameFromKey(CFStringRef key) +{ + CFArrayRef a; + CFStringRef name = NULL; + + a = CFStringCreateArrayBySeparatingStrings(NULL, key, CFSTR("/")); + if (a && CFArrayGetCount(a) == 5) name = CFRetain(CFArrayGetValueAtIndex(a, 3)); + if (a != NULL) CFRelease(a); + + return name; +} + +// Whether a key from a network change notification corresponds to +// an IP service that is explicitly configured for IPv4 Link Local +mDNSlocal mDNSBool ChangedKeysHaveIPv4LL(CFArrayRef inkeys) +{ + SCDynamicStoreRef store = NULL; + CFDictionaryRef dict = NULL; + CFMutableArrayRef a; + const void **keys = NULL, **vals = NULL; + CFStringRef pattern = NULL; + int i, ic, j, jc; + mDNSBool found = mDNSfalse; + + jc = CFArrayGetCount(inkeys); + if (!jc) goto done; + + store = SCDynamicStoreCreate(NULL, CFSTR("mDNSResponder:ChangedKeysHaveIPv4LL"), NULL, NULL); + if (store == NULL) goto done; + + a = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); + if (a == NULL) goto done; + + // Setup:/Network/Service/[^/]+/Interface + pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL, kSCDynamicStoreDomainSetup, kSCCompAnyRegex, kSCEntNetInterface); + if (pattern == NULL) goto done; + CFArrayAppendValue(a, pattern); + CFRelease(pattern); + + // Setup:/Network/Service/[^/]+/IPv4 + pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL, kSCDynamicStoreDomainSetup, kSCCompAnyRegex, kSCEntNetIPv4); + if (pattern == NULL) goto done; + CFArrayAppendValue(a, pattern); + CFRelease(pattern); + + dict = SCDynamicStoreCopyMultiple(store, NULL, a); + CFRelease(a); + + if (!dict) + { + LogMsg("ChangedKeysHaveIPv4LL: Empty dictionary"); + goto done; + } + + ic = CFDictionaryGetCount(dict); + vals = mDNSPlatformMemAllocate(sizeof (void *) * ic); + keys = mDNSPlatformMemAllocate(sizeof (void *) * ic); + CFDictionaryGetKeysAndValues(dict, keys, vals); + + for (j = 0; j < jc && !found; j++) + { + CFStringRef key = CFArrayGetValueAtIndex(inkeys, j); + CFStringRef ifname = NULL; + + char buf[256]; + + // It would be nice to use a regex here + if (!CFStringHasPrefix(key, CFSTR("State:/Network/Interface/")) || !CFStringHasSuffix(key, kSCEntNetIPv4)) continue; + + if ((ifname = CopyNameFromKey(key)) == NULL) continue; + if (mDNS_LoggingEnabled) + { + if (!CFStringGetCString(ifname, buf, sizeof(buf), kCFStringEncodingUTF8)) buf[0] = 0; + LogInfo("ChangedKeysHaveIPv4LL: potential ifname %s", buf); + } + + for (i = 0; i < ic; i++) + { + CFDictionaryRef ipv4dict; + CFStringRef name; + CFStringRef serviceid; + CFStringRef configmethod; + + if (!CFStringHasSuffix(keys[i], kSCEntNetInterface)) continue; + + if (CFDictionaryGetTypeID() != CFGetTypeID(vals[i])) continue; + + if ((name = CFDictionaryGetValue(vals[i], kSCPropNetInterfaceDeviceName)) == NULL) continue; + + if (!CFEqual(ifname, name)) continue; + + if ((serviceid = CopyNameFromKey(keys[i])) == NULL) continue; + if (mDNS_LoggingEnabled) + { + if (!CFStringGetCString(serviceid, buf, sizeof(buf), kCFStringEncodingUTF8)) buf[0] = 0; + LogInfo("ChangedKeysHaveIPv4LL: found serviceid %s", buf); + } + + pattern = SCDynamicStoreKeyCreateNetworkServiceEntity(NULL, kSCDynamicStoreDomainSetup, serviceid, kSCEntNetIPv4); + CFRelease(serviceid); + if (pattern == NULL) continue; + + ipv4dict = CFDictionaryGetValue(dict, pattern); + CFRelease(pattern); + if (!ipv4dict || CFDictionaryGetTypeID() != CFGetTypeID(ipv4dict)) continue; + + configmethod = CFDictionaryGetValue(ipv4dict, kSCPropNetIPv4ConfigMethod); + if (!configmethod) continue; + + if (mDNS_LoggingEnabled) + { + if (!CFStringGetCString(configmethod, buf, sizeof(buf), kCFStringEncodingUTF8)) buf[0] = 0; + LogInfo("ChangedKeysHaveIPv4LL: configmethod %s", buf); + } + + if (CFEqual(configmethod, kSCValNetIPv4ConfigMethodLinkLocal)) { found = mDNStrue; break; } + } + + CFRelease(ifname); + } + +done: + if (vals != NULL) mDNSPlatformMemFree(vals); + if (keys != NULL) mDNSPlatformMemFree(keys); + if (dict != NULL) CFRelease(dict); + if (store != NULL) CFRelease(store); + + return found; +} + +mDNSlocal void NetworkChanged(SCDynamicStoreRef store, CFArrayRef changedKeys, void *context) +{ + (void)store; // Parameter not used + mDNSBool changeNow = mDNSfalse; + mDNS *const m = (mDNS *const)context; + KQueueLock(m); + mDNS_Lock(m); + + mDNSs32 delay = mDNSPlatformOneSecond * 2; // Start off assuming a two-second delay + + int c = CFArrayGetCount(changedKeys); // Count changes + CFRange range = { 0, c }; + int c1 = (CFArrayContainsValue(changedKeys, range, NetworkChangedKey_Hostnames ) != 0); + int c2 = (CFArrayContainsValue(changedKeys, range, NetworkChangedKey_Computername) != 0); + int c3 = (CFArrayContainsValue(changedKeys, range, NetworkChangedKey_DynamicDNS ) != 0); + int c4 = (CFArrayContainsValue(changedKeys, range, NetworkChangedKey_DNS ) != 0); + if (c && c - c1 - c2 - c3 - c4 == 0) + delay = mDNSPlatformOneSecond/10; // If these were the only changes, shorten delay + + // Do immediate network changed processing for "p2p*" interfaces and + // for interfaces with the IFEF_DIRECTLINK flag set. + { + CFArrayRef labels; + CFIndex n; + for (int i = 0; i < c; i++) + { + CFStringRef key = CFArrayGetValueAtIndex(changedKeys, i); + + // Only look at keys with prefix "State:/Network/Interface/" + if (!CFStringHasPrefix(key, NetworkChangedKey_StateInterfacePrefix)) + continue; + + // And suffix "IPv6" or "IPv4". + if (!CFStringHasSuffix(key, kSCEntNetIPv6) && !CFStringHasSuffix(key, kSCEntNetIPv4)) + continue; + + labels = CFStringCreateArrayBySeparatingStrings(NULL, key, CFSTR("/")); + if (labels == NULL) + break; + n = CFArrayGetCount(labels); + + // Interface changes will have keys of the form: + // State:/Network/Interface/<interfaceName>/IPv6 + // Thus five '/' seperated fields, the 4th one being the <interfaceName> string. + if (n == 5) + { + char buf[256]; + + // The 4th label (index = 3) should be the interface name. + if (CFStringGetCString(CFArrayGetValueAtIndex(labels, 3), buf, sizeof(buf), kCFStringEncodingUTF8) + && (strstr(buf, "p2p") || (getExtendedFlags(buf) & IFEF_DIRECTLINK))) + { + LogInfo("NetworkChanged: interface %s, not delaying network change", buf); + changeNow = mDNStrue; + CFRelease(labels); + break; + } + } + CFRelease(labels); + } + } + + mDNSBool btmmChanged = CFArrayContainsValue(changedKeys, range, NetworkChangedKey_BackToMyMac); + if (btmmChanged) delay = 0; + + if (mDNS_LoggingEnabled) + { + int i; + for (i=0; i<c; i++) + { + char buf[256]; + if (!CFStringGetCString(CFArrayGetValueAtIndex(changedKeys, i), buf, sizeof(buf), kCFStringEncodingUTF8)) buf[0] = 0; + LogInfo("*** NetworkChanged SC key: %s", buf); + } + LogInfo("*** NetworkChanged *** %d change%s %s%s%s%sdelay %d", + c, c>1 ? "s" : "", + c1 ? "(Local Hostname) " : "", + c2 ? "(Computer Name) " : "", + c3 ? "(DynamicDNS) " : "", + c4 ? "(DNS) " : "", + changeNow ? 0 : delay); + } + + if (!changeNow) + SetNetworkChanged(m, delay); + + // Other software might pick up these changes to register or browse in WAB or BTMM domains, + // so in order for secure updates to be made to the server, make sure to read the keychain and + // setup the DomainAuthInfo before handing the network change. + // If we don't, then we will first try to register services in the clear, then later setup the + // DomainAuthInfo, which is incorrect. + if (c3 || btmmChanged) + SetKeyChainTimer(m, delay); + + mDNS_Unlock(m); + + // If DNS settings changed, immediately force a reconfig (esp. cache flush) + // Similarly, if an interface changed that is explicitly IPv4 link local, immediately force a reconfig + if (c4 || ChangedKeysHaveIPv4LL(changedKeys) || changeNow) mDNSMacOSXNetworkChanged(m); + + KQueueUnlock(m, "NetworkChanged"); +} + +#if APPLE_OSX_mDNSResponder +mDNSlocal void RefreshSPSStatus(const void *key, const void *value, void *context) +{ + (void)context; + char buf[IFNAMSIZ]; + + CFStringRef ifnameStr = (CFStringRef)key; + CFArrayRef array = (CFArrayRef)value; + if (!CFStringGetCString(ifnameStr, buf, sizeof(buf), kCFStringEncodingUTF8)) + buf[0] = 0; + + LogInfo("RefreshSPSStatus: Updating SPS state for key %s, array count %d", buf, CFArrayGetCount(array)); + mDNSDynamicStoreSetConfig(kmDNSSleepProxyServersState, buf, value); +} +#endif + +mDNSlocal void DynamicStoreReconnected(SCDynamicStoreRef store, void *info) +{ + mDNS *const m = (mDNS *const)info; + (void)store; + + LogInfo("DynamicStoreReconnected: Reconnected"); + + // State:/Network/MulticastDNS + SetLocalDomains(); + + // State:/Network/DynamicDNS + if (m->FQDN.c[0]) + mDNSPlatformDynDNSHostNameStatusChanged(&m->FQDN, 1); + + // Note: PrivateDNS and BackToMyMac are automatically populated when configd is restarted + // as we receive network change notifications and thus not necessary. But we leave it here + // so that if things are done differently in the future, this code still works. + + // State:/Network/PrivateDNS + if (privateDnsArray) + mDNSDynamicStoreSetConfig(kmDNSPrivateConfig, mDNSNULL, privateDnsArray); + +#if APPLE_OSX_mDNSResponder + mDNS_Lock(m); + // State:/Network/BackToMyMac + UpdateAutoTunnelDomainStatuses(m); + mDNS_Unlock(m); + + // State:/Network/Interface/en0/SleepProxyServers + if (spsStatusDict) + CFDictionaryApplyFunction(spsStatusDict, RefreshSPSStatus, NULL); +#endif +} + +mDNSlocal mStatus WatchForNetworkChanges(mDNS *const m) +{ + mStatus err = -1; + SCDynamicStoreContext context = { 0, m, NULL, NULL, NULL }; + SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("mDNSResponder:WatchForNetworkChanges"), NetworkChanged, &context); + CFMutableArrayRef keys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); + CFStringRef pattern1 = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL, kSCDynamicStoreDomainState, kSCCompAnyRegex, kSCEntNetIPv4); + CFStringRef pattern2 = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL, kSCDynamicStoreDomainState, kSCCompAnyRegex, kSCEntNetIPv6); + CFMutableArrayRef patterns = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); + + if (!store) { LogMsg("SCDynamicStoreCreate failed: %s", SCErrorString(SCError())); goto error; } + if (!keys || !pattern1 || !pattern2 || !patterns) goto error; + + CFArrayAppendValue(keys, NetworkChangedKey_IPv4); + CFArrayAppendValue(keys, NetworkChangedKey_IPv6); + CFArrayAppendValue(keys, NetworkChangedKey_Hostnames); + CFArrayAppendValue(keys, NetworkChangedKey_Computername); + CFArrayAppendValue(keys, NetworkChangedKey_DNS); + CFArrayAppendValue(keys, NetworkChangedKey_DynamicDNS); + CFArrayAppendValue(keys, NetworkChangedKey_BackToMyMac); + CFArrayAppendValue(keys, NetworkChangedKey_PowerSettings); // should remove as part of <rdar://problem/6751656> + CFArrayAppendValue(keys, NetworkChangedKey_BTMMConnectivity); + CFArrayAppendValue(patterns, pattern1); + CFArrayAppendValue(patterns, pattern2); + CFArrayAppendValue(patterns, CFSTR("State:/Network/Interface/[^/]+/AirPort")); + if (!SCDynamicStoreSetNotificationKeys(store, keys, patterns)) + { LogMsg("SCDynamicStoreSetNotificationKeys failed: %s", SCErrorString(SCError())); goto error; } + +#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM + if (!SCDynamicStoreSetDispatchQueue(store, dispatch_get_main_queue())) + { LogMsg("SCDynamicStoreCreateRunLoopSource failed: %s", SCErrorString(SCError())); goto error; } +#else + m->p->StoreRLS = SCDynamicStoreCreateRunLoopSource(NULL, store, 0); + if (!m->p->StoreRLS) { LogMsg("SCDynamicStoreCreateRunLoopSource failed: %s", SCErrorString(SCError())); goto error; } + CFRunLoopAddSource(CFRunLoopGetCurrent(), m->p->StoreRLS, kCFRunLoopDefaultMode); +#endif + SCDynamicStoreSetDisconnectCallBack(store, DynamicStoreReconnected); + m->p->Store = store; + err = 0; + goto exit; + +error: + if (store) CFRelease(store); + +exit: + if (patterns) CFRelease(patterns); + if (pattern2) CFRelease(pattern2); + if (pattern1) CFRelease(pattern1); + if (keys) CFRelease(keys); + + return(err); +} + +#if 0 // <rdar://problem/6751656> +mDNSlocal void PMChanged(void *context) +{ + mDNS *const m = (mDNS *const)context; + + KQueueLock(m); + mDNS_Lock(m); + + LogSPS("PMChanged"); + + SetNetworkChanged(m, mDNSPlatformOneSecond * 2); + + mDNS_Unlock(m); + KQueueUnlock(m, "PMChanged"); +} + +mDNSlocal mStatus WatchForPMChanges(mDNS *const m) +{ + m->p->PMRLS = IOPMPrefsNotificationCreateRunLoopSource(PMChanged, m); + if (!m->p->PMRLS) { LogMsg("IOPMPrefsNotificationCreateRunLoopSource failed!"); return mStatus_UnknownErr; } + + CFRunLoopAddSource(CFRunLoopGetCurrent(), m->p->PMRLS, kCFRunLoopDefaultMode); + + return mStatus_NoError; +} +#endif + +#if !TARGET_OS_EMBEDDED // don't setup packet filter rules on embedded + +mDNSlocal void mDNSSetPacketFilterRules(mDNS *const m, char * ifname, const ResourceRecord *const excludeRecord) +{ + AuthRecord *rr; + pfArray_t portArray; + pfArray_t protocolArray; + uint32_t count = 0; + + for (rr = m->ResourceRecords; rr; rr=rr->next) + { + if ((rr->resrec.rrtype == kDNSServiceType_SRV) + && ((rr->ARType == AuthRecordAnyIncludeP2P) || (rr->ARType == AuthRecordAnyIncludeAWDLandP2P))) + { + const mDNSu8 *p; + + if (count >= PFPortArraySize) + { + LogMsg("mDNSSetPacketFilterRules: %d service limit, skipping %s", PFPortArraySize, ARDisplayString(m, rr)); + continue; + } + + if (excludeRecord && IdenticalResourceRecord(&rr->resrec, excludeRecord)) + { + LogInfo("mDNSSetPacketFilterRules: record being removed, skipping %s", ARDisplayString(m, rr)); + continue; + } + + LogInfo("mDNSSetPacketFilterRules: found %s", ARDisplayString(m, rr)); + + portArray[count] = rr->resrec.rdata->u.srv.port.NotAnInteger; + + // Assume <Service Instance>.<App Protocol>.<Transport Protocol>.<Name> + p = rr->resrec.name->c; + + // Skip to App Protocol + if (p[0]) p += 1 + p[0]; + + // Skip to Transport Protocol + if (p[0]) p += 1 + p[0]; + + if (SameDomainLabel(p, (mDNSu8 *)"\x4" "_tcp")) protocolArray[count] = IPPROTO_TCP; + else if (SameDomainLabel(p, (mDNSu8 *)"\x4" "_udp")) protocolArray[count] = IPPROTO_UDP; + else + { + LogMsg("mDNSSetPacketFilterRules: could not determine transport protocol of service"); + LogMsg("mDNSSetPacketFilterRules: %s", ARDisplayString(m, rr)); + return; + } + count++; + } + } + mDNSPacketFilterControl(PF_SET_RULES, ifname, count, portArray, protocolArray); +} + +// If the p2p interface already exists, update the Bonjour packet filter rules for it. +mDNSexport void mDNSUpdatePacketFilter(const ResourceRecord *const excludeRecord) +{ + mDNS *const m = &mDNSStorage; + + NetworkInterfaceInfo *intf = GetFirstActiveInterface(m->HostInterfaces); + while (intf) + { + if (strncmp(intf->ifname, "p2p", 3) == 0) + { + LogInfo("mDNSInitPacketFilter: Setting rules for ifname %s", intf->ifname); + mDNSSetPacketFilterRules(m, intf->ifname, excludeRecord); + break; + } + intf = GetFirstActiveInterface(intf->next); + } +} + +#else // !TARGET_OS_EMBEDDED + +// Currently no packet filter setup required on embedded platforms. +mDNSexport void mDNSUpdatePacketFilter(const ResourceRecord *const excludeRecord) +{ + (void) excludeRecord; // unused +} + +#endif // !TARGET_OS_EMBEDDED + +// Handle AWDL KEV_DL_MASTER_ELECTED event by restarting queries and advertisements +// marked to include the AWDL interface. +mDNSlocal void newMasterElected(mDNS *const m, struct net_event_data * ptr) +{ + char ifname[IFNAMSIZ]; + mDNSu32 interfaceIndex; + DNSQuestion *q; + AuthRecord *rr; + NetworkInterfaceInfoOSX *infoOSX; + mDNSInterfaceID InterfaceID; + + snprintf(ifname, IFNAMSIZ, "%s%d", ptr->if_name, ptr->if_unit); + interfaceIndex = if_nametoindex(ifname); + + if (!interfaceIndex) + { + LogMsg("newMasterElected: if_nametoindex(%s) failed", ifname); + return; + } + + LogInfo("newMasterElected: ifname = %s, interfaceIndex = %d", ifname, interfaceIndex); + infoOSX = IfindexToInterfaceInfoOSX(m, (mDNSInterfaceID)(uintptr_t)interfaceIndex); + + // Can get an KEV_DL_MASTER_ELECTED event prior to the interface existing + // when it is first brought up. + if (!infoOSX) + { + LogInfo("newMasterElected: interface not yet active"); + return; + } + InterfaceID = infoOSX->ifinfo.InterfaceID; + + for (q = m->Questions; q; q=q->next) + { + if ((!q->InterfaceID && (q->flags & kDNSServiceFlagsIncludeAWDL)) + || q->InterfaceID == InterfaceID) + { + LogInfo("newMasterElected: restarting %s query for %##s", DNSTypeName(q->qtype), q->qname.c); + mDNSCoreRestartQuestion(m, q); + } + } + + for (rr = m->ResourceRecords; rr; rr=rr->next) + { + if ((!rr->resrec.InterfaceID + && ((rr->ARType == AuthRecordAnyIncludeAWDL) || ((rr->ARType == AuthRecordAnyIncludeAWDLandP2P)))) + || rr->resrec.InterfaceID == InterfaceID) + { + LogInfo("newMasterElected: restarting %s announcements for %##s", DNSTypeName(rr->resrec.rrtype), rr->namestorage.c); + mDNSCoreRestartRegistration(m, rr, -1); + } + } +} + +// An ssth array of all zeroes indicates the peer has no services registered. +mDNSlocal mDNSBool allZeroSSTH(struct opaque_presence_indication *op) +{ + int i; + int *intp = (int *) op->ssth; + + // MAX_SSTH_SIZE should always be a multiple of sizeof(int), if + // it's not, print an error message and return false so that + // corresponding peer records are not flushed when KEV_DL_NODE_PRESENCE event + // is received. + if (MAX_SSTH_SIZE % sizeof(int)) + { + LogInfo("allZeroSSTH: MAX_SSTH_SIZE = %d not a multiple of sizeof(int)", MAX_SSTH_SIZE); + return mDNSfalse; + } + + for (i = 0; i < (int)(MAX_SSTH_SIZE / sizeof(int)); i++, intp++) + { + if (*intp) + return mDNSfalse; + } + return mDNStrue; +} + +// mDNS_Reconfirm_internal() adds 33% to this interval, so the records should +// be removed in 4 seconds. +#define kAWDLReconfirmTime ((mDNSu32)mDNSPlatformOneSecond * 3) + +// Mark records from this peer for deletion from the cache. +mDNSlocal void removeCachedPeerRecords(mDNS *const m, mDNSu32 ifindex, mDNSAddr *ap) +{ + mDNSu32 slot; + CacheGroup *cg; + CacheRecord *cr; + mDNSInterfaceID InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(m, ifindex); + + if (!InterfaceID) + { + LogInfo("removeCachedPeerRecords: Invalid ifindex: %d", ifindex); + return; + } + + FORALL_CACHERECORDS(slot, cg, cr) + { + if ((InterfaceID == cr->resrec.InterfaceID) && mDNSSameAddress(ap, & cr->sourceAddress)) + { + LogInfo("removeCachedPeerRecords: %s %##s marking for deletion", + DNSTypeName(cr->resrec.rrtype), cr->resrec.name->c); + mDNS_Reconfirm_internal(m, cr, kAWDLReconfirmTime); + } + } +} + +// Handle KEV_DL_NODE_PRESENCE event. +mDNSlocal void nodePresence(mDNS *const m, struct kev_dl_node_presence * p) +{ + char buf[INET6_ADDRSTRLEN]; + struct opaque_presence_indication *op = (struct opaque_presence_indication *) p->node_service_info; + + if (inet_ntop(AF_INET6, & p->sin6_node_address.sin6_addr, buf, sizeof(buf))) + LogInfo("nodePresence: IPv6 address: %s, SUI %d", buf, op->SUI); + else + LogInfo("nodePresence: inet_ntop() error"); + + // AWDL will generate a KEV_DL_NODE_PRESENCE event with SSTH field of + // all zeroes when a node is present and has no services registered. + if (allZeroSSTH(op)) + { + mDNSAddr peerAddr; + + peerAddr.type = mDNSAddrType_IPv6; + peerAddr.ip.v6 = *(mDNSv6Addr*)&p->sin6_node_address.sin6_addr; + + LogInfo("nodePresence: ssth is all zeroes, delete cached records from this peer"); + removeCachedPeerRecords(m, p->sdl_node_address.sdl_index, & peerAddr); + } +} + +// Handle KEV_DL_NODE_ABSENCE event. +mDNSlocal void nodeAbsence(mDNS *const m, struct kev_dl_node_absence * p) +{ + mDNSAddr peerAddr; + char buf[INET6_ADDRSTRLEN]; + + if (inet_ntop(AF_INET6, & p->sin6_node_address.sin6_addr, buf, sizeof(buf))) + LogInfo("nodeAbsence: IPv6 address: %s", buf); + else + LogInfo("nodeAbsence: inet_ntop() error"); + + peerAddr.type = mDNSAddrType_IPv6; + peerAddr.ip.v6 = *(mDNSv6Addr*)&p->sin6_node_address.sin6_addr; + + LogInfo("nodeAbsence: delete cached records from this peer"); + removeCachedPeerRecords(m, p->sdl_node_address.sdl_index, & peerAddr); +} + +mDNSlocal void SysEventCallBack(int s1, short __unused filter, void *context) +{ + mDNS *const m = (mDNS *const)context; + + mDNS_Lock(m); + + struct { struct kern_event_msg k; char extra[256]; } msg; + int bytes = recv(s1, &msg, sizeof(msg), 0); + if (bytes < 0) + LogMsg("SysEventCallBack: recv error %d errno %d (%s)", bytes, errno, strerror(errno)); + else + { + LogInfo("SysEventCallBack got %d bytes size %d %X %s %X %s %X %s id %d code %d %s", + bytes, msg.k.total_size, + msg.k.vendor_code, msg.k.vendor_code == KEV_VENDOR_APPLE ? "KEV_VENDOR_APPLE" : "?", + msg.k.kev_class, msg.k.kev_class == KEV_NETWORK_CLASS ? "KEV_NETWORK_CLASS" : "?", + msg.k.kev_subclass, msg.k.kev_subclass == KEV_DL_SUBCLASS ? "KEV_DL_SUBCLASS" : "?", + msg.k.id, msg.k.event_code, + msg.k.event_code == KEV_DL_SIFFLAGS ? "KEV_DL_SIFFLAGS" : + msg.k.event_code == KEV_DL_SIFMETRICS ? "KEV_DL_SIFMETRICS" : + msg.k.event_code == KEV_DL_SIFMTU ? "KEV_DL_SIFMTU" : + msg.k.event_code == KEV_DL_SIFPHYS ? "KEV_DL_SIFPHYS" : + msg.k.event_code == KEV_DL_SIFMEDIA ? "KEV_DL_SIFMEDIA" : + msg.k.event_code == KEV_DL_SIFGENERIC ? "KEV_DL_SIFGENERIC" : + msg.k.event_code == KEV_DL_ADDMULTI ? "KEV_DL_ADDMULTI" : + msg.k.event_code == KEV_DL_DELMULTI ? "KEV_DL_DELMULTI" : + msg.k.event_code == KEV_DL_IF_ATTACHED ? "KEV_DL_IF_ATTACHED" : + msg.k.event_code == KEV_DL_IF_DETACHING ? "KEV_DL_IF_DETACHING" : + msg.k.event_code == KEV_DL_IF_DETACHED ? "KEV_DL_IF_DETACHED" : + msg.k.event_code == KEV_DL_LINK_OFF ? "KEV_DL_LINK_OFF" : + msg.k.event_code == KEV_DL_LINK_ON ? "KEV_DL_LINK_ON" : + msg.k.event_code == KEV_DL_PROTO_ATTACHED ? "KEV_DL_PROTO_ATTACHED" : + msg.k.event_code == KEV_DL_PROTO_DETACHED ? "KEV_DL_PROTO_DETACHED" : + msg.k.event_code == KEV_DL_LINK_ADDRESS_CHANGED ? "KEV_DL_LINK_ADDRESS_CHANGED" : + msg.k.event_code == KEV_DL_WAKEFLAGS_CHANGED ? "KEV_DL_WAKEFLAGS_CHANGED" : + msg.k.event_code == KEV_DL_IF_IDLE_ROUTE_REFCNT ? "KEV_DL_IF_IDLE_ROUTE_REFCNT" : + msg.k.event_code == KEV_DL_IFCAP_CHANGED ? "KEV_DL_IFCAP_CHANGED" : + msg.k.event_code == KEV_DL_LINK_QUALITY_METRIC_CHANGED ? "KEV_DL_LINK_QUALITY_METRIC_CHANGED" : + msg.k.event_code == KEV_DL_NODE_PRESENCE ? "KEV_DL_NODE_PRESENCE" : + msg.k.event_code == KEV_DL_NODE_ABSENCE ? "KEV_DL_NODE_ABSENCE" : + msg.k.event_code == KEV_DL_MASTER_ELECTED ? "KEV_DL_MASTER_ELECTED" : + "?"); + + if (msg.k.event_code == KEV_DL_NODE_PRESENCE) + nodePresence(m, (struct kev_dl_node_presence *) &msg.k.event_data); + + if (msg.k.event_code == KEV_DL_NODE_ABSENCE) + nodeAbsence(m, (struct kev_dl_node_absence *) &msg.k.event_data); + + if (msg.k.event_code == KEV_DL_MASTER_ELECTED) + newMasterElected(m, (struct net_event_data *) &msg.k.event_data); + + // We receive network change notifications both through configd and through SYSPROTO_EVENT socket. + // Configd may not generate network change events for manually configured interfaces (i.e., non-DHCP) + // always during sleep/wakeup due to some race conditions (See radar:8666757). At the same time, if + // "Wake on Network Access" is not turned on, the notification will not have KEV_DL_WAKEFLAGS_CHANGED. + // Hence, during wake up, if we see a KEV_DL_LINK_ON (i.e., link is UP), we trigger a network change. + + if (msg.k.event_code == KEV_DL_WAKEFLAGS_CHANGED || msg.k.event_code == KEV_DL_LINK_ON) + SetNetworkChanged(m, mDNSPlatformOneSecond * 2); + +#if !TARGET_OS_EMBEDDED // don't setup packet filter rules on embedded + + // For p2p interfaces, need to open the advertised service port in the firewall. + if (msg.k.event_code == KEV_DL_IF_ATTACHED) + { + struct net_event_data * p; + p = (struct net_event_data *) &msg.k.event_data; + + if (strncmp(p->if_name, "p2p", 3) == 0) + { + char ifname[IFNAMSIZ]; + snprintf(ifname, IFNAMSIZ, "%s%d", p->if_name, p->if_unit); + + LogInfo("SysEventCallBack: KEV_DL_IF_ATTACHED if_family = %d, if_unit = %d, if_name = %s", p->if_family, p->if_unit, p->if_name); + + mDNSSetPacketFilterRules(m, ifname, NULL); + } + } + + // For p2p interfaces, need to clear the firewall rules on interface detach + if (msg.k.event_code == KEV_DL_IF_DETACHED) + { + struct net_event_data * p; + p = (struct net_event_data *) &msg.k.event_data; + + if (strncmp(p->if_name, "p2p", 3) == 0) + { + pfArray_t portArray, protocolArray; // not initialized since count is 0 for PF_CLEAR_RULES + char ifname[IFNAMSIZ]; + snprintf(ifname, IFNAMSIZ, "%s%d", p->if_name, p->if_unit); + + LogInfo("SysEventCallBack: KEV_DL_IF_DETACHED if_family = %d, if_unit = %d, if_name = %s", p->if_family, p->if_unit, p->if_name); + + mDNSPacketFilterControl(PF_CLEAR_RULES, ifname, 0, portArray, protocolArray); + } + } +#endif // !TARGET_OS_EMBEDDED + + } + + mDNS_Unlock(m); +} + +mDNSlocal mStatus WatchForSysEvents(mDNS *const m) +{ + m->p->SysEventNotifier = socket(PF_SYSTEM, SOCK_RAW, SYSPROTO_EVENT); + if (m->p->SysEventNotifier < 0) + { LogMsg("WatchForSysEvents: socket failed error %d errno %d (%s)", m->p->SysEventNotifier, errno, strerror(errno)); return(mStatus_NoMemoryErr); } + + struct kev_request kev_req = { KEV_VENDOR_APPLE, KEV_NETWORK_CLASS, KEV_DL_SUBCLASS }; + int err = ioctl(m->p->SysEventNotifier, SIOCSKEVFILT, &kev_req); + if (err < 0) + { + LogMsg("WatchForSysEvents: SIOCSKEVFILT failed error %d errno %d (%s)", err, errno, strerror(errno)); + close(m->p->SysEventNotifier); + m->p->SysEventNotifier = -1; + return(mStatus_UnknownErr); + } + + m->p->SysEventKQueue.KQcallback = SysEventCallBack; + m->p->SysEventKQueue.KQcontext = m; + m->p->SysEventKQueue.KQtask = "System Event Notifier"; + KQueueSet(m->p->SysEventNotifier, EV_ADD, EVFILT_READ, &m->p->SysEventKQueue); + + return(mStatus_NoError); +} + +#ifndef NO_SECURITYFRAMEWORK +mDNSlocal OSStatus KeychainChanged(SecKeychainEvent keychainEvent, SecKeychainCallbackInfo *info, void *context) +{ + LogInfo("*** Keychain Changed ***"); + mDNS *const m = (mDNS *const)context; + SecKeychainRef skc; + OSStatus err = SecKeychainCopyDefault(&skc); + if (!err) + { + if (info->keychain == skc) + { + // For delete events, attempt to verify what item was deleted fail because the item is already gone, so we just assume they may be relevant + mDNSBool relevant = (keychainEvent == kSecDeleteEvent); + if (!relevant) + { + UInt32 tags[3] = { kSecTypeItemAttr, kSecServiceItemAttr, kSecAccountItemAttr }; + SecKeychainAttributeInfo attrInfo = { 3, tags, NULL }; // Count, array of tags, array of formats + SecKeychainAttributeList *a = NULL; + err = SecKeychainItemCopyAttributesAndData(info->item, &attrInfo, NULL, &a, NULL, NULL); + if (!err) + { + relevant = ((a->attr[0].length == 4 && (!strncasecmp(a->attr[0].data, "ddns", 4) || !strncasecmp(a->attr[0].data, "sndd", 4))) || + (a->attr[1].length >= mDNSPlatformStrLen(dnsprefix) && (!strncasecmp(a->attr[1].data, dnsprefix, mDNSPlatformStrLen(dnsprefix)))) || + (a->attr[1].length >= mDNSPlatformStrLen(btmmprefix) && (!strncasecmp(a->attr[1].data, btmmprefix, mDNSPlatformStrLen(btmmprefix))))); + SecKeychainItemFreeAttributesAndData(a, NULL); + } + } + if (relevant) + { + LogInfo("*** Keychain Changed *** KeychainEvent=%d %s", + keychainEvent, + keychainEvent == kSecAddEvent ? "kSecAddEvent" : + keychainEvent == kSecDeleteEvent ? "kSecDeleteEvent" : + keychainEvent == kSecUpdateEvent ? "kSecUpdateEvent" : "<Unknown>"); + // We're running on the CFRunLoop (Mach port) thread, not the kqueue thread, so we need to grab the KQueueLock before proceeding + KQueueLock(m); + mDNS_Lock(m); + + // To not read the keychain twice: when BTMM is enabled, changes happen to the keychain + // then the BTMM DynStore dictionary, so delay reading the keychain for a second. + // NetworkChanged() will reset the keychain timer to fire immediately when the DynStore changes. + // + // In the "fixup" case where the BTMM DNS servers aren't accepting the key mDNSResponder has, + // the DynStore dictionary won't change (because the BTMM zone won't change). In that case, + // a one second delay is ok, as we'll still converge to correctness, and there's no race + // condition between the RegistrationDomain and the DomainAuthInfo. + // + // Lastly, non-BTMM WAB cases can use the keychain but not the DynStore, so we need to set + // the timer here, as it will not get set by NetworkChanged(). + SetKeyChainTimer(m, mDNSPlatformOneSecond); + + mDNS_Unlock(m); + KQueueUnlock(m, "KeychainChanged"); + } + } + CFRelease(skc); + } + + return 0; +} +#endif + +mDNSlocal void PowerOn(mDNS *const m) +{ + mDNSCoreMachineSleep(m, false); // Will set m->SleepState = SleepState_Awake; + if (m->p->WakeAtUTC) + { + long utc = mDNSPlatformUTC(); + mDNSPowerRequest(-1,-1); // Need to explicitly clear any previous power requests -- they're not cleared automatically on wake + if (m->p->WakeAtUTC - utc > 30) + { + LogSPS("PowerChanged PowerOn %d seconds early, assuming not maintenance wake", m->p->WakeAtUTC - utc); + } + else if (utc - m->p->WakeAtUTC > 30) + { + LogSPS("PowerChanged PowerOn %d seconds late, assuming not maintenance wake", utc - m->p->WakeAtUTC); + } + else if (IsAppleTV()) + { + LogSPS("PowerChanged PowerOn %d seconds late, device is an AppleTV running iOS so not re-sleeping", utc - m->p->WakeAtUTC); + } + else + { + LogSPS("PowerChanged: Waking for network maintenance operations %d seconds early; re-sleeping in 20 seconds", m->p->WakeAtUTC - utc); + m->p->RequestReSleep = mDNS_TimeNow(m) + 20 * mDNSPlatformOneSecond; + } + } +} + +mDNSlocal void PowerChanged(void *refcon, io_service_t service, natural_t messageType, void *messageArgument) +{ + mDNS *const m = (mDNS *const)refcon; + KQueueLock(m); + (void)service; // Parameter not used + debugf("PowerChanged %X %lX", messageType, messageArgument); + + // Make sure our m->SystemWakeOnLANEnabled value correctly reflects the current system setting + m->SystemWakeOnLANEnabled = SystemWakeForNetworkAccess(); + + switch(messageType) + { + case kIOMessageCanSystemPowerOff: LogSPS("PowerChanged kIOMessageCanSystemPowerOff (no action)"); break; // E0000240 + case kIOMessageSystemWillPowerOff: LogSPS("PowerChanged kIOMessageSystemWillPowerOff"); // E0000250 + mDNSCoreMachineSleep(m, true); + if (m->SleepState == SleepState_Sleeping) mDNSMacOSXNetworkChanged(m); + break; + case kIOMessageSystemWillNotPowerOff: LogSPS("PowerChanged kIOMessageSystemWillNotPowerOff (no action)"); break; // E0000260 + case kIOMessageCanSystemSleep: LogSPS("PowerChanged kIOMessageCanSystemSleep (no action)"); break; // E0000270 + case kIOMessageSystemWillSleep: LogSPS("PowerChanged kIOMessageSystemWillSleep"); // E0000280 + mDNSCoreMachineSleep(m, true); + break; + case kIOMessageSystemWillNotSleep: LogSPS("PowerChanged kIOMessageSystemWillNotSleep (no action)"); break; // E0000290 + case kIOMessageSystemHasPoweredOn: LogSPS("PowerChanged kIOMessageSystemHasPoweredOn"); // E0000300 + // If still sleeping (didn't get 'WillPowerOn' message for some reason?) wake now + if (m->SleepState) + { + LogMsg("PowerChanged kIOMessageSystemHasPoweredOn: ERROR m->SleepState %d", m->SleepState); + PowerOn(m); + } + // Just to be safe, schedule a mDNSMacOSXNetworkChanged(), in case we never received + // the System Configuration Framework "network changed" event that we expect + // to receive some time shortly after the kIOMessageSystemWillPowerOn message + mDNS_Lock(m); + if (!m->p->NetworkChanged || + m->p->NetworkChanged - NonZeroTime(m->timenow + mDNSPlatformOneSecond * 2) < 0) + m->p->NetworkChanged = NonZeroTime(m->timenow + mDNSPlatformOneSecond * 2); + mDNS_Unlock(m); + + break; + case kIOMessageSystemWillRestart: LogSPS("PowerChanged kIOMessageSystemWillRestart (no action)"); break; // E0000310 + case kIOMessageSystemWillPowerOn: LogSPS("PowerChanged kIOMessageSystemWillPowerOn"); // E0000320 + + // Make sure our interface list is cleared to the empty state, then tell mDNSCore to wake + if (m->SleepState != SleepState_Sleeping) + { + LogMsg("kIOMessageSystemWillPowerOn: ERROR m->SleepState %d", m->SleepState); + m->SleepState = SleepState_Sleeping; + mDNSMacOSXNetworkChanged(m); + } + PowerOn(m); + break; + default: LogSPS("PowerChanged unknown message %X", messageType); break; + } + + if (messageType == kIOMessageSystemWillSleep) m->p->SleepCookie = (long)messageArgument; + else IOAllowPowerChange(m->p->PowerConnection, (long)messageArgument); + + KQueueUnlock(m, "PowerChanged Sleep/Wake"); +} + +// iPhone OS doesn't currently have SnowLeopard's IO Power Management +// but it does define kIOPMAcknowledgmentOptionSystemCapabilityRequirements +#if defined(kIOPMAcknowledgmentOptionSystemCapabilityRequirements) && !TARGET_OS_EMBEDDED +mDNSlocal void SnowLeopardPowerChanged(void *refcon, IOPMConnection connection, IOPMConnectionMessageToken token, IOPMSystemPowerStateCapabilities eventDescriptor) +{ + mDNS *const m = (mDNS *const)refcon; + KQueueLock(m); + LogSPS("SnowLeopardPowerChanged %X %X %X%s%s%s%s%s", + connection, token, eventDescriptor, + eventDescriptor & kIOPMSystemPowerStateCapabilityCPU ? " CPU" : "", + eventDescriptor & kIOPMSystemPowerStateCapabilityVideo ? " Video" : "", + eventDescriptor & kIOPMSystemPowerStateCapabilityAudio ? " Audio" : "", + eventDescriptor & kIOPMSystemPowerStateCapabilityNetwork ? " Network" : "", + eventDescriptor & kIOPMSystemPowerStateCapabilityDisk ? " Disk" : ""); + + // Make sure our m->SystemWakeOnLANEnabled value correctly reflects the current system setting + m->SystemWakeOnLANEnabled = SystemWakeForNetworkAccess(); + + if (eventDescriptor & kIOPMSystemPowerStateCapabilityCPU) + { + // We might be in Sleeping or Transferring state. When we go from "wakeup" to "sleep" state, we don't + // go directly to sleep state, but transfer in to the sleep state during which SleepState is set to + // SleepState_Transferring. During that time, we might get another wakeup before we transition to Sleeping + // state. In that case, we need to acknowledge the previous "sleep" before we acknowledge the wakeup. + if (m->SleepLimit) + { + LogSPS("SnowLeopardPowerChanged: Waking up, Acking old Sleep, SleepLimit %d SleepState %d", m->SleepLimit, m->SleepState); + IOPMConnectionAcknowledgeEvent(connection, m->p->SleepCookie); + m->SleepLimit = 0; + } + LogSPS("SnowLeopardPowerChanged: Waking up, Acking Wakeup, SleepLimit %d SleepState %d", m->SleepLimit, m->SleepState); + // If the network notifications have already come before we got the wakeup, we ignored them and + // in case we get no more, we need to trigger one. + mDNS_Lock(m); + SetNetworkChanged(m, 2 * mDNSPlatformOneSecond); + mDNS_Unlock(m); + // CPU Waking. Note: Can get this message repeatedly, as other subsystems power up or down. + if (m->SleepState != SleepState_Awake) PowerOn(m); + IOPMConnectionAcknowledgeEvent(connection, token); + } + else + { + // CPU sleeping. Should not get this repeatedly -- once we're told that the CPU is halting + // we should hear nothing more until we're told that the CPU has started executing again. + if (m->SleepState) LogMsg("SnowLeopardPowerChanged: Sleep Error %X m->SleepState %d", eventDescriptor, m->SleepState); + //sleep(5); + //mDNSMacOSXNetworkChanged(m); + mDNSCoreMachineSleep(m, true); + //if (m->SleepState == SleepState_Sleeping) mDNSMacOSXNetworkChanged(m); + m->p->SleepCookie = token; + } + + KQueueUnlock(m, "SnowLeopardPowerChanged Sleep/Wake"); +} +#endif + +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - +#pragma mark - /etc/hosts support +#endif + +// Implementation Notes +// +// As /etc/hosts file can be huge (1000s of entries - when this comment was written, the test file had about +// 23000 entries with about 4000 duplicates), we can't use a linked list to store these entries. So, we parse +// them into a hash table. The implementation need to be able to do the following things efficiently +// +// 1. Detect duplicates e.g., two entries with "1.2.3.4 foo" +// 2. Detect whether /etc/hosts has changed and what has changed since the last read from the disk +// 3. Ability to support multiple addresses per name e.g., "1.2.3.4 foo, 2.3.4.5 foo". To support this, we +// need to be able set the RRSet of a resource record to the first one in the list and also update when +// one of them go away. This is needed so that the core thinks that they are all part of the same RRSet and +// not a duplicate +// 4. Don't maintain any local state about any records registered with the core to detect changes to /etc/hosts +// +// CFDictionary is not a suitable candidate because it does not support duplicates and even if we use a custom +// "hash" function to solve this, the others are hard to solve. Hence, we share the hash (AuthHash) implementation +// of the core layer which does all of the above very efficiently + +#define ETCHOSTS_BUFSIZE 1024 // Buffer size to parse a single line in /etc/hosts + +mDNSexport void FreeEtcHosts(mDNS *const m, AuthRecord *const rr, mStatus result) +{ + (void)m; // unused + (void)rr; + (void)result; + if (result == mStatus_MemFree) + { + LogInfo("FreeEtcHosts: %s", ARDisplayString(m, rr)); + freeL("etchosts", rr); + } +} + +// Returns true on success and false on failure +mDNSlocal mDNSBool mDNSMacOSXCreateEtcHostsEntry(mDNS *const m, const domainname *domain, const struct sockaddr *sa, const domainname *cname, char *ifname, AuthHash *auth) +{ + AuthRecord *rr; + mDNSu32 slot; + mDNSu32 namehash; + AuthGroup *ag; + mDNSInterfaceID InterfaceID = mDNSInterface_LocalOnly; + mDNSu16 rrtype; + + if (!domain) + { + LogMsg("mDNSMacOSXCreateEtcHostsEntry: ERROR!! name NULL"); + return mDNSfalse; + } + if (!sa && !cname) + { + LogMsg("mDNSMacOSXCreateEtcHostsEntry: ERROR!! sa and cname both NULL"); + return mDNSfalse; + } + + if (sa && sa->sa_family != AF_INET && sa->sa_family != AF_INET6) + { + LogMsg("mDNSMacOSXCreateEtcHostsEntry: ERROR!! sa with bad family %d", sa->sa_family); + return mDNSfalse; + } + + + if (ifname) + { + mDNSu32 ifindex = if_nametoindex(ifname); + if (!ifindex) + { + LogMsg("mDNSMacOSXCreateEtcHostsEntry: hosts entry %##s with invalid ifname %s", domain->c, ifname); + return mDNSfalse; + } + InterfaceID = (mDNSInterfaceID)(uintptr_t)ifindex; + } + + if (sa) + rrtype = (sa->sa_family == AF_INET ? kDNSType_A : kDNSType_AAAA); + else + rrtype = kDNSType_CNAME; + + // Check for duplicates. See whether we parsed an entry before like this ? + slot = AuthHashSlot(domain); + namehash = DomainNameHashValue(domain); + ag = AuthGroupForName(auth, slot, namehash, domain); + if (ag) + { + rr = ag->members; + while (rr) + { + if (rr->resrec.rrtype == rrtype) + { + if (rrtype == kDNSType_A) + { + mDNSv4Addr ip; + ip.NotAnInteger = ((struct sockaddr_in*)sa)->sin_addr.s_addr; + if (mDNSSameIPv4Address(rr->resrec.rdata->u.ipv4, ip)) + { + LogInfo("mDNSMacOSXCreateEtcHostsEntry: Same IPv4 address for name %##s", domain->c); + return mDNSfalse; + } + } + else if (rrtype == kDNSType_AAAA) + { + mDNSv6Addr ip6; + ip6.l[0] = ((struct sockaddr_in6*)sa)->sin6_addr.__u6_addr.__u6_addr32[0]; + ip6.l[1] = ((struct sockaddr_in6*)sa)->sin6_addr.__u6_addr.__u6_addr32[1]; + ip6.l[2] = ((struct sockaddr_in6*)sa)->sin6_addr.__u6_addr.__u6_addr32[2]; + ip6.l[3] = ((struct sockaddr_in6*)sa)->sin6_addr.__u6_addr.__u6_addr32[3]; + if (mDNSSameIPv6Address(rr->resrec.rdata->u.ipv6, ip6)) + { + LogInfo("mDNSMacOSXCreateEtcHostsEntry: Same IPv6 address for name %##s", domain->c); + return mDNSfalse; + } + } + else if (rrtype == kDNSType_CNAME) + { + if (SameDomainName(&rr->resrec.rdata->u.name, cname)) + { + LogInfo("mDNSMacOSXCreateEtcHostsEntry: Same cname %##s for name %##s", cname->c, domain->c); + return mDNSfalse; + } + } + } + rr = rr->next; + } + } + rr= mallocL("etchosts", sizeof(*rr)); + if (rr == NULL) return mDNSfalse; + mDNSPlatformMemZero(rr, sizeof(*rr)); + mDNS_SetupResourceRecord(rr, NULL, InterfaceID, rrtype, 1, kDNSRecordTypeKnownUnique, AuthRecordLocalOnly, FreeEtcHosts, NULL); + AssignDomainName(&rr->namestorage, domain); + + if (sa) + { + rr->resrec.rdlength = sa->sa_family == AF_INET ? sizeof(mDNSv4Addr) : sizeof(mDNSv6Addr); + if (sa->sa_family == AF_INET) + rr->resrec.rdata->u.ipv4.NotAnInteger = ((struct sockaddr_in*)sa)->sin_addr.s_addr; + else + { + rr->resrec.rdata->u.ipv6.l[0] = ((struct sockaddr_in6*)sa)->sin6_addr.__u6_addr.__u6_addr32[0]; + rr->resrec.rdata->u.ipv6.l[1] = ((struct sockaddr_in6*)sa)->sin6_addr.__u6_addr.__u6_addr32[1]; + rr->resrec.rdata->u.ipv6.l[2] = ((struct sockaddr_in6*)sa)->sin6_addr.__u6_addr.__u6_addr32[2]; + rr->resrec.rdata->u.ipv6.l[3] = ((struct sockaddr_in6*)sa)->sin6_addr.__u6_addr.__u6_addr32[3]; + } + } + else + { + rr->resrec.rdlength = DomainNameLength(cname); + rr->resrec.rdata->u.name.c[0] = 0; + AssignDomainName(&rr->resrec.rdata->u.name, cname); + } + rr->resrec.namehash = DomainNameHashValue(rr->resrec.name); + SetNewRData(&rr->resrec, mDNSNULL, 0); // Sets rr->rdatahash for us + LogInfo("mDNSMacOSXCreateEtcHostsEntry: Adding resource record %s", ARDisplayString(m, rr)); + InsertAuthRecord(m, auth, rr); + return mDNStrue; +} + +mDNSlocal int EtcHostsParseOneName(int start, int length, char *buffer, char **name) +{ + int i; + + *name = NULL; + for (i = start; i < length; i++) + { + if (buffer[i] == '#') + return -1; + if (buffer[i] != ' ' && buffer[i] != ',' && buffer[i] != '\t') + { + *name = &buffer[i]; + + // Found the start of a name, find the end and null terminate + for (i++; i < length; i++) + { + if (buffer[i] == ' ' || buffer[i] == ',' || buffer[i] == '\t') + { + buffer[i] = 0; + break; + } + } + return i; + } + } + return -1; +} + +mDNSlocal void mDNSMacOSXParseEtcHostsLine(mDNS *const m, char *buffer, ssize_t length, AuthHash *auth) +{ + int i; + int ifStart = 0; + char *ifname = NULL; + domainname name1d; + domainname name2d; + char *name1; + char *name2; + int aliasIndex; + + //Ignore leading whitespaces and tabs + while (*buffer == ' ' || *buffer == '\t') + { + buffer++; + length--; + } + + // Find the end of the address string + for (i = 0; i < length; i++) + { + if (buffer[i] == ' ' || buffer[i] == ',' || buffer[i] == '\t' || buffer[i] == '%') + { + if (buffer[i] == '%') + ifStart = i + 1; + buffer[i] = 0; + break; + } + } + + // Convert the address string to an address + struct addrinfo hints; + bzero(&hints, sizeof(hints)); + hints.ai_flags = AI_NUMERICHOST; + struct addrinfo *gairesults = NULL; + if (getaddrinfo(buffer, NULL, &hints, &gairesults) != 0) + { + LogInfo("mDNSMacOSXParseEtcHostsLine: getaddrinfo returning null"); + return; + } + + if (ifStart) + { + // Parse the interface + ifname = &buffer[ifStart]; + for (i = ifStart + 1; i < length; i++) + { + if (buffer[i] == ' ' || buffer[i] == ',' || buffer[i] == '\t') + { + buffer[i] = 0; + break; + } + } + } + + i = EtcHostsParseOneName(i + 1, length, buffer, &name1); + if (i == length) + { + // Common case (no aliases) : The entry is of the form "1.2.3.4 somehost" with no trailing white spaces/tabs etc. + if (!MakeDomainNameFromDNSNameString(&name1d, name1)) + { + LogMsg("mDNSMacOSXParseEtcHostsLine: ERROR!! cannot convert to domain name %s", name1); + freeaddrinfo(gairesults); + return; + } + mDNSMacOSXCreateEtcHostsEntry(m, &name1d, gairesults->ai_addr, mDNSNULL, ifname, auth); + } + else if (i != -1) + { + domainname first; + // We might have some extra white spaces at the end for the common case of "1.2.3.4 somehost". + // When we parse again below, EtchHostsParseOneName would return -1 and we will end up + // doing the right thing. + if (!MakeDomainNameFromDNSNameString(&first, name1)) + { + LogMsg("mDNSMacOSXParseEtcHostsLine: ERROR!! cannot convert to domain name %s", name1); + freeaddrinfo(gairesults); + return; + } + // If the /etc/hosts has an entry like this + // + // 1.2.3.4 sun star bright + // + // star and bright are aliases (gethostbyname h_alias should point to these) and sun is the canonical + // name (getaddrinfo ai_cannonname and gethostbyname h_name points to "sun") + // + // To achieve this, we need to add the entry like this: + // + // star CNAME bright + // bright CNAME sun + // sun A 1.2.3.4 + // + // We store the first name we parsed in "first". Then we parse additional names adding CNAME records + // till we reach the end. When we reach the end, we wrap around and add one final CNAME with the last + // entry and the first entry. Finally, we add the Address (A/AAAA) record. + aliasIndex = 0; + while (i <= length) + { + // Parse a name. If there are no names, we need to know whether we + // parsed CNAMEs before or not. If we parsed CNAMEs before, then we + // add a CNAME with the last name and the first name. Otherwise, this + // is same as the common case above where the line has just one name + // but with trailing white spaces. + i = EtcHostsParseOneName(i + 1, length, buffer, &name2); + if (name2) + { + if (!MakeDomainNameFromDNSNameString(&name2d, name2)) + { + LogMsg("mDNSMacOSXParseEtcHostsLine: ERROR!! cannot convert to domain name %s", name2); + freeaddrinfo(gairesults); + return; + } + aliasIndex++; + } + else if (!aliasIndex) + { + // We have never parsed any aliases. This case happens if there + // is just one name and some extra white spaces at the end. + LogInfo("mDNSMacOSXParseEtcHostsLine: White space at the end of %##s", first.c); + break; + } + else + { + // We have parsed at least one alias before and we reached the end of the line. + // Setup a CNAME for the last name with "first" name as its RDATA + name2d.c[0] = 0; + AssignDomainName(&name2d, &first); + } + + // Don't add a CNAME for the first alias we parse (see the example above). + // As we parse more, we might discover that there are no more aliases, in + // which case we would have set "name2d" to "first" above. We need to add + // the CNAME in that case. + + if (aliasIndex > 1 || SameDomainName(&name2d, &first)) + { + // Ignore if it points to itself + if (!SameDomainName(&name1d, &name2d)) + { + if (!mDNSMacOSXCreateEtcHostsEntry(m, &name1d, mDNSNULL, &name2d, ifname, auth)) + { + freeaddrinfo(gairesults); + return; + } + } + else + LogMsg("mDNSMacOSXParseEtcHostsLine: Ignoring entry with same names name1 %##s, name2 %##s", name1d.c, name2d.c); + } + + // If we have already wrapped around, we just need to add the A/AAAA record alone + // which is done below + if (SameDomainName(&name2d, &first)) break; + + // Remember the current name so that we can set the CNAME record if we parse one + // more name + name1d.c[0] = 0; + AssignDomainName(&name1d, &name2d); + } + // Added all the CNAMEs if any, add the "A/AAAA" record + mDNSMacOSXCreateEtcHostsEntry(m, &first, gairesults->ai_addr, mDNSNULL, ifname, auth); + } + freeaddrinfo(gairesults); +} + +mDNSlocal void mDNSMacOSXParseEtcHosts(mDNS *const m, int fd, AuthHash *auth) +{ + mDNSBool good; + char buf[ETCHOSTS_BUFSIZE]; + ssize_t len; + FILE *fp; + + if (fd == -1) { LogInfo("mDNSMacOSXParseEtcHosts: fd is -1"); return; } + + fp = fopen("/etc/hosts", "r"); + if (!fp) { LogInfo("mDNSMacOSXParseEtcHosts: fp is NULL"); return; } + + while (1) + { + good = (fgets(buf, ETCHOSTS_BUFSIZE, fp) != NULL); + if (!good) break; + + // skip comment and empty lines + if (buf[0] == '#' || buf[0] == '\r' || buf[0] == '\n') + continue; + + len = strlen(buf); + if (!len) break; // sanity check + //Check for end of line code(mostly only \n but pre-OS X Macs could have only \r) + if (buf[len - 1] == '\r' || buf[len - 1] == '\n') + { + buf[len - 1] = '\0'; + len = len - 1; + } + // fgets always null terminates and hence even if we have no + // newline at the end, it is null terminated. The callee + // (mDNSMacOSXParseEtcHostsLine) expects the length to be such that + // buf[length] is zero and hence we decrement len to reflect that. + if (len) + { + //Additional check when end of line code is 2 chars ie\r\n(DOS, other old OSes) + //here we need to check for just \r but taking extra caution. + if (buf[len - 1] == '\r' || buf[len - 1] == '\n') + { + buf[len - 1] = '\0'; + len = len - 1; + } + } + if (!len) //Sanity Check: len should never be zero + { + LogMsg("mDNSMacOSXParseEtcHosts: Length is zero!"); + continue; + } + mDNSMacOSXParseEtcHostsLine(m, buf, len, auth); + } + fclose(fp); +} + +mDNSlocal void mDNSMacOSXUpdateEtcHosts(mDNS *const m); + +mDNSlocal int mDNSMacOSXGetEtcHostsFD(mDNS *const m) +{ +#ifdef __DISPATCH_GROUP__ + // Can't do this stuff to be notified of changes in /etc/hosts if we don't have libdispatch + static dispatch_queue_t etcq = 0; + static dispatch_source_t etcsrc = 0; + static dispatch_source_t hostssrc = 0; + + // First time through? just schedule ourselves on the main queue and we'll do the work later + if (!etcq) + { + etcq = dispatch_get_main_queue(); + if (etcq) + { + // Do this work on the queue, not here - solves potential synchronization issues + dispatch_async(etcq, ^{mDNSMacOSXUpdateEtcHosts(m);}); + } + return -1; + } + + if (hostssrc) return dispatch_source_get_handle(hostssrc); +#endif + + int fd = open("/etc/hosts", O_RDONLY); + +#ifdef __DISPATCH_GROUP__ + // Can't do this stuff to be notified of changes in /etc/hosts if we don't have libdispatch + if (fd == -1) + { + // If the open failed and we're already watching /etc, we're done + if (etcsrc) { LogInfo("mDNSMacOSXGetEtcHostsFD: Returning etcfd because no etchosts"); return fd; } + + // we aren't watching /etc, we should be + fd = open("/etc", O_RDONLY); + if (fd == -1) { LogInfo("mDNSMacOSXGetEtcHostsFD: etc does not exist"); return -1; } + etcsrc = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, fd, DISPATCH_VNODE_DELETE | DISPATCH_VNODE_WRITE | DISPATCH_VNODE_RENAME, etcq); + if (etcsrc == NULL) + { + close(fd); + return -1; + } + dispatch_source_set_event_handler(etcsrc, + ^{ + u_int32_t flags = dispatch_source_get_data(etcsrc); + LogMsg("mDNSMacOSXGetEtcHostsFD: /etc changed 0x%x", flags); + if ((flags & (DISPATCH_VNODE_DELETE | DISPATCH_VNODE_RENAME)) != 0) + { + dispatch_source_cancel(etcsrc); + dispatch_release(etcsrc); + etcsrc = NULL; + dispatch_async(etcq, ^{mDNSMacOSXUpdateEtcHosts(m);}); + return; + } + if ((flags & DISPATCH_VNODE_WRITE) != 0 && hostssrc == NULL) + { + mDNSMacOSXUpdateEtcHosts(m); + } + }); + dispatch_source_set_cancel_handler(etcsrc, ^{close(fd);}); + dispatch_resume(etcsrc); + + // Try and open /etc/hosts once more now that we're watching /etc, in case we missed the creation + fd = open("/etc/hosts", O_RDONLY | O_EVTONLY); + if (fd == -1) { LogMsg("mDNSMacOSXGetEtcHostsFD etc hosts does not exist, watching etc"); return -1; } + } + + // create a dispatch source to watch for changes to hosts file + hostssrc = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, fd, + (DISPATCH_VNODE_DELETE | DISPATCH_VNODE_WRITE | DISPATCH_VNODE_RENAME | + DISPATCH_VNODE_ATTRIB | DISPATCH_VNODE_EXTEND | DISPATCH_VNODE_LINK | DISPATCH_VNODE_REVOKE), etcq); + if (hostssrc == NULL) + { + close(fd); + return -1; + } + dispatch_source_set_event_handler(hostssrc, + ^{ + u_int32_t flags = dispatch_source_get_data(hostssrc); + LogInfo("mDNSMacOSXGetEtcHostsFD: /etc/hosts changed 0x%x", flags); + if ((flags & (DISPATCH_VNODE_DELETE | DISPATCH_VNODE_RENAME)) != 0) + { + dispatch_source_cancel(hostssrc); + dispatch_release(hostssrc); + hostssrc = NULL; + // Bug in LibDispatch: wait a second before scheduling the block. If we schedule + // the block immediately, we try to open the file and the file may not exist and may + // fail to get a notification in the future. When the file does not exist and + // we start to monitor the directory, on "dispatch_resume" of that source, there + // is no guarantee that the file creation will be notified always because when + // the dispatch_resume returns, the kevent manager may not have registered the + // kevent yet but the file may have been created + usleep(1000000); + dispatch_async(etcq, ^{mDNSMacOSXUpdateEtcHosts(m);}); + return; + } + if ((flags & DISPATCH_VNODE_WRITE) != 0) + { + mDNSMacOSXUpdateEtcHosts(m); + } + }); + dispatch_source_set_cancel_handler(hostssrc, ^{LogInfo("mDNSMacOSXGetEtcHostsFD: Closing etchosts fd %d", fd); close(fd);}); + dispatch_resume(hostssrc); + + // Cleanup /etc source, no need to watch it if we already have /etc/hosts + if (etcsrc) + { + dispatch_source_cancel(etcsrc); + dispatch_release(etcsrc); + etcsrc = NULL; + } + + LogInfo("mDNSMacOSXGetEtcHostsFD: /etc/hosts being monitored, and not etc"); + return hostssrc ? (int)dispatch_source_get_handle(hostssrc) : -1; +#else + (void)m; + return fd; +#endif +} + +// When /etc/hosts is modified, flush all the cache records as there may be local +// authoritative answers now +mDNSlocal void FlushAllCacheRecords(mDNS *const m) +{ + CacheRecord *cr; + mDNSu32 slot; + CacheGroup *cg; + + FORALL_CACHERECORDS(slot, cg, cr) + { + // Skip multicast. + if (cr->resrec.InterfaceID) continue; + + // If a resource record can answer A or AAAA, they need to be flushed so that we will + // never used to deliver an ADD or RMV + if (RRTypeAnswersQuestionType(&cr->resrec, kDNSType_A) || + RRTypeAnswersQuestionType(&cr->resrec, kDNSType_AAAA)) + { + LogInfo("FlushAllCacheRecords: Purging Resourcerecord %s", CRDisplayString(m, cr)); + mDNS_PurgeCacheResourceRecord(m, cr); + } + } +} + +// Add new entries to the core. If justCheck is set, this function does not add, just returns true +mDNSlocal mDNSBool EtcHostsAddNewEntries(mDNS *const m, AuthHash *newhosts, mDNSBool justCheck) +{ + AuthGroup *ag; + mDNSu32 slot; + AuthRecord *rr, *primary, *rrnext; + for (slot = 0; slot < AUTH_HASH_SLOTS; slot++) + for (ag = newhosts->rrauth_hash[slot]; ag; ag = ag->next) + { + primary = NULL; + for (rr = ag->members; rr; rr = rrnext) + { + rrnext = rr->next; + AuthGroup *ag1; + AuthRecord *rr1; + mDNSBool found = mDNSfalse; + ag1 = AuthGroupForRecord(&m->rrauth, slot, &rr->resrec); + if (ag1 && ag1->members) + { + if (!primary) primary = ag1->members; + rr1 = ag1->members; + while (rr1) + { + // We are not using InterfaceID in checking for duplicates. This means, + // if there are two addresses for a given name e.g., fe80::1%en0 and + // fe80::1%en1, we only add the first one. It is not clear whether + // this is a common case. To fix this, we also need to modify + // mDNS_Register_internal in how it handles duplicates. If it becomes a + // common case, we will fix it then. + if (IdenticalResourceRecord(&rr1->resrec, &rr->resrec)) + { + LogInfo("EtcHostsAddNewEntries: Skipping, not adding %s", ARDisplayString(m, rr1)); + found = mDNStrue; + break; + } + rr1 = rr1->next; + } + } + if (!found) + { + if (justCheck) + { + LogInfo("EtcHostsAddNewEntries: Entry %s not registered with core yet", ARDisplayString(m, rr)); + return mDNStrue; + } + RemoveAuthRecord(m, newhosts, rr); + // if there is no primary, point to self + rr->RRSet = (primary ? primary : rr); + rr->next = NULL; + LogInfo("EtcHostsAddNewEntries: Adding %s", ARDisplayString(m, rr)); + if (mDNS_Register_internal(m, rr) != mStatus_NoError) + LogMsg("EtcHostsAddNewEntries: mDNS_Register failed for %s", ARDisplayString(m, rr)); + } + } + } + return mDNSfalse; +} + +// Delete entries from the core that are no longer needed. If justCheck is set, this function +// does not delete, just returns true +mDNSlocal mDNSBool EtcHostsDeleteOldEntries(mDNS *const m, AuthHash *newhosts, mDNSBool justCheck) +{ + AuthGroup *ag; + mDNSu32 slot; + AuthRecord *rr, *primary, *rrnext; + for (slot = 0; slot < AUTH_HASH_SLOTS; slot++) + for (ag = m->rrauth.rrauth_hash[slot]; ag; ag = ag->next) + for (rr = ag->members; rr; rr = rrnext) + { + mDNSBool found = mDNSfalse; + AuthGroup *ag1; + AuthRecord *rr1; + rrnext = rr->next; + if (rr->RecordCallback != FreeEtcHosts) continue; + ag1 = AuthGroupForRecord(newhosts, slot, &rr->resrec); + if (ag1) + { + primary = rr1 = ag1->members; + while (rr1) + { + if (IdenticalResourceRecord(&rr1->resrec, &rr->resrec)) + { + LogInfo("EtcHostsDeleteOldEntries: Old record %s found in new, skipping", ARDisplayString(m, rr)); + found = mDNStrue; + break; + } + rr1 = rr1->next; + } + } + // there is no corresponding record in newhosts for the same name. This means + // we should delete this from the core. + if (!found) + { + if (justCheck) + { + LogInfo("EtcHostsDeleteOldEntries: Record %s not found in new, deleting", ARDisplayString(m, rr)); + return mDNStrue; + } + // if primary is going away, make sure that the rest of the records + // point to the new primary + if (rr == ag->members) + { + AuthRecord *new_primary = rr->next; + AuthRecord *r = new_primary; + while (r) + { + if (r->RRSet == rr) + { + LogInfo("EtcHostsDeleteOldEntries: Updating Resource Record %s to primary", ARDisplayString(m, r)); + r->RRSet = new_primary; + } + else LogMsg("EtcHostsDeleteOldEntries: ERROR!! Resource Record %s not pointing to primary %##s", ARDisplayString(m, r), r->resrec.name); + r = r->next; + } + } + LogInfo("EtcHostsDeleteOldEntries: Deleting %s", ARDisplayString(m, rr)); + mDNS_Deregister_internal(m, rr, mDNS_Dereg_normal); + } + } + return mDNSfalse; +} + +mDNSlocal void UpdateEtcHosts(mDNS *const m, void *context) +{ + AuthHash *newhosts = (AuthHash *)context; + + mDNS_CheckLock(m); + + //Delete old entries from the core if they are not present in the newhosts + EtcHostsDeleteOldEntries(m, newhosts, mDNSfalse); + // Add the new entries to the core if not already present in the core + EtcHostsAddNewEntries(m, newhosts, mDNSfalse); +} + +mDNSlocal void FreeNewHosts(AuthHash *newhosts) +{ + mDNSu32 slot; + AuthGroup *ag, *agnext; + AuthRecord *rr, *rrnext; + + for (slot = 0; slot < AUTH_HASH_SLOTS; slot++) + for (ag = newhosts->rrauth_hash[slot]; ag; ag = agnext) + { + agnext = ag->next; + for (rr = ag->members; rr; rr = rrnext) + { + rrnext = rr->next; + freeL("etchosts", rr); + } + freeL("AuthGroups", ag); + } +} + +mDNSlocal void mDNSMacOSXUpdateEtcHosts(mDNS *const m) +{ + AuthHash newhosts; + + // As we will be modifying the core, we can only have one thread running at + // any point in time. + KQueueLock(m); + + mDNSPlatformMemZero(&newhosts, sizeof(AuthHash)); + + // Get the file desecriptor (will trigger us to start watching for changes) + int fd = mDNSMacOSXGetEtcHostsFD(m); + if (fd != -1) + { + LogInfo("mDNSMacOSXUpdateEtcHosts: Parsing /etc/hosts fd %d", fd); + mDNSMacOSXParseEtcHosts(m, fd, &newhosts); + } + else LogInfo("mDNSMacOSXUpdateEtcHosts: /etc/hosts is not present"); + + // Optimization: Detect whether /etc/hosts changed or not. + // + // 1. Check to see if there are any new entries. We do this by seeing whether any entries in + // newhosts is already registered with core. If we find at least one entry that is not + // registered with core, then it means we have work to do. + // + // 2. Next, we check to see if any of the entries that are registered with core is not present + // in newhosts. If we find at least one entry that is not present, it means we have work to + // do. + // + // Note: We may not have to hold the lock right here as KQueueLock is held which prevents any + // other thread from running. But mDNS_Lock is needed here as we will be traversing the core + // data structure in EtcHostsDeleteOldEntries/NewEntries which might expect the lock to be held + // in the future and this code does not have to change. + mDNS_Lock(m); + // Add the new entries to the core if not already present in the core + if (!EtcHostsAddNewEntries(m, &newhosts, mDNStrue)) + { + // No new entries to add, check to see if we need to delete any old entries from the + // core if they are not present in the newhosts + if (!EtcHostsDeleteOldEntries(m, &newhosts, mDNStrue)) + { + LogInfo("mDNSMacOSXUpdateEtcHosts: No work"); + mDNS_Unlock(m); + KQueueUnlock(m, "/etc/hosts changed"); + FreeNewHosts(&newhosts); + return; + } + } + + // This will flush the cache, stop and start the query so that the queries + // can look at the /etc/hosts again + // + // Notes: + // + // We can't delete and free the records here. We wait for the mDNSCoreRestartAddressQueries to + // deliver RMV events. It has to be done in a deferred way because we can't deliver RMV + // events for local records *before* the RMV events for cache records. mDNSCoreRestartAddressQueries + // delivers these events in the right order and then calls us back to delete them. + // + // Similarly, we do a deferred Registration of the record because mDNSCoreRestartAddressQueries + // is a common function that looks at all local auth records and delivers a RMV including + // the records that we might add here. If we deliver a ADD here, it will get a RMV and then when + // the query is restarted, it will get another ADD. To avoid this (ADD-RMV-ADD), we defer registering + // the record until the RMVs are delivered in mDNSCoreRestartAddressQueries after which UpdateEtcHosts + // is called back where we do the Registration of the record. This results in RMV followed by ADD which + // looks normal. + mDNSCoreRestartAddressQueries(m, mDNSfalse, FlushAllCacheRecords, UpdateEtcHosts, &newhosts); + mDNS_Unlock(m); + + KQueueUnlock(m, "/etc/hosts changed"); + FreeNewHosts(&newhosts); +} + +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - +#pragma mark - Initialization & Teardown +#endif + +CF_EXPORT CFDictionaryRef _CFCopySystemVersionDictionary(void); +CF_EXPORT const CFStringRef _kCFSystemVersionProductNameKey; +CF_EXPORT const CFStringRef _kCFSystemVersionProductVersionKey; +CF_EXPORT const CFStringRef _kCFSystemVersionBuildVersionKey; + +// Major version 13 is 10.9.x +mDNSexport void mDNSMacOSXSystemBuildNumber(char *HINFO_SWstring) +{ + int major = 0, minor = 0; + char letter = 0, prodname[256]="<Unknown>", prodvers[256]="<Unknown>", buildver[256]="<Unknown>"; + CFDictionaryRef vers = _CFCopySystemVersionDictionary(); + if (vers) + { + CFStringRef cfprodname = CFDictionaryGetValue(vers, _kCFSystemVersionProductNameKey); + CFStringRef cfprodvers = CFDictionaryGetValue(vers, _kCFSystemVersionProductVersionKey); + CFStringRef cfbuildver = CFDictionaryGetValue(vers, _kCFSystemVersionBuildVersionKey); + if (cfprodname) + CFStringGetCString(cfprodname, prodname, sizeof(prodname), kCFStringEncodingUTF8); + if (cfprodvers) + CFStringGetCString(cfprodvers, prodvers, sizeof(prodvers), kCFStringEncodingUTF8); + if (cfbuildver && CFStringGetCString(cfbuildver, buildver, sizeof(buildver), kCFStringEncodingUTF8)) + sscanf(buildver, "%d%c%d", &major, &letter, &minor); + CFRelease(vers); + } + if (!major) + { + major = 13; + LogMsg("Note: No Major Build Version number found; assuming 13"); + } + if (HINFO_SWstring) + mDNS_snprintf(HINFO_SWstring, 256, "%s %s (%s), %s", prodname, prodvers, buildver, STRINGIFY(mDNSResponderVersion)); + //LogMsg("%s %s (%s), %d %c %d", prodname, prodvers, buildver, major, letter, minor); + + // If product name is "Mac OS X" (or similar) we set OSXVers, else we set iOSVers; + if ((prodname[0] & 0xDF) == 'M') + OSXVers = major; + else + iOSVers = major; +} + +// Test to see if we're the first client running on UDP port 5353, by trying to bind to 5353 without using SO_REUSEPORT. +// If we fail, someone else got here first. That's not a big problem; we can share the port for multicast responses -- +// we just need to be aware that we shouldn't expect to successfully receive unicast UDP responses. +mDNSlocal mDNSBool mDNSPlatformInit_CanReceiveUnicast(void) +{ + int err = -1; + int s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (s < 3) + LogMsg("mDNSPlatformInit_CanReceiveUnicast: socket error %d errno %d (%s)", s, errno, strerror(errno)); + else + { + struct sockaddr_in s5353; + s5353.sin_family = AF_INET; + s5353.sin_port = MulticastDNSPort.NotAnInteger; + s5353.sin_addr.s_addr = 0; + err = bind(s, (struct sockaddr *)&s5353, sizeof(s5353)); + close(s); + } + + if (err) LogMsg("No unicast UDP responses"); + else debugf("Unicast UDP responses okay"); + return(err == 0); +} + +mDNSlocal void CreatePTRRecord(mDNS *const m, const domainname *domain) +{ + AuthRecord *rr; + const domainname *pname = (domainname *)"\x9" "localhost"; + + rr= mallocL("localhosts", sizeof(*rr)); + if (rr == NULL) return; + mDNSPlatformMemZero(rr, sizeof(*rr)); + + mDNS_SetupResourceRecord(rr, mDNSNULL, mDNSInterface_LocalOnly, kDNSType_PTR, kHostNameTTL, kDNSRecordTypeKnownUnique, AuthRecordLocalOnly, mDNSNULL, mDNSNULL); + AssignDomainName(&rr->namestorage, domain); + + rr->resrec.rdlength = DomainNameLength(pname); + rr->resrec.rdata->u.name.c[0] = 0; + AssignDomainName(&rr->resrec.rdata->u.name, pname); + + rr->resrec.namehash = DomainNameHashValue(rr->resrec.name); + SetNewRData(&rr->resrec, mDNSNULL, 0); // Sets rr->rdatahash for us + mDNS_Register(m, rr); +} + +// Setup PTR records for 127.0.0.1 and ::1. This helps answering them locally rather than relying +// on the external DNS server to answer this. Sometimes, the DNS servers don't respond in a timely +// fashion and applications depending on this e.g., telnetd, times out after 30 seconds creating +// a bad user experience. For now, we specifically create only localhosts to handle radar://9354225 +// +// Note: We could have set this up while parsing the entries in /etc/hosts. But this is kept separate +// intentionally to avoid adding to the complexity of code handling /etc/hosts. +mDNSlocal void SetupLocalHostRecords(mDNS *const m) +{ + char buffer[MAX_REVERSE_MAPPING_NAME]; + domainname name; + int i; + struct in6_addr addr; + mDNSu8 *ptr = addr.__u6_addr.__u6_addr8; + + if (inet_pton(AF_INET, "127.0.0.1", &addr) == 1) + { + mDNS_snprintf(buffer, sizeof(buffer), "%d.%d.%d.%d.in-addr.arpa.", + ptr[3], ptr[2], ptr[1], ptr[0]); + MakeDomainNameFromDNSNameString(&name, buffer); + CreatePTRRecord(m, &name); + } + else LogMsg("SetupLocalHostRecords: ERROR!! inet_pton AF_INET failed"); + + if (inet_pton(AF_INET6, "::1", &addr) == 1) + { + for (i = 0; i < 16; i++) + { + static const char hexValues[] = "0123456789ABCDEF"; + buffer[i * 4 ] = hexValues[ptr[15 - i] & 0x0F]; + buffer[i * 4 + 1] = '.'; + buffer[i * 4 + 2] = hexValues[ptr[15 - i] >> 4]; + buffer[i * 4 + 3] = '.'; + } + mDNS_snprintf(&buffer[64], sizeof(buffer)-64, "ip6.arpa."); + MakeDomainNameFromDNSNameString(&name, buffer); + CreatePTRRecord(m, &name); + } + else LogMsg("SetupLocalHostRecords: ERROR!! inet_pton AF_INET6 failed"); +} + +// Construction of Default Browse domain list (i.e. when clients pass NULL) is as follows: +// 1) query for b._dns-sd._udp.local on LocalOnly interface +// (.local manually generated via explicit callback) +// 2) for each search domain (from prefs pane), query for b._dns-sd._udp.<searchdomain>. +// 3) for each result from (2), register LocalOnly PTR record b._dns-sd._udp.local. -> <result> +// 4) result above should generate a callback from question in (1). result added to global list +// 5) global list delivered to client via GetSearchDomainList() +// 6) client calls to enumerate domains now go over LocalOnly interface +// (!!!KRS may add outgoing interface in addition) + +mDNSlocal mStatus mDNSPlatformInit_setup(mDNS *const m) +{ + mStatus err; + m->p->CFRunLoop = CFRunLoopGetCurrent(); + + char HINFO_SWstring[256] = ""; + mDNSMacOSXSystemBuildNumber(HINFO_SWstring); + + err = mDNSHelperInit(); + if (err) + return err; + + DynamicStoreQueue = dispatch_queue_create("com.apple.mDNSResponder.DynamicStoreQueue", NULL); + if (DynamicStoreQueue == NULL) + { + LogMsg("dispatch_queue_create: DynamicStoreQueue NULL!"); + return mStatus_NoMemoryErr; + } + + // Store mDNSResponder Platform + if (OSXVers) + { + m->mDNS_plat = platform_OSX; + } + else if (iOSVers) + { + if (IsAppleTV()) + m->mDNS_plat = platform_Atv; + else + m->mDNS_plat = platform_iOS; + } + else + { + m->mDNS_plat = platform_NonApple; + } + + // In 10.4, mDNSResponder is launched very early in the boot process, while other subsystems are still in the process of starting up. + // If we can't read the user's preferences, then we sleep a bit and try again, for up to five seconds before we give up. + int i; + for (i=0; i<100; i++) + { + domainlabel testlabel; + testlabel.c[0] = 0; + GetUserSpecifiedLocalHostName(&testlabel); + if (testlabel.c[0]) break; + usleep(50000); + } + + m->hostlabel.c[0] = 0; + + int get_model[2] = { CTL_HW, HW_MODEL }; + size_t len_model = sizeof(HINFO_HWstring_buffer); + + // Normal Apple model names are of the form "iPhone2,1", and + // internal code names are strings containing no commas, e.g. "N88AP". + // We used to ignore internal code names, but Apple now uses these internal code names + // even in released shipping products, so we no longer ignore strings containing no commas. +// if (sysctl(get_model, 2, HINFO_HWstring_buffer, &len_model, NULL, 0) == 0 && strchr(HINFO_HWstring_buffer, ',')) + if (sysctl(get_model, 2, HINFO_HWstring_buffer, &len_model, NULL, 0) == 0) + HINFO_HWstring = HINFO_HWstring_buffer; + + // For names of the form "iPhone2,1" we use "iPhone" as the prefix for automatic name generation. + // For names of the form "N88AP" containg no comma, we use the entire string. + HINFO_HWstring_prefixlen = strchr(HINFO_HWstring_buffer, ',') ? strcspn(HINFO_HWstring, "0123456789") : strlen(HINFO_HWstring); + + if (mDNSPlatformInit_CanReceiveUnicast()) + m->CanReceiveUnicastOn5353 = mDNStrue; + + mDNSu32 hlen = mDNSPlatformStrLen(HINFO_HWstring); + mDNSu32 slen = mDNSPlatformStrLen(HINFO_SWstring); + if (hlen + slen < 254) + { + m->HIHardware.c[0] = hlen; + m->HISoftware.c[0] = slen; + mDNSPlatformMemCopy(&m->HIHardware.c[1], HINFO_HWstring, hlen); + mDNSPlatformMemCopy(&m->HISoftware.c[1], HINFO_SWstring, slen); + } + + m->p->permanentsockets.port = MulticastDNSPort; + m->p->permanentsockets.m = m; + m->p->permanentsockets.sktv4 = -1; + m->p->permanentsockets.kqsv4.KQcallback = myKQSocketCallBack; + m->p->permanentsockets.kqsv4.KQcontext = &m->p->permanentsockets; + m->p->permanentsockets.kqsv4.KQtask = "UDP packet reception"; + m->p->permanentsockets.sktv6 = -1; + m->p->permanentsockets.kqsv6.KQcallback = myKQSocketCallBack; + m->p->permanentsockets.kqsv6.KQcontext = &m->p->permanentsockets; + m->p->permanentsockets.kqsv6.KQtask = "UDP packet reception"; + + err = SetupSocket(&m->p->permanentsockets, MulticastDNSPort, AF_INET, mDNSNULL); + err = SetupSocket(&m->p->permanentsockets, MulticastDNSPort, AF_INET6, mDNSNULL); + + struct sockaddr_in s4; + socklen_t n4 = sizeof(s4); + if (getsockname(m->p->permanentsockets.sktv4, (struct sockaddr *)&s4, &n4) < 0) + LogMsg("getsockname v4 error %d (%s)", errno, strerror(errno)); + else + m->UnicastPort4.NotAnInteger = s4.sin_port; + + if (m->p->permanentsockets.sktv6 >= 0) + { + struct sockaddr_in6 s6; + socklen_t n6 = sizeof(s6); + if (getsockname(m->p->permanentsockets.sktv6, (struct sockaddr *)&s6, &n6) < 0) LogMsg("getsockname v6 error %d (%s)", errno, strerror(errno)); + else m->UnicastPort6.NotAnInteger = s6.sin6_port; + } + + m->p->InterfaceList = mDNSNULL; + m->p->userhostlabel.c[0] = 0; + m->p->usernicelabel.c[0] = 0; + m->p->prevoldnicelabel.c[0] = 0; + m->p->prevnewnicelabel.c[0] = 0; + m->p->prevoldhostlabel.c[0] = 0; + m->p->prevnewhostlabel.c[0] = 0; + m->p->NotifyUser = 0; + m->p->KeyChainTimer = 0; + m->p->WakeAtUTC = 0; + m->p->RequestReSleep = 0; + // Assume that everything is good to begin with. If something is not working, + // we will detect that when we start sending questions. + m->p->v4answers = 1; + m->p->v6answers = 1; + m->p->DNSTrigger = 0; + m->p->LastConfigGeneration = 0; + +#if APPLE_OSX_mDNSResponder + uuid_generate(m->asl_uuid); +#endif + + m->AutoTunnelRelayAddr = zerov6Addr; + + NetworkChangedKey_IPv4 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, kSCDynamicStoreDomainState, kSCEntNetIPv4); + NetworkChangedKey_IPv6 = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, kSCDynamicStoreDomainState, kSCEntNetIPv6); + NetworkChangedKey_Hostnames = SCDynamicStoreKeyCreateHostNames(NULL); + NetworkChangedKey_Computername = SCDynamicStoreKeyCreateComputerName(NULL); + NetworkChangedKey_DNS = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, kSCDynamicStoreDomainState, kSCEntNetDNS); + NetworkChangedKey_StateInterfacePrefix = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL, kSCDynamicStoreDomainState, CFSTR(""), NULL); + if (!NetworkChangedKey_IPv4 || !NetworkChangedKey_IPv6 || !NetworkChangedKey_Hostnames || !NetworkChangedKey_Computername || !NetworkChangedKey_DNS || !NetworkChangedKey_StateInterfacePrefix) + { LogMsg("SCDynamicStore string setup failed"); return(mStatus_NoMemoryErr); } + + err = WatchForNetworkChanges(m); + if (err) { LogMsg("mDNSPlatformInit_setup: WatchForNetworkChanges failed %d", err); return(err); } + +#if 0 // <rdar://problem/6751656> + err = WatchForPMChanges(m); + if (err) { LogMsg("mDNSPlatformInit_setup: WatchForPMChanges failed %d", err); return(err); } +#endif + + err = WatchForSysEvents(m); + if (err) { LogMsg("mDNSPlatformInit_setup: WatchForSysEvents failed %d", err); return(err); } + + mDNSs32 utc = mDNSPlatformUTC(); + m->SystemWakeOnLANEnabled = SystemWakeForNetworkAccess(); + UpdateInterfaceList(m, utc); + SetupActiveInterfaces(m, utc); + + // Explicitly ensure that our Keychain operations utilize the system domain. +#ifndef NO_SECURITYFRAMEWORK + SecKeychainSetPreferenceDomain(kSecPreferencesDomainSystem); +#endif + + mDNS_Lock(m); + SetDomainSecrets(m); + SetLocalDomains(); + mDNS_Unlock(m); + +#ifndef NO_SECURITYFRAMEWORK + err = SecKeychainAddCallback(KeychainChanged, kSecAddEventMask|kSecDeleteEventMask|kSecUpdateEventMask, m); + if (err) { LogMsg("mDNSPlatformInit_setup: SecKeychainAddCallback failed %d", err); return(err); } +#endif + +#if !defined(kIOPMAcknowledgmentOptionSystemCapabilityRequirements) || TARGET_OS_EMBEDDED + LogMsg("Note: Compiled without SnowLeopard Fine-Grained Power Management support"); +#else + IOPMConnection c; + IOReturn iopmerr = IOPMConnectionCreate(CFSTR("mDNSResponder"), kIOPMSystemPowerStateCapabilityCPU, &c); + if (iopmerr) LogMsg("IOPMConnectionCreate failed %d", iopmerr); + else + { + iopmerr = IOPMConnectionSetNotification(c, m, SnowLeopardPowerChanged); + if (iopmerr) LogMsg("IOPMConnectionSetNotification failed %d", iopmerr); + else + { +#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM + IOPMConnectionSetDispatchQueue(c, dispatch_get_main_queue()); + LogInfo("IOPMConnectionSetDispatchQueue is now running"); +#else + iopmerr = IOPMConnectionScheduleWithRunLoop(c, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); + if (iopmerr) LogMsg("IOPMConnectionScheduleWithRunLoop failed %d", iopmerr); + LogInfo("IOPMConnectionScheduleWithRunLoop is now running"); +#endif /* MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM */ + } + } + m->p->IOPMConnection = iopmerr ? mDNSNULL : c; + if (iopmerr) // If IOPMConnectionCreate unavailable or failed, proceed with old-style power notification code below +#endif // kIOPMAcknowledgmentOptionSystemCapabilityRequirements + { + m->p->PowerConnection = IORegisterForSystemPower(m, &m->p->PowerPortRef, PowerChanged, &m->p->PowerNotifier); + if (!m->p->PowerConnection) { LogMsg("mDNSPlatformInit_setup: IORegisterForSystemPower failed"); return(-1); } + else + { +#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM + IONotificationPortSetDispatchQueue(m->p->PowerPortRef, dispatch_get_main_queue()); +#else + CFRunLoopAddSource(CFRunLoopGetCurrent(), IONotificationPortGetRunLoopSource(m->p->PowerPortRef), kCFRunLoopDefaultMode); +#endif /* MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM */ + } + } + +#if APPLE_OSX_mDNSResponder + // Note: We use SPMetricPortability > 35 to indicate a laptop of some kind + // SPMetricPortability <= 35 means nominally a non-portable machine (i.e. Mac mini or better) + // Apple TVs, AirPort base stations, and Time Capsules do not actually weigh 3kg, but we assign them + // higher 'nominal' masses to indicate they should be treated as being relatively less portable than a laptop + if (!strncasecmp(HINFO_HWstring, "Xserve", 6)) { SPMetricPortability = 25 /* 30kg */; SPMetricMarginalPower = 84 /* 250W */; SPMetricTotalPower = 85 /* 300W */; } + else if (!strncasecmp(HINFO_HWstring, "RackMac", 7)) { SPMetricPortability = 25 /* 30kg */; SPMetricMarginalPower = 84 /* 250W */; SPMetricTotalPower = 85 /* 300W */; } + else if (!strncasecmp(HINFO_HWstring, "MacPro", 6)) { SPMetricPortability = 27 /* 20kg */; SPMetricMarginalPower = 84 /* 250W */; SPMetricTotalPower = 85 /* 300W */; } + else if (!strncasecmp(HINFO_HWstring, "PowerMac", 8)) { SPMetricPortability = 27 /* 20kg */; SPMetricMarginalPower = 82 /* 160W */; SPMetricTotalPower = 83 /* 200W */; } + else if (!strncasecmp(HINFO_HWstring, "iMac", 4)) { SPMetricPortability = 30 /* 10kg */; SPMetricMarginalPower = 77 /* 50W */; SPMetricTotalPower = 78 /* 60W */; } + else if (!strncasecmp(HINFO_HWstring, "Macmini", 7)) { SPMetricPortability = 33 /* 5kg */; SPMetricMarginalPower = 73 /* 20W */; SPMetricTotalPower = 74 /* 25W */; } + else if (!strncasecmp(HINFO_HWstring, "TimeCapsule", 11)) { SPMetricPortability = 34 /* 4kg */; SPMetricMarginalPower = 10 /* ~0W */; SPMetricTotalPower = 70 /* 13W */; } + else if (!strncasecmp(HINFO_HWstring, "AirPort", 7)) { SPMetricPortability = 35 /* 3kg */; SPMetricMarginalPower = 10 /* ~0W */; SPMetricTotalPower = 70 /* 12W */; } + else if ( IsAppleTV() ) { SPMetricPortability = 35 /* 3kg */; SPMetricMarginalPower = 60 /* 1W */; SPMetricTotalPower = 63 /* 2W */; } + else if (!strncasecmp(HINFO_HWstring, "MacBook", 7)) { SPMetricPortability = 37 /* 2kg */; SPMetricMarginalPower = 71 /* 13W */; SPMetricTotalPower = 72 /* 15W */; } + else if (!strncasecmp(HINFO_HWstring, "PowerBook", 9)) { SPMetricPortability = 37 /* 2kg */; SPMetricMarginalPower = 71 /* 13W */; SPMetricTotalPower = 72 /* 15W */; } + LogSPS("HW_MODEL: %.*s (%s) Portability %d Marginal Power %d Total Power %d Features %d", + HINFO_HWstring_prefixlen, HINFO_HWstring, HINFO_HWstring, SPMetricPortability, SPMetricMarginalPower, SPMetricTotalPower, SPMetricFeatures); +#endif // APPLE_OSX_mDNSResponder + + // Currently this is not defined. SSL code will eventually fix this. If it becomes + // critical, we will define this to workaround the bug in SSL. +#ifdef __SSL_NEEDS_SERIALIZATION__ + SSLqueue = dispatch_queue_create("com.apple.mDNSResponder.SSLQueue", NULL); +#else + SSLqueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); +#endif + if (SSLqueue == mDNSNULL) LogMsg("dispatch_queue_create: SSL queue NULL"); + + mDNSMacOSXUpdateEtcHosts(m); + SetupLocalHostRecords(m); + CUPInit(m); + + return(mStatus_NoError); +} + +mDNSexport mStatus mDNSPlatformInit(mDNS *const m) +{ +#if MDNS_NO_DNSINFO + LogMsg("Note: Compiled without Apple-specific Split-DNS support"); +#endif + + // Adding interfaces will use this flag, so set it now. + m->DivertMulticastAdvertisements = !m->AdvertiseLocalAddresses; + +#if APPLE_OSX_mDNSResponder + m->SPSBrowseCallback = UpdateSPSStatus; +#endif // APPLE_OSX_mDNSResponder + + mStatus result = mDNSPlatformInit_setup(m); + + // We don't do asynchronous initialization on OS X, so by the time we get here the setup will already + // have succeeded or failed -- so if it succeeded, we should just call mDNSCoreInitComplete() immediately + if (result == mStatus_NoError) + { + mDNSCoreInitComplete(m, mStatus_NoError); + +#if !NO_D2D + // We only initialize if mDNSCore successfully initialized. + if (D2DInitialize) + { + D2DStatus ds = D2DInitialize(m->p->CFRunLoop, xD2DServiceCallback, m) ; + if (ds != kD2DSuccess) + LogMsg("D2DInitialiize failed: %d", ds); + else + LogMsg("D2DInitialize succeeded"); + } +#endif // ! NO_D2D + + } + result = DNSSECCryptoInit(m); + return(result); +} + +mDNSexport void mDNSPlatformClose(mDNS *const m) +{ + if (m->p->PowerConnection) + { +#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM + IONotificationPortSetDispatchQueue(m->p->PowerPortRef, NULL); +#else + CFRunLoopRemoveSource(CFRunLoopGetCurrent(), IONotificationPortGetRunLoopSource(m->p->PowerPortRef), kCFRunLoopDefaultMode); +#endif + // According to <http://developer.apple.com/qa/qa2004/qa1340.html>, a single call + // to IORegisterForSystemPower creates *three* objects that need to be disposed individually: + IODeregisterForSystemPower(&m->p->PowerNotifier); + IOServiceClose ( m->p->PowerConnection); + IONotificationPortDestroy ( m->p->PowerPortRef); + m->p->PowerConnection = 0; + } + + if (m->p->Store) + { +#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM + if (!SCDynamicStoreSetDispatchQueue(m->p->Store, NULL)) + LogMsg("mDNSPlatformClose: SCDynamicStoreSetDispatchQueue failed"); +#else + CFRunLoopRemoveSource(CFRunLoopGetCurrent(), m->p->StoreRLS, kCFRunLoopDefaultMode); + CFRunLoopSourceInvalidate(m->p->StoreRLS); + CFRelease(m->p->StoreRLS); + m->p->StoreRLS = NULL; +#endif + CFRelease(m->p->Store); + m->p->Store = NULL; + } + + if (m->p->PMRLS) + { + CFRunLoopRemoveSource(CFRunLoopGetCurrent(), m->p->PMRLS, kCFRunLoopDefaultMode); + CFRunLoopSourceInvalidate(m->p->PMRLS); + CFRelease(m->p->PMRLS); + m->p->PMRLS = NULL; + } + + if (m->p->SysEventNotifier >= 0) { close(m->p->SysEventNotifier); m->p->SysEventNotifier = -1; } + +#if !NO_D2D + if (D2DTerminate) + { + D2DStatus ds = D2DTerminate(); + if (ds != kD2DSuccess) + LogMsg("D2DTerminate failed: %d", ds); + else + LogMsg("D2DTerminate succeeded"); + } +#endif // ! NO_D2D + + mDNSs32 utc = mDNSPlatformUTC(); + MarkAllInterfacesInactive(m, utc); + ClearInactiveInterfaces(m, utc); + CloseSocketSet(&m->p->permanentsockets); + +#if APPLE_OSX_mDNSResponder + // clean up tunnels + while (m->TunnelClients) + { + ClientTunnel *cur = m->TunnelClients; + LogInfo("mDNSPlatformClose: removing client tunnel %p %##s from list", cur, cur->dstname.c); + if (cur->q.ThisQInterval >= 0) mDNS_StopQuery(m, &cur->q); + AutoTunnelSetKeys(cur, mDNSfalse); + m->TunnelClients = cur->next; + freeL("ClientTunnel", cur); + } + + if (AnonymousRacoonConfig) + { + AnonymousRacoonConfig = mDNSNULL; + LogInfo("mDNSPlatformClose: Deconfiguring autotunnel"); + (void)mDNSConfigureServer(kmDNSDown, mDNSNULL, mDNSNULL); + } +#endif // APPLE_OSX_mDNSResponder +} + +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - +#pragma mark - General Platform Support Layer functions +#endif + +mDNSexport mDNSu32 mDNSPlatformRandomNumber(void) +{ + return(arc4random()); +} + +mDNSexport mDNSs32 mDNSPlatformOneSecond = 1000; +mDNSexport mDNSu32 mDNSPlatformClockDivisor = 0; + +mDNSexport mStatus mDNSPlatformTimeInit(void) +{ + // Notes: Typical values for mach_timebase_info: + // tbi.numer = 1000 million + // tbi.denom = 33 million + // These are set such that (mach_absolute_time() * numer/denom) gives us nanoseconds; + // numer / denom = nanoseconds per hardware clock tick (e.g. 30); + // denom / numer = hardware clock ticks per nanosecond (e.g. 0.033) + // (denom*1000000) / numer = hardware clock ticks per millisecond (e.g. 33333) + // So: mach_absolute_time() / ((denom*1000000)/numer) = milliseconds + // + // Arithmetic notes: + // tbi.denom is at least 1, and not more than 2^32-1. + // Therefore (tbi.denom * 1000000) is at least one million, but cannot overflow a uint64_t. + // tbi.denom is at least 1, and not more than 2^32-1. + // Therefore clockdivisor should end up being a number roughly in the range 10^3 - 10^9. + // If clockdivisor is less than 10^3 then that means that the native clock frequency is less than 1MHz, + // which is unlikely on any current or future Macintosh. + // If clockdivisor is greater than 10^9 then that means the native clock frequency is greater than 1000GHz. + // When we ship Macs with clock frequencies above 1000GHz, we may have to update this code. + struct mach_timebase_info tbi; + kern_return_t result = mach_timebase_info(&tbi); + if (result == KERN_SUCCESS) mDNSPlatformClockDivisor = ((uint64_t)tbi.denom * 1000000) / tbi.numer; + return(result); +} + +mDNSexport mDNSs32 mDNSPlatformRawTime(void) +{ + if (mDNSPlatformClockDivisor == 0) { LogMsg("mDNSPlatformRawTime called before mDNSPlatformTimeInit"); return(0); } + + static uint64_t last_mach_absolute_time = 0; + //static uint64_t last_mach_absolute_time = 0x8000000000000000LL; // Use this value for testing the alert display + uint64_t this_mach_absolute_time = mach_absolute_time(); + if ((int64_t)this_mach_absolute_time - (int64_t)last_mach_absolute_time < 0) + { + LogMsg("mDNSPlatformRawTime: last_mach_absolute_time %08X%08X", last_mach_absolute_time); + LogMsg("mDNSPlatformRawTime: this_mach_absolute_time %08X%08X", this_mach_absolute_time); + // Update last_mach_absolute_time *before* calling NotifyOfElusiveBug() + last_mach_absolute_time = this_mach_absolute_time; + // Note: This bug happens all the time on 10.3 + NotifyOfElusiveBug("mach_absolute_time went backwards!", + "This error occurs from time to time, often on newly released hardware, " + "and usually the exact cause is different in each instance.\r\r" + "Please file a new Radar bug report with the title “mach_absolute_time went backwards” " + "and assign it to Radar Component “Kernel” Version “X”."); + } + last_mach_absolute_time = this_mach_absolute_time; + + return((mDNSs32)(this_mach_absolute_time / mDNSPlatformClockDivisor)); +} + +mDNSexport mDNSs32 mDNSPlatformUTC(void) +{ + return time(NULL); +} + +// Locking is a no-op here, because we're single-threaded with a CFRunLoop, so we can never interrupt ourselves +mDNSexport void mDNSPlatformLock (const mDNS *const m) { (void)m; } +mDNSexport void mDNSPlatformUnlock (const mDNS *const m) { (void)m; } +mDNSexport void mDNSPlatformStrCopy( void *dst, const void *src) { strcpy((char *)dst, (char *)src); } +mDNSexport mDNSu32 mDNSPlatformStrLen ( const void *src) { return(strlen((char*)src)); } +mDNSexport void mDNSPlatformMemCopy( void *dst, const void *src, mDNSu32 len) { memcpy(dst, src, len); } +mDNSexport mDNSBool mDNSPlatformMemSame(const void *dst, const void *src, mDNSu32 len) { return(memcmp(dst, src, len) == 0); } +mDNSexport int mDNSPlatformMemCmp(const void *dst, const void *src, mDNSu32 len) { return(memcmp(dst, src, len)); } +mDNSexport void mDNSPlatformMemZero( void *dst, mDNSu32 len) { memset(dst, 0, len); } +mDNSexport void mDNSPlatformQsort ( void *base, int nel, int width, int (*compar)(const void *, const void *)) +{ + return (qsort(base, nel, width, compar)); +} +#if !(APPLE_OSX_mDNSResponder && MACOSX_MDNS_MALLOC_DEBUGGING) +mDNSexport void * mDNSPlatformMemAllocate(mDNSu32 len) { return(mallocL("mDNSPlatformMemAllocate", len)); } +#endif +mDNSexport void mDNSPlatformMemFree (void *mem) { freeL("mDNSPlatformMemFree", mem); } + +mDNSexport void mDNSPlatformSetAllowSleep(mDNS *const m, mDNSBool allowSleep, const char *reason) +{ + if (allowSleep && m->p->IOPMAssertion) + { + LogInfo("%s Destroying NoIdleSleep power assertion", __FUNCTION__); + IOPMAssertionRelease(m->p->IOPMAssertion); + m->p->IOPMAssertion = 0; + } + else if (!allowSleep) + { +#ifdef kIOPMAssertionTypeNoIdleSleep + if (m->p->IOPMAssertion) + { + IOPMAssertionRelease(m->p->IOPMAssertion); + m->p->IOPMAssertion = 0; + } + + CFStringRef assertionName = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%s.%d %s"), getprogname(), getpid(), reason ? reason : ""); + IOPMAssertionCreateWithName(kIOPMAssertionTypeNoIdleSleep, kIOPMAssertionLevelOn, assertionName ? assertionName : CFSTR("mDNSResponder"), &m->p->IOPMAssertion); + if (assertionName) CFRelease(assertionName); + LogInfo("%s Creating NoIdleSleep power assertion", __FUNCTION__); +#endif + } +} + +mDNSexport void mDNSPlatformSendWakeupPacket(mDNS *const m, mDNSInterfaceID InterfaceID, char *EthAddr, char *IPAddr, int iteration) +{ + mDNSu32 ifindex; + + // Sanity check + ifindex = mDNSPlatformInterfaceIndexfromInterfaceID(m, InterfaceID, mDNStrue); + if (ifindex <= 0) + { + LogMsg("mDNSPlatformSendWakeupPacket: ERROR!! Invalid InterfaceID %u", ifindex); + return; + } + mDNSSendWakeupPacket(ifindex, EthAddr, IPAddr, iteration); +} + +mDNSexport mDNSBool mDNSPlatformInterfaceIsD2D(mDNSInterfaceID InterfaceID) +{ + NetworkInterfaceInfoOSX *info; + + if (InterfaceID == mDNSInterface_P2P) + return mDNStrue; + + if ( (InterfaceID == mDNSInterface_Any) + || (InterfaceID == mDNSInterfaceMark) + || (InterfaceID == mDNSInterface_LocalOnly) + || (InterfaceID == mDNSInterface_Unicast)) + return mDNSfalse; + + info = IfindexToInterfaceInfoOSX(&mDNSStorage, InterfaceID); + if (info == NULL) + { + // this log message can print when operations are stopped on an interface that has gone away + LogInfo("mDNSPlatformInterfaceIsD2D: Invalid interface index %d", InterfaceID); + return mDNSfalse; + } + + return (mDNSBool) info->D2DInterface; +} + +// Filter records send over P2P (D2D) type interfaces +// Note that the terms P2P and D2D are used synonymously in the current code and comments. +mDNSexport mDNSBool mDNSPlatformValidRecordForInterface(AuthRecord *rr, const NetworkInterfaceInfo *intf) +{ + // For an explicit match to a valid interface ID, return true. + if (rr->resrec.InterfaceID == intf->InterfaceID) + return mDNStrue; + + // Only filtering records for D2D type interfaces, return true for all other interface types. + if (!mDNSPlatformInterfaceIsD2D(intf->InterfaceID)) + return mDNStrue; + + // If it's an AWDL interface the record must be explicitly marked to include AWDL. + if (intf->InterfaceID == AWDLInterfaceID) + { + if (rr->ARType == AuthRecordAnyIncludeAWDL || rr->ARType == AuthRecordAnyIncludeAWDLandP2P) + return mDNStrue; + else + return mDNSfalse; + } + + // Send record if it is explicitly marked to include all other P2P type interfaces. + if (rr->ARType == AuthRecordAnyIncludeP2P || rr->ARType == AuthRecordAnyIncludeAWDLandP2P) + return mDNStrue; + + // Don't send the record over this interface. + return mDNSfalse; +} + +// Filter questions send over P2P (D2D) type interfaces. +mDNSexport mDNSBool mDNSPlatformValidQuestionForInterface(DNSQuestion *q, const NetworkInterfaceInfo *intf) +{ + // For an explicit match to a valid interface ID, return true. + if (q->InterfaceID == intf->InterfaceID) + return mDNStrue; + + // Only filtering questions for D2D type interfaces + if (!mDNSPlatformInterfaceIsD2D(intf->InterfaceID)) + return mDNStrue; + + // If it's an AWDL interface the question must be explicitly marked to include AWDL. + if (intf->InterfaceID == AWDLInterfaceID) + { + if (q->flags & kDNSServiceFlagsIncludeAWDL) + return mDNStrue; + else + return mDNSfalse; + } + + // Sent question if it is explicitly marked to include all other P2P type interfaces. + if (q->flags & kDNSServiceFlagsIncludeP2P) + return mDNStrue; + + // Don't send the question over this interface. + return mDNSfalse; +} + +// Returns true unless record was received over the AWDL interface and +// the question was not specific to the AWDL interface or did not specify kDNSServiceInterfaceIndexAny +// with the kDNSServiceFlagsIncludeAWDL flag set. +mDNSexport mDNSBool mDNSPlatformValidRecordForQuestion(const ResourceRecord *const rr, const DNSQuestion *const q) +{ + if (!rr->InterfaceID || (rr->InterfaceID == q->InterfaceID)) + return mDNStrue; + + if ((rr->InterfaceID == AWDLInterfaceID) && !(q->flags & kDNSServiceFlagsIncludeAWDL)) + { + LogInfo("mDNSPlatformValidRecordForQuestion: Record recieved over AWDL not returned for %s %##s query", + DNSTypeName(q->qtype), q->qname.c); + return mDNSfalse; + } + + return mDNStrue; +} + +// formating time to RFC 4034 format +mDNSexport void mDNSPlatformFormatTime(unsigned long te, mDNSu8 *buf, int bufsize) +{ + struct tm tmTime; + time_t t = (time_t)te; + // Time since epoch : strftime takes "tm". Convert seconds to "tm" using + // gmtime_r first and then use strftime + gmtime_r(&t, &tmTime); + strftime((char *)buf, bufsize, "%Y%m%d%H%M%S", &tmTime); +} + +mDNSexport mDNSs32 mDNSPlatformGetPID() +{ + return getpid(); +} + +// Schedule a function asynchronously on the main queue +mDNSexport void mDNSPlatformDispatchAsync(mDNS *const m, void *context, AsyncDispatchFunc func) +{ + // KQueueLock/Unlock is used for two purposes + // + // 1. We can't be running along with the KQueue thread and hence acquiring the lock + // serializes the access to the "core" + // + // 2. KQueueUnlock also sends a message wake up the KQueue thread which in turn wakes + // up and calls udsserver_idle which schedules the messages across the uds socket. + // If "func" delivers something to the uds socket from the dispatch thread, it will + // not be delivered immediately if not for the Unlock. + dispatch_async(dispatch_get_main_queue(), ^{ + KQueueLock(m); + func(m, context); + KQueueUnlock(m, "mDNSPlatformDispatchAsync"); +#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM + // KQueueUnlock is a noop. Hence, we need to run kick off the idle loop + // to handle any message that "func" might deliver. + TriggerEventCompletion(); +#endif + }); +} + +// definitions for device-info record construction +#define DEVINFO_MODEL "model=" +#define DEVINFO_MODEL_LEN strlen(DEVINFO_MODEL) + +#define OSX_VER "osxvers=" +#define OSX_VER_LEN strlen(OSX_VER) +#define VER_NUM_LEN 2 // 2 digits of version number added to base string + +// Bytes available in TXT record for model name after subtracting space for other +// fixed size strings and their length bytes. +#define MAX_MODEL_NAME_LEN (256 - (DEVINFO_MODEL_LEN + 1) - (OSX_VER_LEN + VER_NUM_LEN + 1)) + +// Initialize device-info TXT record contents and return total length of record data. +mDNSexport mDNSu32 initializeDeviceInfoTXT(mDNS *m, mDNSu8 *ptr) +{ + mDNSu8 *bufferStart = ptr; + mDNSu8 len = m->HIHardware.c[0] < MAX_MODEL_NAME_LEN ? m->HIHardware.c[0] : MAX_MODEL_NAME_LEN; + + *ptr = DEVINFO_MODEL_LEN + len; // total length of DEVINFO_MODEL string plus the hardware name string + ptr++; + mDNSPlatformMemCopy(ptr, DEVINFO_MODEL, DEVINFO_MODEL_LEN); + ptr += DEVINFO_MODEL_LEN; + mDNSPlatformMemCopy(ptr, m->HIHardware.c + 1, len); + ptr += len; + + // only include this string for OSX + if (OSXVers) + { + char ver_num[VER_NUM_LEN + 1]; // version digits + null written by snprintf + *ptr = OSX_VER_LEN + VER_NUM_LEN; // length byte + ptr++; + mDNSPlatformMemCopy(ptr, OSX_VER, OSX_VER_LEN); + ptr += OSX_VER_LEN; + // convert version number to ASCII, add 1 for terminating null byte written by snprintf() + snprintf(ver_num, VER_NUM_LEN + 1, "%d", OSXVers); + mDNSPlatformMemCopy(ptr, ver_num, VER_NUM_LEN); + ptr += VER_NUM_LEN; + } + + return (ptr - bufferStart); +} diff --git a/mDNSResponder/mDNSMacOSX/mDNSMacOSX.h b/mDNSResponder/mDNSMacOSX/mDNSMacOSX.h new file mode 100644 index 00000000..00bfb87c --- /dev/null +++ b/mDNSResponder/mDNSMacOSX/mDNSMacOSX.h @@ -0,0 +1,295 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2002-2003 Apple Computer, 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. + */ + +#ifndef __mDNSMacOSX_h +#define __mDNSMacOSX_h + +#ifdef __cplusplus +extern "C" { +#endif + +#include <SystemConfiguration/SystemConfiguration.h> +#include <IOKit/pwr_mgt/IOPM.h> +#include <IOKit/pwr_mgt/IOPMLib.h> +#include <IOKit/pwr_mgt/IOPMLibPrivate.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include "mDNSEmbeddedAPI.h" // for domain name structure + +#include <net/if.h> + +//#define MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM +#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM +#include <dispatch/dispatch.h> +#include <dispatch/private.h> +#endif + +#if TARGET_OS_EMBEDDED +#define NO_SECURITYFRAMEWORK 1 +#define NO_CFUSERNOTIFICATION 1 +#include <MobileGestalt.h> // for IsAppleTV() +#include <SystemConfiguration/scprefs_observer.h> // for _scprefs_observer_watch() +extern mDNSBool GetmDNSManagedPref(CFStringRef key); +#endif + +#ifndef NO_SECURITYFRAMEWORK +#include <Security/SecureTransport.h> +#include <Security/Security.h> +#endif /* NO_SECURITYFRAMEWORK */ + +#if TARGET_OS_IPHONE +#include "cellular_usage_policy.h" +#endif + +#define kmDNSResponderServName "com.apple.mDNSResponder" + +enum mDNSDynamicStoreSetConfigKey +{ + kmDNSMulticastConfig = 1, + kmDNSDynamicConfig, + kmDNSPrivateConfig, + kmDNSBackToMyMacConfig, + kmDNSSleepProxyServersState, + kmDNSDebugState, +}; + +typedef struct NetworkInterfaceInfoOSX_struct NetworkInterfaceInfoOSX; + +typedef void (*KQueueEventCallback)(int fd, short filter, void *context); +typedef struct +{ + KQueueEventCallback KQcallback; + void *KQcontext; + const char *KQtask; // For debugging messages +#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM + dispatch_source_t readSource; + dispatch_source_t writeSource; + mDNSBool fdClosed; +#endif +} KQueueEntry; + +typedef struct +{ + mDNSIPPort port; // MUST BE FIRST FIELD -- UDPSocket_struct begins with a KQSocketSet, + // and mDNSCore requires every UDPSocket_struct to begin with a mDNSIPPort port + mDNS *m; + int sktv4; + KQueueEntry kqsv4; + int sktv6; + KQueueEntry kqsv6; + int *closeFlag; + mDNSBool proxy; +} KQSocketSet; + +struct UDPSocket_struct +{ + KQSocketSet ss; // First field of KQSocketSet has to be mDNSIPPort -- mDNSCore requires every UDPSocket_struct to begin with mDNSIPPort port +}; + +// TCP socket support + +typedef enum +{ + handshake_required, + handshake_in_progress, + handshake_completed, + handshake_to_be_closed +} handshakeStatus; + +struct TCPSocket_struct +{ + TCPSocketFlags flags; // MUST BE FIRST FIELD -- mDNSCore expects every TCPSocket_struct to begin with TCPSocketFlags flags + TCPConnectionCallback callback; + int fd; + KQueueEntry *kqEntry; + KQSocketSet ss; +#ifndef NO_SECURITYFRAMEWORK + SSLContextRef tlsContext; + pthread_t handshake_thread; +#endif /* NO_SECURITYFRAMEWORK */ + domainname hostname; + void *context; + mDNSBool setup; + mDNSBool connected; + handshakeStatus handshake; + mDNS *m; // So we can call KQueueLock from the SSLHandshake thread + mStatus err; +}; + +struct NetworkInterfaceInfoOSX_struct +{ + NetworkInterfaceInfo ifinfo; // MUST be the first element in this structure + NetworkInterfaceInfoOSX *next; + mDNS *m; + mDNSu8 Exists; // 1 = currently exists in getifaddrs list; 0 = doesn't + // 2 = exists, but McastTxRx state changed + mDNSu8 Flashing; // Set if interface appeared for less than 60 seconds and then vanished + mDNSu8 Occulting; // Set if interface vanished for less than 60 seconds and then came back + mDNSu8 D2DInterface; // IFEF_LOCALNET_PRIVATE flag indicates we should call + // D2D plugin for operations over this interface + mDNSu8 DirectLink; // IFEF_DIRECTLINK flag is set for interface + + mDNSs32 AppearanceTime; // Time this interface appeared most recently in getifaddrs list + // i.e. the first time an interface is seen, AppearanceTime is set. + // If an interface goes away temporarily and then comes back then + // AppearanceTime is updated to the time of the most recent appearance. + mDNSs32 LastSeen; // If Exists==0, last time this interface appeared in getifaddrs list + unsigned int ifa_flags; + struct in_addr ifa_v4addr; + mDNSu32 scope_id; // interface index / IPv6 scope ID + mDNSEthAddr BSSID; // BSSID of 802.11 base station, if applicable + u_short sa_family; + int BPF_fd; // -1 uninitialized; -2 requested BPF; -3 failed + int BPF_mcfd; // Socket for our IPv6 ND group membership + u_int BPF_len; +#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM + dispatch_source_t BPF_source; +#else + CFSocketRef BPF_cfs; + CFRunLoopSourceRef BPF_rls; +#endif + NetworkInterfaceInfoOSX *Registered; // non-NULL means registered with mDNS Core +}; + +struct mDNS_PlatformSupport_struct +{ + NetworkInterfaceInfoOSX *InterfaceList; + KQSocketSet permanentsockets; + domainlabel userhostlabel; // The hostlabel as it was set in System Preferences the last time we looked + domainlabel usernicelabel; // The nicelabel as it was set in System Preferences the last time we looked + // Following four variables are used for optimization where the helper is not + // invoked when not needed. It records the state of what we told helper the + // last time we invoked mDNSPreferencesSetName + domainlabel prevoldhostlabel; // Previous m->p->userhostlabel + domainlabel prevnewhostlabel; // Previous m->hostlabel + domainlabel prevoldnicelabel; // Previous m->p->usernicelabel + domainlabel prevnewnicelabel; // Previous m->nicelabel + mDNSs32 NotifyUser; + mDNSs32 HostNameConflict; // Time we experienced conflict on our link-local host name + mDNSs32 NetworkChanged; + mDNSs32 KeyChainTimer; + + CFRunLoopRef CFRunLoop; + SCDynamicStoreRef Store; + CFRunLoopSourceRef StoreRLS; + CFRunLoopSourceRef PMRLS; + int SysEventNotifier; + KQueueEntry SysEventKQueue; + IONotificationPortRef PowerPortRef; + io_connect_t PowerConnection; + io_object_t PowerNotifier; +#ifdef kIOPMAcknowledgmentOptionSystemCapabilityRequirements + IOPMConnection IOPMConnection; +#endif + IOPMAssertionID IOPMAssertion; + long SleepCookie; // Cookie we need to pass to IOAllowPowerChange() + long WakeAtUTC; + mDNSs32 RequestReSleep; +#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM + dispatch_source_t timer; + dispatch_source_t custom; +#else + pthread_mutex_t BigMutex; +#endif + mDNSs32 BigMutexStartTime; + int WakeKQueueLoopFD; + mDNSu8 v4answers; // non-zero if we are receiving answers + mDNSu8 v6answers; // for A/AAAA from external DNS servers + mDNSs32 DNSTrigger; // Time the DNSTrigger was given + uint64_t LastConfigGeneration; // DNS configuration generation number + UDPSocket UDPProxy; + TCPSocket TCPProxy; + ProxyCallback *UDPProxyCallback; + ProxyCallback *TCPProxyCallback; +#if TARGET_OS_IPHONE + cellular_usage_policy_client_t handle; +#endif +}; + +extern int OfferSleepProxyService; +extern int DisableSleepProxyClient; +extern int UseInternalSleepProxy; +extern int OSXVers, iOSVers; + +extern int KQueueFD; + +extern void NotifyOfElusiveBug(const char *title, const char *msg); // Both strings are UTF-8 text +extern void SetDomainSecrets(mDNS *m); +extern void mDNSMacOSXNetworkChanged(mDNS *const m); +extern void mDNSMacOSXSystemBuildNumber(char *HINFO_SWstring); +extern NetworkInterfaceInfoOSX *IfindexToInterfaceInfoOSX(const mDNS *const m, mDNSInterfaceID ifindex); +extern void mDNSUpdatePacketFilter(const ResourceRecord *const excludeRecord); +extern void myKQSocketCallBack(int s1, short filter, void *context); +extern void mDNSDynamicStoreSetConfig(int key, const char *subkey, CFPropertyListRef value); +extern void UpdateDebugState(void); + +#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM +extern int KQueueSet(int fd, u_short flags, short filter, KQueueEntry *const entryRef); +mDNSexport void TriggerEventCompletion(void); +#else +extern int KQueueSet(int fd, u_short flags, short filter, const KQueueEntry *const entryRef); +#endif + +// When events are processed on the non-kqueue thread (i.e. CFRunLoop notifications like Sleep/Wake, +// Interface changes, Keychain changes, etc.) they must use KQueueLock/KQueueUnlock to lock out the kqueue thread +extern void KQueueLock(mDNS *const m); +extern void KQueueUnlock(mDNS *const m, const char* task); +extern void mDNSPlatformCloseFD(KQueueEntry *kq, int fd); +extern ssize_t myrecvfrom(const int s, void *const buffer, const size_t max, + struct sockaddr *const from, size_t *const fromlen, mDNSAddr *dstaddr, char *ifname, mDNSu8 *ttl); + +extern mDNSBool DictionaryIsEnabled(CFDictionaryRef dict); + +extern void CUPInit(mDNS *const m); +extern const char *DNSScopeToString(mDNSu32 scope); + +// If any event takes more than WatchDogReportingThreshold milliseconds to be processed, we log a warning message +// General event categories are: +// o Mach client request initiated / terminated +// o UDS client request +// o Handling UDP packets received from the network +// o Environmental change events: +// - network interface changes +// - sleep/wake +// - keychain changes +// o Name conflict dialog dismissal +// o Reception of Unix signal (e.g. SIGINFO) +// o Idle task processing +// If we find that we're getting warnings for any of these categories, and it's not evident +// what's causing the problem, we may need to subdivide some categories into finer-grained +// sub-categories (e.g. "Idle task processing" covers a pretty broad range of sub-tasks). + +extern int WatchDogReportingThreshold; + +struct CompileTimeAssertionChecks_mDNSMacOSX +{ + // Check our structures are reasonable sizes. Including overly-large buffers, or embedding + // other overly-large structures instead of having a pointer to them, can inadvertently + // cause structure sizes (and therefore memory usage) to balloon unreasonably. + + // Checks commented out when sizeof(DNSQuestion) change cascaded into having to change yet another + // set of hardcoded size values because these structures contain one or more DNSQuestion + // instances. +// char sizecheck_NetworkInterfaceInfoOSX[(sizeof(NetworkInterfaceInfoOSX) <= 7084) ? 1 : -1]; + char sizecheck_mDNS_PlatformSupport [(sizeof(mDNS_PlatformSupport) <= 1378) ? 1 : -1]; +}; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/mDNSResponder/mDNSMacOSX/mDNSResponder-bundle/Resources/English.lproj/Localizable.strings b/mDNSResponder/mDNSMacOSX/mDNSResponder-bundle/Resources/English.lproj/Localizable.strings new file mode 100644 index 00000000..7b88ff90 --- /dev/null +++ b/mDNSResponder/mDNSMacOSX/mDNSResponder-bundle/Resources/English.lproj/Localizable.strings @@ -0,0 +1,13 @@ +"The name of your computer " = "The name of your computer "; +"This computer’s local hostname " = "This computer’s local hostname "; +"“" = "“"; +"”" = "”"; +" is already in use on this network. " = " is already in use on this network. "; +"The name has been changed to " = "The name has been changed to "; +"." = "."; + +"To change the name of your computer, open System Preferences and click Sharing, then type the name in the Computer Name field." = "To change the name of your computer, open System Preferences and click Sharing, then type the name in the Computer Name field."; +"To change the local hostname, open System Preferences and click Sharing, then click “Edit” and type the name in the Local Hostname field." = "To change the local hostname, open System Preferences and click Sharing, then click “Edit” and type the name in the Local Hostname field."; + +"All attempts to find an available name by adding a number to the name were also unsuccessful." = "All attempts to find an available name by adding a number to the name were also unsuccessful."; +"This may indicate a problem with the local network. Please inform your network administrator." = "This may indicate a problem with the local network. Please inform your network administrator."; diff --git a/mDNSResponder/mDNSMacOSX/mDNSResponder-bundle/Resources/French.lproj/Localizable.strings b/mDNSResponder/mDNSMacOSX/mDNSResponder-bundle/Resources/French.lproj/Localizable.strings new file mode 100644 index 00000000..453d5560 --- /dev/null +++ b/mDNSResponder/mDNSMacOSX/mDNSResponder-bundle/Resources/French.lproj/Localizable.strings @@ -0,0 +1,13 @@ +"The name of your computer " = "Le nom de l’ordinateur "; +"This computer’s local hostname " = "Le nom d’hôte local de cet ordinateur "; +"“" = "“"; +"”" = "”"; +" is already in use on this network. " = " est déjà utilié sur ce réseau. "; +"The name has been changed to " = "Le nom a été changé pa "; +"." = "."; + +"To change the name of your computer, open System Preferences and click Sharing, then type the name in the Computer Name field." = "Pour changer le nom de votre ordinateur, ouvrez Préférences Systèmes et cliquer Partage. Ensuite entrez le nom dans le champ Nom de l’ordinateur."; +"To change the local hostname, open System Preferences and click Sharing, then click “Edit” and type the name in the Local Hostname field." = "Pour changer le nom d’hôte local, ouvrez Préférences Systèmes et cliquer Partage. Ensuite cliquer Modifier et entrez le nom dans le champ Nom local de l’ordinateur."; + +"All attempts to find an available name by adding a number to the name were also unsuccessful." = "Toutes les tentatives de trouver un nom disponible en ajoutant un nombre au nom étaient également non réussies."; +"This may indicate a problem with the local network. Please inform your network administrator." = "Ceci peut indiquer un problème avec le réseau local. Veuillez informer votre administrateur de réseau."; diff --git a/mDNSResponder/mDNSMacOSX/mDNSResponder-entitlements.plist b/mDNSResponder/mDNSMacOSX/mDNSResponder-entitlements.plist new file mode 100644 index 00000000..bb3f05e8 --- /dev/null +++ b/mDNSResponder/mDNSMacOSX/mDNSResponder-entitlements.plist @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>com.apple.wifi.manager-access</key> + <true/> + <key>com.apple.SystemConfiguration.trailing-edge-agent</key> + <true/> + <key>com.apple.private.network.socket-delegate</key> + <true/> + <key>com.apple.networkd.cellular_blocked.notify</key> + <true/> + <key>com.apple.private.SCNetworkConnection-proxy-user</key> + <true/> + <key>com.apple.private.network.reserved-port</key> + <true/> + <key>com.apple.SystemConfiguration.SCDynamicStore-write-access</key> + <true/> + <key>com.apple.private.snhelper</key> + <true/> + <key>com.apple.telephony.cupolicy-monitor-access</key> + <true/> +</dict> +</plist> diff --git a/mDNSResponder/mDNSMacOSX/mDNSResponder.order b/mDNSResponder/mDNSMacOSX/mDNSResponder.order new file mode 100644 index 00000000..55cfd6fe --- /dev/null +++ b/mDNSResponder/mDNSMacOSX/mDNSResponder.order @@ -0,0 +1,314 @@ +_start +__start +_dyld_stub_binding_helper +__dyld_func_lookup +_main +_LogMsgWithLevel +_mDNS_vsnprintf +_mDNSPlatformWriteLogMsg +_KQueueSet +_mDNSMacOSXSystemBuildNumber +_mDNSDaemonInitialize +_mDNS_Init +_mDNSPlatformTimeInit +_mDNSRandom +_mDNSPlatformRandomNumber +_mDNSPlatformRawTime +_mDNSPlatformInit +_mDNS_snprintf +_GetUserSpecifiedLocalHostName +_SetupSocket +_SystemWakeForNetworkAccess +_UpdateInterfaceList +_myGetIfAddrs +_AddInterfaceToList +_SetupAddr +_NetWakeInterface +_mDNS_SetFQDN +_AppendDomainLabel +_AppendLiteralLabelString +_mDNS_Lock_ +_mDNSPlatformLock +_SameDomainNameCS +_DomainNameLengthLimit +_mDNSPlatformMemCopy +_mDNS_Unlock_ +_mDNSPlatformUnlock +_SetupActiveInterfaces +_SearchForInterfaceByName +_mDNS_RegisterInterface +_AdvertiseInterface +_mDNS_SetupResourceRecord +_MakeDomainNameFromDNSNameString +_AppendDNSNameString +_mDNS_Register_internal +_InitializeLastAPTime +_SetNextAnnounceProbeTime +_GetRDLength +_ValidateRData +_DomainNameHashValue +_RDataHashValue +_SetTargetToHostName +_SameDomainName +_SetNewRData +_CompressedDomainNameLength +_AcknowledgeRecord +_mDNS_StartBrowse_internal +_ConstructServiceName +_AppendDomainName +_mDNS_StartQuery_internal +_IsLocalDomain +_CheckForSoonToExpireRecords +_GetAuthInfoForQuestion +_GetAuthInfoForName_internal +_FindDuplicateQuestion +_SetNextQueryTime +_SetDomainSecrets +_mDNSKeychainGetSecrets +_getHelperPort +_proxy_mDNSKeychainGetSecrets +_mDNS_SetSecretForDomain +_DNSDigest_ConstructHMACKeyfromBase64 +_mDNSPlatformMemZero +_UpdateAutoTunnelDomainStatus +_ConvertDomainLabelToCString_withescape +_mDNSASLLog +_mDNSDynamicStoreSetConfig +_proxy_mDNSDynamicStoreSetConfig +_ConvertDomainNameToCString_withescape +_UpdateConfigureServer +_TunnelServers +_mDNSCoreInitComplete +_mDNS_StatusCallback +_mDNSPlatformMemSame +_uDNS_SetupDNSConfig +_mDNSPlatformSetDNSConfig +_dns_configuration_copy +___dns_initialize +__dns_configuration_server_port +_shared_dns_infoGet +_CountLabels +_SkipLeadingLabels +_dns_configuration_free +_mDNSPlatformGetPrimaryInterface +_mDNS_SetPrimaryInterfaceInfo +_udsserver_init +_udsSupportAddFDToEventLoop +_mDNS_GetDomains +_mDNS_StartQuery +_RegisterLocalOnlyDomainEnumPTR +_mDNSPlatformMemAllocate +_mDNS_Register +_AddAutoBrowseDomain +_udsserver_automatic_browse_domain_changed +_machserver_automatic_browse_domain_changed +_udsserver_handle_configchange +_UpdateDeviceInfoRecord +_AppendDNameListElem +_SetPrefsBrowseDomains +_udsserver_default_reg_domain_changed +_machserver_automatic_registration_domain_changed +_mDNSMacOSXNetworkChanged +_mDNSSameAddress +_ClearInactiveInterfaces +_mDNSCoreBeSleepProxyServer +_mDNS_ConfigChanged +_mDNSPreferencesSetName +_proxy_mDNSPreferencesSetName +_SameRDataBody +_DeregisterLocalOnlyDomainEnumPTR +_mDNS_Deregister +_mDNS_Deregister_internal +_mDNSPlatformMemFree +_RmvAutoBrowseDomain +_KQueueLoop +_mDNS_TimeNow +_mDNS_Execute +_SendQueries +_GetFirstActiveInterface +_InitializeDNSMessage +_uDNS_Execute +_mDNSv4AddrIsRFC1918 +_udsserver_idle +_connect_callback +_NewRequest +_request_callback +_ConvertHeaderBytes +_handle_regservice_request +_get_uint32 +_mDNSPlatformInterfaceIDfromInterfaceIndex +_get_string +_mDNSPlatformStrCopy +_get_uint16 +_ChopSubTypes +_AuthorizedDomain +_register_service_instance +_AllocateSubTypes +_mDNS_RegisterService +_ServiceCallback +_GetServiceTarget +_SetupLocalAutoTunnelInterface_internal +_NetworkChanged +_KQueueLock +_AbortDeregistration +_mDNS_StartNATOperation_internal +_put_uint32 +_send_all +_uDNS_SendNATMsg +_KQueueUnlock +_KQWokenFlushBytes +_handle_queryrecord_request +_mDNS_NewMessageID +_GetServerForName +_ActivateUnicastQuery +_LastLabel +_SameDomainLabel +_uDNS_CheckCurrentQuestion +_CacheGroupForName +_MakeNegativeCacheRecord +_CreateNewCacheEntry +_GetCacheEntity +_ResourceRecordAnswersQuestion +_SetNextCacheCheckTime +_SameNameRecordAnswersQuestion +_CheckCacheExpiration +_CacheRecordDeferredAdd +_AnswerCurrentQuestionWithResourceRecord +_queryrecord_result_callback +_create_reply +_mDNSPlatformInterfaceIndexfromInterfaceID +_put_string +_put_uint16 +_abort_request +_queryrecord_termination_callback +_mDNS_StopQuery +_mDNS_StopQuery_internal +_putQuestion +_putDomainNameAsLabels +_FindCompressionPointer +_GetNextActiveInterfaceID +_PutResourceRecordTTLWithLimit +_putRData +_mDNSSendDNSMessage +_putHINFO +_mDNSPlatformSendUDP +_mDNSAddrIsDNSMulticast +_myKQSocketCallBack +_mDNSCoreReceive +_mDNSCoreReceiveQuery +_AddressIsLocalSubnet +_LocateOptRR +_LocateAdditionals +_LocateAuthorities +_LocateAnswers +_skipResourceRecord +_getQuestion +_getDomainName +_GetLargeResourceRecord +_PacketRRConflict +_AddAdditionalsToResponseList +_mDNS_HostNameCallback +_regservice_callback +_GenerateNTDResponse +_DeconstructServiceName +_CountPeerRegistrations +_RecordUpdatedNiceLabel +_ClearProxyRecords +_SendResponses +_uDNS_recvLLQResponse +_AnswerAllLocalQuestionsWithLocalAuthRecord +_handle_regrecord_request +_read_rr_from_ipc_msg +_get_rdata +_regrecord_callback +_StartGetZoneData +_GetZoneData_StartQuery +_SetRecordRetry +_GetZoneData_QuestionCallback +_RecordRegistrationGotZoneData +_SameResourceRecordSignature +_FindIdenticalRecordInCache +_mDNS_GrowCache +_ShouldSuppressKnownAnswer +_AutoTunnelNATCallback +_RegisterAutoTunnelRecords +_SysEventCallBack +_UpdateSRVRecords +_UpdateSRV +_mDNS_DeregisterInterface +_mDNS_PurgeCacheResourceRecord +_DeadvertiseInterface +_NumCacheRecordsForInterfaceID +_AddrRequiresPPPConnection +_mDNS_AddDNSServer +_PurgeOrReconfirmCacheRecord +_LNT_ClearState +_UpdateAutoTunnelDomainStatuses +_ReleaseCacheGroup +_mDNS_AddDynDNSHostName +_AdvertiseHostname +_mDNSConfigureServer +_mDNSPlatformStrLen +_proxy_mDNSConfigureServer +_CancelGetZoneData +_mDNSPlatformUDPSocket +_uDNS_ReceiveMsg +_GetLLQOptData +_ExpectingUnicastResponseForQuestion +_UpdateSPSStatus +_SPSStatusPutNumber +_mDNSPlatformUDPClose +_CloseSocketSet +_SendRecordRegistration +_putZone +_putUpdateLease +_MakeTCPConn +_mDNSPlatformTCPSocket +_mDNSPlatformTCPConnect +_putDeleteRRSet +_ServiceRegistrationGotZoneData +_SendServiceRegistration +_tcpKQSocketCallback +_tlsSetupSock +_doSSLHandshake +_tlsWriteSock +_tlsReadSock +_tcpCallback +_GetAuthInfoForName +_DNSDigest_SignMessage +_MD5_Update +_md5_block_data_order +_md5_block_host_order +_mDNSPlatformUTC +_MD5_Final +_mDNSPlatformWriteTCP +_mDNSPlatformReadTCP +_DisposeTCPConn +_mDNSPlatformTCPCloseConnection +_GetPktLease +_checkUpdateResult +_HostnameCallback +_AutoTunnelRecordCallback +_handle_getproperty_request +_AbortUnlinkAndFree +_udsSupportRemoveFDFromEventLoop +_handle_browse_request +_add_domain_to_browser +_mDNS_StartBrowse +_ReconfirmAntecedents +_FoundInstance +_connection_termination +_startLLQHandshake +_LLQNATCallback +_uDNS_RegisterSearchDomains +_mDNS_AddSearchDomain +_AnswerLocalQuestionWithLocalAuthRecord +_enum_result_callback +_LLQGotZoneData +_GetLLQEventPort +_mDNSPlatformSourceAddrForDest +_putLLQ +_SetLLQTimer +_sendLLQRefresh +_SendDelayedUnicastResponse +_mDNS_Reconfirm_internal diff --git a/mDNSResponder/mDNSMacOSX/mDNSResponder.pbproj/project.pbxproj b/mDNSResponder/mDNSMacOSX/mDNSResponder.pbproj/project.pbxproj new file mode 100644 index 00000000..3e7df115 --- /dev/null +++ b/mDNSResponder/mDNSMacOSX/mDNSResponder.pbproj/project.pbxproj @@ -0,0 +1,2338 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 38; + objects = { + 000753D303367C1C0CCA2C71 = { + fileEncoding = 4; + isa = PBXFileReference; + path = mDNSMacOSX.h; + refType = 4; + }; + 00AD62A3032D799A0CCA2C71 = { + buildPhases = ( + FFF7174A07614A8600E10551, + 00AD62A4032D799A0CCA2C71, + 00AD62AC032D799A0CCA2C71, + 00AD62B3032D799A0CCA2C71, + 00AD62B7032D799A0CCA2C71, + ); + buildSettings = { + FRAMEWORK_SEARCH_PATHS = ""; + GCC_TREAT_WARNINGS_AS_ERRORS = YES; + HEADER_SEARCH_PATHS = "../mDNSShared \"${APPLE_INTERNAL_DEVELOPER_DIR}/Headers\" \"${CONFIGURATION_TEMP_DIR}\""; + LIBRARY_SEARCH_PATHS = "\"${CONFIGURATION_TEMP_DIR}\""; + MACOSX_DEPLOYMENT_TARGET = 10.2; + OPTIMIZATION_CFLAGS = "-O0"; + OTHER_CFLAGS = "-no-cpp-precomp -mdynamic-no-pic -DmDNSResponderVersion=${MVERS} -DAPPLE_OSX_mDNSResponder=1 -D_LEGACY_NAT_TRAVERSAL_ -DMDNS_DEBUGMSGS=1"; + OTHER_LDFLAGS = "-ldnsinfo"; + OTHER_REZFLAGS = ""; + PRODUCT_NAME = mDNSResponder.debug; + REZ_EXECUTABLE = YES; + SECTORDER_FLAGS = "-sectorder __TEXT __text mDNSResponder.order"; + STRIPFLAGS = "-S"; + WARNING_CFLAGS = "-W -Wall -Wmissing-prototypes -Wno-four-char-constants -Wno-unknown-pragmas"; + }; + dependencies = ( + ); + isa = PBXToolTarget; + name = "mDNSResponder debug"; + productName = mDNSResponder; + productReference = 00AD62B8032D799A0CCA2C71; + }; + 00AD62A4032D799A0CCA2C71 = { + buildActionMask = 2147483647; + files = ( + 00AD62A5032D799A0CCA2C71, + F5E11B5F04A28126019798ED, + F515E29604A37BB701CA296C, + F515E29704A37BB801CA296C, + F515E29904A37BBB01CA296C, + ); + isa = PBXHeadersBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + 00AD62A5032D799A0CCA2C71 = { + fileRef = 6575FBFF022EAFBA00000109; + isa = PBXBuildFile; + settings = { + }; + }; + 00AD62AC032D799A0CCA2C71 = { + buildActionMask = 2147483647; + files = ( + 00AD62AD032D799A0CCA2C71, + 00AD62AE032D799A0CCA2C71, + 00AD62AF032D799A0CCA2C71, + 7F18A9FB0587CEF6001880B3, + 7F18A9FA0587CEF6001880B3, + 7F461DB7062DBF2900672BF3, + DBAAFE2E057E8F660085CAD0, + DBAAFE2B057E8F4D0085CAD0, + F525E72B04AA167A01F1CF4D, + F5E11B5E04A28126019798ED, + FFCB6D75075D595E00B8AF62, + 00AD62B0032D799A0CCA2C71, + 7FC8F9D606D14E66007E879D, + 00AD62B1032D799A0CCA2C71, + ); + isa = PBXSourcesBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + 00AD62AD032D799A0CCA2C71 = { + fileRef = 6575FC00022EAFBA00000109; + isa = PBXBuildFile; + settings = { + ATTRIBUTES = ( + Client, + ); + }; + }; + 00AD62AE032D799A0CCA2C71 = { + fileRef = 6575FC01022EAFBA00000109; + isa = PBXBuildFile; + settings = { + ATTRIBUTES = ( + Server, + Client, + ); + }; + }; + 00AD62AF032D799A0CCA2C71 = { + fileRef = 6575FBE9022EAF5A00000109; + isa = PBXBuildFile; + settings = { + }; + }; + 00AD62B0032D799A0CCA2C71 = { + fileRef = 6575FBEB022EAF7200000109; + isa = PBXBuildFile; + settings = { + }; + }; + 00AD62B1032D799A0CCA2C71 = { + fileRef = 6575FBEC022EAF7200000109; + isa = PBXBuildFile; + settings = { + }; + }; + 00AD62B3032D799A0CCA2C71 = { + buildActionMask = 2147483647; + files = ( + 00AD62B4032D799A0CCA2C71, + 00AD62B5032D799A0CCA2C71, + 00AD62B6032D799A0CCA2C71, + 7F869687066EE02400D2A2DC, + ); + isa = PBXFrameworksBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + 00AD62B4032D799A0CCA2C71 = { + fileRef = 09AB6884FE841BABC02AAC07; + isa = PBXBuildFile; + settings = { + }; + }; + 00AD62B5032D799A0CCA2C71 = { + fileRef = 65713D46025A293200000109; + isa = PBXBuildFile; + settings = { + }; + }; + 00AD62B6032D799A0CCA2C71 = { + fileRef = 00CA213D02786FC30CCA2C71; + isa = PBXBuildFile; + settings = { + }; + }; + 00AD62B7032D799A0CCA2C71 = { + buildActionMask = 2147483647; + files = ( + ); + isa = PBXRezBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + 00AD62B8032D799A0CCA2C71 = { + isa = PBXExecutableFileReference; + path = mDNSResponder.debug; + refType = 3; + }; + 00AD62BB032D7A0C0CCA2C71 = { + buildPhases = ( + ); + buildSettings = { + OTHER_CFLAGS = ""; + OTHER_LDFLAGS = ""; + OTHER_REZFLAGS = ""; + PRODUCT_NAME = "Build All"; + SECTORDER_FLAGS = ""; + }; + dependencies = ( + FF25795106C9AB1D00376F7B, + 00AD62BC032D7A160CCA2C71, + 00AD62BD032D7A1B0CCA2C71, + 00AD62BE032D7A1D0CCA2C71, + FF16238F07023BD2001AB7D7, + FFD41DDB0664169900F0C438, + FFD41DDC0664169B00F0C438, + FF2A870707B4481500B14068, + ); + isa = PBXAggregateTarget; + name = "Build All"; + productName = "Build All"; + }; + 00AD62BC032D7A160CCA2C71 = { + isa = PBXTargetDependency; + target = 08FB779FFE84155DC02AAC07; + }; + 00AD62BD032D7A1B0CCA2C71 = { + isa = PBXTargetDependency; + target = 00AD62A3032D799A0CCA2C71; + }; + 00AD62BE032D7A1D0CCA2C71 = { + isa = PBXTargetDependency; + target = 6575FC1C022EB76000000109; + }; + 00B2AB0C032D7B220CCA2C71 = { + buildRules = ( + ); + buildSettings = { + CONFIGURATION_BUILD_DIR = "${BUILD_DIR}"; + CONFIGURATION_TEMP_DIR = "${BUILD_DIR}/mDNSResponder.build"; + MVERS = "\"mDNSResponder (Engineering Build)\""; + }; + isa = PBXBuildStyle; + name = Development; + }; + 00CA213D02786FC30CCA2C71 = { + isa = PBXFrameworkReference; + name = IOKit.framework; + path = /System/Library/Frameworks/IOKit.framework; + refType = 0; + }; +//000 +//001 +//002 +//003 +//004 +//030 +//031 +//032 +//033 +//034 + 034768E2FF38A6DC11DB9C8B = { + isa = PBXExecutableFileReference; + path = mDNSResponder; + refType = 3; + }; +//030 +//031 +//032 +//033 +//034 +//080 +//081 +//082 +//083 +//084 + 08FB7793FE84155DC02AAC07 = { + buildStyles = ( + 00B2AB0C032D7B220CCA2C71, + ); + hasScannedForEncodings = 1; + isa = PBXProject; + mainGroup = 08FB7794FE84155DC02AAC07; + projectDirPath = ""; + targets = ( + 00AD62BB032D7A0C0CCA2C71, + 08FB779FFE84155DC02AAC07, + 00AD62A3032D799A0CCA2C71, + 6575FC1C022EB76000000109, + FF1C919207021C84001048AB, + DB2CC4530662DD6800335AB3, + DB2CC4660662DF5C00335AB3, + FF25792906C9A70800376F7B, + FFFB0DA907B43C9100B88D48, + FF2609E107B440DD00CE10E5, + ); + }; + 08FB7794FE84155DC02AAC07 = { + children = ( + 08FB7795FE84155DC02AAC07, + 6575FC1F022EB78C00000109, + 6575FBFE022EAFA800000109, + DB2CC4420662DCE500335AB3, + FFFB0DA407B43BED00B88D48, + 08FB779DFE84155DC02AAC07, + 19C28FBDFE9D53C911CA2CBB, + ); + isa = PBXGroup; + name = mDNSResponder; + refType = 4; + }; + 08FB7795FE84155DC02AAC07 = { + children = ( + 7FC8F9D406D14E66007E879D, + 7F461DB5062DBF2900672BF3, + F525E72804AA167501F1CF4D, + F5E11B5A04A28126019798ED, + F5E11B5B04A28126019798ED, + 6575FBEC022EAF7200000109, + 6575FBE9022EAF5A00000109, + 6575FBEB022EAF7200000109, + 654BE64F02B63B93000001D1, + 654BE65002B63B93000001D1, + DBAAFE29057E8F4D0085CAD0, + 000753D303367C1C0CCA2C71, + DBAAFE2C057E8F660085CAD0, + FFCB6D73075D539900B8AF62, + FF0E0B5D065ADC7600FE4D9C, + FF1C919D07021D77001048AB, + FF485D5105632E0000130380, + FFF4F63A06CFE4DD00459EFD, + 7F18A9F60587CEF6001880B3, + 7F18A9F70587CEF6001880B3, + FF25794606C9A8BF00376F7B, + FF13FFEA0A5DA44A00897C81, + FF13FFEC0A5DA45500897C81, + ); + isa = PBXGroup; + name = "mDNS Server Sources"; + path = ""; + refType = 4; + }; + 08FB779DFE84155DC02AAC07 = { + children = ( + 7F869685066EE02400D2A2DC, + FFFB0DB407B43D2700B88D48, + 09AB6884FE841BABC02AAC07, + 65713D46025A293200000109, + 00CA213D02786FC30CCA2C71, + DB2CC4680662DFF500335AB3, + FF2609FA07B4433800CE10E5, + FF260A1F07B4436900CE10E5, + ); + isa = PBXGroup; + name = "External Frameworks and Libraries"; + refType = 4; + }; + 08FB779FFE84155DC02AAC07 = { + buildPhases = ( + FF37BE9207614059003C0420, + 08FB77A0FE84155DC02AAC07, + 08FB77A1FE84155DC02AAC07, + 08FB77A3FE84155DC02AAC07, + 08FB77A5FE84155DC02AAC07, + FF5A0AE705632EA600743C27, + FF5585E507790732008D1C14, + ); + buildSettings = { + FRAMEWORK_SEARCH_PATHS = ""; + GCC_TREAT_WARNINGS_AS_ERRORS = YES; + HEADER_SEARCH_PATHS = "../mDNSShared \"${APPLE_INTERNAL_DEVELOPER_DIR}/Headers\" \"${CONFIGURATION_TEMP_DIR}\""; + INSTALL_PATH = /usr/sbin; + LIBRARY_SEARCH_PATHS = "\"${CONFIGURATION_TEMP_DIR}\""; + MACOSX_DEPLOYMENT_TARGET = 10.2; + OTHER_CFLAGS = "-no-cpp-precomp -mdynamic-no-pic -DmDNSResponderVersion=${MVERS} -DAPPLE_OSX_mDNSResponder=1 -D_LEGACY_NAT_TRAVERSAL_ -D__MigTypeCheck=1"; + OTHER_LDFLAGS = "-ldnsinfo"; + OTHER_REZFLAGS = ""; + PRODUCT_NAME = mDNSResponder; + REZ_EXECUTABLE = YES; + SECTORDER_FLAGS = "-sectorder __TEXT __text mDNSResponder.order"; + STRIPFLAGS = "-S"; + WARNING_CFLAGS = "-W -Wall -Wmissing-prototypes -Wno-four-char-constants -Wno-unknown-pragmas"; + }; + dependencies = ( + ); + isa = PBXToolTarget; + name = mDNSResponder; + productInstallPath = "${HOME}/bin"; + productName = mDNSResponder; + productReference = 034768E2FF38A6DC11DB9C8B; + }; + 08FB77A0FE84155DC02AAC07 = { + buildActionMask = 2147483647; + files = ( + 6575FC02022EAFBA00000109, + F5E11B5D04A28126019798ED, + ); + isa = PBXHeadersBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + 08FB77A1FE84155DC02AAC07 = { + buildActionMask = 2147483647; + files = ( + 6575FC0D022EB18700000109, + 6575FC0E022EB18700000109, + 6575FBEA022EAF5A00000109, + 7F18A9F90587CEF6001880B3, + 7F18A9F80587CEF6001880B3, + 7F461DB6062DBF2900672BF3, + DBAAFE2D057E8F660085CAD0, + DBAAFE2A057E8F4D0085CAD0, + F525E72904AA167501F1CF4D, + F5E11B5C04A28126019798ED, + FFCB6D74075D539900B8AF62, + 6575FBED022EAF7200000109, + 7FC8F9D506D14E66007E879D, + 6575FBEE022EAF7200000109, + ); + isa = PBXSourcesBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + 08FB77A3FE84155DC02AAC07 = { + buildActionMask = 2147483647; + files = ( + 09AB6885FE841BABC02AAC07, + 65713D66025A293200000109, + 6585DD640279A3B7000001D1, + 7F869686066EE02400D2A2DC, + ); + isa = PBXFrameworksBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + 08FB77A5FE84155DC02AAC07 = { + buildActionMask = 2147483647; + files = ( + ); + isa = PBXRezBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; +//080 +//081 +//082 +//083 +//084 +//090 +//091 +//092 +//093 +//094 + 09AB6884FE841BABC02AAC07 = { + isa = PBXFrameworkReference; + name = CoreFoundation.framework; + path = /System/Library/Frameworks/CoreFoundation.framework; + refType = 0; + }; + 09AB6885FE841BABC02AAC07 = { + fileRef = 09AB6884FE841BABC02AAC07; + isa = PBXBuildFile; + settings = { + }; + }; +//090 +//091 +//092 +//093 +//094 +//190 +//191 +//192 +//193 +//194 + 19C28FBDFE9D53C911CA2CBB = { + children = ( + 034768E2FF38A6DC11DB9C8B, + 6575FC1D022EB76000000109, + 00AD62B8032D799A0CCA2C71, + DB2CC4670662DF5C00335AB3, + FFD41DDA0664157900F0C438, + FF25794406C9A70800376F7B, + FF1C919B07021C84001048AB, + FFFB0DAA07B43C9100B88D48, + FF2609E207B440DD00CE10E5, + ); + isa = PBXGroup; + name = Products; + refType = 4; + }; +//190 +//191 +//192 +//193 +//194 +//650 +//651 +//652 +//653 +//654 + 654BE64F02B63B93000001D1 = { + fileEncoding = 4; + isa = PBXFileReference; + name = mDNSEmbeddedAPI.h; + path = ../mDNSCore/mDNSEmbeddedAPI.h; + refType = 4; + }; + 654BE65002B63B93000001D1 = { + fileEncoding = 4; + isa = PBXFileReference; + name = mDNSDebug.h; + path = ../mDNSCore/mDNSDebug.h; + refType = 4; + }; + 65713D46025A293200000109 = { + isa = PBXFrameworkReference; + name = SystemConfiguration.framework; + path = /System/Library/Frameworks/SystemConfiguration.framework; + refType = 0; + }; + 65713D66025A293200000109 = { + fileRef = 65713D46025A293200000109; + isa = PBXBuildFile; + settings = { + }; + }; + 6575FBE9022EAF5A00000109 = { + fileEncoding = 4; + indentWidth = 4; + isa = PBXFileReference; + name = mDNS.c; + path = ../mDNSCore/mDNS.c; + refType = 4; + tabWidth = 4; + usesTabs = 1; + }; + 6575FBEA022EAF5A00000109 = { + fileRef = 6575FBE9022EAF5A00000109; + isa = PBXBuildFile; + settings = { + }; + }; + 6575FBEB022EAF7200000109 = { + fileEncoding = 4; + indentWidth = 4; + isa = PBXFileReference; + path = mDNSMacOSX.c; + refType = 4; + tabWidth = 4; + usesTabs = 1; + }; + 6575FBEC022EAF7200000109 = { + fileEncoding = 4; + indentWidth = 4; + isa = PBXFileReference; + path = daemon.c; + refType = 4; + tabWidth = 4; + usesTabs = 1; + }; + 6575FBED022EAF7200000109 = { + fileRef = 6575FBEB022EAF7200000109; + isa = PBXBuildFile; + settings = { + }; + }; + 6575FBEE022EAF7200000109 = { + fileRef = 6575FBEC022EAF7200000109; + isa = PBXBuildFile; + settings = { + }; + }; + 6575FBFE022EAFA800000109 = { + children = ( + 6575FBFF022EAFBA00000109, + 6575FC00022EAFBA00000109, + 6575FC01022EAFBA00000109, + ); + isa = PBXGroup; + name = "DNS Service Discovery MIG files"; + refType = 4; + }; + 6575FBFF022EAFBA00000109 = { + fileEncoding = 4; + isa = PBXFileReference; + path = DNSServiceDiscoveryDefines.h; + refType = 4; + }; + 6575FC00022EAFBA00000109 = { + fileEncoding = 4; + isa = PBXFileReference; + path = DNSServiceDiscoveryReply.defs; + refType = 4; + }; + 6575FC01022EAFBA00000109 = { + fileEncoding = 4; + isa = PBXFileReference; + path = DNSServiceDiscoveryRequest.defs; + refType = 4; + }; + 6575FC02022EAFBA00000109 = { + fileRef = 6575FBFF022EAFBA00000109; + isa = PBXBuildFile; + settings = { + }; + }; + 6575FC0D022EB18700000109 = { + fileRef = 6575FC00022EAFBA00000109; + isa = PBXBuildFile; + settings = { + ATTRIBUTES = ( + Client, + ); + }; + }; + 6575FC0E022EB18700000109 = { + fileRef = 6575FC01022EAFBA00000109; + isa = PBXBuildFile; + settings = { + ATTRIBUTES = ( + Server, + Client, + ); + }; + }; + 6575FC18022EB76000000109 = { + buildActionMask = 2147483647; + files = ( + ); + isa = PBXHeadersBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + 6575FC19022EB76000000109 = { + buildActionMask = 2147483647; + files = ( + 6575FC21022EB7AA00000109, + ); + isa = PBXSourcesBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + 6575FC1A022EB76000000109 = { + buildActionMask = 2147483647; + files = ( + 6575FC24022EBA5D00000109, + ); + isa = PBXFrameworksBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + 6575FC1B022EB76000000109 = { + buildActionMask = 2147483647; + files = ( + ); + isa = PBXRezBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + 6575FC1C022EB76000000109 = { + buildPhases = ( + 6575FC18022EB76000000109, + 6575FC19022EB76000000109, + 6575FC1A022EB76000000109, + 6575FC1B022EB76000000109, + FFF4F63C06CFE53300459EFD, + ); + buildSettings = { + GCC_TREAT_WARNINGS_AS_ERRORS = YES; + INSTALL_PATH = /usr/bin; + MACOSX_DEPLOYMENT_TARGET = 10.2; + OTHER_CFLAGS = "-no-cpp-precomp -mdynamic-no-pic"; + OTHER_LDFLAGS = ""; + OTHER_REZFLAGS = ""; + PRODUCT_NAME = mDNS; + REZ_EXECUTABLE = YES; + SECTORDER_FLAGS = ""; + STRIPFLAGS = "-S"; + WARNING_CFLAGS = "-W -Wall -Wmissing-prototypes -Wno-four-char-constants -Wno-unknown-pragmas"; + }; + dependencies = ( + ); + isa = PBXToolTarget; + name = "mDNS command-line tool"; + productInstallPath = /usr/bin; + productName = "mDNS command-line tool"; + productReference = 6575FC1D022EB76000000109; + }; + 6575FC1D022EB76000000109 = { + isa = PBXExecutableFileReference; + path = mDNS; + refType = 3; + }; + 6575FC1F022EB78C00000109 = { + children = ( + 6575FC20022EB7AA00000109, + FF1C919F07021E3F001048AB, + ); + isa = PBXGroup; + name = "Command-Line Clients"; + refType = 4; + }; + 6575FC20022EB7AA00000109 = { + fileEncoding = 4; + indentWidth = 4; + isa = PBXFileReference; + path = SamplemDNSClient.c; + refType = 2; + tabWidth = 4; + usesTabs = 0; + }; + 6575FC21022EB7AA00000109 = { + fileRef = 6575FC20022EB7AA00000109; + isa = PBXBuildFile; + settings = { + }; + }; + 6575FC24022EBA5D00000109 = { + fileRef = 09AB6884FE841BABC02AAC07; + isa = PBXBuildFile; + settings = { + }; + }; + 6585DD640279A3B7000001D1 = { + fileRef = 00CA213D02786FC30CCA2C71; + isa = PBXBuildFile; + settings = { + }; + }; +//650 +//651 +//652 +//653 +//654 +//7F0 +//7F1 +//7F2 +//7F3 +//7F4 + 7F18A9F60587CEF6001880B3 = { + fileEncoding = 4; + isa = PBXFileReference; + name = DNSCommon.c; + path = ../mDNSCore/DNSCommon.c; + refType = 2; + }; + 7F18A9F70587CEF6001880B3 = { + fileEncoding = 4; + isa = PBXFileReference; + name = uDNS.c; + path = ../mDNSCore/uDNS.c; + refType = 2; + }; + 7F18A9F80587CEF6001880B3 = { + fileRef = 7F18A9F60587CEF6001880B3; + isa = PBXBuildFile; + settings = { + }; + }; + 7F18A9F90587CEF6001880B3 = { + fileRef = 7F18A9F70587CEF6001880B3; + isa = PBXBuildFile; + settings = { + }; + }; + 7F18A9FA0587CEF6001880B3 = { + fileRef = 7F18A9F60587CEF6001880B3; + isa = PBXBuildFile; + settings = { + }; + }; + 7F18A9FB0587CEF6001880B3 = { + fileRef = 7F18A9F70587CEF6001880B3; + isa = PBXBuildFile; + settings = { + }; + }; + 7F461DB5062DBF2900672BF3 = { + fileEncoding = 4; + isa = PBXFileReference; + name = DNSDigest.c; + path = ../mDNSCore/DNSDigest.c; + refType = 2; + }; + 7F461DB6062DBF2900672BF3 = { + fileRef = 7F461DB5062DBF2900672BF3; + isa = PBXBuildFile; + settings = { + }; + }; + 7F461DB7062DBF2900672BF3 = { + fileRef = 7F461DB5062DBF2900672BF3; + isa = PBXBuildFile; + settings = { + }; + }; + 7F869685066EE02400D2A2DC = { + isa = PBXFrameworkReference; + name = Security.framework; + path = /System/Library/Frameworks/Security.framework; + refType = 0; + }; + 7F869686066EE02400D2A2DC = { + fileRef = 7F869685066EE02400D2A2DC; + isa = PBXBuildFile; + settings = { + }; + }; + 7F869687066EE02400D2A2DC = { + fileRef = 7F869685066EE02400D2A2DC; + isa = PBXBuildFile; + settings = { + }; + }; + 7FC8F9D406D14E66007E879D = { + fileEncoding = 4; + isa = PBXFileReference; + path = LegacyNATTraversal.c; + refType = 2; + }; + 7FC8F9D506D14E66007E879D = { + fileRef = 7FC8F9D406D14E66007E879D; + isa = PBXBuildFile; + settings = { + }; + }; + 7FC8F9D606D14E66007E879D = { + fileRef = 7FC8F9D406D14E66007E879D; + isa = PBXBuildFile; + settings = { + }; + }; +//7F0 +//7F1 +//7F2 +//7F3 +//7F4 +//DB0 +//DB1 +//DB2 +//DB3 +//DB4 + DB2CC4420662DCE500335AB3 = { + children = ( + DB2CC4430662DD1100335AB3, + DB2CC4440662DD1100335AB3, + DB2CC4450662DD1100335AB3, + DB2CC4460662DD1100335AB3, + DB2CC4470662DD1100335AB3, + DB2CC4480662DD1100335AB3, + DB2CC4490662DD1100335AB3, + DB2CC44A0662DD1100335AB3, + DB2CC44B0662DD1100335AB3, + DB2CC44C0662DD1100335AB3, + DB2CC44D0662DD1100335AB3, + DB2CC44E0662DD1100335AB3, + DB2CC44F0662DD1100335AB3, + FF2C5FB00A48B8680066DA11, + FF2C5FB20A48B86E0066DA11, + ); + isa = PBXGroup; + name = "Java Support"; + refType = 4; + }; + DB2CC4430662DD1100335AB3 = { + fileEncoding = 4; + isa = PBXFileReference; + name = BaseListener.java; + path = ../mDNSShared/Java/BaseListener.java; + refType = 2; + }; + DB2CC4440662DD1100335AB3 = { + fileEncoding = 4; + isa = PBXFileReference; + name = BrowseListener.java; + path = ../mDNSShared/Java/BrowseListener.java; + refType = 2; + }; + DB2CC4450662DD1100335AB3 = { + fileEncoding = 4; + isa = PBXFileReference; + name = DNSRecord.java; + path = ../mDNSShared/Java/DNSRecord.java; + refType = 2; + }; + DB2CC4460662DD1100335AB3 = { + fileEncoding = 4; + isa = PBXFileReference; + name = DNSSD.java; + path = ../mDNSShared/Java/DNSSD.java; + refType = 2; + }; + DB2CC4470662DD1100335AB3 = { + fileEncoding = 4; + isa = PBXFileReference; + name = DNSSDException.java; + path = ../mDNSShared/Java/DNSSDException.java; + refType = 2; + }; + DB2CC4480662DD1100335AB3 = { + fileEncoding = 4; + isa = PBXFileReference; + name = DNSSDRegistration.java; + path = ../mDNSShared/Java/DNSSDRegistration.java; + refType = 2; + }; + DB2CC4490662DD1100335AB3 = { + fileEncoding = 4; + isa = PBXFileReference; + name = DNSSDService.java; + path = ../mDNSShared/Java/DNSSDService.java; + refType = 2; + }; + DB2CC44A0662DD1100335AB3 = { + fileEncoding = 4; + isa = PBXFileReference; + name = DomainListener.java; + path = ../mDNSShared/Java/DomainListener.java; + refType = 2; + }; + DB2CC44B0662DD1100335AB3 = { + fileEncoding = 4; + isa = PBXFileReference; + name = JNISupport.c; + path = ../mDNSShared/Java/JNISupport.c; + refType = 2; + }; + DB2CC44C0662DD1100335AB3 = { + fileEncoding = 4; + isa = PBXFileReference; + name = QueryListener.java; + path = ../mDNSShared/Java/QueryListener.java; + refType = 2; + }; + DB2CC44D0662DD1100335AB3 = { + fileEncoding = 4; + isa = PBXFileReference; + name = RegisterListener.java; + path = ../mDNSShared/Java/RegisterListener.java; + refType = 2; + }; + DB2CC44E0662DD1100335AB3 = { + fileEncoding = 4; + isa = PBXFileReference; + name = ResolveListener.java; + path = ../mDNSShared/Java/ResolveListener.java; + refType = 2; + }; + DB2CC44F0662DD1100335AB3 = { + fileEncoding = 4; + isa = PBXFileReference; + name = TXTRecord.java; + path = ../mDNSShared/Java/TXTRecord.java; + refType = 2; + }; + DB2CC4500662DD6800335AB3 = { + buildActionMask = 2147483647; + files = ( + DB2CC4560662DE4500335AB3, + DB2CC4570662DE4600335AB3, + DB2CC4580662DE4700335AB3, + DB2CC4590662DE4700335AB3, + DB2CC45A0662DE4800335AB3, + DB2CC45B0662DE4900335AB3, + DB2CC45C0662DE4900335AB3, + DB2CC45D0662DE4A00335AB3, + DB2CC45E0662DE4B00335AB3, + DB2CC45F0662DE4C00335AB3, + DB2CC4600662DE4C00335AB3, + DB2CC4610662DE4D00335AB3, + FF2C5FB10A48B8680066DA11, + FF2C5FB30A48B86E0066DA11, + ); + isa = PBXSourcesBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + DB2CC4510662DD6800335AB3 = { + buildActionMask = 2147483647; + files = ( + ); + isa = PBXJavaArchiveBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + DB2CC4520662DD6800335AB3 = { + buildActionMask = 2147483647; + files = ( + ); + isa = PBXFrameworksBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + DB2CC4530662DD6800335AB3 = { + buildPhases = ( + DB2CC4500662DD6800335AB3, + DB2CC4510662DD6800335AB3, + DB2CC4520662DD6800335AB3, + DB2CC4550662DE1700335AB3, + FFD41DDD06641B4200F0C438, + ); + buildSettings = { + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + INSTALL_PATH = "${SYSTEM_LIBRARY_DIR}/Java/Extensions"; + JAVA_ARCHIVE_CLASSES = YES; + JAVA_ARCHIVE_COMPRESSION = YES; + JAVA_ARCHIVE_TYPE = JAR; + JAVA_COMPILER_DEBUGGING_SYMBOLS = NO; + JAVA_COMPILER_SOURCE_VERSION = 1.4; + JAVA_COMPILER_TARGET_VM_VERSION = 1.4; + JAVA_SOURCE_SUBDIR = .; + LIBRARY_STYLE = STATIC; + MACOSX_DEPLOYMENT_TARGET = 10.2; + OTHER_CFLAGS = ""; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOL_FLAGS = ""; + OTHER_REZFLAGS = ""; + PRODUCT_NAME = dns_sd; + PURE_JAVA = YES; + REZ_EXECUTABLE = YES; + SECTORDER_FLAGS = ""; + STRIPFLAGS = "-S"; + WARNING_CFLAGS = "-Wmost -Wno-four-char-constants -Wno-unknown-pragmas"; + }; + comments = "Multiplatform .jar file that implements Java interface to DNS-SD"; + dependencies = ( + ); + isa = PBXLibraryTarget; + name = dns_sd.jar; + productInstallPath = /System/Library/Java/Extensions; + productName = dns_sd.jar; + productReference = FFD41DDA0664157900F0C438; + }; + DB2CC4550662DE1700335AB3 = { + buildActionMask = 12; + files = ( + ); + generatedFileNames = ( + ); + isa = PBXShellScriptBuildPhase; + neededFileNames = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "javah -force -J-Xbootclasspath/p:${CONFIGURATION_TEMP_DIR}/dns_sd.jar.build/JavaClasses -o ${CONFIGURATION_TEMP_DIR}/dns_sd.jar.build/DNSSD.java.h com.apple.dnssd.AppleDNSSD com.apple.dnssd.AppleBrowser com.apple.dnssd.AppleResolver com.apple.dnssd.AppleRegistration com.apple.dnssd.AppleQuery com.apple.dnssd.AppleDomainEnum com.apple.dnssd.AppleService"; + }; + DB2CC4560662DE4500335AB3 = { + fileRef = DB2CC4430662DD1100335AB3; + isa = PBXBuildFile; + settings = { + }; + }; + DB2CC4570662DE4600335AB3 = { + fileRef = DB2CC4440662DD1100335AB3; + isa = PBXBuildFile; + settings = { + }; + }; + DB2CC4580662DE4700335AB3 = { + fileRef = DB2CC4450662DD1100335AB3; + isa = PBXBuildFile; + settings = { + }; + }; + DB2CC4590662DE4700335AB3 = { + fileRef = DB2CC4460662DD1100335AB3; + isa = PBXBuildFile; + settings = { + }; + }; + DB2CC45A0662DE4800335AB3 = { + fileRef = DB2CC4470662DD1100335AB3; + isa = PBXBuildFile; + settings = { + }; + }; + DB2CC45B0662DE4900335AB3 = { + fileRef = DB2CC4480662DD1100335AB3; + isa = PBXBuildFile; + settings = { + }; + }; + DB2CC45C0662DE4900335AB3 = { + fileRef = DB2CC4490662DD1100335AB3; + isa = PBXBuildFile; + settings = { + }; + }; + DB2CC45D0662DE4A00335AB3 = { + fileRef = DB2CC44A0662DD1100335AB3; + isa = PBXBuildFile; + settings = { + }; + }; + DB2CC45E0662DE4B00335AB3 = { + fileRef = DB2CC44C0662DD1100335AB3; + isa = PBXBuildFile; + settings = { + }; + }; + DB2CC45F0662DE4C00335AB3 = { + fileRef = DB2CC44D0662DD1100335AB3; + isa = PBXBuildFile; + settings = { + }; + }; + DB2CC4600662DE4C00335AB3 = { + fileRef = DB2CC44E0662DD1100335AB3; + isa = PBXBuildFile; + settings = { + }; + }; + DB2CC4610662DE4D00335AB3 = { + fileRef = DB2CC44F0662DD1100335AB3; + isa = PBXBuildFile; + settings = { + }; + }; + DB2CC4620662DF5C00335AB3 = { + buildActionMask = 2147483647; + files = ( + ); + isa = PBXHeadersBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + DB2CC4630662DF5C00335AB3 = { + buildActionMask = 2147483647; + files = ( + DB2CC46A0662E00700335AB3, + ); + isa = PBXSourcesBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + DB2CC4640662DF5C00335AB3 = { + buildActionMask = 2147483647; + files = ( + DB2CC4690662DFF500335AB3, + ); + isa = PBXFrameworksBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + DB2CC4650662DF5C00335AB3 = { + buildActionMask = 2147483647; + files = ( + ); + isa = PBXRezBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + DB2CC4660662DF5C00335AB3 = { + buildPhases = ( + DB2CC4620662DF5C00335AB3, + DB2CC4630662DF5C00335AB3, + DB2CC4640662DF5C00335AB3, + DB2CC4650662DF5C00335AB3, + ); + buildSettings = { + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + HEADER_SEARCH_PATHS = "../mDNSShared \"${SYSTEM_LIBRARY_DIR}/Frameworks/JavaVM.framework/Versions/A/Headers\" \"${SYSTEM_LIBRARY_DIR}/Frameworks/JavaVM.framework/Versions/1.3.1/Headers\" \"${CONFIGURATION_TEMP_DIR}/dns_sd.jar.build\""; + INSTALL_PATH = /usr/lib/java; + LIBRARY_STYLE = DYNAMIC; + MACOSX_DEPLOYMENT_TARGET = 10.2; + OTHER_CFLAGS = ""; + OTHER_LIBTOOL_FLAGS = ""; + OTHER_REZFLAGS = ""; + PRODUCT_NAME = libjdns_sd.jnilib; + REZ_EXECUTABLE = YES; + SECTORDER_FLAGS = ""; + STRIPFLAGS = "-S"; + WARNING_CFLAGS = "-Wmost -Wno-four-char-constants -Wno-unknown-pragmas"; + }; + comments = "Platform-specific JNI library that bridges dns_sd.jar to <dns_sd.h>."; + dependencies = ( + FFD41DDF06641BBB00F0C438, + ); + isa = PBXLibraryTarget; + name = libjdns_sd.jnilib; + productInstallPath = /usr/lib/java; + productName = libjdns_sd.jnilib; + productReference = DB2CC4670662DF5C00335AB3; + }; + DB2CC4670662DF5C00335AB3 = { + isa = PBXLibraryReference; + path = libjdns_sd.jnilib; + refType = 3; + }; + DB2CC4680662DFF500335AB3 = { + isa = PBXFrameworkReference; + name = JavaVM.framework; + path = /System/Library/Frameworks/JavaVM.framework; + refType = 0; + }; + DB2CC4690662DFF500335AB3 = { + fileRef = DB2CC4680662DFF500335AB3; + isa = PBXBuildFile; + settings = { + }; + }; + DB2CC46A0662E00700335AB3 = { + fileRef = DB2CC44B0662DD1100335AB3; + isa = PBXBuildFile; + settings = { + }; + }; + DBAAFE29057E8F4D0085CAD0 = { + fileEncoding = 4; + isa = PBXFileReference; + name = mDNSDebug.c; + path = ../mDNSShared/mDNSDebug.c; + refType = 2; + }; + DBAAFE2A057E8F4D0085CAD0 = { + fileRef = DBAAFE29057E8F4D0085CAD0; + isa = PBXBuildFile; + settings = { + }; + }; + DBAAFE2B057E8F4D0085CAD0 = { + fileRef = DBAAFE29057E8F4D0085CAD0; + isa = PBXBuildFile; + settings = { + }; + }; + DBAAFE2C057E8F660085CAD0 = { + fileEncoding = 4; + isa = PBXFileReference; + name = GenLinkedList.c; + path = ../mDNSShared/GenLinkedList.c; + refType = 2; + }; + DBAAFE2D057E8F660085CAD0 = { + fileRef = DBAAFE2C057E8F660085CAD0; + isa = PBXBuildFile; + settings = { + }; + }; + DBAAFE2E057E8F660085CAD0 = { + fileRef = DBAAFE2C057E8F660085CAD0; + isa = PBXBuildFile; + settings = { + }; + }; +//DB0 +//DB1 +//DB2 +//DB3 +//DB4 +//F50 +//F51 +//F52 +//F53 +//F54 + F515E29604A37BB701CA296C = { + fileRef = 654BE64F02B63B93000001D1; + isa = PBXBuildFile; + settings = { + }; + }; + F515E29704A37BB801CA296C = { + fileRef = 654BE65002B63B93000001D1; + isa = PBXBuildFile; + settings = { + }; + }; + F515E29904A37BBB01CA296C = { + fileRef = 000753D303367C1C0CCA2C71; + isa = PBXBuildFile; + settings = { + }; + }; + F525E72804AA167501F1CF4D = { + fileEncoding = 4; + isa = PBXFileReference; + name = uds_daemon.c; + path = ../mDNSShared/uds_daemon.c; + refType = 2; + }; + F525E72904AA167501F1CF4D = { + fileRef = F525E72804AA167501F1CF4D; + isa = PBXBuildFile; + settings = { + }; + }; + F525E72B04AA167A01F1CF4D = { + fileRef = F525E72804AA167501F1CF4D; + isa = PBXBuildFile; + settings = { + }; + }; + F5E11B5A04A28126019798ED = { + fileEncoding = 4; + isa = PBXFileReference; + name = dnssd_ipc.c; + path = ../mDNSShared/dnssd_ipc.c; + refType = 2; + }; + F5E11B5B04A28126019798ED = { + fileEncoding = 4; + isa = PBXFileReference; + name = dnssd_ipc.h; + path = ../mDNSShared/dnssd_ipc.h; + refType = 2; + }; + F5E11B5C04A28126019798ED = { + fileRef = F5E11B5A04A28126019798ED; + isa = PBXBuildFile; + settings = { + }; + }; + F5E11B5D04A28126019798ED = { + fileRef = F5E11B5B04A28126019798ED; + isa = PBXBuildFile; + settings = { + }; + }; + F5E11B5E04A28126019798ED = { + fileRef = F5E11B5A04A28126019798ED; + isa = PBXBuildFile; + settings = { + }; + }; + F5E11B5F04A28126019798ED = { + fileRef = F5E11B5B04A28126019798ED; + isa = PBXBuildFile; + settings = { + }; + }; +//F50 +//F51 +//F52 +//F53 +//F54 +//FF0 +//FF1 +//FF2 +//FF3 +//FF4 + FF08480607CEB8E800AE6769 = { + isa = PBXFileReference; + name = inprogress.tiff; + path = PreferencePane/Artwork/inprogress.tiff; + refType = 2; + }; + FF08480707CEB8E800AE6769 = { + fileRef = FF08480607CEB8E800AE6769; + isa = PBXBuildFile; + settings = { + }; + }; + FF0E0B5D065ADC7600FE4D9C = { + fileEncoding = 4; + isa = PBXFileReference; + name = mDNS.1; + path = ../mDNSShared/mDNS.1; + refType = 2; + }; + FF13FFE90A5DA40200897C81 = { + fileRef = 6575FBEB022EAF7200000109; + isa = PBXBuildFile; + settings = { + }; + }; + FF13FFEA0A5DA44A00897C81 = { + fileEncoding = 4; + isa = PBXFileReference; + name = dnsextd_lexer.l; + path = ../mDNSShared/dnsextd_lexer.l; + refType = 2; + }; + FF13FFEB0A5DA44A00897C81 = { + fileRef = FF13FFEA0A5DA44A00897C81; + isa = PBXBuildFile; + settings = { + }; + }; + FF13FFEC0A5DA45500897C81 = { + fileEncoding = 4; + isa = PBXFileReference; + name = dnsextd_parser.y; + path = ../mDNSShared/dnsextd_parser.y; + refType = 2; + }; + FF13FFED0A5DA45500897C81 = { + fileRef = FF13FFEC0A5DA45500897C81; + isa = PBXBuildFile; + settings = { + }; + }; + FF13FFEE0A5DA52700897C81 = { + isa = PBXTargetDependency; + target = 08FB779FFE84155DC02AAC07; + }; + FF13FFEF0A5DA6FD00897C81 = { + fileRef = FFCB6D73075D539900B8AF62; + isa = PBXBuildFile; + settings = { + }; + }; + FF16238F07023BD2001AB7D7 = { + isa = PBXTargetDependency; + target = FF1C919207021C84001048AB; + }; + FF1C919207021C84001048AB = { + buildPhases = ( + FF1C919307021C84001048AB, + FF1C919407021C84001048AB, + FF1C919607021C84001048AB, + FF1C919807021C84001048AB, + FF1C919907021C84001048AB, + ); + buildSettings = { + GCC_TREAT_WARNINGS_AS_ERRORS = YES; + INSTALL_PATH = /usr/bin; + MACOSX_DEPLOYMENT_TARGET = 10.2; + OTHER_CFLAGS = "-no-cpp-precomp -mdynamic-no-pic -I../mDNSShared"; + OTHER_LDFLAGS = ""; + OTHER_REZFLAGS = ""; + PRODUCT_NAME = "dns-sd"; + REZ_EXECUTABLE = YES; + SECTORDER_FLAGS = ""; + STRIPFLAGS = "-S"; + WARNING_CFLAGS = "-W -Wall -Wmissing-prototypes -Wno-four-char-constants -Wno-unknown-pragmas"; + }; + dependencies = ( + ); + isa = PBXToolTarget; + name = "dns-sd command-line tool"; + productInstallPath = /usr/bin; + productName = "dns-sd command-line tool"; + productReference = FF1C919B07021C84001048AB; + }; + FF1C919307021C84001048AB = { + buildActionMask = 2147483647; + files = ( + ); + isa = PBXHeadersBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + FF1C919407021C84001048AB = { + buildActionMask = 2147483647; + files = ( + FF1C91A007021E40001048AB, + ); + isa = PBXSourcesBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + FF1C919607021C84001048AB = { + buildActionMask = 2147483647; + files = ( + ); + isa = PBXFrameworksBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + FF1C919807021C84001048AB = { + buildActionMask = 2147483647; + files = ( + ); + isa = PBXRezBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + FF1C919907021C84001048AB = { + buildActionMask = 8; + dstPath = /usr/share/man/man1; + dstSubfolderSpec = 0; + files = ( + FF1C919E07021D78001048AB, + ); + isa = PBXCopyFilesBuildPhase; + runOnlyForDeploymentPostprocessing = 1; + }; + FF1C919B07021C84001048AB = { + isa = PBXExecutableFileReference; + path = "dns-sd"; + refType = 3; + }; + FF1C919D07021D77001048AB = { + fileEncoding = 4; + isa = PBXFileReference; + name = "dns-sd.1"; + path = "../mDNSShared/dns-sd.1"; + refType = 2; + }; + FF1C919E07021D78001048AB = { + fileRef = FF1C919D07021D77001048AB; + isa = PBXBuildFile; + settings = { + }; + }; + FF1C919F07021E3F001048AB = { + fileEncoding = 4; + isa = PBXFileReference; + name = "dns-sd.c"; + path = "../Clients/dns-sd.c"; + refType = 2; + }; + FF1C91A007021E40001048AB = { + fileRef = FF1C919F07021E3F001048AB; + isa = PBXBuildFile; + settings = { + }; + }; + FF25792906C9A70800376F7B = { + buildPhases = ( + FF25792A06C9A70800376F7B, + FF25792D06C9A70800376F7B, + FF25793A06C9A70800376F7B, + FF25793F06C9A70800376F7B, + FF25794006C9A70800376F7B, + ); + buildSettings = { + FRAMEWORK_SEARCH_PATHS = ""; + GCC_TREAT_WARNINGS_AS_ERRORS = YES; + HEADER_SEARCH_PATHS = "\"${APPLE_INTERNAL_DEVELOPER_DIR}/Headers\" \"${CONFIGURATION_TEMP_DIR}\""; + INSTALL_PATH = /usr/sbin; + LEX = /usr/bin/flex; + LEXFLAGS = "-i"; + LIBRARY_SEARCH_PATHS = "\"${CONFIGURATION_TEMP_DIR}\""; + MACOSX_DEPLOYMENT_TARGET = 10.2; + OTHER_CFLAGS = "-no-cpp-precomp -mdynamic-no-pic"; + OTHER_LDFLAGS = "-ldnsinfo"; + OTHER_REZFLAGS = ""; + PRODUCT_NAME = dnsextd; + REZ_EXECUTABLE = YES; + SECTORDER_FLAGS = ""; + STRIPFLAGS = "-S"; + WARNING_CFLAGS = "-W -Wall -Wmissing-prototypes -Wno-four-char-constants -Wno-unknown-pragmas"; + YACC = "/usr/bin/bison -y"; + }; + dependencies = ( + FF13FFEE0A5DA52700897C81, + ); + isa = PBXToolTarget; + name = dnsextd; + productInstallPath = /usr/sbin; + productName = mDNSResponder; + productReference = FF25794406C9A70800376F7B; + }; + FF25792A06C9A70800376F7B = { + buildActionMask = 2147483647; + files = ( + FF25792B06C9A70800376F7B, + ); + isa = PBXHeadersBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + FF25792B06C9A70800376F7B = { + fileRef = 6575FBFF022EAFBA00000109; + isa = PBXBuildFile; + settings = { + }; + }; + FF25792D06C9A70800376F7B = { + buildActionMask = 2147483647; + files = ( + FF25793606C9A70800376F7B, + FF25793806C9A70800376F7B, + FF25794706C9A8BF00376F7B, + FF25794A06C9A98700376F7B, + FF25794E06C9AA3000376F7B, + FF13FFE90A5DA40200897C81, + FF13FFEB0A5DA44A00897C81, + FF13FFED0A5DA45500897C81, + FF13FFEF0A5DA6FD00897C81, + ); + isa = PBXSourcesBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + FF25793606C9A70800376F7B = { + fileRef = 7F18A9F60587CEF6001880B3; + isa = PBXBuildFile; + settings = { + }; + }; + FF25793806C9A70800376F7B = { + fileRef = 7F461DB5062DBF2900672BF3; + isa = PBXBuildFile; + settings = { + }; + }; + FF25793A06C9A70800376F7B = { + buildActionMask = 2147483647; + files = ( + FF25793B06C9A70800376F7B, + FF25793C06C9A70800376F7B, + FF25793D06C9A70800376F7B, + FF25793E06C9A70800376F7B, + ); + isa = PBXFrameworksBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + FF25793B06C9A70800376F7B = { + fileRef = 09AB6884FE841BABC02AAC07; + isa = PBXBuildFile; + settings = { + }; + }; + FF25793C06C9A70800376F7B = { + fileRef = 65713D46025A293200000109; + isa = PBXBuildFile; + settings = { + }; + }; + FF25793D06C9A70800376F7B = { + fileRef = 00CA213D02786FC30CCA2C71; + isa = PBXBuildFile; + settings = { + }; + }; + FF25793E06C9A70800376F7B = { + fileRef = 7F869685066EE02400D2A2DC; + isa = PBXBuildFile; + settings = { + }; + }; + FF25793F06C9A70800376F7B = { + buildActionMask = 2147483647; + files = ( + ); + isa = PBXRezBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + FF25794006C9A70800376F7B = { + buildActionMask = 8; + dstPath = /usr/share/man/man8; + dstSubfolderSpec = 0; + files = ( + FFF4F63B06CFE4DD00459EFD, + ); + isa = PBXCopyFilesBuildPhase; + runOnlyForDeploymentPostprocessing = 1; + }; + FF25794406C9A70800376F7B = { + isa = PBXExecutableFileReference; + path = dnsextd; + refType = 3; + }; + FF25794606C9A8BF00376F7B = { + fileEncoding = 4; + isa = PBXFileReference; + name = dnsextd.c; + path = ../mDNSShared/dnsextd.c; + refType = 2; + }; + FF25794706C9A8BF00376F7B = { + fileRef = FF25794606C9A8BF00376F7B; + isa = PBXBuildFile; + settings = { + }; + }; + FF25794A06C9A98700376F7B = { + fileRef = DBAAFE29057E8F4D0085CAD0; + isa = PBXBuildFile; + settings = { + }; + }; + FF25794E06C9AA3000376F7B = { + fileRef = DBAAFE2C057E8F660085CAD0; + isa = PBXBuildFile; + settings = { + }; + }; + FF25795106C9AB1D00376F7B = { + isa = PBXTargetDependency; + target = FF25792906C9A70800376F7B; + }; + FF2609DC07B440DD00CE10E5 = { + buildActionMask = 2147483647; + files = ( + ); + isa = PBXHeadersBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + FF2609DD07B440DD00CE10E5 = { + buildActionMask = 2147483647; + files = ( + FF260A2B07B4464B00CE10E5, + FF260A2C07B4464B00CE10E5, + FF260A2D07B4464B00CE10E5, + FF260A2E07B4464B00CE10E5, + FF260A2F07B4464B00CE10E5, + FF260A3007B4464B00CE10E5, + FF260A3107B4464B00CE10E5, + FF260A3407B4466900CE10E5, + FF260A3507B4466900CE10E5, + FF260A4A07B4475600CE10E5, + FF260A4D07B4477F00CE10E5, + FF2A870607B447EF00B14068, + FF08480707CEB8E800AE6769, + FF354EB208516C63007C00E1, + ); + isa = PBXResourcesBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + FF2609DE07B440DD00CE10E5 = { + buildActionMask = 2147483647; + files = ( + FF2609E407B441D400CE10E5, + FF2609E507B441D700CE10E5, + FF2609E607B441DB00CE10E5, + ); + isa = PBXSourcesBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + FF2609DF07B440DD00CE10E5 = { + buildActionMask = 2147483647; + files = ( + FF2609F607B442BA00CE10E5, + FF2609F707B442C000CE10E5, + FF2609FB07B4433800CE10E5, + FF260A2007B4436900CE10E5, + FF260A2107B443B500CE10E5, + ); + isa = PBXFrameworksBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + FF2609E007B440DD00CE10E5 = { + buildActionMask = 2147483647; + files = ( + ); + isa = PBXRezBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + FF2609E107B440DD00CE10E5 = { + buildPhases = ( + FF2609DC07B440DD00CE10E5, + FF2609DD07B440DD00CE10E5, + FF2609DE07B440DD00CE10E5, + FF2609DF07B440DD00CE10E5, + FF2609E007B440DD00CE10E5, + ); + buildSettings = { + EXPORTED_SYMBOLS_FILE = ""; + INSTALL_PATH = "${SYSTEM_LIBRARY_DIR}/PreferencePanes"; + MACOSX_DEPLOYMENT_TARGET = 10.2; + OTHER_CFLAGS = ""; + OTHER_LDFLAGS = "-twolevel_namespace"; + OTHER_REZFLAGS = ""; + PREBINDING = NO; + PRODUCT_NAME = Bonjour; + SECTORDER_FLAGS = ""; + STRIPFLAGS = "-S"; + WARNING_CFLAGS = "-Wmost -Wno-four-char-constants -Wno-unknown-pragmas"; + WRAPPER_EXTENSION = prefPane; + }; + dependencies = ( + FF2609E307B440EC00CE10E5, + ); + isa = PBXBundleTarget; + name = PreferencePane; + productInstallPath = "${SYSTEM_LIBRARY_DIR}/PreferencePanes"; + productName = PreferencePane; + productReference = FF2609E207B440DD00CE10E5; + productSettingsXML = "<?xml version=\"1.0\" encoding=\"UTF-8\"?> +<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\"> +<plist version=\"1.0\"> +<dict> + <key>CFBundleDevelopmentRegion</key> + <string>English</string> + <key>CFBundleExecutable</key> + <string>Bonjour</string> + <key>CFBundleGetInfoString</key> + <string></string> + <key>CFBundleIconFile</key> + <string>BonjourPref</string> + <key>CFBundleIdentifier</key> + <string>com.apple.preference.bonjour</string> + <key>CFBundleInfoDictionaryVersion</key> + <string>6.0</string> + <key>CFBundleName</key> + <string></string> + <key>CFBundlePackageType</key> + <string>BNDL</string> + <key>CFBundleShortVersionString</key> + <string></string> + <key>CFBundleSignature</key> + <string>????</string> + <key>CFBundleVersion</key> + <string>1.0</string> + <key>NSMainNibFile</key> + <string>DNSServiceDiscoveryPref</string> + <key>NSPrefPaneIconFile</key> + <string>BonjourPref.tiff</string> + <key>NSPrefPaneIconLabel</key> + <string>Bonjour</string> + <key>NSPrincipalClass</key> + <string>DNSServiceDiscoveryPref</string> +</dict> +</plist> +"; + }; + FF2609E207B440DD00CE10E5 = { + isa = PBXBundleReference; + path = Bonjour.prefPane; + refType = 3; + }; + FF2609E307B440EC00CE10E5 = { + isa = PBXTargetDependency; + target = FFFB0DA907B43C9100B88D48; + }; + FF2609E407B441D400CE10E5 = { + fileRef = FFFB0DAC07B43CBA00B88D48; + isa = PBXBuildFile; + settings = { + }; + }; + FF2609E507B441D700CE10E5 = { + fileRef = FFFB0DAD07B43CBA00B88D48; + isa = PBXBuildFile; + settings = { + }; + }; + FF2609E607B441DB00CE10E5 = { + fileRef = FFFB0DAE07B43CBA00B88D48; + isa = PBXBuildFile; + settings = { + }; + }; + FF2609F607B442BA00CE10E5 = { + fileRef = 65713D46025A293200000109; + isa = PBXBuildFile; + settings = { + }; + }; + FF2609F707B442C000CE10E5 = { + fileRef = 7F869685066EE02400D2A2DC; + isa = PBXBuildFile; + settings = { + }; + }; + FF2609FA07B4433800CE10E5 = { + isa = PBXFrameworkReference; + name = Cocoa.framework; + path = /System/Library/Frameworks/Cocoa.framework; + refType = 0; + }; + FF2609FB07B4433800CE10E5 = { + fileRef = FF2609FA07B4433800CE10E5; + isa = PBXBuildFile; + settings = { + }; + }; + FF260A1F07B4436900CE10E5 = { + isa = PBXFrameworkReference; + name = PreferencePanes.framework; + path = /System/Library/Frameworks/PreferencePanes.framework; + refType = 0; + }; + FF260A2007B4436900CE10E5 = { + fileRef = FF260A1F07B4436900CE10E5; + isa = PBXBuildFile; + settings = { + }; + }; + FF260A2107B443B500CE10E5 = { + fileRef = 09AB6884FE841BABC02AAC07; + isa = PBXBuildFile; + settings = { + }; + }; + FF260A2207B443C500CE10E5 = { + fileRef = 09AB6884FE841BABC02AAC07; + isa = PBXBuildFile; + settings = { + }; + }; + FF260A2307B4463400CE10E5 = { + children = ( + FF260A2407B4464B00CE10E5, + FF260A2507B4464B00CE10E5, + FF260A2607B4464B00CE10E5, + FF260A2707B4464B00CE10E5, + FF260A2907B4464B00CE10E5, + FF260A2807B4464B00CE10E5, + FF08480607CEB8E800AE6769, + FF260A2A07B4464B00CE10E5, + FF260A3207B4466900CE10E5, + FF260A3307B4466900CE10E5, + FF354EB108516C63007C00E1, + FF260A4807B4475600CE10E5, + FF260A4B07B4477F00CE10E5, + ); + isa = PBXGroup; + name = Resources; + refType = 4; + }; + FF260A2407B4464B00CE10E5 = { + isa = PBXFileReference; + name = remove_idle.tiff; + path = PreferencePane/Artwork/remove_idle.tiff; + refType = 2; + }; + FF260A2507B4464B00CE10E5 = { + isa = PBXFileReference; + name = add_pressed.tiff; + path = PreferencePane/Artwork/add_pressed.tiff; + refType = 2; + }; + FF260A2607B4464B00CE10E5 = { + isa = PBXFileReference; + name = remove_disabled.tiff; + path = PreferencePane/Artwork/remove_disabled.tiff; + refType = 2; + }; + FF260A2707B4464B00CE10E5 = { + isa = PBXFileReference; + name = add_idle.tiff; + path = PreferencePane/Artwork/add_idle.tiff; + refType = 2; + }; + FF260A2807B4464B00CE10E5 = { + isa = PBXFileReference; + name = success.tiff; + path = PreferencePane/Artwork/success.tiff; + refType = 2; + }; + FF260A2907B4464B00CE10E5 = { + isa = PBXFileReference; + name = remove_pressed.tiff; + path = PreferencePane/Artwork/remove_pressed.tiff; + refType = 2; + }; + FF260A2A07B4464B00CE10E5 = { + isa = PBXFileReference; + name = failure.tiff; + path = PreferencePane/Artwork/failure.tiff; + refType = 2; + }; + FF260A2B07B4464B00CE10E5 = { + fileRef = FF260A2407B4464B00CE10E5; + isa = PBXBuildFile; + settings = { + }; + }; + FF260A2C07B4464B00CE10E5 = { + fileRef = FF260A2507B4464B00CE10E5; + isa = PBXBuildFile; + settings = { + }; + }; + FF260A2D07B4464B00CE10E5 = { + fileRef = FF260A2607B4464B00CE10E5; + isa = PBXBuildFile; + settings = { + }; + }; + FF260A2E07B4464B00CE10E5 = { + fileRef = FF260A2707B4464B00CE10E5; + isa = PBXBuildFile; + settings = { + }; + }; + FF260A2F07B4464B00CE10E5 = { + fileRef = FF260A2807B4464B00CE10E5; + isa = PBXBuildFile; + settings = { + }; + }; + FF260A3007B4464B00CE10E5 = { + fileRef = FF260A2907B4464B00CE10E5; + isa = PBXBuildFile; + settings = { + }; + }; + FF260A3107B4464B00CE10E5 = { + fileRef = FF260A2A07B4464B00CE10E5; + isa = PBXBuildFile; + settings = { + }; + }; + FF260A3207B4466900CE10E5 = { + isa = PBXFileReference; + name = BonjourPref.icns; + path = PreferencePane/BonjourPref.icns; + refType = 2; + }; + FF260A3307B4466900CE10E5 = { + isa = PBXFileReference; + name = BonjourPref.tiff; + path = PreferencePane/BonjourPref.tiff; + refType = 2; + }; + FF260A3407B4466900CE10E5 = { + fileRef = FF260A3207B4466900CE10E5; + isa = PBXBuildFile; + settings = { + }; + }; + FF260A3507B4466900CE10E5 = { + fileRef = FF260A3307B4466900CE10E5; + isa = PBXBuildFile; + settings = { + }; + }; + FF260A4807B4475600CE10E5 = { + children = ( + FF260A4907B4475600CE10E5, + ); + isa = PBXVariantGroup; + name = DNSServiceDiscoveryPref.nib; + path = PreferencePane; + refType = 2; + }; + FF260A4907B4475600CE10E5 = { + isa = PBXFileReference; + name = English; + path = PreferencePane/English.lproj/DNSServiceDiscoveryPref.nib; + refType = 2; + }; + FF260A4A07B4475600CE10E5 = { + fileRef = FF260A4807B4475600CE10E5; + isa = PBXBuildFile; + settings = { + }; + }; + FF260A4B07B4477F00CE10E5 = { + children = ( + FF260A4C07B4477F00CE10E5, + ); + isa = PBXVariantGroup; + name = InfoPlist.strings; + path = PreferencePane; + refType = 2; + }; + FF260A4C07B4477F00CE10E5 = { + fileEncoding = 4; + isa = PBXFileReference; + name = English; + path = PreferencePane/English.lproj/InfoPlist.strings; + refType = 2; + }; + FF260A4D07B4477F00CE10E5 = { + fileRef = FF260A4B07B4477F00CE10E5; + isa = PBXBuildFile; + settings = { + }; + }; + FF2A870607B447EF00B14068 = { + fileRef = FFFB0DAA07B43C9100B88D48; + isa = PBXBuildFile; + settings = { + }; + }; + FF2A870707B4481500B14068 = { + isa = PBXTargetDependency; + target = FF2609E107B440DD00CE10E5; + }; + FF2C5FB00A48B8680066DA11 = { + fileEncoding = 4; + isa = PBXFileReference; + name = DNSSDRecordRegistrar.java; + path = ../mDNSShared/Java/DNSSDRecordRegistrar.java; + refType = 2; + }; + FF2C5FB10A48B8680066DA11 = { + fileRef = FF2C5FB00A48B8680066DA11; + isa = PBXBuildFile; + settings = { + }; + }; + FF2C5FB20A48B86E0066DA11 = { + fileEncoding = 4; + isa = PBXFileReference; + name = RegisterRecordListener.java; + path = ../mDNSShared/Java/RegisterRecordListener.java; + refType = 2; + }; + FF2C5FB30A48B86E0066DA11 = { + fileRef = FF2C5FB20A48B86E0066DA11; + isa = PBXBuildFile; + settings = { + }; + }; + FF354EB108516C63007C00E1 = { + fileEncoding = 4; + isa = PBXExecutableFileReference; + name = installtool; + path = PreferencePane/installtool; + refType = 2; + }; + FF354EB208516C63007C00E1 = { + fileRef = FF354EB108516C63007C00E1; + isa = PBXBuildFile; + settings = { + }; + }; + FF37BE9207614059003C0420 = { + buildActionMask = 2147483647; + files = ( + ); + generatedFileNames = ( + ); + isa = PBXShellScriptBuildPhase; + neededFileNames = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "if [ -e /usr/local/include/dnsinfo.h ]\nthen\nrm -f \"${CONFIGURATION_TEMP_DIR}/dnsinfo.h\"\nrm -f \"${CONFIGURATION_TEMP_DIR}/libdnsinfo.a\"\nelse\necho \"#define MDNS_NO_DNSINFO 1\" > ${CONFIGURATION_TEMP_DIR}/dnsinfo.h\ntouch ${CONFIGURATION_TEMP_DIR}/empty.c\ncc ${CONFIGURATION_TEMP_DIR}/empty.c -c -o \"${CONFIGURATION_TEMP_DIR}/libdnsinfo.a\"\nrm -f ${CONFIGURATION_TEMP_DIR}/empty.c\nfi"; + }; + FF485D5105632E0000130380 = { + fileEncoding = 4; + isa = PBXFileReference; + name = mDNSResponder.8; + path = ../mDNSShared/mDNSResponder.8; + refType = 2; + }; + FF5585E507790732008D1C14 = { + buildActionMask = 8; + files = ( + ); + generatedFileNames = ( + ); + isa = PBXShellScriptBuildPhase; + neededFileNames = ( + ); + runOnlyForDeploymentPostprocessing = 1; + shellPath = /bin/tcsh; + shellScript = "# Install plist to tell launchd to start mDNSResponder\nmkdir -p ${DSTROOT}${SYSTEM_LIBRARY_DIR}/LaunchDaemons\ncp ${SRCROOT}/LaunchDaemonInfo.plist ${DSTROOT}${SYSTEM_LIBRARY_DIR}/LaunchDaemons/com.apple.mDNSResponder.plist\n\n# Install mDNSResponder.bundle containing language localizations\nmkdir -p ${DSTROOT}${SYSTEM_LIBRARY_DIR}/CoreServices\ncp -R ${SRCROOT}/mDNSResponder-bundle ${DSTROOT}${SYSTEM_LIBRARY_DIR}/CoreServices/mDNSResponder.bundle\n\n# Remove unwanted CVS directories\nfind ${DSTROOT}${SYSTEM_LIBRARY_DIR}/CoreServices/mDNSResponder.bundle -depth -name CVS -exec rm -rf {} \\;\n\n# Expand UTF-8 files to UTF-16 (not necessary, but required by B&I policy)\nforeach file (`find ${DSTROOT}${SYSTEM_LIBRARY_DIR}/CoreServices/mDNSResponder.bundle -name Localizable.strings`)\niconv -f utf-8 -t utf-16 ${file} > ${file}.new\nmv -f ${file}.new ${file}\nend\n\n# Remove French localization (not wanted for Apple B&I builds)\nrm -rf ${DSTROOT}${SYSTEM_LIBRARY_DIR}/CoreServices/mDNSResponder.bundle/Resources/French.lproj\n"; + }; + FF5A0AE705632EA600743C27 = { + buildActionMask = 8; + dstPath = /usr/share/man/man8; + dstSubfolderSpec = 0; + files = ( + FF5A0AE805632EAE00743C27, + ); + isa = PBXCopyFilesBuildPhase; + runOnlyForDeploymentPostprocessing = 1; + }; + FF5A0AE805632EAE00743C27 = { + fileRef = FF485D5105632E0000130380; + isa = PBXBuildFile; + settings = { + }; + }; + FFCB6D73075D539900B8AF62 = { + fileEncoding = 4; + isa = PBXFileReference; + name = PlatformCommon.c; + path = ../mDNSShared/PlatformCommon.c; + refType = 2; + }; + FFCB6D74075D539900B8AF62 = { + fileRef = FFCB6D73075D539900B8AF62; + isa = PBXBuildFile; + settings = { + }; + }; + FFCB6D75075D595E00B8AF62 = { + fileRef = FFCB6D73075D539900B8AF62; + isa = PBXBuildFile; + settings = { + }; + }; + FFD41DDA0664157900F0C438 = { + includeInIndex = 0; + isa = PBXZipArchiveReference; + path = dns_sd.jar; + refType = 3; + }; + FFD41DDB0664169900F0C438 = { + isa = PBXTargetDependency; + target = DB2CC4530662DD6800335AB3; + }; + FFD41DDC0664169B00F0C438 = { + isa = PBXTargetDependency; + target = DB2CC4660662DF5C00335AB3; + }; + FFD41DDD06641B4200F0C438 = { + buildActionMask = 2147483647; + files = ( + ); + generatedFileNames = ( + ); + isa = PBXShellScriptBuildPhase; + neededFileNames = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "rm -f ${CONFIGURATION_BUILD_DIR}/dns_sd"; + }; + FFD41DDF06641BBB00F0C438 = { + isa = PBXTargetDependency; + target = DB2CC4530662DD6800335AB3; + }; + FFE6935007C2CA7F00283007 = { + fileEncoding = 4; + isa = PBXFileReference; + name = ConfigurationAuthority.h; + path = PreferencePane/ConfigurationAuthority.h; + refType = 2; + }; + FFE6935207C2CAA400283007 = { + fileEncoding = 4; + isa = PBXFileReference; + name = DNSServiceDiscoveryPref.h; + path = PreferencePane/DNSServiceDiscoveryPref.h; + refType = 2; + }; + FFE6935407C2CABD00283007 = { + fileEncoding = 4; + isa = PBXFileReference; + name = PrivilegedOperations.h; + path = PreferencePane/PrivilegedOperations.h; + refType = 2; + }; + FFF4F63A06CFE4DD00459EFD = { + fileEncoding = 4; + isa = PBXFileReference; + name = dnsextd.8; + path = ../mDNSShared/dnsextd.8; + refType = 2; + }; + FFF4F63B06CFE4DD00459EFD = { + fileRef = FFF4F63A06CFE4DD00459EFD; + isa = PBXBuildFile; + settings = { + }; + }; + FFF4F63C06CFE53300459EFD = { + buildActionMask = 8; + dstPath = /usr/share/man/man1; + dstSubfolderSpec = 0; + files = ( + FFF4F63D06CFE54300459EFD, + ); + isa = PBXCopyFilesBuildPhase; + runOnlyForDeploymentPostprocessing = 1; + }; + FFF4F63D06CFE54300459EFD = { + fileRef = FF0E0B5D065ADC7600FE4D9C; + isa = PBXBuildFile; + settings = { + }; + }; + FFF7174A07614A8600E10551 = { + buildActionMask = 2147483647; + files = ( + ); + generatedFileNames = ( + ); + isa = PBXShellScriptBuildPhase; + neededFileNames = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "if [ -e /usr/local/include/dnsinfo.h ]\nthen\nrm -f \"${CONFIGURATION_TEMP_DIR}/dnsinfo.h\"\nrm -f \"${CONFIGURATION_TEMP_DIR}/libdnsinfo.a\"\nelse\necho \"#define MDNS_NO_DNSINFO 1\" > ${CONFIGURATION_TEMP_DIR}/dnsinfo.h\ntouch ${CONFIGURATION_TEMP_DIR}/empty.c\ncc ${CONFIGURATION_TEMP_DIR}/empty.c -c -o \"${CONFIGURATION_TEMP_DIR}/libdnsinfo.a\"\nrm -f ${CONFIGURATION_TEMP_DIR}/empty.c\nfi"; + }; + FFFB0DA407B43BED00B88D48 = { + children = ( + FFE6935007C2CA7F00283007, + FFFB0DAE07B43CBA00B88D48, + FFE6935207C2CAA400283007, + FFFB0DAC07B43CBA00B88D48, + FFE6935407C2CABD00283007, + FFFB0DAD07B43CBA00B88D48, + FFFB0DAF07B43CBA00B88D48, + FF260A2307B4463400CE10E5, + ); + isa = PBXGroup; + path = PreferencePane; + refType = 2; + }; + FFFB0DA507B43C9100B88D48 = { + buildActionMask = 2147483647; + files = ( + ); + isa = PBXHeadersBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + FFFB0DA607B43C9100B88D48 = { + buildActionMask = 2147483647; + files = ( + FFFB0DB307B43CBA00B88D48, + ); + isa = PBXSourcesBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + FFFB0DA707B43C9100B88D48 = { + buildActionMask = 2147483647; + files = ( + FFFB0DB507B43D2700B88D48, + FFFB0DB907B43D5F00B88D48, + FFFB0DBD07B43D7400B88D48, + FF260A2207B443C500CE10E5, + ); + isa = PBXFrameworksBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + FFFB0DA807B43C9100B88D48 = { + buildActionMask = 2147483647; + files = ( + ); + isa = PBXRezBuildPhase; + runOnlyForDeploymentPostprocessing = 0; + }; + FFFB0DA907B43C9100B88D48 = { + buildPhases = ( + FFFB0DA507B43C9100B88D48, + FFFB0DA607B43C9100B88D48, + FFFB0DA707B43C9100B88D48, + FFFB0DA807B43C9100B88D48, + ); + buildSettings = { + INSTALL_PATH = "/Library/Application Support/Bonjour"; + MACOSX_DEPLOYMENT_TARGET = 10.2; + OTHER_CFLAGS = ""; + OTHER_LDFLAGS = ""; + OTHER_REZFLAGS = ""; + PRODUCT_NAME = ddnswriteconfig; + REZ_EXECUTABLE = YES; + SECTORDER_FLAGS = ""; + STRIPFLAGS = "-S"; + WARNING_CFLAGS = "-Wmost -Wno-four-char-constants -Wno-unknown-pragmas"; + }; + dependencies = ( + ); + isa = PBXToolTarget; + name = ddnswriteconfig; + productInstallPath = "/Library/Application Support/Bonjour"; + productName = ddnswriteconfig; + productReference = FFFB0DAA07B43C9100B88D48; + }; + FFFB0DAA07B43C9100B88D48 = { + isa = PBXExecutableFileReference; + path = ddnswriteconfig; + refType = 3; + }; + FFFB0DAC07B43CBA00B88D48 = { + fileEncoding = 4; + isa = PBXFileReference; + path = DNSServiceDiscoveryPref.m; + refType = 4; + }; + FFFB0DAD07B43CBA00B88D48 = { + fileEncoding = 4; + isa = PBXFileReference; + path = PrivilegedOperations.c; + refType = 4; + }; + FFFB0DAE07B43CBA00B88D48 = { + fileEncoding = 4; + isa = PBXFileReference; + path = ConfigurationAuthority.c; + refType = 4; + }; + FFFB0DAF07B43CBA00B88D48 = { + fileEncoding = 4; + isa = PBXFileReference; + path = ddnswriteconfig.m; + refType = 4; + }; + FFFB0DB307B43CBA00B88D48 = { + fileRef = FFFB0DAF07B43CBA00B88D48; + isa = PBXBuildFile; + settings = { + }; + }; + FFFB0DB407B43D2700B88D48 = { + isa = PBXFrameworkReference; + name = Foundation.framework; + path = /System/Library/Frameworks/Foundation.framework; + refType = 0; + }; + FFFB0DB507B43D2700B88D48 = { + fileRef = FFFB0DB407B43D2700B88D48; + isa = PBXBuildFile; + settings = { + }; + }; + FFFB0DB907B43D5F00B88D48 = { + fileRef = 7F869685066EE02400D2A2DC; + isa = PBXBuildFile; + settings = { + }; + }; + FFFB0DBD07B43D7400B88D48 = { + fileRef = 65713D46025A293200000109; + isa = PBXBuildFile; + settings = { + }; + }; + }; + rootObject = 08FB7793FE84155DC02AAC07; +} diff --git a/mDNSResponder/mDNSMacOSX/mDNSResponder.plist b/mDNSResponder/mDNSMacOSX/mDNSResponder.plist new file mode 100644 index 00000000..6150bc37 --- /dev/null +++ b/mDNSResponder/mDNSMacOSX/mDNSResponder.plist @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<array> +<dict> + <key>OpenSourceProject</key> + <string>libipsec</string> + <key>OpenSourceVersion</key> + <string>Original version number unavailable, possibly RELENG_4_9_0_RELEASE</string> + <key>OpenSourceWebsiteURL</key> + <string>http://www.freebsd.org</string> + <key>OpenSourceSCM</key> + <string>svn export http://svn.freebsd.org/base/release/4.9.0/lib/libipsec</string> + <key>OpenSourceImportDate</key> + <string>2007-07-31</string> + <key>OpenSourceModifications</key> + <array> + <string>Removed all files except ipsec_strerror.h libpfkey.h pfkey.c</string> + <string>Added Apple Computer copyright and Apache license</string> + <string>Removed unused include netkey/key_var.h</string> + <string>Fixed compiler warnings such as "unused function parameter" and "signed/unsigned comparison"</string> + <string>Added code to conditionally compile only on OSX</string> + <string>Whitespace changes</string> + </array> + <key>OpenSourceLicense</key> + <string>bsd</string> + <key>OpenSourceLicenseFile</key> + <string>mDNSResponder.txt</string> +</dict> +</array> +</plist> diff --git a/mDNSResponder/mDNSMacOSX/mDNSResponder.sb b/mDNSResponder/mDNSMacOSX/mDNSResponder.sb new file mode 100644 index 00000000..eee623c8 --- /dev/null +++ b/mDNSResponder/mDNSMacOSX/mDNSResponder.sb @@ -0,0 +1,151 @@ +; -*- Mode: Scheme; tab-width: 4 -*- +; +; Copyright (c) 2012 Apple Inc. All rights reserved. +; +; Redistribution and use in source and binary forms, with or without +; modification, are permitted provided that the following conditions are met: +; +; 1. Redistributions of source code must retain the above copyright notice, +; this list of conditions and the following disclaimer. +; 2. Redistributions in binary form must reproduce the above copyright notice, +; this list of conditions and the following disclaimer in the documentation +; and/or other materials provided with the distribution. +; 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of its +; contributors may be used to endorse or promote products derived from this +; software without specific prior written permission. +; +; THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY +; EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +; DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY +; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +; (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +; ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +; +;############################################################################ + + +; WARNING: The sandbox rule capabilities and syntax used in this file are currently an +; Apple SPI (System Private Interface) and are subject to change at any time without notice. + +(version 1) +; When mDNSResponder is denied access, we want to avoid symoblification of mDNSResponder +; to get the stack trace as that can get into deadlock. no-callout will prevent +; symbolification. +(deny default (with no-callout)) + +(import "system.sb") + +; Baseline +(allow file-read-metadata ipc-posix-shm) + +; Mach communications +; These are needed for things like getpwnam, hostname changes, & keychain +(allow mach-lookup + (global-name "com.apple.bsd.dirhelper") + (global-name "com.apple.distributed_notifications.2") + (global-name "com.apple.ocspd") + (global-name "com.apple.PowerManagement.control") + (global-name "com.apple.mDNSResponderHelper") + (global-name "com.apple.SecurityServer") + (global-name "com.apple.SystemConfiguration.configd") + (global-name "com.apple.SystemConfiguration.SCNetworkReachability") + (global-name "com.apple.SystemConfiguration.DNSConfiguration") + (global-name "com.apple.SystemConfiguration.NetworkInformation") + (global-name "com.apple.system.notification_center") + (global-name "com.apple.system.logger") + (global-name "com.apple.webcontentfilter.dns") + (global-name "com.apple.server.bluetooth") + (global-name "com.apple.awacs") + (global-name "com.apple.networkd") + (global-name "com.apple.securityd") + (global-name "com.apple.wifi.manager") + (global-name "com.apple.commcenter.cupolicy.xpc") + (global-name "com.apple.blued") + (global-name "com.apple.mobilegestalt.xpc") + (global-name "com.apple.snhelper")) + +(allow mach-register + (global-name "com.apple.d2d.ipc")) + +; Networking, including Unix Domain Sockets +(allow network*) + +; Raw sockets +(if (defined? 'system-socket) + (allow system-socket)) + +; Hardware model information +(allow sysctl-read) + +; Syslog early in the boot process +(allow file-read-data file-write-data (literal "/dev/console")) + +(allow file-read-data + ; /etc/hosts support + (literal "/private/etc/hosts") + (literal "/private/etc")) + +; Our socket +(allow file-read* file-write* (literal "/private/var/run/mDNSResponder")) + +; System version, settings, and other miscellaneous necessary file system accesses +(allow file-read-data + ; Needed for CFCopyVersionDictionary() + (literal "/usr/sbin") + (literal "/usr/sbin/mDNSResponder") + + (literal "/Library/Preferences/SystemConfiguration/preferences.plist") + (literal "/Library/Preferences/SystemConfiguration/com.apple.nat.plist") + (regex #"^/Library/Preferences/(ByHost/)?\.GlobalPreferences\.") + (literal "/Library/Preferences/com.apple.crypto.plist") + (literal "/Library/Security/Trust Settings/Admin.plist") + (regex #"^/Library/Preferences/com\.apple\.security\.") + (literal "/Library/Preferences/SystemConfiguration/com.apple.PowerManagement.plist") + (literal "/private/var/preferences/SystemConfiguration/preferences.plist")) + +; For MAC Address +(allow system-info (info-type "net.link.addr")) + +; We just need access to System.keychain. But we don't want errors logged if other keychains are +; accessed under /Library/Keychains. Other keychains may be accessed as part of setting up an SSL +; connection. Instead of adding access to it here (to things which we don't need), we disable any +; logging that might happen during the access +(deny file-read-data (regex #"^/Library/Keychains/") (with no-log)) +(allow file-read-data (literal "/Library/Keychains/System.keychain")) + +; Access to mDNSResponder Managed Preferences profile +; instead of using (mobile-preferences-read "com.apple.mDNSResponder") we use the lines below for OSX compatibility +(allow file-read* (literal "/private/var/Managed Preferences/mobile")) +(allow file-read* (literal "/private/var/Library/Preferences/")) +(allow file-read* (literal "/Library/Managed Preferences")) +(allow file-read* (literal "/private/var/Managed Preferences/mobile/com.apple.mDNSResponder.plist")) + +; Our Module Directory Services cache +(allow file-read-data + (subpath "/private/var/tmp/mds") + (subpath "/private/var/db/mds")) + +(allow file-read* file-write* + (regex #"^/private/var/tmp/mds/[0-9]+(/|$)") + (regex #"^/private/var/db/mds/[0-9]+(/|$)") + (regex #"^/private/var/folders/[^/]+/[^/]+/C/mds(/|$)") + + ; Required on 10.5 and 10.6 + (regex #"^/private/var/folders/[^/]+/[^/]+/-Caches-/mds(/|$)")) + +; CRL Cache for SSL/TLS connections +(allow file-read-data (literal "/private/var/db/crls/crlcache.db")) + +; For mDNS sleep proxy offload and IOPMConnectionCreate +(if (defined? 'iokit-open) + (begin + (allow iokit-open + (iokit-user-client-class "NVEthernetUserClientMDNS") + (iokit-user-client-class "mDNSOffloadUserClient") + (iokit-user-client-class "wlDNSOffloadUserClient") + (iokit-user-client-class "RootDomainUserClient") + (iokit-user-client-class "AppleMobileFileIntegrityUserClient")))) diff --git a/mDNSResponder/mDNSMacOSX/mDNSResponder.txt b/mDNSResponder/mDNSMacOSX/mDNSResponder.txt new file mode 100644 index 00000000..f798e565 --- /dev/null +++ b/mDNSResponder/mDNSMacOSX/mDNSResponder.txt @@ -0,0 +1,55 @@ +1) + * Copyright (C) 1995, 1996, 1997, 1998, and 1999 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + +2) + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. diff --git a/mDNSResponder/mDNSMacOSX/mDNSResponder.xcodeproj/project.pbxproj b/mDNSResponder/mDNSMacOSX/mDNSResponder.xcodeproj/project.pbxproj new file mode 100644 index 00000000..a5c44ef3 --- /dev/null +++ b/mDNSResponder/mDNSMacOSX/mDNSResponder.xcodeproj/project.pbxproj @@ -0,0 +1,3137 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 45; + objects = { + +/* Begin PBXAggregateTarget section */ + 00AD62BB032D7A0C0CCA2C71 /* Build More */ = { + isa = PBXAggregateTarget; + buildConfigurationList = D284BE280ADD78180027CCDF /* Build configuration list for PBXAggregateTarget "Build More" */; + buildPhases = ( + ); + dependencies = ( + 03067D860C849CC30022BE1F /* PBXTargetDependency */, + D284BF2C0ADD815A0027CCDF /* PBXTargetDependency */, + D284BF2E0ADD81600027CCDF /* PBXTargetDependency */, + D284BF300ADD81630027CCDF /* PBXTargetDependency */, + ); + name = "Build More"; + productName = "Build All"; + }; + 03067D640C83A3700022BE1F /* Build Some */ = { + isa = PBXAggregateTarget; + buildConfigurationList = 03067D730C83A3CB0022BE1F /* Build configuration list for PBXAggregateTarget "Build Some" */; + buildPhases = ( + FF045B6A0C7E4AA600448140 /* ShellScript */, + ); + dependencies = ( + 217A4C49138EE14C000A5BA8 /* PBXTargetDependency */, + 03067D680C83A3830022BE1F /* PBXTargetDependency */, + 03067D6A0C83A3890022BE1F /* PBXTargetDependency */, + 03067D6C0C83A3920022BE1F /* PBXTargetDependency */, + 03067D6E0C83A39C0022BE1F /* PBXTargetDependency */, + 84C5B3411665544B00C324A8 /* PBXTargetDependency */, + 72FB546A166D5FE40090B2D9 /* PBXTargetDependency */, + ); + name = "Build Some"; + productName = "Build Some"; + }; + 2141DCF8123FFB5D0086D23E /* SystemLibraries */ = { + isa = PBXAggregateTarget; + buildConfigurationList = 2141DD08123FFB830086D23E /* Build configuration list for PBXAggregateTarget "SystemLibraries" */; + buildPhases = ( + ); + dependencies = ( + 2141DD0E123FFC960086D23E /* PBXTargetDependency */, + 2130257112400E9300AC839F /* PBXTargetDependency */, + ); + name = SystemLibraries; + productName = SystemLibraries; + }; + 2141DD0B123FFC7F0086D23E /* SystemLibrariesStatic */ = { + isa = PBXAggregateTarget; + buildConfigurationList = 2141DD18123FFC990086D23E /* Build configuration list for PBXAggregateTarget "SystemLibrariesStatic" */; + buildPhases = ( + ); + dependencies = ( + 215FFB1D124002CC00470DE1 /* PBXTargetDependency */, + 215FFB1B124002C700470DE1 /* PBXTargetDependency */, + 215FFB19124002C100470DE1 /* PBXTargetDependency */, + ); + name = SystemLibrariesStatic; + productName = SystemLibrariesStatic; + }; + FFA572650AF190F10055A0F1 /* SystemLibrariesDynamic */ = { + isa = PBXAggregateTarget; + buildConfigurationList = FFA5726E0AF191200055A0F1 /* Build configuration list for PBXAggregateTarget "SystemLibrariesDynamic" */; + buildPhases = ( + 1F7B473C12B82BFD00868AEF /* ShellScript */, + ); + dependencies = ( + FFA572690AF190FF0055A0F1 /* PBXTargetDependency */, + FFA5726B0AF191010055A0F1 /* PBXTargetDependency */, + FFA5726D0AF191020055A0F1 /* PBXTargetDependency */, + ); + name = SystemLibrariesDynamic; + productName = SystemLibraries; + }; + FFB7657B0AEED96B00583A2C /* Build All */ = { + isa = PBXAggregateTarget; + buildConfigurationList = FFB7657E0AEED99D00583A2C /* Build configuration list for PBXAggregateTarget "Build All" */; + buildPhases = ( + ); + dependencies = ( + FFB7657D0AEED97F00583A2C /* PBXTargetDependency */, + 2141DCFD123FFB7D0086D23E /* PBXTargetDependency */, + ); + name = "Build All"; + productName = "Build All"; + }; +/* End PBXAggregateTarget section */ + +/* Begin PBXBuildFile section */ + 21070E5F16486B9000A69507 /* DNSSECSupport.c in Sources */ = {isa = PBXBuildFile; fileRef = 21070E5D16486B9000A69507 /* DNSSECSupport.c */; }; + 21070E6016486B9000A69507 /* DNSSECSupport.c in Sources */ = {isa = PBXBuildFile; fileRef = 21070E5D16486B9000A69507 /* DNSSECSupport.c */; }; + 21070E6116486B9000A69507 /* DNSSECSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = 21070E5E16486B9000A69507 /* DNSSECSupport.h */; }; + 21070E6216486B9000A69507 /* DNSSECSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = 21070E5E16486B9000A69507 /* DNSSECSupport.h */; }; + 2120ABD516B71614007089B6 /* CUPolicy.c in Sources */ = {isa = PBXBuildFile; fileRef = 2120ABD416B71614007089B6 /* CUPolicy.c */; }; + 2120ABD616B71614007089B6 /* CUPolicy.c in Sources */ = {isa = PBXBuildFile; fileRef = 2120ABD416B71614007089B6 /* CUPolicy.c */; }; + 2124FA2C1471E98C0021D7BB /* nsec.h in Headers */ = {isa = PBXBuildFile; fileRef = 2124FA2B1471E98C0021D7BB /* nsec.h */; }; + 2124FA2D1471E98C0021D7BB /* nsec.h in Headers */ = {isa = PBXBuildFile; fileRef = 2124FA2B1471E98C0021D7BB /* nsec.h */; }; + 2124FA301471E9B50021D7BB /* dnssec.h in Headers */ = {isa = PBXBuildFile; fileRef = 2124FA2F1471E9B50021D7BB /* dnssec.h */; }; + 2124FA311471E9B50021D7BB /* dnssec.h in Headers */ = {isa = PBXBuildFile; fileRef = 2124FA2F1471E9B50021D7BB /* dnssec.h */; }; + 2124FA331471E9DE0021D7BB /* nsec.c in Sources */ = {isa = PBXBuildFile; fileRef = 2124FA321471E9DE0021D7BB /* nsec.c */; }; + 2124FA341471E9DE0021D7BB /* nsec.c in Sources */ = {isa = PBXBuildFile; fileRef = 2124FA321471E9DE0021D7BB /* nsec.c */; }; + 2127A47715C3C7B900A857FC /* nsec3.c in Sources */ = {isa = PBXBuildFile; fileRef = 2127A47515C3C7B900A857FC /* nsec3.c */; }; + 2127A47815C3C7B900A857FC /* nsec3.c in Sources */ = {isa = PBXBuildFile; fileRef = 2127A47515C3C7B900A857FC /* nsec3.c */; }; + 2127A47915C3C7B900A857FC /* nsec3.h in Headers */ = {isa = PBXBuildFile; fileRef = 2127A47615C3C7B900A857FC /* nsec3.h */; }; + 2127A47A15C3C7B900A857FC /* nsec3.h in Headers */ = {isa = PBXBuildFile; fileRef = 2127A47615C3C7B900A857FC /* nsec3.h */; }; + 213BDC6D147319F400000896 /* dnssec.c in Sources */ = {isa = PBXBuildFile; fileRef = 213BDC6C147319F400000896 /* dnssec.c */; }; + 213BDC6E147319F400000896 /* dnssec.c in Sources */ = {isa = PBXBuildFile; fileRef = 213BDC6C147319F400000896 /* dnssec.c */; }; + 213FB23C12028C4A002B3A08 /* BonjourEvents.c in Sources */ = {isa = PBXBuildFile; fileRef = 213FB22C12028B53002B3A08 /* BonjourEvents.c */; }; + 213FB23D12028C5A002B3A08 /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 09AB6884FE841BABC02AAC07 /* CoreFoundation.framework */; }; + 215FFAEE124000F900470DE1 /* dnssd_ipc.c in Sources */ = {isa = PBXBuildFile; fileRef = F5E11B5A04A28126019798ED /* dnssd_ipc.c */; }; + 215FFAEF124000F900470DE1 /* dnssd_clientlib.c in Sources */ = {isa = PBXBuildFile; fileRef = FFFA38620AEEDB090065B80A /* dnssd_clientlib.c */; }; + 215FFAF0124000F900470DE1 /* dnssd_clientstub.c in Sources */ = {isa = PBXBuildFile; fileRef = FFFA38640AEEDB130065B80A /* dnssd_clientstub.c */; }; + 215FFAF1124000F900470DE1 /* DNSServiceDiscovery.c in Sources */ = {isa = PBXBuildFile; fileRef = FFA572480AF18FCC0055A0F1 /* DNSServiceDiscovery.c */; }; + 215FFAF2124000F900470DE1 /* DNSServiceDiscoveryRequest.defs in Sources */ = {isa = PBXBuildFile; fileRef = 6575FC01022EAFBA00000109 /* DNSServiceDiscoveryRequest.defs */; }; + 215FFAF3124000F900470DE1 /* DNSServiceDiscoveryReply.defs in Sources */ = {isa = PBXBuildFile; fileRef = 6575FC00022EAFBA00000109 /* DNSServiceDiscoveryReply.defs */; settings = {ATTRIBUTES = (Server, ); }; }; + 215FFAF41240011800470DE1 /* dnssd_ipc.c in Sources */ = {isa = PBXBuildFile; fileRef = F5E11B5A04A28126019798ED /* dnssd_ipc.c */; }; + 215FFAF51240011800470DE1 /* dnssd_clientlib.c in Sources */ = {isa = PBXBuildFile; fileRef = FFFA38620AEEDB090065B80A /* dnssd_clientlib.c */; }; + 215FFAF61240011800470DE1 /* dnssd_clientstub.c in Sources */ = {isa = PBXBuildFile; fileRef = FFFA38640AEEDB130065B80A /* dnssd_clientstub.c */; }; + 215FFAF71240011800470DE1 /* DNSServiceDiscovery.c in Sources */ = {isa = PBXBuildFile; fileRef = FFA572480AF18FCC0055A0F1 /* DNSServiceDiscovery.c */; }; + 215FFAF81240011800470DE1 /* DNSServiceDiscoveryRequest.defs in Sources */ = {isa = PBXBuildFile; fileRef = 6575FC01022EAFBA00000109 /* DNSServiceDiscoveryRequest.defs */; }; + 215FFAF91240011800470DE1 /* DNSServiceDiscoveryReply.defs in Sources */ = {isa = PBXBuildFile; fileRef = 6575FC00022EAFBA00000109 /* DNSServiceDiscoveryReply.defs */; settings = {ATTRIBUTES = (Server, ); }; }; + 215FFAFA1240013400470DE1 /* dnssd_ipc.c in Sources */ = {isa = PBXBuildFile; fileRef = F5E11B5A04A28126019798ED /* dnssd_ipc.c */; }; + 215FFAFB1240013400470DE1 /* dnssd_clientlib.c in Sources */ = {isa = PBXBuildFile; fileRef = FFFA38620AEEDB090065B80A /* dnssd_clientlib.c */; }; + 215FFAFC1240013400470DE1 /* dnssd_clientstub.c in Sources */ = {isa = PBXBuildFile; fileRef = FFFA38640AEEDB130065B80A /* dnssd_clientstub.c */; }; + 215FFAFD1240013400470DE1 /* DNSServiceDiscovery.c in Sources */ = {isa = PBXBuildFile; fileRef = FFA572480AF18FCC0055A0F1 /* DNSServiceDiscovery.c */; }; + 215FFAFE1240013400470DE1 /* DNSServiceDiscoveryRequest.defs in Sources */ = {isa = PBXBuildFile; fileRef = 6575FC01022EAFBA00000109 /* DNSServiceDiscoveryRequest.defs */; }; + 215FFAFF1240013400470DE1 /* DNSServiceDiscoveryReply.defs in Sources */ = {isa = PBXBuildFile; fileRef = 6575FC00022EAFBA00000109 /* DNSServiceDiscoveryReply.defs */; settings = {ATTRIBUTES = (Server, ); }; }; + 216D9ACE1720C9F5008066E1 /* VPNService.c in Sources */ = {isa = PBXBuildFile; fileRef = 216D9ACD1720C9F5008066E1 /* VPNService.c */; }; + 216D9ACF1720C9F5008066E1 /* VPNService.c in Sources */ = {isa = PBXBuildFile; fileRef = 216D9ACD1720C9F5008066E1 /* VPNService.c */; }; + 218E8E51156D8C0300720DA0 /* dnsproxy.c in Sources */ = {isa = PBXBuildFile; fileRef = 218E8E4F156D8C0300720DA0 /* dnsproxy.c */; }; + 218E8E52156D8C0300720DA0 /* dnsproxy.c in Sources */ = {isa = PBXBuildFile; fileRef = 218E8E4F156D8C0300720DA0 /* dnsproxy.c */; }; + 218E8E53156D8C0300720DA0 /* dnsproxy.h in Headers */ = {isa = PBXBuildFile; fileRef = 218E8E50156D8C0300720DA0 /* dnsproxy.h */; }; + 218E8E54156D8C0300720DA0 /* dnsproxy.h in Headers */ = {isa = PBXBuildFile; fileRef = 218E8E50156D8C0300720DA0 /* dnsproxy.h */; }; + 219D5542149ED645004464AE /* libxml2.2.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 219D5541149ED645004464AE /* libxml2.2.dylib */; }; + 219D5543149ED645004464AE /* libxml2.2.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 219D5541149ED645004464AE /* libxml2.2.dylib */; }; + 21A57F4C145B2AE100939099 /* CryptoAlg.c in Sources */ = {isa = PBXBuildFile; fileRef = 21A57F4A145B2AE100939099 /* CryptoAlg.c */; }; + 21A57F4D145B2AE100939099 /* CryptoAlg.c in Sources */ = {isa = PBXBuildFile; fileRef = 21A57F4A145B2AE100939099 /* CryptoAlg.c */; }; + 21A57F4E145B2AE100939099 /* CryptoAlg.h in Headers */ = {isa = PBXBuildFile; fileRef = 21A57F4B145B2AE100939099 /* CryptoAlg.h */; }; + 21A57F4F145B2AE100939099 /* CryptoAlg.h in Headers */ = {isa = PBXBuildFile; fileRef = 21A57F4B145B2AE100939099 /* CryptoAlg.h */; }; + 21A57F53145B2B1400939099 /* CryptoSupport.c in Sources */ = {isa = PBXBuildFile; fileRef = 21A57F51145B2B1400939099 /* CryptoSupport.c */; }; + 21A57F54145B2B1400939099 /* CryptoSupport.c in Sources */ = {isa = PBXBuildFile; fileRef = 21A57F51145B2B1400939099 /* CryptoSupport.c */; }; + 21A57F55145B2B1400939099 /* CryptoSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = 21A57F52145B2B1400939099 /* CryptoSupport.h */; }; + 21A57F56145B2B1400939099 /* CryptoSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = 21A57F52145B2B1400939099 /* CryptoSupport.h */; }; + 21DCD05C1461B23700702FC8 /* CryptoAlg.c in Sources */ = {isa = PBXBuildFile; fileRef = 21A57F4A145B2AE100939099 /* CryptoAlg.c */; }; + 21DCD05D1461B23700702FC8 /* CryptoAlg.h in Sources */ = {isa = PBXBuildFile; fileRef = 21A57F4B145B2AE100939099 /* CryptoAlg.h */; }; + 21DD8FBF161E9A250033C8F8 /* anonymous.c in Sources */ = {isa = PBXBuildFile; fileRef = 21DD8FBD161E9A250033C8F8 /* anonymous.c */; }; + 21DD8FC0161E9A250033C8F8 /* anonymous.c in Sources */ = {isa = PBXBuildFile; fileRef = 21DD8FBD161E9A250033C8F8 /* anonymous.c */; }; + 21DD8FC1161E9A250033C8F8 /* anonymous.h in Headers */ = {isa = PBXBuildFile; fileRef = 21DD8FBE161E9A250033C8F8 /* anonymous.h */; }; + 21DD8FC2161E9A250033C8F8 /* anonymous.h in Headers */ = {isa = PBXBuildFile; fileRef = 21DD8FBE161E9A250033C8F8 /* anonymous.h */; }; + 21DED43515702C0F0060B6B9 /* DNSProxySupport.c in Sources */ = {isa = PBXBuildFile; fileRef = 21DED43415702C0F0060B6B9 /* DNSProxySupport.c */; }; + 21DED43615702C0F0060B6B9 /* DNSProxySupport.c in Sources */ = {isa = PBXBuildFile; fileRef = 21DED43415702C0F0060B6B9 /* DNSProxySupport.c */; }; + 2E0405F50C3195F700F13B59 /* helper.c in Sources */ = {isa = PBXBuildFile; fileRef = 2E0405F40C3195F700F13B59 /* helper.c */; }; + 2E0405F60C31961100F13B59 /* helpermsg.defs in Sources */ = {isa = PBXBuildFile; fileRef = 2E0405EB0C3190DC00F13B59 /* helpermsg.defs */; settings = {ATTRIBUTES = (Client, Server, ); COMPILER_FLAGS = "-Wno-error"; }; }; + 2E0406150C3197CB00F13B59 /* libbsm.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 2E0406140C3197CB00F13B59 /* libbsm.dylib */; }; + 2E04061F0C3198B700F13B59 /* helpermsg.defs in Sources */ = {isa = PBXBuildFile; fileRef = 2E0405EB0C3190DC00F13B59 /* helpermsg.defs */; }; + 2E0406200C3198B700F13B59 /* helpermsg.defs in Sources */ = {isa = PBXBuildFile; fileRef = 2E0405EB0C3190DC00F13B59 /* helpermsg.defs */; }; + 2E04070A0C31EEEC00F13B59 /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 09AB6884FE841BABC02AAC07 /* CoreFoundation.framework */; }; + 2E04070B0C31EEEC00F13B59 /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 65713D46025A293200000109 /* SystemConfiguration.framework */; }; + 2E3552900C3A95C100CA1CB7 /* helper-error.h in Headers */ = {isa = PBXBuildFile; fileRef = 2E35528F0C3A95C100CA1CB7 /* helper-error.h */; }; + 2E3552910C3A95C100CA1CB7 /* helper-error.h in Headers */ = {isa = PBXBuildFile; fileRef = 2E35528F0C3A95C100CA1CB7 /* helper-error.h */; }; + 2E3552920C3A95C100CA1CB7 /* helper-error.h in Headers */ = {isa = PBXBuildFile; fileRef = 2E35528F0C3A95C100CA1CB7 /* helper-error.h */; }; + 2E35529D0C3A9E7600CA1CB7 /* helper-error.h in Headers */ = {isa = PBXBuildFile; fileRef = 2E35528F0C3A95C100CA1CB7 /* helper-error.h */; }; + 2E35529E0C3A9E7600CA1CB7 /* helper-stubs.c in Sources */ = {isa = PBXBuildFile; fileRef = 2E96A52D0C39C1A50087C4D2 /* helper-stubs.c */; }; + 2E35529F0C3A9E7600CA1CB7 /* helper.h in Headers */ = {isa = PBXBuildFile; fileRef = 2E96A5250C39BE480087C4D2 /* helper.h */; }; + 2E4D9B050C38C19500480551 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7F869685066EE02400D2A2DC /* Security.framework */; }; + 2E8165E80C5980E300485EB2 /* libpfkey.h in Headers */ = {isa = PBXBuildFile; fileRef = 4A8202520C56C36500DDFD48 /* libpfkey.h */; }; + 2E8165E90C5980EE00485EB2 /* pfkey.c in Sources */ = {isa = PBXBuildFile; fileRef = 4A8202530C56C36600DDFD48 /* pfkey.c */; }; + 2E8165EA0C5980F700485EB2 /* ipsec_strerror.h in Headers */ = {isa = PBXBuildFile; fileRef = 4A8202510C56C36500DDFD48 /* ipsec_strerror.h */; }; + 2E8165F90C59838100485EB2 /* libipsec.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 2E8165F60C59835F00485EB2 /* libipsec.dylib */; }; + 2E96A51D0C39BDAC0087C4D2 /* helper-main.c in Sources */ = {isa = PBXBuildFile; fileRef = 2E0406CA0C31E9AD00F13B59 /* helper-main.c */; }; + 2E96A5260C39BE480087C4D2 /* helper.h in Headers */ = {isa = PBXBuildFile; fileRef = 2E96A5250C39BE480087C4D2 /* helper.h */; }; + 2E96A5270C39BE480087C4D2 /* helper.h in Headers */ = {isa = PBXBuildFile; fileRef = 2E96A5250C39BE480087C4D2 /* helper.h */; }; + 2E96A5300C39C1A50087C4D2 /* helper-stubs.c in Sources */ = {isa = PBXBuildFile; fileRef = 2E96A52D0C39C1A50087C4D2 /* helper-stubs.c */; }; + 2E96A5320C39C1A50087C4D2 /* helper-stubs.c in Sources */ = {isa = PBXBuildFile; fileRef = 2E96A52D0C39C1A50087C4D2 /* helper-stubs.c */; }; + 2EAE955A0C31F4D30021F738 /* helpermsg.defs in Sources */ = {isa = PBXBuildFile; fileRef = 2E0405EB0C3190DC00F13B59 /* helpermsg.defs */; }; + 2EC8F8EC0C39CCAC003C9C48 /* helper.h in Headers */ = {isa = PBXBuildFile; fileRef = 2E96A5250C39BE480087C4D2 /* helper.h */; }; + 2ECC11A60C4FEC3800CB1885 /* helpermsg-types.h in Headers */ = {isa = PBXBuildFile; fileRef = 2ECC11A50C4FEC3800CB1885 /* helpermsg-types.h */; }; + 2ECC11A70C4FEC3800CB1885 /* helpermsg-types.h in Headers */ = {isa = PBXBuildFile; fileRef = 2ECC11A50C4FEC3800CB1885 /* helpermsg-types.h */; }; + 2ECC11A80C4FEC3800CB1885 /* helpermsg-types.h in Headers */ = {isa = PBXBuildFile; fileRef = 2ECC11A50C4FEC3800CB1885 /* helpermsg-types.h */; }; + 2EDC5E730C39EA640092701B /* helper-server.h in Headers */ = {isa = PBXBuildFile; fileRef = 2EDC5E720C39EA640092701B /* helper-server.h */; }; + 2EDC5E740C39EA640092701B /* helper-server.h in Headers */ = {isa = PBXBuildFile; fileRef = 2EDC5E720C39EA640092701B /* helper-server.h */; }; + 2EDC5E750C39EA640092701B /* helper-server.h in Headers */ = {isa = PBXBuildFile; fileRef = 2EDC5E720C39EA640092701B /* helper-server.h */; }; + 4A7B9E8014FDA25000B84CC1 /* mDNSResponder.plist in CopyFiles */ = {isa = PBXBuildFile; fileRef = 4A7B9E7E14FDA1BB00B84CC1 /* mDNSResponder.plist */; }; + 4A7B9E8214FDA26C00B84CC1 /* mDNSResponder.txt in CopyFiles */ = {isa = PBXBuildFile; fileRef = 4A7B9E7C14FDA19F00B84CC1 /* mDNSResponder.txt */; }; + 4AAE0C9A0C68EA81003882A5 /* mDNSResponderHelper.8 in CopyFiles */ = {isa = PBXBuildFile; fileRef = 4AAE0C7A0C68E97F003882A5 /* mDNSResponderHelper.8 */; }; + 4BD2B63A134FE09F002B96D5 /* P2PPacketFilter.c in Sources */ = {isa = PBXBuildFile; fileRef = 4BD2B638134FE09F002B96D5 /* P2PPacketFilter.c */; }; + 4BD2B63B134FE09F002B96D5 /* P2PPacketFilter.h in Headers */ = {isa = PBXBuildFile; fileRef = 4BD2B639134FE09F002B96D5 /* P2PPacketFilter.h */; }; + 72FB5467166D5FCA0090B2D9 /* dnsctl.c in Sources */ = {isa = PBXBuildFile; fileRef = 72FB545A166D5F960090B2D9 /* dnsctl.c */; }; + 72FB5468166D5FD20090B2D9 /* libdns_services.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 84C5B3351665529800C324A8 /* libdns_services.dylib */; }; + 8418673E15AB8C2D00BB7F70 /* com.apple.networking.mDNSResponder in CopyFiles */ = {isa = PBXBuildFile; fileRef = 8418673A15AB8B6900BB7F70 /* com.apple.networking.mDNSResponder */; }; + 848DA5C7165477E000D2E8B4 /* xpc_services.c in Sources */ = {isa = PBXBuildFile; fileRef = 848DA5C6165477E000D2E8B4 /* xpc_services.c */; }; + 848DA5C8165477E000D2E8B4 /* xpc_services.c in Sources */ = {isa = PBXBuildFile; fileRef = 848DA5C6165477E000D2E8B4 /* xpc_services.c */; }; + 848DA5CA165477EB00D2E8B4 /* xpc_services.h in Headers */ = {isa = PBXBuildFile; fileRef = 848DA5C9165477EB00D2E8B4 /* xpc_services.h */; }; + 848DA5CB165477EB00D2E8B4 /* xpc_services.h in Headers */ = {isa = PBXBuildFile; fileRef = 848DA5C9165477EB00D2E8B4 /* xpc_services.h */; }; + 848DA5D616547F7200D2E8B4 /* dns_xpc.h in Headers */ = {isa = PBXBuildFile; fileRef = 848DA5D516547F7200D2E8B4 /* dns_xpc.h */; }; + 848DA5D716547F7200D2E8B4 /* dns_xpc.h in Headers */ = {isa = PBXBuildFile; fileRef = 848DA5D516547F7200D2E8B4 /* dns_xpc.h */; }; + 84C5B33C166553F100C324A8 /* dns_services.c in Sources */ = {isa = PBXBuildFile; fileRef = 84C5B339166553AF00C324A8 /* dns_services.c */; }; + 84C5B33D166553F900C324A8 /* dns_services.h in Headers */ = {isa = PBXBuildFile; fileRef = 84C5B338166553A000C324A8 /* dns_services.h */; settings = {ATTRIBUTES = (Private, ); }; }; + D284BE530ADD80740027CCDF /* DNSServiceDiscoveryDefines.h in Headers */ = {isa = PBXBuildFile; fileRef = 6575FBFF022EAFBA00000109 /* DNSServiceDiscoveryDefines.h */; }; + D284BE540ADD80740027CCDF /* dnssd_ipc.h in Headers */ = {isa = PBXBuildFile; fileRef = F5E11B5B04A28126019798ED /* dnssd_ipc.h */; }; + D284BE560ADD80740027CCDF /* DNSServiceDiscoveryReply.defs in Sources */ = {isa = PBXBuildFile; fileRef = 6575FC00022EAFBA00000109 /* DNSServiceDiscoveryReply.defs */; settings = {ATTRIBUTES = (Client, ); }; }; + D284BE570ADD80740027CCDF /* DNSServiceDiscoveryRequest.defs in Sources */ = {isa = PBXBuildFile; fileRef = 6575FC01022EAFBA00000109 /* DNSServiceDiscoveryRequest.defs */; settings = {ATTRIBUTES = (Server, ); }; }; + D284BE580ADD80740027CCDF /* mDNS.c in Sources */ = {isa = PBXBuildFile; fileRef = 6575FBE9022EAF5A00000109 /* mDNS.c */; }; + D284BE590ADD80740027CCDF /* uDNS.c in Sources */ = {isa = PBXBuildFile; fileRef = 7F18A9F70587CEF6001880B3 /* uDNS.c */; }; + D284BE5A0ADD80740027CCDF /* DNSCommon.c in Sources */ = {isa = PBXBuildFile; fileRef = 7F18A9F60587CEF6001880B3 /* DNSCommon.c */; }; + D284BE5B0ADD80740027CCDF /* DNSDigest.c in Sources */ = {isa = PBXBuildFile; fileRef = 7F461DB5062DBF2900672BF3 /* DNSDigest.c */; }; + D284BE5D0ADD80740027CCDF /* mDNSDebug.c in Sources */ = {isa = PBXBuildFile; fileRef = DBAAFE29057E8F4D0085CAD0 /* mDNSDebug.c */; }; + D284BE5E0ADD80740027CCDF /* uds_daemon.c in Sources */ = {isa = PBXBuildFile; fileRef = F525E72804AA167501F1CF4D /* uds_daemon.c */; }; + D284BE5F0ADD80740027CCDF /* dnssd_ipc.c in Sources */ = {isa = PBXBuildFile; fileRef = F5E11B5A04A28126019798ED /* dnssd_ipc.c */; }; + D284BE600ADD80740027CCDF /* PlatformCommon.c in Sources */ = {isa = PBXBuildFile; fileRef = FFCB6D73075D539900B8AF62 /* PlatformCommon.c */; }; + D284BE610ADD80740027CCDF /* mDNSMacOSX.c in Sources */ = {isa = PBXBuildFile; fileRef = 6575FBEB022EAF7200000109 /* mDNSMacOSX.c */; }; + D284BE620ADD80740027CCDF /* LegacyNATTraversal.c in Sources */ = {isa = PBXBuildFile; fileRef = 7FC8F9D406D14E66007E879D /* LegacyNATTraversal.c */; }; + D284BE630ADD80740027CCDF /* daemon.c in Sources */ = {isa = PBXBuildFile; fileRef = 6575FBEC022EAF7200000109 /* daemon.c */; }; + D284BE650ADD80740027CCDF /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 09AB6884FE841BABC02AAC07 /* CoreFoundation.framework */; }; + D284BE660ADD80740027CCDF /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 65713D46025A293200000109 /* SystemConfiguration.framework */; }; + D284BE670ADD80740027CCDF /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 00CA213D02786FC30CCA2C71 /* IOKit.framework */; }; + D284BE680ADD80740027CCDF /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7F869685066EE02400D2A2DC /* Security.framework */; }; + D284BE6B0ADD80740027CCDF /* mDNSResponder.8 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FF485D5105632E0000130380 /* mDNSResponder.8 */; }; + D284BE780ADD80800027CCDF /* DNSServiceDiscoveryDefines.h in Headers */ = {isa = PBXBuildFile; fileRef = 6575FBFF022EAFBA00000109 /* DNSServiceDiscoveryDefines.h */; }; + D284BE790ADD80800027CCDF /* dnssd_ipc.h in Headers */ = {isa = PBXBuildFile; fileRef = F5E11B5B04A28126019798ED /* dnssd_ipc.h */; }; + D284BE7A0ADD80800027CCDF /* mDNSEmbeddedAPI.h in Headers */ = {isa = PBXBuildFile; fileRef = 654BE64F02B63B93000001D1 /* mDNSEmbeddedAPI.h */; }; + D284BE7B0ADD80800027CCDF /* mDNSDebug.h in Headers */ = {isa = PBXBuildFile; fileRef = 654BE65002B63B93000001D1 /* mDNSDebug.h */; }; + D284BE7C0ADD80800027CCDF /* mDNSMacOSX.h in Headers */ = {isa = PBXBuildFile; fileRef = 000753D303367C1C0CCA2C71 /* mDNSMacOSX.h */; }; + D284BE7E0ADD80800027CCDF /* DNSServiceDiscoveryReply.defs in Sources */ = {isa = PBXBuildFile; fileRef = 6575FC00022EAFBA00000109 /* DNSServiceDiscoveryReply.defs */; settings = {ATTRIBUTES = (Client, ); }; }; + D284BE7F0ADD80800027CCDF /* DNSServiceDiscoveryRequest.defs in Sources */ = {isa = PBXBuildFile; fileRef = 6575FC01022EAFBA00000109 /* DNSServiceDiscoveryRequest.defs */; settings = {ATTRIBUTES = (Server, ); }; }; + D284BE800ADD80800027CCDF /* mDNS.c in Sources */ = {isa = PBXBuildFile; fileRef = 6575FBE9022EAF5A00000109 /* mDNS.c */; }; + D284BE810ADD80800027CCDF /* uDNS.c in Sources */ = {isa = PBXBuildFile; fileRef = 7F18A9F70587CEF6001880B3 /* uDNS.c */; }; + D284BE820ADD80800027CCDF /* DNSCommon.c in Sources */ = {isa = PBXBuildFile; fileRef = 7F18A9F60587CEF6001880B3 /* DNSCommon.c */; }; + D284BE830ADD80800027CCDF /* DNSDigest.c in Sources */ = {isa = PBXBuildFile; fileRef = 7F461DB5062DBF2900672BF3 /* DNSDigest.c */; }; + D284BE850ADD80800027CCDF /* mDNSDebug.c in Sources */ = {isa = PBXBuildFile; fileRef = DBAAFE29057E8F4D0085CAD0 /* mDNSDebug.c */; }; + D284BE860ADD80800027CCDF /* uds_daemon.c in Sources */ = {isa = PBXBuildFile; fileRef = F525E72804AA167501F1CF4D /* uds_daemon.c */; }; + D284BE870ADD80800027CCDF /* dnssd_ipc.c in Sources */ = {isa = PBXBuildFile; fileRef = F5E11B5A04A28126019798ED /* dnssd_ipc.c */; }; + D284BE880ADD80800027CCDF /* PlatformCommon.c in Sources */ = {isa = PBXBuildFile; fileRef = FFCB6D73075D539900B8AF62 /* PlatformCommon.c */; }; + D284BE890ADD80800027CCDF /* mDNSMacOSX.c in Sources */ = {isa = PBXBuildFile; fileRef = 6575FBEB022EAF7200000109 /* mDNSMacOSX.c */; }; + D284BE8A0ADD80800027CCDF /* LegacyNATTraversal.c in Sources */ = {isa = PBXBuildFile; fileRef = 7FC8F9D406D14E66007E879D /* LegacyNATTraversal.c */; }; + D284BE8B0ADD80800027CCDF /* daemon.c in Sources */ = {isa = PBXBuildFile; fileRef = 6575FBEC022EAF7200000109 /* daemon.c */; }; + D284BE8D0ADD80800027CCDF /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 09AB6884FE841BABC02AAC07 /* CoreFoundation.framework */; }; + D284BE8E0ADD80800027CCDF /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 65713D46025A293200000109 /* SystemConfiguration.framework */; }; + D284BE8F0ADD80800027CCDF /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 00CA213D02786FC30CCA2C71 /* IOKit.framework */; }; + D284BE900ADD80800027CCDF /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7F869685066EE02400D2A2DC /* Security.framework */; }; + D284BEA80ADD80920027CCDF /* dns-sd.c in Sources */ = {isa = PBXBuildFile; fileRef = FF1C919F07021E3F001048AB /* dns-sd.c */; }; + D284BEAC0ADD80920027CCDF /* dns-sd.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FF1C919D07021D77001048AB /* dns-sd.1 */; }; + D284BEB70ADD809A0027CCDF /* JNISupport.c in Sources */ = {isa = PBXBuildFile; fileRef = DB2CC44B0662DD1100335AB3 /* JNISupport.c */; }; + D284BEB90ADD809A0027CCDF /* JavaVM.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DB2CC4680662DFF500335AB3 /* JavaVM.framework */; }; + D284BEC50ADD80A20027CCDF /* DNSCommon.c in Sources */ = {isa = PBXBuildFile; fileRef = 7F18A9F60587CEF6001880B3 /* DNSCommon.c */; }; + D284BEC60ADD80A20027CCDF /* DNSDigest.c in Sources */ = {isa = PBXBuildFile; fileRef = 7F461DB5062DBF2900672BF3 /* DNSDigest.c */; }; + D284BEC70ADD80A20027CCDF /* dnsextd.c in Sources */ = {isa = PBXBuildFile; fileRef = FF25794606C9A8BF00376F7B /* dnsextd.c */; }; + D284BEC80ADD80A20027CCDF /* mDNSDebug.c in Sources */ = {isa = PBXBuildFile; fileRef = DBAAFE29057E8F4D0085CAD0 /* mDNSDebug.c */; }; + D284BEC90ADD80A20027CCDF /* GenLinkedList.c in Sources */ = {isa = PBXBuildFile; fileRef = DBAAFE2C057E8F660085CAD0 /* GenLinkedList.c */; }; + D284BECA0ADD80A20027CCDF /* mDNSMacOSX.c in Sources */ = {isa = PBXBuildFile; fileRef = 6575FBEB022EAF7200000109 /* mDNSMacOSX.c */; }; + D284BECB0ADD80A20027CCDF /* dnsextd_lexer.l in Sources */ = {isa = PBXBuildFile; fileRef = FF13FFEA0A5DA44A00897C81 /* dnsextd_lexer.l */; settings = {COMPILER_FLAGS = "-Wno-error"; }; }; + D284BECC0ADD80A20027CCDF /* dnsextd_parser.y in Sources */ = {isa = PBXBuildFile; fileRef = FF13FFEC0A5DA45500897C81 /* dnsextd_parser.y */; }; + D284BECD0ADD80A20027CCDF /* PlatformCommon.c in Sources */ = {isa = PBXBuildFile; fileRef = FFCB6D73075D539900B8AF62 /* PlatformCommon.c */; }; + D284BECF0ADD80A20027CCDF /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 09AB6884FE841BABC02AAC07 /* CoreFoundation.framework */; }; + D284BED00ADD80A20027CCDF /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 65713D46025A293200000109 /* SystemConfiguration.framework */; }; + D284BED10ADD80A20027CCDF /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 00CA213D02786FC30CCA2C71 /* IOKit.framework */; }; + D284BED20ADD80A20027CCDF /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7F869685066EE02400D2A2DC /* Security.framework */; }; + D284BED50ADD80A20027CCDF /* dnsextd.8 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FFF4F63A06CFE4DD00459EFD /* dnsextd.8 */; }; + D284BEDE0ADD80A70027CCDF /* ddnswriteconfig.m in Sources */ = {isa = PBXBuildFile; fileRef = FFFB0DAF07B43CBA00B88D48 /* ddnswriteconfig.m */; }; + D284BEE00ADD80A70027CCDF /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FFFB0DB407B43D2700B88D48 /* Foundation.framework */; }; + D284BEE10ADD80A70027CCDF /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7F869685066EE02400D2A2DC /* Security.framework */; }; + D284BEE20ADD80A70027CCDF /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 65713D46025A293200000109 /* SystemConfiguration.framework */; }; + D284BEE30ADD80A70027CCDF /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 09AB6884FE841BABC02AAC07 /* CoreFoundation.framework */; }; + D284BEEF0ADD80B00027CCDF /* remove_idle.tiff in Resources */ = {isa = PBXBuildFile; fileRef = FF260A2407B4464B00CE10E5 /* remove_idle.tiff */; }; + D284BEF00ADD80B00027CCDF /* add_pressed.tiff in Resources */ = {isa = PBXBuildFile; fileRef = FF260A2507B4464B00CE10E5 /* add_pressed.tiff */; }; + D284BEF10ADD80B00027CCDF /* remove_disabled.tiff in Resources */ = {isa = PBXBuildFile; fileRef = FF260A2607B4464B00CE10E5 /* remove_disabled.tiff */; }; + D284BEF20ADD80B00027CCDF /* add_idle.tiff in Resources */ = {isa = PBXBuildFile; fileRef = FF260A2707B4464B00CE10E5 /* add_idle.tiff */; }; + D284BEF30ADD80B00027CCDF /* success.tiff in Resources */ = {isa = PBXBuildFile; fileRef = FF260A2807B4464B00CE10E5 /* success.tiff */; }; + D284BEF40ADD80B00027CCDF /* remove_pressed.tiff in Resources */ = {isa = PBXBuildFile; fileRef = FF260A2907B4464B00CE10E5 /* remove_pressed.tiff */; }; + D284BEF50ADD80B00027CCDF /* failure.tiff in Resources */ = {isa = PBXBuildFile; fileRef = FF260A2A07B4464B00CE10E5 /* failure.tiff */; }; + D284BEF60ADD80B00027CCDF /* BonjourPref.icns in Resources */ = {isa = PBXBuildFile; fileRef = FF260A3207B4466900CE10E5 /* BonjourPref.icns */; }; + D284BEF70ADD80B00027CCDF /* BonjourPref.tiff in Resources */ = {isa = PBXBuildFile; fileRef = FF260A3307B4466900CE10E5 /* BonjourPref.tiff */; }; + D284BEF80ADD80B00027CCDF /* DNSServiceDiscoveryPref.nib in Resources */ = {isa = PBXBuildFile; fileRef = FF260A4807B4475600CE10E5 /* DNSServiceDiscoveryPref.nib */; }; + D284BEF90ADD80B00027CCDF /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = FF260A4B07B4477F00CE10E5 /* InfoPlist.strings */; }; + D284BEFB0ADD80B00027CCDF /* inprogress.tiff in Resources */ = {isa = PBXBuildFile; fileRef = FF08480607CEB8E800AE6769 /* inprogress.tiff */; }; + D284BEFC0ADD80B00027CCDF /* installtool in Resources */ = {isa = PBXBuildFile; fileRef = FF354EB108516C63007C00E1 /* installtool */; }; + D284BEFE0ADD80B00027CCDF /* DNSServiceDiscoveryPref.m in Sources */ = {isa = PBXBuildFile; fileRef = FFFB0DAC07B43CBA00B88D48 /* DNSServiceDiscoveryPref.m */; }; + D284BEFF0ADD80B00027CCDF /* PrivilegedOperations.c in Sources */ = {isa = PBXBuildFile; fileRef = FFFB0DAD07B43CBA00B88D48 /* PrivilegedOperations.c */; }; + D284BF000ADD80B00027CCDF /* ConfigurationAuthority.c in Sources */ = {isa = PBXBuildFile; fileRef = FFFB0DAE07B43CBA00B88D48 /* ConfigurationAuthority.c */; }; + D284BF020ADD80B00027CCDF /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 65713D46025A293200000109 /* SystemConfiguration.framework */; }; + D284BF030ADD80B00027CCDF /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7F869685066EE02400D2A2DC /* Security.framework */; }; + D284BF040ADD80B00027CCDF /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FF2609FA07B4433800CE10E5 /* Cocoa.framework */; }; + D284BF050ADD80B00027CCDF /* PreferencePanes.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FF260A1F07B4436900CE10E5 /* PreferencePanes.framework */; }; + D284BF060ADD80B00027CCDF /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 09AB6884FE841BABC02AAC07 /* CoreFoundation.framework */; }; + FFA572330AF18F1C0055A0F1 /* dnssd_ipc.c in Sources */ = {isa = PBXBuildFile; fileRef = F5E11B5A04A28126019798ED /* dnssd_ipc.c */; }; + FFA572340AF18F1C0055A0F1 /* dnssd_clientlib.c in Sources */ = {isa = PBXBuildFile; fileRef = FFFA38620AEEDB090065B80A /* dnssd_clientlib.c */; }; + FFA572350AF18F1C0055A0F1 /* dnssd_clientstub.c in Sources */ = {isa = PBXBuildFile; fileRef = FFFA38640AEEDB130065B80A /* dnssd_clientstub.c */; }; + FFA5723F0AF18F450055A0F1 /* dnssd_ipc.c in Sources */ = {isa = PBXBuildFile; fileRef = F5E11B5A04A28126019798ED /* dnssd_ipc.c */; }; + FFA572400AF18F450055A0F1 /* dnssd_clientlib.c in Sources */ = {isa = PBXBuildFile; fileRef = FFFA38620AEEDB090065B80A /* dnssd_clientlib.c */; }; + FFA572410AF18F450055A0F1 /* dnssd_clientstub.c in Sources */ = {isa = PBXBuildFile; fileRef = FFFA38640AEEDB130065B80A /* dnssd_clientstub.c */; }; + FFA572490AF18FCC0055A0F1 /* DNSServiceDiscovery.c in Sources */ = {isa = PBXBuildFile; fileRef = FFA572480AF18FCC0055A0F1 /* DNSServiceDiscovery.c */; }; + FFA5724A0AF18FCC0055A0F1 /* DNSServiceDiscovery.c in Sources */ = {isa = PBXBuildFile; fileRef = FFA572480AF18FCC0055A0F1 /* DNSServiceDiscovery.c */; }; + FFA5724B0AF18FCC0055A0F1 /* DNSServiceDiscovery.c in Sources */ = {isa = PBXBuildFile; fileRef = FFA572480AF18FCC0055A0F1 /* DNSServiceDiscovery.c */; }; + FFAE66F0105F0CD900162116 /* ddnswriteconfig in Resources */ = {isa = PBXBuildFile; fileRef = D284BEE80ADD80A70027CCDF /* ddnswriteconfig */; }; + FFB437150EB165BD00E17C68 /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 00CA213D02786FC30CCA2C71 /* IOKit.framework */; }; + FFC22AA20B00F42A00BAB070 /* DNSServiceDiscoveryRequest.defs in Sources */ = {isa = PBXBuildFile; fileRef = 6575FC01022EAFBA00000109 /* DNSServiceDiscoveryRequest.defs */; }; + FFC22AA30B00F42B00BAB070 /* DNSServiceDiscoveryRequest.defs in Sources */ = {isa = PBXBuildFile; fileRef = 6575FC01022EAFBA00000109 /* DNSServiceDiscoveryRequest.defs */; }; + FFC22AA40B00F42C00BAB070 /* DNSServiceDiscoveryRequest.defs in Sources */ = {isa = PBXBuildFile; fileRef = 6575FC01022EAFBA00000109 /* DNSServiceDiscoveryRequest.defs */; }; + FFC22AA50B00F43000BAB070 /* DNSServiceDiscoveryReply.defs in Sources */ = {isa = PBXBuildFile; fileRef = 6575FC00022EAFBA00000109 /* DNSServiceDiscoveryReply.defs */; settings = {ATTRIBUTES = (Server, ); }; }; + FFC22AA60B00F43100BAB070 /* DNSServiceDiscoveryReply.defs in Sources */ = {isa = PBXBuildFile; fileRef = 6575FC00022EAFBA00000109 /* DNSServiceDiscoveryReply.defs */; settings = {ATTRIBUTES = (Server, ); }; }; + FFC22AA70B00F43100BAB070 /* DNSServiceDiscoveryReply.defs in Sources */ = {isa = PBXBuildFile; fileRef = 6575FC00022EAFBA00000109 /* DNSServiceDiscoveryReply.defs */; settings = {ATTRIBUTES = (Server, ); }; }; + FFF589B70E37F66800EF515C /* ClientCommon.c in Sources */ = {isa = PBXBuildFile; fileRef = FF5852100DD27BD300862BDF /* ClientCommon.c */; }; + FFF589C10E37F67E00EF515C /* ClientCommon.c in Sources */ = {isa = PBXBuildFile; fileRef = FF5852100DD27BD300862BDF /* ClientCommon.c */; }; + FFFA38630AEEDB090065B80A /* dnssd_clientlib.c in Sources */ = {isa = PBXBuildFile; fileRef = FFFA38620AEEDB090065B80A /* dnssd_clientlib.c */; }; + FFFA38650AEEDB130065B80A /* dnssd_clientstub.c in Sources */ = {isa = PBXBuildFile; fileRef = FFFA38640AEEDB130065B80A /* dnssd_clientstub.c */; }; + FFFA38660AEEDB2B0065B80A /* dnssd_ipc.c in Sources */ = {isa = PBXBuildFile; fileRef = F5E11B5A04A28126019798ED /* dnssd_ipc.c */; }; + FFFF8F810C3307C400722979 /* dnsextd.conf in CopyFiles */ = {isa = PBXBuildFile; fileRef = FFFF8F800C3307AC00722979 /* dnsextd.conf */; }; +/* End PBXBuildFile section */ + +/* Begin PBXBuildRule section */ + D284BF750ADD850C0027CCDF /* PBXBuildRule */ = { + isa = PBXBuildRule; + compilerSpec = com.apple.compilers.proxy.script; + fileType = sourcecode.yacc; + isEditable = 1; + outputFiles = ( + "$(DERIVED_FILE_DIR)/$(INPUT_FILE_BASE).h", + "$(DERIVED_FILE_DIR)/$(INPUT_FILE_BASE).c", + ); + script = "echo NOOP yacc ${INPUT_FILE_PATH}"; + }; + D284BFB80ADD8E510027CCDF /* PBXBuildRule */ = { + isa = PBXBuildRule; + compilerSpec = com.apple.compilers.proxy.script; + fileType = sourcecode.lex; + isEditable = 1; + outputFiles = ( + "$(DERIVED_FILE_DIR)/$(INPUT_FILE_BASE).c", + ); + script = "/usr/bin/flex -i -o${DERIVED_FILE_DIR}/${INPUT_FILE_BASE}.c ${INPUT_FILE_PATH}"; + }; +/* End PBXBuildRule section */ + +/* Begin PBXContainerItemProxy section */ + 03067D670C83A3830022BE1F /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = D284BE500ADD80740027CCDF; + remoteInfo = mDNSResponder; + }; + 03067D690C83A3890022BE1F /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = D284BE750ADD80800027CCDF; + remoteInfo = "mDNSResponder debug"; + }; + 03067D6B0C83A3920022BE1F /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = D284BEA50ADD80920027CCDF; + remoteInfo = "dns-sd tool"; + }; + 03067D6D0C83A39C0022BE1F /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 2E0405EF0C31955500F13B59; + remoteInfo = mDNSResponderHelper; + }; + 03067D850C849CC30022BE1F /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 03067D640C83A3700022BE1F; + remoteInfo = "Build Some"; + }; + 2130257012400E9300AC839F /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = FFA572650AF190F10055A0F1; + remoteInfo = SystemLibrariesDynamic; + }; + 2141DCFC123FFB7D0086D23E /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 2141DCF8123FFB5D0086D23E; + remoteInfo = SystemLibraries; + }; + 2141DD0D123FFC960086D23E /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 2141DD0B123FFC7F0086D23E; + remoteInfo = SystemLibrariesStatic; + }; + 215FFB18124002C100470DE1 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 2141DD1C123FFCDB0086D23E; + remoteInfo = libdns_sd_static; + }; + 215FFB1A124002C700470DE1 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 2141DD23123FFD0F0086D23E; + remoteInfo = libdns_sd_debug_static; + }; + 215FFB1C124002CC00470DE1 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 2141DD29123FFD2C0086D23E; + remoteInfo = libdns_sd_profile_static; + }; + 217A4C48138EE14C000A5BA8 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 213FB21712028A7A002B3A08; + remoteInfo = BonjourEvents; + }; + 4AE471690EAFF83800A6C5AD /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 4AE471670EAFF81900A6C5AD; + remoteInfo = dns_sd.jar; + }; + 72FB5469166D5FE40090B2D9 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 72FB545E166D5FB00090B2D9; + remoteInfo = dnsctl; + }; + 84C5B3401665544B00C324A8 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 84C5B3341665529800C324A8; + remoteInfo = dns_services; + }; + D284BF2B0ADD815A0027CCDF /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = D284BEBF0ADD80A20027CCDF; + remoteInfo = dnsextd; + }; + D284BF2D0ADD81600027CCDF /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = D284BEDB0ADD80A70027CCDF; + remoteInfo = ddnswriteconfig; + }; + D284BF2F0ADD81630027CCDF /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = D284BEEA0ADD80B00027CCDF; + remoteInfo = PreferencePane; + }; + FFA572680AF190FF0055A0F1 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = FFB765830AEED9C700583A2C; + remoteInfo = libdns_sd; + }; + FFA5726A0AF191010055A0F1 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = FFA572300AF18F1C0055A0F1; + remoteInfo = "libdns_sd debug"; + }; + FFA5726C0AF191020055A0F1 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = FFA5723C0AF18F450055A0F1; + remoteInfo = "libdns_sd profile"; + }; + FFAE66F8105F0CF100162116 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = D284BEDB0ADD80A70027CCDF; + remoteInfo = ddnswriteconfig; + }; + FFB7657C0AEED97F00583A2C /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 00AD62BB032D7A0C0CCA2C71; + remoteInfo = "Build Main"; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 4A7B9E7F14FDA21B00B84CC1 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 8; + dstPath = /usr/local/OpenSourceVersions; + dstSubfolderSpec = 0; + files = ( + 4A7B9E8014FDA25000B84CC1 /* mDNSResponder.plist in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + 4A7B9E8114FDA25500B84CC1 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 8; + dstPath = /usr/local/OpenSourceLicenses; + dstSubfolderSpec = 0; + files = ( + 4A7B9E8214FDA26C00B84CC1 /* mDNSResponder.txt in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + 4AAE0C5A0C68E6EC003882A5 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 8; + dstPath = /usr/share/man/man8; + dstSubfolderSpec = 0; + files = ( + 4AAE0C9A0C68EA81003882A5 /* mDNSResponderHelper.8 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + 72FB545D166D5FB00090B2D9 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 12; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 8418673D15AB8BFF00BB7F70 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 8; + dstPath = /private/etc/asl/; + dstSubfolderSpec = 0; + files = ( + 8418673E15AB8C2D00BB7F70 /* com.apple.networking.mDNSResponder in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + D284BE6A0ADD80740027CCDF /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 8; + dstPath = /usr/share/man/man8; + dstSubfolderSpec = 0; + files = ( + D284BE6B0ADD80740027CCDF /* mDNSResponder.8 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + D284BEAB0ADD80920027CCDF /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 8; + dstPath = /usr/share/man/man1; + dstSubfolderSpec = 0; + files = ( + D284BEAC0ADD80920027CCDF /* dns-sd.1 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + D284BED40ADD80A20027CCDF /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 8; + dstPath = /usr/share/man/man8; + dstSubfolderSpec = 0; + files = ( + D284BED50ADD80A20027CCDF /* dnsextd.8 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + FFFF8F770C32F0FD00722979 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 8; + dstPath = /private/etc; + dstSubfolderSpec = 0; + files = ( + FFFF8F810C3307C400722979 /* dnsextd.conf in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 000753D303367C1C0CCA2C71 /* mDNSMacOSX.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mDNSMacOSX.h; sourceTree = "<group>"; }; + 00CA213D02786FC30CCA2C71 /* IOKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOKit.framework; path = /System/Library/Frameworks/IOKit.framework; sourceTree = "<absolute>"; }; + 09AB6884FE841BABC02AAC07 /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = /System/Library/Frameworks/CoreFoundation.framework; sourceTree = "<absolute>"; }; + 21070E5D16486B9000A69507 /* DNSSECSupport.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = DNSSECSupport.c; sourceTree = "<group>"; }; + 21070E5E16486B9000A69507 /* DNSSECSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DNSSECSupport.h; sourceTree = "<group>"; }; + 2120ABD416B71614007089B6 /* CUPolicy.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = CUPolicy.c; sourceTree = "<group>"; }; + 2124FA2B1471E98C0021D7BB /* nsec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = nsec.h; path = ../mDNSCore/nsec.h; sourceTree = "<group>"; }; + 2124FA2F1471E9B50021D7BB /* dnssec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dnssec.h; path = ../mDNSCore/dnssec.h; sourceTree = "<group>"; }; + 2124FA321471E9DE0021D7BB /* nsec.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = nsec.c; path = ../mDNSCore/nsec.c; sourceTree = "<group>"; }; + 2127A47515C3C7B900A857FC /* nsec3.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = nsec3.c; path = ../mDNSCore/nsec3.c; sourceTree = "<group>"; }; + 2127A47615C3C7B900A857FC /* nsec3.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = nsec3.h; path = ../mDNSCore/nsec3.h; sourceTree = "<group>"; }; + 213BDC6C147319F400000896 /* dnssec.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = dnssec.c; path = ../mDNSCore/dnssec.c; sourceTree = "<group>"; }; + 213FB21812028A7A002B3A08 /* BonjourEvents.plugin */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = BonjourEvents.plugin; sourceTree = BUILT_PRODUCTS_DIR; }; + 213FB22C12028B53002B3A08 /* BonjourEvents.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = BonjourEvents.c; sourceTree = "<group>"; }; + 213FB22D12028B53002B3A08 /* BonjourEvents-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "BonjourEvents-Info.plist"; sourceTree = "<group>"; }; + 2141DD1D123FFCDB0086D23E /* libdns_sd.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libdns_sd.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 2141DD24123FFD0F0086D23E /* libdns_sd_debug.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libdns_sd_debug.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 2141DD2A123FFD2C0086D23E /* libdns_sd_profile.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libdns_sd_profile.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 216D9ACD1720C9F5008066E1 /* VPNService.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = VPNService.c; sourceTree = "<group>"; }; + 218E8E4F156D8C0300720DA0 /* dnsproxy.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = dnsproxy.c; path = ../mDNSCore/dnsproxy.c; sourceTree = "<group>"; }; + 218E8E50156D8C0300720DA0 /* dnsproxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dnsproxy.h; path = ../mDNSCore/dnsproxy.h; sourceTree = "<group>"; }; + 219D5541149ED645004464AE /* libxml2.2.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libxml2.2.dylib; path = SDKs/MacOSX10.8.sdk/usr/lib/libxml2.2.dylib; sourceTree = DEVELOPER_DIR; }; + 21A57F4A145B2AE100939099 /* CryptoAlg.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = CryptoAlg.c; path = ../mDNSCore/CryptoAlg.c; sourceTree = "<group>"; }; + 21A57F4B145B2AE100939099 /* CryptoAlg.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CryptoAlg.h; path = ../mDNSCore/CryptoAlg.h; sourceTree = "<group>"; }; + 21A57F51145B2B1400939099 /* CryptoSupport.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = CryptoSupport.c; sourceTree = "<group>"; }; + 21A57F52145B2B1400939099 /* CryptoSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CryptoSupport.h; sourceTree = "<group>"; }; + 21DD8FBD161E9A250033C8F8 /* anonymous.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = anonymous.c; path = ../mDNSCore/anonymous.c; sourceTree = "<group>"; }; + 21DD8FBE161E9A250033C8F8 /* anonymous.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = anonymous.h; path = ../mDNSCore/anonymous.h; sourceTree = "<group>"; }; + 21DED43415702C0F0060B6B9 /* DNSProxySupport.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = DNSProxySupport.c; sourceTree = "<group>"; }; + 21F432971134AA6800581B69 /* WebFilterDNS.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WebFilterDNS.framework; path = /System/Library/PrivateFrameworks/WebFilterDNS.framework; sourceTree = "<absolute>"; }; + 2E0405EB0C3190DC00F13B59 /* helpermsg.defs */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.mig; path = helpermsg.defs; sourceTree = "<group>"; }; + 2E0405F00C31955500F13B59 /* mDNSResponderHelper */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = mDNSResponderHelper; sourceTree = BUILT_PRODUCTS_DIR; }; + 2E0405F40C3195F700F13B59 /* helper.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; path = helper.c; sourceTree = "<group>"; }; + 2E0406140C3197CB00F13B59 /* libbsm.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libbsm.dylib; path = /usr/lib/libbsm.dylib; sourceTree = "<absolute>"; }; + 2E0406CA0C31E9AD00F13B59 /* helper-main.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; path = "helper-main.c"; sourceTree = "<group>"; }; + 2E35528F0C3A95C100CA1CB7 /* helper-error.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = "helper-error.h"; sourceTree = "<group>"; }; + 2E8165F60C59835F00485EB2 /* libipsec.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libipsec.dylib; path = /usr/lib/libipsec.dylib; sourceTree = "<absolute>"; }; + 2E96A5250C39BE480087C4D2 /* helper.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = helper.h; sourceTree = "<group>"; }; + 2E96A52D0C39C1A50087C4D2 /* helper-stubs.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "helper-stubs.c"; sourceTree = "<group>"; }; + 2ECC11A50C4FEC3800CB1885 /* helpermsg-types.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "helpermsg-types.h"; sourceTree = "<group>"; }; + 2EDC5E720C39EA640092701B /* helper-server.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = "helper-server.h"; sourceTree = "<group>"; }; + 4A2E69DD0F5475A3004A87B0 /* uds_daemon.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = uds_daemon.h; path = ../mDNSShared/uds_daemon.h; sourceTree = SOURCE_ROOT; }; + 4A3600DF0F34F8CD00453EFB /* DeviceToDeviceManager.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = DeviceToDeviceManager.framework; path = /System/Library/PrivateFrameworks/DeviceToDeviceManager.framework; sourceTree = "<absolute>"; }; + 4A7B9E7C14FDA19F00B84CC1 /* mDNSResponder.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = mDNSResponder.txt; sourceTree = "<group>"; }; + 4A7B9E7E14FDA1BB00B84CC1 /* mDNSResponder.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = mDNSResponder.plist; sourceTree = "<group>"; }; + 4A8202510C56C36500DDFD48 /* ipsec_strerror.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ipsec_strerror.h; sourceTree = "<group>"; }; + 4A8202520C56C36500DDFD48 /* libpfkey.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = libpfkey.h; sourceTree = "<group>"; }; + 4A8202530C56C36600DDFD48 /* pfkey.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = pfkey.c; sourceTree = "<group>"; }; + 4AAE0C7A0C68E97F003882A5 /* mDNSResponderHelper.8 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = mDNSResponderHelper.8; sourceTree = SOURCE_ROOT; }; + 4ADB5F230F6AB9F400B95BF3 /* helper-entitlements.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "helper-entitlements.plist"; sourceTree = "<group>"; }; + 4BD2B638134FE09F002B96D5 /* P2PPacketFilter.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = P2PPacketFilter.c; sourceTree = "<group>"; }; + 4BD2B639134FE09F002B96D5 /* P2PPacketFilter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = P2PPacketFilter.h; sourceTree = "<group>"; }; + 654BE64F02B63B93000001D1 /* mDNSEmbeddedAPI.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = mDNSEmbeddedAPI.h; path = ../mDNSCore/mDNSEmbeddedAPI.h; sourceTree = "<group>"; }; + 654BE65002B63B93000001D1 /* mDNSDebug.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = mDNSDebug.h; path = ../mDNSCore/mDNSDebug.h; sourceTree = "<group>"; }; + 65713D46025A293200000109 /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = /System/Library/Frameworks/SystemConfiguration.framework; sourceTree = "<absolute>"; }; + 6575FBE9022EAF5A00000109 /* mDNS.c */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.c; name = mDNS.c; path = ../mDNSCore/mDNS.c; sourceTree = "<group>"; tabWidth = 4; usesTabs = 1; }; + 6575FBEB022EAF7200000109 /* mDNSMacOSX.c */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.c; path = mDNSMacOSX.c; sourceTree = "<group>"; tabWidth = 4; usesTabs = 1; }; + 6575FBEC022EAF7200000109 /* daemon.c */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.c; path = daemon.c; sourceTree = "<group>"; tabWidth = 4; usesTabs = 1; }; + 6575FBFF022EAFBA00000109 /* DNSServiceDiscoveryDefines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DNSServiceDiscoveryDefines.h; sourceTree = "<group>"; }; + 6575FC00022EAFBA00000109 /* DNSServiceDiscoveryReply.defs */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.mig; path = DNSServiceDiscoveryReply.defs; sourceTree = "<group>"; }; + 6575FC01022EAFBA00000109 /* DNSServiceDiscoveryRequest.defs */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.mig; path = DNSServiceDiscoveryRequest.defs; sourceTree = "<group>"; }; + 6575FC20022EB7AA00000109 /* SamplemDNSClient.c */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.c; path = SamplemDNSClient.c; sourceTree = SOURCE_ROOT; tabWidth = 4; usesTabs = 0; }; + 72FB545A166D5F960090B2D9 /* dnsctl.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = dnsctl.c; path = ../Clients/dnsctl.c; sourceTree = "<group>"; }; + 72FB545F166D5FB00090B2D9 /* dnsctl */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = dnsctl; sourceTree = BUILT_PRODUCTS_DIR; }; + 7F18A9F60587CEF6001880B3 /* DNSCommon.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = DNSCommon.c; path = ../mDNSCore/DNSCommon.c; sourceTree = SOURCE_ROOT; }; + 7F18A9F70587CEF6001880B3 /* uDNS.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = uDNS.c; path = ../mDNSCore/uDNS.c; sourceTree = SOURCE_ROOT; }; + 7F461DB5062DBF2900672BF3 /* DNSDigest.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = DNSDigest.c; path = ../mDNSCore/DNSDigest.c; sourceTree = SOURCE_ROOT; }; + 7F869685066EE02400D2A2DC /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = /System/Library/Frameworks/Security.framework; sourceTree = "<absolute>"; }; + 7FC8F9D406D14E66007E879D /* LegacyNATTraversal.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = LegacyNATTraversal.c; sourceTree = SOURCE_ROOT; }; + 8418673A15AB8B6900BB7F70 /* com.apple.networking.mDNSResponder */ = {isa = PBXFileReference; lastKnownFileType = text; path = com.apple.networking.mDNSResponder; sourceTree = "<group>"; }; + 8418673C15AB8B8000BB7F70 /* mDNSResponderLogging.mobileconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = mDNSResponderLogging.mobileconfig; sourceTree = "<group>"; }; + 848DA5C6165477E000D2E8B4 /* xpc_services.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = xpc_services.c; path = Private/xpc_services.c; sourceTree = "<group>"; }; + 848DA5C9165477EB00D2E8B4 /* xpc_services.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = xpc_services.h; path = Private/xpc_services.h; sourceTree = "<group>"; }; + 848DA5D516547F7200D2E8B4 /* dns_xpc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dns_xpc.h; path = Private/dns_xpc.h; sourceTree = "<group>"; }; + 84C5B3351665529800C324A8 /* libdns_services.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libdns_services.dylib; sourceTree = BUILT_PRODUCTS_DIR; }; + 84C5B338166553A000C324A8 /* dns_services.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = dns_services.h; path = Private/dns_services.h; sourceTree = "<group>"; }; + 84C5B339166553AF00C324A8 /* dns_services.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = dns_services.c; path = Private/dns_services.c; sourceTree = "<group>"; }; + D284BE730ADD80740027CCDF /* mDNSResponder */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = mDNSResponder; sourceTree = BUILT_PRODUCTS_DIR; }; + D284BE950ADD80800027CCDF /* mDNSResponder.debug */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = mDNSResponder.debug; sourceTree = BUILT_PRODUCTS_DIR; }; + D284BEB00ADD80920027CCDF /* dns-sd */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "dns-sd"; sourceTree = BUILT_PRODUCTS_DIR; }; + D284BEBE0ADD809A0027CCDF /* libjdns_sd.jnilib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libjdns_sd.jnilib; sourceTree = BUILT_PRODUCTS_DIR; }; + D284BED90ADD80A20027CCDF /* dnsextd */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = dnsextd; sourceTree = BUILT_PRODUCTS_DIR; }; + D284BEE80ADD80A70027CCDF /* ddnswriteconfig */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = ddnswriteconfig; sourceTree = BUILT_PRODUCTS_DIR; }; + D284BF0C0ADD80B00027CCDF /* Bonjour.prefPane */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Bonjour.prefPane; sourceTree = BUILT_PRODUCTS_DIR; }; + D284C04D0ADD95D30027CCDF /* Info-PreferencePane.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = "Info-PreferencePane.plist"; path = "PreferencePane/Info-PreferencePane.plist"; sourceTree = "<group>"; }; + DB2CC4430662DD1100335AB3 /* BaseListener.java */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.java; name = BaseListener.java; path = ../mDNSShared/Java/BaseListener.java; sourceTree = SOURCE_ROOT; }; + DB2CC4440662DD1100335AB3 /* BrowseListener.java */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.java; name = BrowseListener.java; path = ../mDNSShared/Java/BrowseListener.java; sourceTree = SOURCE_ROOT; }; + DB2CC4450662DD1100335AB3 /* DNSRecord.java */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.java; name = DNSRecord.java; path = ../mDNSShared/Java/DNSRecord.java; sourceTree = SOURCE_ROOT; }; + DB2CC4460662DD1100335AB3 /* DNSSD.java */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.java; name = DNSSD.java; path = ../mDNSShared/Java/DNSSD.java; sourceTree = SOURCE_ROOT; }; + DB2CC4470662DD1100335AB3 /* DNSSDException.java */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.java; name = DNSSDException.java; path = ../mDNSShared/Java/DNSSDException.java; sourceTree = SOURCE_ROOT; }; + DB2CC4480662DD1100335AB3 /* DNSSDRegistration.java */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.java; name = DNSSDRegistration.java; path = ../mDNSShared/Java/DNSSDRegistration.java; sourceTree = SOURCE_ROOT; }; + DB2CC4490662DD1100335AB3 /* DNSSDService.java */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.java; name = DNSSDService.java; path = ../mDNSShared/Java/DNSSDService.java; sourceTree = SOURCE_ROOT; }; + DB2CC44A0662DD1100335AB3 /* DomainListener.java */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.java; name = DomainListener.java; path = ../mDNSShared/Java/DomainListener.java; sourceTree = SOURCE_ROOT; }; + DB2CC44B0662DD1100335AB3 /* JNISupport.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = JNISupport.c; path = ../mDNSShared/Java/JNISupport.c; sourceTree = SOURCE_ROOT; }; + DB2CC44C0662DD1100335AB3 /* QueryListener.java */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.java; name = QueryListener.java; path = ../mDNSShared/Java/QueryListener.java; sourceTree = SOURCE_ROOT; }; + DB2CC44D0662DD1100335AB3 /* RegisterListener.java */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.java; name = RegisterListener.java; path = ../mDNSShared/Java/RegisterListener.java; sourceTree = SOURCE_ROOT; }; + DB2CC44E0662DD1100335AB3 /* ResolveListener.java */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.java; name = ResolveListener.java; path = ../mDNSShared/Java/ResolveListener.java; sourceTree = SOURCE_ROOT; }; + DB2CC44F0662DD1100335AB3 /* TXTRecord.java */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.java; name = TXTRecord.java; path = ../mDNSShared/Java/TXTRecord.java; sourceTree = SOURCE_ROOT; }; + DB2CC4680662DFF500335AB3 /* JavaVM.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaVM.framework; path = /System/Library/Frameworks/JavaVM.framework; sourceTree = "<absolute>"; }; + DBAAFE29057E8F4D0085CAD0 /* mDNSDebug.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = mDNSDebug.c; path = ../mDNSShared/mDNSDebug.c; sourceTree = SOURCE_ROOT; }; + DBAAFE2C057E8F660085CAD0 /* GenLinkedList.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = GenLinkedList.c; path = ../mDNSShared/GenLinkedList.c; sourceTree = SOURCE_ROOT; }; + F525E72804AA167501F1CF4D /* uds_daemon.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = uds_daemon.c; path = ../mDNSShared/uds_daemon.c; sourceTree = SOURCE_ROOT; }; + F5E11B5A04A28126019798ED /* dnssd_ipc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = dnssd_ipc.c; path = ../mDNSShared/dnssd_ipc.c; sourceTree = SOURCE_ROOT; }; + F5E11B5B04A28126019798ED /* dnssd_ipc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dnssd_ipc.h; path = ../mDNSShared/dnssd_ipc.h; sourceTree = SOURCE_ROOT; }; + FF08480607CEB8E800AE6769 /* inprogress.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; name = inprogress.tiff; path = PreferencePane/Artwork/inprogress.tiff; sourceTree = SOURCE_ROOT; }; + FF13FFEA0A5DA44A00897C81 /* dnsextd_lexer.l */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.lex; name = dnsextd_lexer.l; path = ../mDNSShared/dnsextd_lexer.l; sourceTree = SOURCE_ROOT; }; + FF13FFEC0A5DA45500897C81 /* dnsextd_parser.y */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.yacc; name = dnsextd_parser.y; path = ../mDNSShared/dnsextd_parser.y; sourceTree = SOURCE_ROOT; }; + FF1C919D07021D77001048AB /* dns-sd.1 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.man; name = "dns-sd.1"; path = "../mDNSShared/dns-sd.1"; sourceTree = SOURCE_ROOT; }; + FF1C919F07021E3F001048AB /* dns-sd.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = "dns-sd.c"; path = "../Clients/dns-sd.c"; sourceTree = SOURCE_ROOT; }; + FF25794606C9A8BF00376F7B /* dnsextd.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = dnsextd.c; path = ../mDNSShared/dnsextd.c; sourceTree = SOURCE_ROOT; }; + FF2609FA07B4433800CE10E5 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = "<absolute>"; }; + FF260A1F07B4436900CE10E5 /* PreferencePanes.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = PreferencePanes.framework; path = /System/Library/Frameworks/PreferencePanes.framework; sourceTree = "<absolute>"; }; + FF260A2407B4464B00CE10E5 /* remove_idle.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; name = remove_idle.tiff; path = PreferencePane/Artwork/remove_idle.tiff; sourceTree = SOURCE_ROOT; }; + FF260A2507B4464B00CE10E5 /* add_pressed.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; name = add_pressed.tiff; path = PreferencePane/Artwork/add_pressed.tiff; sourceTree = SOURCE_ROOT; }; + FF260A2607B4464B00CE10E5 /* remove_disabled.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; name = remove_disabled.tiff; path = PreferencePane/Artwork/remove_disabled.tiff; sourceTree = SOURCE_ROOT; }; + FF260A2707B4464B00CE10E5 /* add_idle.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; name = add_idle.tiff; path = PreferencePane/Artwork/add_idle.tiff; sourceTree = SOURCE_ROOT; }; + FF260A2807B4464B00CE10E5 /* success.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; name = success.tiff; path = PreferencePane/Artwork/success.tiff; sourceTree = SOURCE_ROOT; }; + FF260A2907B4464B00CE10E5 /* remove_pressed.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; name = remove_pressed.tiff; path = PreferencePane/Artwork/remove_pressed.tiff; sourceTree = SOURCE_ROOT; }; + FF260A2A07B4464B00CE10E5 /* failure.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; name = failure.tiff; path = PreferencePane/Artwork/failure.tiff; sourceTree = SOURCE_ROOT; }; + FF260A3207B4466900CE10E5 /* BonjourPref.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; name = BonjourPref.icns; path = PreferencePane/BonjourPref.icns; sourceTree = SOURCE_ROOT; }; + FF260A3307B4466900CE10E5 /* BonjourPref.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; name = BonjourPref.tiff; path = PreferencePane/BonjourPref.tiff; sourceTree = SOURCE_ROOT; }; + FF260A4907B4475600CE10E5 /* English */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = English; path = PreferencePane/English.lproj/DNSServiceDiscoveryPref.nib; sourceTree = SOURCE_ROOT; }; + FF260A4C07B4477F00CE10E5 /* English */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = English; path = PreferencePane/English.lproj/InfoPlist.strings; sourceTree = SOURCE_ROOT; }; + FF2C5FB00A48B8680066DA11 /* DNSSDRecordRegistrar.java */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.java; name = DNSSDRecordRegistrar.java; path = ../mDNSShared/Java/DNSSDRecordRegistrar.java; sourceTree = SOURCE_ROOT; }; + FF2C5FB20A48B86E0066DA11 /* RegisterRecordListener.java */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.java; name = RegisterRecordListener.java; path = ../mDNSShared/Java/RegisterRecordListener.java; sourceTree = SOURCE_ROOT; }; + FF354EB108516C63007C00E1 /* installtool */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.perl; name = installtool; path = PreferencePane/installtool; sourceTree = SOURCE_ROOT; }; + FF485D5105632E0000130380 /* mDNSResponder.8 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = mDNSResponder.8; path = ../mDNSShared/mDNSResponder.8; sourceTree = SOURCE_ROOT; }; + FF5852100DD27BD300862BDF /* ClientCommon.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = ClientCommon.c; path = ../Clients/ClientCommon.c; sourceTree = SOURCE_ROOT; }; + FF85880B0BD599F40080D89F /* mDNSResponder.sb */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = mDNSResponder.sb; sourceTree = SOURCE_ROOT; }; + FFA572390AF18F1C0055A0F1 /* libsystem_dnssd_debug.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libsystem_dnssd_debug.dylib; sourceTree = BUILT_PRODUCTS_DIR; }; + FFA572450AF18F450055A0F1 /* libsystem_dnssd_profile.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libsystem_dnssd_profile.dylib; sourceTree = BUILT_PRODUCTS_DIR; }; + FFA572480AF18FCC0055A0F1 /* DNSServiceDiscovery.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = DNSServiceDiscovery.c; sourceTree = "<group>"; }; + FFA572600AF1908D0055A0F1 /* DNSServiceDiscovery.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DNSServiceDiscovery.h; sourceTree = "<group>"; }; + FFA572630AF190C20055A0F1 /* dns_sd.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dns_sd.h; path = ../mDNSShared/dns_sd.h; sourceTree = SOURCE_ROOT; }; + FFB765840AEED9C700583A2C /* libsystem_dnssd.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libsystem_dnssd.dylib; sourceTree = BUILT_PRODUCTS_DIR; }; + FFCB6D73075D539900B8AF62 /* PlatformCommon.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = PlatformCommon.c; path = ../mDNSShared/PlatformCommon.c; sourceTree = SOURCE_ROOT; }; + FFE6935007C2CA7F00283007 /* ConfigurationAuthority.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ConfigurationAuthority.h; path = PreferencePane/ConfigurationAuthority.h; sourceTree = SOURCE_ROOT; }; + FFE6935207C2CAA400283007 /* DNSServiceDiscoveryPref.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DNSServiceDiscoveryPref.h; path = PreferencePane/DNSServiceDiscoveryPref.h; sourceTree = SOURCE_ROOT; }; + FFE6935407C2CABD00283007 /* PrivilegedOperations.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PrivilegedOperations.h; path = PreferencePane/PrivilegedOperations.h; sourceTree = SOURCE_ROOT; }; + FFF4F63A06CFE4DD00459EFD /* dnsextd.8 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = dnsextd.8; path = ../mDNSShared/dnsextd.8; sourceTree = SOURCE_ROOT; }; + FFFA38620AEEDB090065B80A /* dnssd_clientlib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = dnssd_clientlib.c; path = ../mDNSShared/dnssd_clientlib.c; sourceTree = SOURCE_ROOT; }; + FFFA38640AEEDB130065B80A /* dnssd_clientstub.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = dnssd_clientstub.c; path = ../mDNSShared/dnssd_clientstub.c; sourceTree = SOURCE_ROOT; }; + FFFB0DAC07B43CBA00B88D48 /* DNSServiceDiscoveryPref.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DNSServiceDiscoveryPref.m; sourceTree = "<group>"; }; + FFFB0DAD07B43CBA00B88D48 /* PrivilegedOperations.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = PrivilegedOperations.c; sourceTree = "<group>"; }; + FFFB0DAE07B43CBA00B88D48 /* ConfigurationAuthority.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ConfigurationAuthority.c; sourceTree = "<group>"; }; + FFFB0DAF07B43CBA00B88D48 /* ddnswriteconfig.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ddnswriteconfig.m; sourceTree = "<group>"; }; + FFFB0DB407B43D2700B88D48 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = "<absolute>"; }; + FFFF8F800C3307AC00722979 /* dnsextd.conf */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = dnsextd.conf; path = ../mDNSShared/dnsextd.conf; sourceTree = SOURCE_ROOT; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 213FB21612028A7A002B3A08 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 213FB23D12028C5A002B3A08 /* CoreFoundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 2141DD1B123FFCDB0086D23E /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 2141DD22123FFD0F0086D23E /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 2141DD28123FFD2C0086D23E /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 2E0405EE0C31955500F13B59 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + FFB437150EB165BD00E17C68 /* IOKit.framework in Frameworks */, + 2E0406150C3197CB00F13B59 /* libbsm.dylib in Frameworks */, + 2E04070A0C31EEEC00F13B59 /* CoreFoundation.framework in Frameworks */, + 2E04070B0C31EEEC00F13B59 /* SystemConfiguration.framework in Frameworks */, + 2E4D9B050C38C19500480551 /* Security.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 72FB545C166D5FB00090B2D9 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 72FB5468166D5FD20090B2D9 /* libdns_services.dylib in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 84C5B3321665529800C324A8 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D284BE640ADD80740027CCDF /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + D284BE650ADD80740027CCDF /* CoreFoundation.framework in Frameworks */, + D284BE660ADD80740027CCDF /* SystemConfiguration.framework in Frameworks */, + D284BE670ADD80740027CCDF /* IOKit.framework in Frameworks */, + D284BE680ADD80740027CCDF /* Security.framework in Frameworks */, + 219D5542149ED645004464AE /* libxml2.2.dylib in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D284BE8C0ADD80800027CCDF /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + D284BE8D0ADD80800027CCDF /* CoreFoundation.framework in Frameworks */, + D284BE8E0ADD80800027CCDF /* SystemConfiguration.framework in Frameworks */, + D284BE8F0ADD80800027CCDF /* IOKit.framework in Frameworks */, + D284BE900ADD80800027CCDF /* Security.framework in Frameworks */, + 219D5543149ED645004464AE /* libxml2.2.dylib in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D284BEA90ADD80920027CCDF /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D284BEB80ADD809A0027CCDF /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + D284BEB90ADD809A0027CCDF /* JavaVM.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D284BECE0ADD80A20027CCDF /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + D284BECF0ADD80A20027CCDF /* CoreFoundation.framework in Frameworks */, + D284BED00ADD80A20027CCDF /* SystemConfiguration.framework in Frameworks */, + D284BED10ADD80A20027CCDF /* IOKit.framework in Frameworks */, + D284BED20ADD80A20027CCDF /* Security.framework in Frameworks */, + 2E8165F90C59838100485EB2 /* libipsec.dylib in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D284BEDF0ADD80A70027CCDF /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + D284BEE00ADD80A70027CCDF /* Foundation.framework in Frameworks */, + D284BEE10ADD80A70027CCDF /* Security.framework in Frameworks */, + D284BEE20ADD80A70027CCDF /* SystemConfiguration.framework in Frameworks */, + D284BEE30ADD80A70027CCDF /* CoreFoundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D284BF010ADD80B00027CCDF /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + D284BF020ADD80B00027CCDF /* SystemConfiguration.framework in Frameworks */, + D284BF030ADD80B00027CCDF /* Security.framework in Frameworks */, + D284BF040ADD80B00027CCDF /* Cocoa.framework in Frameworks */, + D284BF050ADD80B00027CCDF /* PreferencePanes.framework in Frameworks */, + D284BF060ADD80B00027CCDF /* CoreFoundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FFA572360AF18F1C0055A0F1 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FFA572420AF18F450055A0F1 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FFB765820AEED9C700583A2C /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 08FB7794FE84155DC02AAC07 /* mDNSResponder */ = { + isa = PBXGroup; + children = ( + 08FB7795FE84155DC02AAC07 /* mDNS Server Sources */, + 6575FC1F022EB78C00000109 /* Command-Line Clients */, + 213FB20912028902002B3A08 /* Bonjour Events Plugin */, + 6575FBFE022EAFA800000109 /* MIG files */, + DB2CC4420662DCE500335AB3 /* Java Support */, + FFFB0DA407B43BED00B88D48 /* PreferencePane */, + 08FB779DFE84155DC02AAC07 /* External Frameworks and Libraries */, + 19C28FBDFE9D53C911CA2CBB /* Products */, + ); + name = mDNSResponder; + sourceTree = "<group>"; + }; + 08FB7795FE84155DC02AAC07 /* mDNS Server Sources */ = { + isa = PBXGroup; + children = ( + 216D9ACD1720C9F5008066E1 /* VPNService.c */, + 2120ABD416B71614007089B6 /* CUPolicy.c */, + 84C5B338166553A000C324A8 /* dns_services.h */, + 72FB545A166D5F960090B2D9 /* dnsctl.c */, + 84C5B339166553AF00C324A8 /* dns_services.c */, + 848DA5D516547F7200D2E8B4 /* dns_xpc.h */, + 848DA5C9165477EB00D2E8B4 /* xpc_services.h */, + 848DA5C6165477E000D2E8B4 /* xpc_services.c */, + 21070E5D16486B9000A69507 /* DNSSECSupport.c */, + 21070E5E16486B9000A69507 /* DNSSECSupport.h */, + 21DD8FBD161E9A250033C8F8 /* anonymous.c */, + 21DD8FBE161E9A250033C8F8 /* anonymous.h */, + 2127A47515C3C7B900A857FC /* nsec3.c */, + 2127A47615C3C7B900A857FC /* nsec3.h */, + 8418673C15AB8B8000BB7F70 /* mDNSResponderLogging.mobileconfig */, + 8418673A15AB8B6900BB7F70 /* com.apple.networking.mDNSResponder */, + 21DED43415702C0F0060B6B9 /* DNSProxySupport.c */, + 218E8E4F156D8C0300720DA0 /* dnsproxy.c */, + 218E8E50156D8C0300720DA0 /* dnsproxy.h */, + 213BDC6C147319F400000896 /* dnssec.c */, + 2124FA321471E9DE0021D7BB /* nsec.c */, + 2124FA2F1471E9B50021D7BB /* dnssec.h */, + 2124FA2B1471E98C0021D7BB /* nsec.h */, + 21A57F51145B2B1400939099 /* CryptoSupport.c */, + 21A57F52145B2B1400939099 /* CryptoSupport.h */, + 21A57F4A145B2AE100939099 /* CryptoAlg.c */, + 21A57F4B145B2AE100939099 /* CryptoAlg.h */, + 4ADB5F230F6AB9F400B95BF3 /* helper-entitlements.plist */, + 4A2E69DD0F5475A3004A87B0 /* uds_daemon.h */, + 4AAE0C7A0C68E97F003882A5 /* mDNSResponderHelper.8 */, + 2ECC11A50C4FEC3800CB1885 /* helpermsg-types.h */, + 2E35528F0C3A95C100CA1CB7 /* helper-error.h */, + 2E96A52D0C39C1A50087C4D2 /* helper-stubs.c */, + 2EDC5E720C39EA640092701B /* helper-server.h */, + 2E96A5250C39BE480087C4D2 /* helper.h */, + 2E0405F40C3195F700F13B59 /* helper.c */, + 2E0406CA0C31E9AD00F13B59 /* helper-main.c */, + 4A8202510C56C36500DDFD48 /* ipsec_strerror.h */, + 4A8202520C56C36500DDFD48 /* libpfkey.h */, + 4A8202530C56C36600DDFD48 /* pfkey.c */, + 7FC8F9D406D14E66007E879D /* LegacyNATTraversal.c */, + 7F461DB5062DBF2900672BF3 /* DNSDigest.c */, + F525E72804AA167501F1CF4D /* uds_daemon.c */, + F5E11B5A04A28126019798ED /* dnssd_ipc.c */, + F5E11B5B04A28126019798ED /* dnssd_ipc.h */, + 6575FBEC022EAF7200000109 /* daemon.c */, + 6575FBE9022EAF5A00000109 /* mDNS.c */, + 6575FBEB022EAF7200000109 /* mDNSMacOSX.c */, + 654BE64F02B63B93000001D1 /* mDNSEmbeddedAPI.h */, + 654BE65002B63B93000001D1 /* mDNSDebug.h */, + DBAAFE29057E8F4D0085CAD0 /* mDNSDebug.c */, + 000753D303367C1C0CCA2C71 /* mDNSMacOSX.h */, + DBAAFE2C057E8F660085CAD0 /* GenLinkedList.c */, + FFCB6D73075D539900B8AF62 /* PlatformCommon.c */, + FF1C919D07021D77001048AB /* dns-sd.1 */, + FF485D5105632E0000130380 /* mDNSResponder.8 */, + FFF4F63A06CFE4DD00459EFD /* dnsextd.8 */, + FFFF8F800C3307AC00722979 /* dnsextd.conf */, + FF85880B0BD599F40080D89F /* mDNSResponder.sb */, + 4A7B9E7C14FDA19F00B84CC1 /* mDNSResponder.txt */, + 4A7B9E7E14FDA1BB00B84CC1 /* mDNSResponder.plist */, + 7F18A9F60587CEF6001880B3 /* DNSCommon.c */, + 7F18A9F70587CEF6001880B3 /* uDNS.c */, + FF25794606C9A8BF00376F7B /* dnsextd.c */, + FF13FFEA0A5DA44A00897C81 /* dnsextd_lexer.l */, + FF13FFEC0A5DA45500897C81 /* dnsextd_parser.y */, + FFFA38620AEEDB090065B80A /* dnssd_clientlib.c */, + FFFA38640AEEDB130065B80A /* dnssd_clientstub.c */, + FFA572600AF1908D0055A0F1 /* DNSServiceDiscovery.h */, + FFA572480AF18FCC0055A0F1 /* DNSServiceDiscovery.c */, + FFA572630AF190C20055A0F1 /* dns_sd.h */, + 4BD2B638134FE09F002B96D5 /* P2PPacketFilter.c */, + 4BD2B639134FE09F002B96D5 /* P2PPacketFilter.h */, + ); + name = "mDNS Server Sources"; + sourceTree = "<group>"; + }; + 08FB779DFE84155DC02AAC07 /* External Frameworks and Libraries */ = { + isa = PBXGroup; + children = ( + 219D5541149ED645004464AE /* libxml2.2.dylib */, + 4A3600DF0F34F8CD00453EFB /* DeviceToDeviceManager.framework */, + 2E8165F60C59835F00485EB2 /* libipsec.dylib */, + 65713D46025A293200000109 /* SystemConfiguration.framework */, + 2E0406140C3197CB00F13B59 /* libbsm.dylib */, + 7F869685066EE02400D2A2DC /* Security.framework */, + FFFB0DB407B43D2700B88D48 /* Foundation.framework */, + 09AB6884FE841BABC02AAC07 /* CoreFoundation.framework */, + 00CA213D02786FC30CCA2C71 /* IOKit.framework */, + DB2CC4680662DFF500335AB3 /* JavaVM.framework */, + FF2609FA07B4433800CE10E5 /* Cocoa.framework */, + FF260A1F07B4436900CE10E5 /* PreferencePanes.framework */, + 21F432971134AA6800581B69 /* WebFilterDNS.framework */, + ); + name = "External Frameworks and Libraries"; + sourceTree = "<group>"; + }; + 19C28FBDFE9D53C911CA2CBB /* Products */ = { + isa = PBXGroup; + children = ( + D284C04D0ADD95D30027CCDF /* Info-PreferencePane.plist */, + D284BE730ADD80740027CCDF /* mDNSResponder */, + D284BE950ADD80800027CCDF /* mDNSResponder.debug */, + D284BEB00ADD80920027CCDF /* dns-sd */, + D284BEBE0ADD809A0027CCDF /* libjdns_sd.jnilib */, + D284BED90ADD80A20027CCDF /* dnsextd */, + D284BEE80ADD80A70027CCDF /* ddnswriteconfig */, + D284BF0C0ADD80B00027CCDF /* Bonjour.prefPane */, + FFB765840AEED9C700583A2C /* libsystem_dnssd.dylib */, + FFA572390AF18F1C0055A0F1 /* libsystem_dnssd_debug.dylib */, + FFA572450AF18F450055A0F1 /* libsystem_dnssd_profile.dylib */, + 2E0405F00C31955500F13B59 /* mDNSResponderHelper */, + 213FB21812028A7A002B3A08 /* BonjourEvents.plugin */, + 2141DD1D123FFCDB0086D23E /* libdns_sd.a */, + 2141DD24123FFD0F0086D23E /* libdns_sd_debug.a */, + 2141DD2A123FFD2C0086D23E /* libdns_sd_profile.a */, + 84C5B3351665529800C324A8 /* libdns_services.dylib */, + 72FB545F166D5FB00090B2D9 /* dnsctl */, + ); + name = Products; + sourceTree = "<group>"; + }; + 213FB20912028902002B3A08 /* Bonjour Events Plugin */ = { + isa = PBXGroup; + children = ( + 213FB22C12028B53002B3A08 /* BonjourEvents.c */, + 213FB22D12028B53002B3A08 /* BonjourEvents-Info.plist */, + ); + name = "Bonjour Events Plugin"; + sourceTree = "<group>"; + }; + 6575FBFE022EAFA800000109 /* MIG files */ = { + isa = PBXGroup; + children = ( + 2E0405EB0C3190DC00F13B59 /* helpermsg.defs */, + 6575FBFF022EAFBA00000109 /* DNSServiceDiscoveryDefines.h */, + 6575FC00022EAFBA00000109 /* DNSServiceDiscoveryReply.defs */, + 6575FC01022EAFBA00000109 /* DNSServiceDiscoveryRequest.defs */, + ); + name = "MIG files"; + sourceTree = "<group>"; + }; + 6575FC1F022EB78C00000109 /* Command-Line Clients */ = { + isa = PBXGroup; + children = ( + 6575FC20022EB7AA00000109 /* SamplemDNSClient.c */, + FF1C919F07021E3F001048AB /* dns-sd.c */, + FF5852100DD27BD300862BDF /* ClientCommon.c */, + ); + name = "Command-Line Clients"; + sourceTree = "<group>"; + }; + DB2CC4420662DCE500335AB3 /* Java Support */ = { + isa = PBXGroup; + children = ( + DB2CC4430662DD1100335AB3 /* BaseListener.java */, + DB2CC4440662DD1100335AB3 /* BrowseListener.java */, + DB2CC4450662DD1100335AB3 /* DNSRecord.java */, + DB2CC4460662DD1100335AB3 /* DNSSD.java */, + DB2CC4470662DD1100335AB3 /* DNSSDException.java */, + DB2CC4480662DD1100335AB3 /* DNSSDRegistration.java */, + DB2CC4490662DD1100335AB3 /* DNSSDService.java */, + DB2CC44A0662DD1100335AB3 /* DomainListener.java */, + DB2CC44B0662DD1100335AB3 /* JNISupport.c */, + DB2CC44C0662DD1100335AB3 /* QueryListener.java */, + DB2CC44D0662DD1100335AB3 /* RegisterListener.java */, + DB2CC44E0662DD1100335AB3 /* ResolveListener.java */, + DB2CC44F0662DD1100335AB3 /* TXTRecord.java */, + FF2C5FB00A48B8680066DA11 /* DNSSDRecordRegistrar.java */, + FF2C5FB20A48B86E0066DA11 /* RegisterRecordListener.java */, + ); + name = "Java Support"; + sourceTree = "<group>"; + }; + FF260A2307B4463400CE10E5 /* Resources */ = { + isa = PBXGroup; + children = ( + FF260A2407B4464B00CE10E5 /* remove_idle.tiff */, + FF260A2507B4464B00CE10E5 /* add_pressed.tiff */, + FF260A2607B4464B00CE10E5 /* remove_disabled.tiff */, + FF260A2707B4464B00CE10E5 /* add_idle.tiff */, + FF260A2907B4464B00CE10E5 /* remove_pressed.tiff */, + FF260A2807B4464B00CE10E5 /* success.tiff */, + FF08480607CEB8E800AE6769 /* inprogress.tiff */, + FF260A2A07B4464B00CE10E5 /* failure.tiff */, + FF260A3207B4466900CE10E5 /* BonjourPref.icns */, + FF260A3307B4466900CE10E5 /* BonjourPref.tiff */, + FF354EB108516C63007C00E1 /* installtool */, + FF260A4807B4475600CE10E5 /* DNSServiceDiscoveryPref.nib */, + FF260A4B07B4477F00CE10E5 /* InfoPlist.strings */, + ); + name = Resources; + sourceTree = "<group>"; + }; + FFFB0DA407B43BED00B88D48 /* PreferencePane */ = { + isa = PBXGroup; + children = ( + FFE6935007C2CA7F00283007 /* ConfigurationAuthority.h */, + FFFB0DAE07B43CBA00B88D48 /* ConfigurationAuthority.c */, + FFE6935207C2CAA400283007 /* DNSServiceDiscoveryPref.h */, + FFFB0DAC07B43CBA00B88D48 /* DNSServiceDiscoveryPref.m */, + FFE6935407C2CABD00283007 /* PrivilegedOperations.h */, + FFFB0DAD07B43CBA00B88D48 /* PrivilegedOperations.c */, + FFFB0DAF07B43CBA00B88D48 /* ddnswriteconfig.m */, + FF260A2307B4463400CE10E5 /* Resources */, + ); + path = PreferencePane; + sourceTree = SOURCE_ROOT; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 2141DD19123FFCDB0086D23E /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 2141DD20123FFD0F0086D23E /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 2141DD26123FFD2C0086D23E /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 2EC8F8ED0C39CCCA003C9C48 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 2EC8F8EC0C39CCAC003C9C48 /* helper.h in Headers */, + 2EDC5E730C39EA640092701B /* helper-server.h in Headers */, + 2E3552920C3A95C100CA1CB7 /* helper-error.h in Headers */, + 2ECC11A80C4FEC3800CB1885 /* helpermsg-types.h in Headers */, + 2E8165E80C5980E300485EB2 /* libpfkey.h in Headers */, + 2E8165EA0C5980F700485EB2 /* ipsec_strerror.h in Headers */, + 4BD2B63B134FE09F002B96D5 /* P2PPacketFilter.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 84C5B3331665529800C324A8 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 84C5B33D166553F900C324A8 /* dns_services.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D284BE520ADD80740027CCDF /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + D284BE530ADD80740027CCDF /* DNSServiceDiscoveryDefines.h in Headers */, + D284BE540ADD80740027CCDF /* dnssd_ipc.h in Headers */, + 2E96A5260C39BE480087C4D2 /* helper.h in Headers */, + 2EDC5E750C39EA640092701B /* helper-server.h in Headers */, + 2E3552900C3A95C100CA1CB7 /* helper-error.h in Headers */, + 2ECC11A60C4FEC3800CB1885 /* helpermsg-types.h in Headers */, + 21A57F4E145B2AE100939099 /* CryptoAlg.h in Headers */, + 21A57F55145B2B1400939099 /* CryptoSupport.h in Headers */, + 2124FA2C1471E98C0021D7BB /* nsec.h in Headers */, + 2124FA301471E9B50021D7BB /* dnssec.h in Headers */, + 218E8E53156D8C0300720DA0 /* dnsproxy.h in Headers */, + 2127A47915C3C7B900A857FC /* nsec3.h in Headers */, + 21DD8FC1161E9A250033C8F8 /* anonymous.h in Headers */, + 21070E6116486B9000A69507 /* DNSSECSupport.h in Headers */, + 848DA5CA165477EB00D2E8B4 /* xpc_services.h in Headers */, + 848DA5D616547F7200D2E8B4 /* dns_xpc.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D284BE770ADD80800027CCDF /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + D284BE780ADD80800027CCDF /* DNSServiceDiscoveryDefines.h in Headers */, + D284BE790ADD80800027CCDF /* dnssd_ipc.h in Headers */, + D284BE7A0ADD80800027CCDF /* mDNSEmbeddedAPI.h in Headers */, + D284BE7B0ADD80800027CCDF /* mDNSDebug.h in Headers */, + D284BE7C0ADD80800027CCDF /* mDNSMacOSX.h in Headers */, + 2E96A5270C39BE480087C4D2 /* helper.h in Headers */, + 2EDC5E740C39EA640092701B /* helper-server.h in Headers */, + 2E3552910C3A95C100CA1CB7 /* helper-error.h in Headers */, + 2ECC11A70C4FEC3800CB1885 /* helpermsg-types.h in Headers */, + 21A57F4F145B2AE100939099 /* CryptoAlg.h in Headers */, + 21A57F56145B2B1400939099 /* CryptoSupport.h in Headers */, + 2124FA2D1471E98C0021D7BB /* nsec.h in Headers */, + 2124FA311471E9B50021D7BB /* dnssec.h in Headers */, + 218E8E54156D8C0300720DA0 /* dnsproxy.h in Headers */, + 2127A47A15C3C7B900A857FC /* nsec3.h in Headers */, + 21DD8FC2161E9A250033C8F8 /* anonymous.h in Headers */, + 21070E6216486B9000A69507 /* DNSSECSupport.h in Headers */, + 848DA5CB165477EB00D2E8B4 /* xpc_services.h in Headers */, + 848DA5D716547F7200D2E8B4 /* dns_xpc.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D284BEA60ADD80920027CCDF /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D284BEB50ADD809A0027CCDF /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D284BEC20ADD80A20027CCDF /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 2E35529D0C3A9E7600CA1CB7 /* helper-error.h in Headers */, + 2E35529F0C3A9E7600CA1CB7 /* helper.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D284BEDC0ADD80A70027CCDF /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D284BEED0ADD80B00027CCDF /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FFA572310AF18F1C0055A0F1 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FFA5723D0AF18F450055A0F1 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FFB765800AEED9C700583A2C /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXLegacyTarget section */ + 4AE471670EAFF81900A6C5AD /* dns_sd.jar */ = { + isa = PBXLegacyTarget; + buildArgumentsString = "-f ${SRCROOT}/../mDNSPosix/Makefile OBJDIR=${CONFIGURATION_TEMP_DIR}/dns_sd.jar.build BUILDDIR=${BUILT_PRODUCTS_DIR} SHAREDDIR=${SRCROOT}/../mDNSShared os=x JavaForXcode_$(ACTION)"; + buildConfigurationList = 4AE471770EAFF84000A6C5AD /* Build configuration list for PBXLegacyTarget "dns_sd.jar" */; + buildPhases = ( + ); + buildToolPath = /usr/bin/make; + buildWorkingDirectory = ""; + comments = "Multiplatform .jar file that implements Java interface to DNS-SD"; + dependencies = ( + ); + name = dns_sd.jar; + passBuildSettingsInEnvironment = 1; + productName = dns_sd.jar; + }; +/* End PBXLegacyTarget section */ + +/* Begin PBXNativeTarget section */ + 213FB21712028A7A002B3A08 /* BonjourEvents */ = { + isa = PBXNativeTarget; + buildConfigurationList = 213FB21B12028A7C002B3A08 /* Build configuration list for PBXNativeTarget "BonjourEvents" */; + buildPhases = ( + 213FB21412028A7A002B3A08 /* Resources */, + 213FB21512028A7A002B3A08 /* Sources */, + 213FB21612028A7A002B3A08 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = BonjourEvents; + productName = BonjourEvents; + productReference = 213FB21812028A7A002B3A08 /* BonjourEvents.plugin */; + productType = "com.apple.product-type.bundle"; + }; + 2141DD1C123FFCDB0086D23E /* libdns_sd_static */ = { + isa = PBXNativeTarget; + buildConfigurationList = 2141DD1F123FFCF90086D23E /* Build configuration list for PBXNativeTarget "libdns_sd_static" */; + buildPhases = ( + 2141DD19123FFCDB0086D23E /* Headers */, + 2141DD1A123FFCDB0086D23E /* Sources */, + 2141DD1B123FFCDB0086D23E /* Frameworks */, + 2130256B12400DE600AC839F /* ShellScript */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = libdns_sd_static; + productName = libdns_sd_static; + productReference = 2141DD1D123FFCDB0086D23E /* libdns_sd.a */; + productType = "com.apple.product-type.library.static"; + }; + 2141DD23123FFD0F0086D23E /* libdns_sd_debug_static */ = { + isa = PBXNativeTarget; + buildConfigurationList = 2141DD35123FFD3B0086D23E /* Build configuration list for PBXNativeTarget "libdns_sd_debug_static" */; + buildPhases = ( + 2141DD20123FFD0F0086D23E /* Headers */, + 2141DD21123FFD0F0086D23E /* Sources */, + 2141DD22123FFD0F0086D23E /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = libdns_sd_debug_static; + productName = libdns_sd_debug_static; + productReference = 2141DD24123FFD0F0086D23E /* libdns_sd_debug.a */; + productType = "com.apple.product-type.library.static"; + }; + 2141DD29123FFD2C0086D23E /* libdns_sd_profile_static */ = { + isa = PBXNativeTarget; + buildConfigurationList = 2141DD36123FFD3B0086D23E /* Build configuration list for PBXNativeTarget "libdns_sd_profile_static" */; + buildPhases = ( + 2141DD26123FFD2C0086D23E /* Headers */, + 2141DD27123FFD2C0086D23E /* Sources */, + 2141DD28123FFD2C0086D23E /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = libdns_sd_profile_static; + productName = libdns_sd_profile_static; + productReference = 2141DD2A123FFD2C0086D23E /* libdns_sd_profile.a */; + productType = "com.apple.product-type.library.static"; + }; + 2E0405EF0C31955500F13B59 /* mDNSResponderHelper */ = { + isa = PBXNativeTarget; + buildConfigurationList = 2E0405F30C31956600F13B59 /* Build configuration list for PBXNativeTarget "mDNSResponderHelper" */; + buildPhases = ( + 030BBED60CE11EEC00472F0C /* ShellScript */, + 2EC8F8ED0C39CCCA003C9C48 /* Headers */, + 2E0405ED0C31955500F13B59 /* Sources */, + 2E0405EE0C31955500F13B59 /* Frameworks */, + 4AAE0C5A0C68E6EC003882A5 /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = mDNSResponderHelper; + productName = mDNSResponderHelper; + productReference = 2E0405F00C31955500F13B59 /* mDNSResponderHelper */; + productType = "com.apple.product-type.tool"; + }; + 72FB545E166D5FB00090B2D9 /* dnsctl */ = { + isa = PBXNativeTarget; + buildConfigurationList = 72FB5465166D5FB00090B2D9 /* Build configuration list for PBXNativeTarget "dnsctl" */; + buildPhases = ( + 72FB545B166D5FB00090B2D9 /* Sources */, + 72FB545C166D5FB00090B2D9 /* Frameworks */, + 72FB545D166D5FB00090B2D9 /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = dnsctl; + productName = dnsctl; + productReference = 72FB545F166D5FB00090B2D9 /* dnsctl */; + productType = "com.apple.product-type.tool"; + }; + 84C5B3341665529800C324A8 /* dns_services */ = { + isa = PBXNativeTarget; + buildConfigurationList = 84C5B3361665529800C324A8 /* Build configuration list for PBXNativeTarget "dns_services" */; + buildPhases = ( + 84C5B3311665529800C324A8 /* Sources */, + 84C5B3321665529800C324A8 /* Frameworks */, + 84C5B3331665529800C324A8 /* Headers */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = dns_services; + productName = dns_services; + productReference = 84C5B3351665529800C324A8 /* libdns_services.dylib */; + productType = "com.apple.product-type.library.dynamic"; + }; + D284BE500ADD80740027CCDF /* mDNSResponder */ = { + isa = PBXNativeTarget; + buildConfigurationList = D284BE6D0ADD80740027CCDF /* Build configuration list for PBXNativeTarget "mDNSResponder" */; + buildPhases = ( + D284BE510ADD80740027CCDF /* ShellScript */, + D284BE520ADD80740027CCDF /* Headers */, + D284BE550ADD80740027CCDF /* Sources */, + D284BE640ADD80740027CCDF /* Frameworks */, + D284BE690ADD80740027CCDF /* Rez */, + D284BE6A0ADD80740027CCDF /* CopyFiles */, + 4A7B9E7F14FDA21B00B84CC1 /* CopyFiles */, + 4A7B9E8114FDA25500B84CC1 /* CopyFiles */, + D284BE6C0ADD80740027CCDF /* ShellScript */, + 8418673D15AB8BFF00BB7F70 /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = mDNSResponder; + productInstallPath = "${HOME}/bin"; + productName = mDNSResponder; + productReference = D284BE730ADD80740027CCDF /* mDNSResponder */; + productType = "com.apple.product-type.tool"; + }; + D284BE750ADD80800027CCDF /* mDNSResponder debug */ = { + isa = PBXNativeTarget; + buildConfigurationList = D284BE920ADD80800027CCDF /* Build configuration list for PBXNativeTarget "mDNSResponder debug" */; + buildPhases = ( + D284BE760ADD80800027CCDF /* ShellScript */, + D284BE770ADD80800027CCDF /* Headers */, + D284BE7D0ADD80800027CCDF /* Sources */, + D284BE8C0ADD80800027CCDF /* Frameworks */, + D284BE910ADD80800027CCDF /* Rez */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "mDNSResponder debug"; + productName = mDNSResponder; + productReference = D284BE950ADD80800027CCDF /* mDNSResponder.debug */; + productType = "com.apple.product-type.tool"; + }; + D284BEA50ADD80920027CCDF /* dns-sd tool */ = { + isa = PBXNativeTarget; + buildConfigurationList = D284BEAD0ADD80920027CCDF /* Build configuration list for PBXNativeTarget "dns-sd tool" */; + buildPhases = ( + D284BEA60ADD80920027CCDF /* Headers */, + D284BEA70ADD80920027CCDF /* Sources */, + D284BEA90ADD80920027CCDF /* Frameworks */, + D284BEAA0ADD80920027CCDF /* Rez */, + D284BEAB0ADD80920027CCDF /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "dns-sd tool"; + productInstallPath = /usr/bin; + productName = "dns-sd command-line tool"; + productReference = D284BEB00ADD80920027CCDF /* dns-sd */; + productType = "com.apple.product-type.tool"; + }; + D284BEB20ADD809A0027CCDF /* libjdns_sd.jnilib */ = { + isa = PBXNativeTarget; + buildConfigurationList = D284BEBB0ADD809A0027CCDF /* Build configuration list for PBXNativeTarget "libjdns_sd.jnilib" */; + buildPhases = ( + D284BEB50ADD809A0027CCDF /* Headers */, + D284BEB60ADD809A0027CCDF /* Sources */, + D284BEB80ADD809A0027CCDF /* Frameworks */, + D284BEBA0ADD809A0027CCDF /* Rez */, + ); + buildRules = ( + ); + comments = "Platform-specific JNI library that bridges dns_sd.jar to <dns_sd.h>."; + dependencies = ( + 4AE4716A0EAFF83800A6C5AD /* PBXTargetDependency */, + ); + name = libjdns_sd.jnilib; + productInstallPath = /usr/lib/java; + productName = libjdns_sd.jnilib; + productReference = D284BEBE0ADD809A0027CCDF /* libjdns_sd.jnilib */; + productType = "com.apple.product-type.library.dynamic"; + }; + D284BEBF0ADD80A20027CCDF /* dnsextd */ = { + isa = PBXNativeTarget; + buildConfigurationList = D284BED60ADD80A20027CCDF /* Build configuration list for PBXNativeTarget "dnsextd" */; + buildPhases = ( + D284BEC20ADD80A20027CCDF /* Headers */, + 4A4EE3A413CB8E82005C624B /* Build yacc file into derived source files */, + D284BEC40ADD80A20027CCDF /* Sources */, + D284BECE0ADD80A20027CCDF /* Frameworks */, + D284BED40ADD80A20027CCDF /* CopyFiles */, + FFFF8F770C32F0FD00722979 /* CopyFiles */, + FF37FAAD0BC581780044A5CF /* ShellScript */, + ); + buildRules = ( + D284BFB80ADD8E510027CCDF /* PBXBuildRule */, + D284BF750ADD850C0027CCDF /* PBXBuildRule */, + ); + dependencies = ( + ); + name = dnsextd; + productInstallPath = /usr/sbin; + productName = mDNSResponder; + productReference = D284BED90ADD80A20027CCDF /* dnsextd */; + productType = "com.apple.product-type.tool"; + }; + D284BEDB0ADD80A70027CCDF /* ddnswriteconfig */ = { + isa = PBXNativeTarget; + buildConfigurationList = D284BEE50ADD80A70027CCDF /* Build configuration list for PBXNativeTarget "ddnswriteconfig" */; + buildPhases = ( + D284BEDC0ADD80A70027CCDF /* Headers */, + D284BEDD0ADD80A70027CCDF /* Sources */, + D284BEDF0ADD80A70027CCDF /* Frameworks */, + D284BEE40ADD80A70027CCDF /* Rez */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = ddnswriteconfig; + productInstallPath = "/Library/Application Support/Bonjour"; + productName = ddnswriteconfig; + productReference = D284BEE80ADD80A70027CCDF /* ddnswriteconfig */; + productType = "com.apple.product-type.tool"; + }; + D284BEEA0ADD80B00027CCDF /* PreferencePane */ = { + isa = PBXNativeTarget; + buildConfigurationList = D284BF080ADD80B00027CCDF /* Build configuration list for PBXNativeTarget "PreferencePane" */; + buildPhases = ( + D284BEED0ADD80B00027CCDF /* Headers */, + D284BEEE0ADD80B00027CCDF /* Resources */, + D284BEFD0ADD80B00027CCDF /* Sources */, + D284BF010ADD80B00027CCDF /* Frameworks */, + D284BF070ADD80B00027CCDF /* Rez */, + ); + buildRules = ( + ); + dependencies = ( + FFAE66F9105F0CF100162116 /* PBXTargetDependency */, + ); + name = PreferencePane; + productInstallPath = "${SYSTEM_LIBRARY_DIR}/PreferencePanes"; + productName = PreferencePane; + productReference = D284BF0C0ADD80B00027CCDF /* Bonjour.prefPane */; + productType = "com.apple.product-type.bundle"; + }; + FFA572300AF18F1C0055A0F1 /* libdns_sd_debug_dynamic */ = { + isa = PBXNativeTarget; + buildConfigurationList = FFA572370AF18F1C0055A0F1 /* Build configuration list for PBXNativeTarget "libdns_sd_debug_dynamic" */; + buildPhases = ( + FFA572310AF18F1C0055A0F1 /* Headers */, + FFA572320AF18F1C0055A0F1 /* Sources */, + FFA572360AF18F1C0055A0F1 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = libdns_sd_debug_dynamic; + productName = libdns_sd.dylib; + productReference = FFA572390AF18F1C0055A0F1 /* libsystem_dnssd_debug.dylib */; + productType = "com.apple.product-type.library.dynamic"; + }; + FFA5723C0AF18F450055A0F1 /* libdns_sd_profile_dynamic */ = { + isa = PBXNativeTarget; + buildConfigurationList = FFA572430AF18F450055A0F1 /* Build configuration list for PBXNativeTarget "libdns_sd_profile_dynamic" */; + buildPhases = ( + FFA5723D0AF18F450055A0F1 /* Headers */, + FFA5723E0AF18F450055A0F1 /* Sources */, + FFA572420AF18F450055A0F1 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = libdns_sd_profile_dynamic; + productName = libdns_sd.dylib; + productReference = FFA572450AF18F450055A0F1 /* libsystem_dnssd_profile.dylib */; + productType = "com.apple.product-type.library.dynamic"; + }; + FFB765830AEED9C700583A2C /* libdns_sd_dynamic */ = { + isa = PBXNativeTarget; + buildConfigurationList = FFB765890AEED9FB00583A2C /* Build configuration list for PBXNativeTarget "libdns_sd_dynamic" */; + buildPhases = ( + FFB765800AEED9C700583A2C /* Headers */, + FFB765810AEED9C700583A2C /* Sources */, + FFB765820AEED9C700583A2C /* Frameworks */, + 21DE714D115831CB00DD4BD1 /* ShellScript */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = libdns_sd_dynamic; + productName = libdns_sd.dylib; + productReference = FFB765840AEED9C700583A2C /* libsystem_dnssd.dylib */; + productType = "com.apple.product-type.library.dynamic"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 08FB7793FE84155DC02AAC07 /* Project object */ = { + isa = PBXProject; + attributes = { + }; + buildConfigurationList = D284BE2B0ADD78180027CCDF /* Build configuration list for PBXProject "mDNSResponder" */; + compatibilityVersion = "Xcode 3.1"; + developmentRegion = English; + hasScannedForEncodings = 1; + knownRegions = ( + English, + Japanese, + French, + German, + ); + mainGroup = 08FB7794FE84155DC02AAC07 /* mDNSResponder */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 00AD62BB032D7A0C0CCA2C71 /* Build More */, + 03067D640C83A3700022BE1F /* Build Some */, + FFB7657B0AEED96B00583A2C /* Build All */, + D284BE500ADD80740027CCDF /* mDNSResponder */, + D284BE750ADD80800027CCDF /* mDNSResponder debug */, + 2E0405EF0C31955500F13B59 /* mDNSResponderHelper */, + D284BEA50ADD80920027CCDF /* dns-sd tool */, + 4AE471670EAFF81900A6C5AD /* dns_sd.jar */, + D284BEB20ADD809A0027CCDF /* libjdns_sd.jnilib */, + D284BEBF0ADD80A20027CCDF /* dnsextd */, + D284BEDB0ADD80A70027CCDF /* ddnswriteconfig */, + D284BEEA0ADD80B00027CCDF /* PreferencePane */, + FFB765830AEED9C700583A2C /* libdns_sd_dynamic */, + FFA572300AF18F1C0055A0F1 /* libdns_sd_debug_dynamic */, + FFA5723C0AF18F450055A0F1 /* libdns_sd_profile_dynamic */, + FFA572650AF190F10055A0F1 /* SystemLibrariesDynamic */, + 213FB21712028A7A002B3A08 /* BonjourEvents */, + 2141DCF8123FFB5D0086D23E /* SystemLibraries */, + 2141DD0B123FFC7F0086D23E /* SystemLibrariesStatic */, + 2141DD1C123FFCDB0086D23E /* libdns_sd_static */, + 2141DD23123FFD0F0086D23E /* libdns_sd_debug_static */, + 2141DD29123FFD2C0086D23E /* libdns_sd_profile_static */, + 84C5B3341665529800C324A8 /* dns_services */, + 72FB545E166D5FB00090B2D9 /* dnsctl */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 213FB21412028A7A002B3A08 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D284BEEE0ADD80B00027CCDF /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D284BEEF0ADD80B00027CCDF /* remove_idle.tiff in Resources */, + D284BEF00ADD80B00027CCDF /* add_pressed.tiff in Resources */, + D284BEF10ADD80B00027CCDF /* remove_disabled.tiff in Resources */, + D284BEF20ADD80B00027CCDF /* add_idle.tiff in Resources */, + D284BEF30ADD80B00027CCDF /* success.tiff in Resources */, + D284BEF40ADD80B00027CCDF /* remove_pressed.tiff in Resources */, + D284BEF50ADD80B00027CCDF /* failure.tiff in Resources */, + D284BEF60ADD80B00027CCDF /* BonjourPref.icns in Resources */, + D284BEF70ADD80B00027CCDF /* BonjourPref.tiff in Resources */, + D284BEF80ADD80B00027CCDF /* DNSServiceDiscoveryPref.nib in Resources */, + D284BEF90ADD80B00027CCDF /* InfoPlist.strings in Resources */, + D284BEFB0ADD80B00027CCDF /* inprogress.tiff in Resources */, + D284BEFC0ADD80B00027CCDF /* installtool in Resources */, + FFAE66F0105F0CD900162116 /* ddnswriteconfig in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXRezBuildPhase section */ + D284BE690ADD80740027CCDF /* Rez */ = { + isa = PBXRezBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D284BE910ADD80800027CCDF /* Rez */ = { + isa = PBXRezBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D284BEAA0ADD80920027CCDF /* Rez */ = { + isa = PBXRezBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D284BEBA0ADD809A0027CCDF /* Rez */ = { + isa = PBXRezBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D284BEE40ADD80A70027CCDF /* Rez */ = { + isa = PBXRezBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D284BF070ADD80B00027CCDF /* Rez */ = { + isa = PBXRezBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXRezBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 030BBED60CE11EEC00472F0C /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "if [ -e \"${SDKROOT}/usr/include/vproc.h\" ]\nthen\nrm -f \"${CONFIGURATION_TEMP_DIR}/vproc.h\"\nelse\ntouch \"${CONFIGURATION_TEMP_DIR}/vproc.h\"\nfi\n\nif [ -e \"${SDKROOT}/usr/lib/libipsec.dylib\" ]\nthen\nrm -f \"${CONFIGURATION_TEMP_DIR}/ipsec_options.h\"\ntouch \"${CONFIGURATION_TEMP_DIR}/ipsec_options.h\"\nrm -f \"${CONFIGURATION_TEMP_DIR}/libipsec.a\"\nelse\necho \"#define MDNS_NO_IPSEC 1\" > ${CONFIGURATION_TEMP_DIR}/ipsec_options.h\ntouch \"${CONFIGURATION_TEMP_DIR}/empty.c\"\nfor i in ${ARCHS}\ndo\nccflags=\"-arch $i $ccflags\"\ndone\ncc ${ccflags} \"${CONFIGURATION_TEMP_DIR}/empty.c\" -c -o \"${CONFIGURATION_TEMP_DIR}/libipsec.a\"\nrm -f \"${CONFIGURATION_TEMP_DIR}/empty.c\"\nfi\n"; + }; + 1F7B473C12B82BFD00868AEF /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/bash; + shellScript = "# check if we're building for the simulator and patch the \"id\" of the library (as reported by otool -D) to\n# be just /usr/lib/system/libsystem_sim_dnssd.dylib etc. Also remove the usr directory as it is not needed\n# for simulator\nif [ \"${RC_ProjectName%_Sim}\" != \"${RC_ProjectName}\" ] ; then\n\tif [ -d ${DSTROOT}${SDKROOT}/usr/lib/system ] ; then\n\t\tfor lib in ${DSTROOT}${SDKROOT}/usr/lib/system/*.dylib ; do\n\t\t\tinstall_name_tool -id \"${lib#${DSTROOT}${SDKROOT}}\" \"${lib}\"\n\t\tdone\n\tfi\n\trm -rf $DSTROOT/usr\nfi\n"; + }; + 2130256B12400DE600AC839F /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 8; + files = ( + ); + inputPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 1; + shellPath = /bin/sh; + shellScript = "#if we are building for simulator, change the installation path\nif [ \"${RC_ProjectName%_Sim}\" != \"${RC_ProjectName}\" ] ; then\n\tDSTROOT=${DSTROOT}${SDKROOT}\nfi\nmkdir -p \"$DSTROOT/usr/include\"\nsed 's/\\(^#define _DNS_SD_LIBDISPATCH \\)0$/\\1 1/' \"$SRCROOT/../mDNSShared/dns_sd.h\" > \"$DSTROOT/usr/include/dns_sd.h\""; + }; + 21DE714D115831CB00DD4BD1 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 8; + files = ( + ); + inputPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 1; + shellPath = /bin/sh; + shellScript = "#if we are building for simulator, change the installation path\nif [ \"${RC_ProjectName%_Sim}\" != \"${RC_ProjectName}\" ] ; then\n\tDSTROOT=${DSTROOT}${SDKROOT}\nfi\nmkdir -p \"$DSTROOT/usr/include\"\nsed 's/\\(^#define _DNS_SD_LIBDISPATCH \\)0$/\\1 1/' \"$SRCROOT/../mDNSShared/dns_sd.h\" > \"$DSTROOT/usr/include/dns_sd.h\""; + }; + 4A4EE3A413CB8E82005C624B /* Build yacc file into derived source files */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "$(SRCROOT)/../mDNSShared/dnsextd_parser.y", + ); + name = "Build yacc file into derived source files"; + outputPaths = ( + "$(DERIVED_FILE_DIR)/dnsextd_parser.c", + "$(DERIVED_FILE_DIR)/dnsextd_parser.h", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/usr/bin/bison -o ${SCRIPT_OUTPUT_FILE_0} -d ${SCRIPT_INPUT_FILE_0}"; + }; + D284BE510ADD80740027CCDF /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "if [ -e \"${SDKROOT}/usr/local/include/dnsinfo.h\" ]\nthen\nrm -f \"${CONFIGURATION_TEMP_DIR}/dnsinfo.h\"\nrm -f \"${CONFIGURATION_TEMP_DIR}/libdnsinfo.a\"\nelse\necho \"#define MDNS_NO_DNSINFO 1\" > ${CONFIGURATION_TEMP_DIR}/dnsinfo.h\ntouch \"${CONFIGURATION_TEMP_DIR}/empty.c\"\nfor i in ${ARCHS}\ndo\nccflags=\"-arch $i $ccflags\"\ndone\ncc ${ccflags} \"${CONFIGURATION_TEMP_DIR}/empty.c\" -c -o \"${CONFIGURATION_TEMP_DIR}/libdnsinfo.a\"\nrm -f \"${CONFIGURATION_TEMP_DIR}/empty.c\"\nfi\n\nif [ -e \"${SDKROOT}/usr/include/sandbox.h\" ]\nthen\nrm -f \"${CONFIGURATION_TEMP_DIR}/sandbox.h\"\nelse\necho \"#define MDNS_NO_SANDBOX 1\" > \"${CONFIGURATION_TEMP_DIR}/sandbox.h\"\nfi\n\nif [ -e \"${SDKROOT}/usr/include/vproc.h\" ]\nthen\nrm -f \"${CONFIGURATION_TEMP_DIR}/vproc.h\"\nelse\ntouch \"${CONFIGURATION_TEMP_DIR}/vproc.h\"\nfi\n\nif [ -e \"${SDKROOT}/System/Library/Frameworks/IOKit.framework/PrivateHeaders/pwr_mgt/IOPMLibPrivate.h\" ]\nthen\nrm -rf \"${CONFIGURATION_TEMP_DIR}/IOKit\"\nelse\nmkdir -p \"${CONFIGURATION_TEMP_DIR}/IOKit/pwr_mgt\"\ntouch \"${CONFIGURATION_TEMP_DIR}/IOKit/pwr_mgt/IOPMLibPrivate.h\"\nfi\n\nif [ -e \"${SDKROOT}/System/Library/PrivateFrameworks/DeviceToDeviceManager.framework/Headers/DeviceToDeviceManager.h\" ]\nthen\nrm -rf \"${CONFIGURATION_TEMP_DIR}/DeviceToDeviceManager\"\nelse\nmkdir -p \"${CONFIGURATION_TEMP_DIR}/DeviceToDeviceManager\"\necho \"#define NO_D2D 1\" > \"${CONFIGURATION_TEMP_DIR}/DeviceToDeviceManager/DeviceToDeviceManager.h\"\nfi\n\nif [ -e \"${SDKROOT}/System/Library/PrivateFrameworks/WebFilterDNS.framework/Headers/WebFilterDNS.h\" ]\nthen\nrm -rf \"${CONFIGURATION_TEMP_DIR}/WebFilterDNS\"\nelse\nmkdir -p \"${CONFIGURATION_TEMP_DIR}/WebFilterDNS\"\necho \"#define NO_WCF 1\" > \"${CONFIGURATION_TEMP_DIR}/WebFilterDNS/WebFilterDNS.h\"\nfi\n\nif [ -e \"${SDKROOT}/usr/local/include/AWACS.h\" ]\nthen\nrm -f \"${CONFIGURATION_TEMP_DIR}/AWACS.h\"\nrm -f \"${CONFIGURATION_TEMP_DIR}/libAWACS.a\"\nelse\necho \"#define NO_AWACS 1\" > \"${CONFIGURATION_TEMP_DIR}/AWACS.h\"\ntouch \"${CONFIGURATION_TEMP_DIR}/AWACSempty.c\"\nfor i in ${ARCHS}\ndo\nccflags=\"-arch $i $ccflags\"\ndone\ncc ${ccflags} \"${CONFIGURATION_TEMP_DIR}/AWACSempty.c\" -c -o \"${CONFIGURATION_TEMP_DIR}/libAWACS.a\"\nrm -f \"${CONFIGURATION_TEMP_DIR}/AWACSempty.c\"\nfi\n"; + }; + D284BE6C0ADD80740027CCDF /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 8; + files = ( + ); + runOnlyForDeploymentPostprocessing = 1; + shellPath = /bin/bash; + shellScript = "# Install mDNSResponder.bundle containing language localizations\nmkdir -p ${DSTROOT}${SYSTEM_LIBRARY_DIR}/CoreServices\ncp -R ${SRCROOT}/mDNSResponder-bundle ${DSTROOT}${SYSTEM_LIBRARY_DIR}/CoreServices/mDNSResponder.bundle\n\n# Remove unwanted CVS directories\nfind ${DSTROOT}${SYSTEM_LIBRARY_DIR}/CoreServices/mDNSResponder.bundle -depth -name CVS -exec rm -rf {} \\;\n\n# Expand UTF-8 files to UTF-16 (at one time this appeared to be necessary, but it's not, so we don't do it any more)\n#foreach file (`find ${DSTROOT}${SYSTEM_LIBRARY_DIR}/CoreServices/mDNSResponder.bundle -name Localizable.strings`)\n#iconv -f utf-8 -t utf-16 ${file} > ${file}.new\n#mv -f ${file}.new ${file}\n#end\n\n# Remove French localization (not wanted for Apple B&I builds)\nrm -rf ${DSTROOT}${SYSTEM_LIBRARY_DIR}/CoreServices/mDNSResponder.bundle/Resources/French.lproj\n\n# Copy Sandbox profile\nif [ ! -z \"${IPHONEOS_DEPLOYMENT_TARGET}\" ] ; then\n SANDBOXDST=\"${DSTROOT}/usr/local/share/sandbox/profiles/embedded/builtin\"\nelse\n SANDBOXDST=\"${DSTROOT}/usr/share/sandbox\"\nfi\n(umask 022; mkdir -p -m 0755 \"$SANDBOXDST\")\ncp \"${SRCROOT}/mDNSResponder.sb\" \"${SANDBOXDST}/mDNSResponder.sb\"\n"; + }; + D284BE760ADD80800027CCDF /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "if [ -e \"${SDKROOT}/usr/local/include/dnsinfo.h\" ]\nthen\nrm -f \"${CONFIGURATION_TEMP_DIR}/dnsinfo.h\"\nrm -f \"${CONFIGURATION_TEMP_DIR}/libdnsinfo.a\"\nelse\necho \"#define MDNS_NO_DNSINFO 1\" > ${CONFIGURATION_TEMP_DIR}/dnsinfo.h\ntouch \"${CONFIGURATION_TEMP_DIR}/empty.c\"\nfor i in ${ARCHS}\ndo\nccflags=\"-arch $i $ccflags\"\ndone\ncc ${ccflags} \"${CONFIGURATION_TEMP_DIR}/empty.c\" -c -o \"${CONFIGURATION_TEMP_DIR}/libdnsinfo.a\"\nrm -f \"${CONFIGURATION_TEMP_DIR}/empty.c\"\nfi\n\nif [ -e \"${SDKROOT}/usr/include/sandbox.h\" ]\nthen\nrm -f \"${CONFIGURATION_TEMP_DIR}/sandbox.h\"\nelse\necho \"#define MDNS_NO_SANDBOX 1\" > \"${CONFIGURATION_TEMP_DIR}/sandbox.h\"\nfi\n\nif [ -e \"${SDKROOT}/usr/include/vproc.h\" ]\nthen\nrm -f \"${CONFIGURATION_TEMP_DIR}/vproc.h\"\nelse\ntouch \"${CONFIGURATION_TEMP_DIR}/vproc.h\"\nfi\n\nif [ -e \"${SDKROOT}/System/Library/Frameworks/IOKit.framework/PrivateHeaders/pwr_mgt/IOPMLibPrivate.h\" ]\nthen\nrm -rf \"${CONFIGURATION_TEMP_DIR}/IOKit\"\nelse\nmkdir -p \"${CONFIGURATION_TEMP_DIR}/IOKit/pwr_mgt\"\ntouch \"${CONFIGURATION_TEMP_DIR}/IOKit/pwr_mgt/IOPMLibPrivate.h\"\nfi\n\nif [ -e \"${SDKROOT}/System/Library/PrivateFrameworks/DeviceToDeviceManager.framework/Headers/DeviceToDeviceManager.h\" ]\nthen\nrm -rf \"${CONFIGURATION_TEMP_DIR}/DeviceToDeviceManager\"\nelse\nmkdir -p \"${CONFIGURATION_TEMP_DIR}/DeviceToDeviceManager\"\necho \"#define NO_D2D 1\" > \"${CONFIGURATION_TEMP_DIR}/DeviceToDeviceManager/DeviceToDeviceManager.h\"\nfi\n\nif [ -e \"${SDKROOT}/System/Library/PrivateFrameworks/WebFilterDNS.framework/Headers/WebFilterDNS.h\" ]\nthen\nrm -rf \"${CONFIGURATION_TEMP_DIR}/WebFilterDNS\"\nelse\nmkdir -p \"${CONFIGURATION_TEMP_DIR}/WebFilterDNS\"\necho \"#define NO_WCF 1\" > \"${CONFIGURATION_TEMP_DIR}/WebFilterDNS/WebFilterDNS.h\"\nfi\n\nif [ -e \"${SDKROOT}/usr/local/include/AWACS.h\" ]\nthen\nrm -f \"${CONFIGURATION_TEMP_DIR}/AWACS.h\"\nrm -f \"${CONFIGURATION_TEMP_DIR}/libAWACS.a\"\nelse\necho \"#define NO_AWACS 1\" > \"${CONFIGURATION_TEMP_DIR}/AWACS.h\"\ntouch \"${CONFIGURATION_TEMP_DIR}/AWACSempty.c\"\nfor i in ${ARCHS}\ndo\nccflags=\"-arch $i $ccflags\"\ndone\ncc ${ccflags} \"${CONFIGURATION_TEMP_DIR}/AWACSempty.c\" -c -o \"${CONFIGURATION_TEMP_DIR}/libAWACS.a\"\nrm -f \"${CONFIGURATION_TEMP_DIR}/AWACSempty.c\"\nfi"; + }; + FF045B6A0C7E4AA600448140 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 8; + files = ( + ); + inputPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 1; + shellPath = /bin/sh; + shellScript = "# Install plists to tell launchd how to start mDNSResponder and mDNSResponderHelper\nmkdir -p ${DSTROOT}${SYSTEM_LIBRARY_DIR}/LaunchDaemons\n\nif [ \"${MACOSX_DEPLOYMENT_TARGET}\" == \"10.4\" ] ; then\ncp ${SRCROOT}/LaunchDaemonInfo-Tiger.plist ${DSTROOT}${SYSTEM_LIBRARY_DIR}/LaunchDaemons/com.apple.mDNSResponder.plist\ncp ${SRCROOT}/LaunchDaemonInfo-Tiger.helper.plist ${DSTROOT}${SYSTEM_LIBRARY_DIR}/LaunchDaemons/com.apple.mDNSResponderHelper.plist\nelse\ncp ${SRCROOT}/LaunchDaemonInfo.plist ${DSTROOT}${SYSTEM_LIBRARY_DIR}/LaunchDaemons/com.apple.mDNSResponder.plist\ncp ${SRCROOT}/LaunchDaemonInfo.helper.plist ${DSTROOT}${SYSTEM_LIBRARY_DIR}/LaunchDaemons/com.apple.mDNSResponderHelper.plist\nfi\n\nif [ ! -z \"${IPHONEOS_DEPLOYMENT_TARGET}\" ] ; then\nplutil -convert binary1 ${DSTROOT}${SYSTEM_LIBRARY_DIR}/LaunchDaemons/com.apple.mDNSResponder.plist\nplutil -convert binary1 ${DSTROOT}${SYSTEM_LIBRARY_DIR}/LaunchDaemons/com.apple.mDNSResponderHelper.plist\nfi\n"; + }; + FF37FAAD0BC581780044A5CF /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 8; + files = ( + ); + runOnlyForDeploymentPostprocessing = 1; + shellPath = /bin/tcsh; + shellScript = "# Install plist to tell launchd how to start dnsextd\nmkdir -p ${DSTROOT}${SYSTEM_LIBRARY_DIR}/LaunchDaemons\ncp ${SRCROOT}/LaunchDaemonInfo.dnsextd.plist ${DSTROOT}${SYSTEM_LIBRARY_DIR}/LaunchDaemons/com.apple.dnsextd.plist\n"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 213FB21512028A7A002B3A08 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 213FB23C12028C4A002B3A08 /* BonjourEvents.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 2141DD1A123FFCDB0086D23E /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 215FFAEE124000F900470DE1 /* dnssd_ipc.c in Sources */, + 215FFAEF124000F900470DE1 /* dnssd_clientlib.c in Sources */, + 215FFAF0124000F900470DE1 /* dnssd_clientstub.c in Sources */, + 215FFAF1124000F900470DE1 /* DNSServiceDiscovery.c in Sources */, + 215FFAF2124000F900470DE1 /* DNSServiceDiscoveryRequest.defs in Sources */, + 215FFAF3124000F900470DE1 /* DNSServiceDiscoveryReply.defs in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 2141DD21123FFD0F0086D23E /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 215FFAF41240011800470DE1 /* dnssd_ipc.c in Sources */, + 215FFAF51240011800470DE1 /* dnssd_clientlib.c in Sources */, + 215FFAF61240011800470DE1 /* dnssd_clientstub.c in Sources */, + 215FFAF71240011800470DE1 /* DNSServiceDiscovery.c in Sources */, + 215FFAF81240011800470DE1 /* DNSServiceDiscoveryRequest.defs in Sources */, + 215FFAF91240011800470DE1 /* DNSServiceDiscoveryReply.defs in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 2141DD27123FFD2C0086D23E /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 215FFAFA1240013400470DE1 /* dnssd_ipc.c in Sources */, + 215FFAFB1240013400470DE1 /* dnssd_clientlib.c in Sources */, + 215FFAFC1240013400470DE1 /* dnssd_clientstub.c in Sources */, + 215FFAFD1240013400470DE1 /* DNSServiceDiscovery.c in Sources */, + 215FFAFE1240013400470DE1 /* DNSServiceDiscoveryRequest.defs in Sources */, + 215FFAFF1240013400470DE1 /* DNSServiceDiscoveryReply.defs in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 2E0405ED0C31955500F13B59 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 2E0405F50C3195F700F13B59 /* helper.c in Sources */, + 2E0405F60C31961100F13B59 /* helpermsg.defs in Sources */, + 2E96A51D0C39BDAC0087C4D2 /* helper-main.c in Sources */, + 2E8165E90C5980EE00485EB2 /* pfkey.c in Sources */, + 4BD2B63A134FE09F002B96D5 /* P2PPacketFilter.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 72FB545B166D5FB00090B2D9 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 72FB5467166D5FCA0090B2D9 /* dnsctl.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 84C5B3311665529800C324A8 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 84C5B33C166553F100C324A8 /* dns_services.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D284BE550ADD80740027CCDF /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D284BE560ADD80740027CCDF /* DNSServiceDiscoveryReply.defs in Sources */, + D284BE570ADD80740027CCDF /* DNSServiceDiscoveryRequest.defs in Sources */, + D284BE580ADD80740027CCDF /* mDNS.c in Sources */, + D284BE590ADD80740027CCDF /* uDNS.c in Sources */, + D284BE5A0ADD80740027CCDF /* DNSCommon.c in Sources */, + D284BE5B0ADD80740027CCDF /* DNSDigest.c in Sources */, + D284BE5D0ADD80740027CCDF /* mDNSDebug.c in Sources */, + D284BE5E0ADD80740027CCDF /* uds_daemon.c in Sources */, + D284BE5F0ADD80740027CCDF /* dnssd_ipc.c in Sources */, + D284BE600ADD80740027CCDF /* PlatformCommon.c in Sources */, + D284BE610ADD80740027CCDF /* mDNSMacOSX.c in Sources */, + D284BE620ADD80740027CCDF /* LegacyNATTraversal.c in Sources */, + D284BE630ADD80740027CCDF /* daemon.c in Sources */, + 2E04061F0C3198B700F13B59 /* helpermsg.defs in Sources */, + 2E96A5320C39C1A50087C4D2 /* helper-stubs.c in Sources */, + 21A57F4C145B2AE100939099 /* CryptoAlg.c in Sources */, + 21A57F53145B2B1400939099 /* CryptoSupport.c in Sources */, + 2124FA331471E9DE0021D7BB /* nsec.c in Sources */, + 213BDC6D147319F400000896 /* dnssec.c in Sources */, + 218E8E51156D8C0300720DA0 /* dnsproxy.c in Sources */, + 21DED43515702C0F0060B6B9 /* DNSProxySupport.c in Sources */, + 216D9ACE1720C9F5008066E1 /* VPNService.c in Sources */, + 2127A47715C3C7B900A857FC /* nsec3.c in Sources */, + 21DD8FBF161E9A250033C8F8 /* anonymous.c in Sources */, + 21070E5F16486B9000A69507 /* DNSSECSupport.c in Sources */, + 848DA5C7165477E000D2E8B4 /* xpc_services.c in Sources */, + 2120ABD516B71614007089B6 /* CUPolicy.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D284BE7D0ADD80800027CCDF /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D284BE7E0ADD80800027CCDF /* DNSServiceDiscoveryReply.defs in Sources */, + D284BE7F0ADD80800027CCDF /* DNSServiceDiscoveryRequest.defs in Sources */, + D284BE800ADD80800027CCDF /* mDNS.c in Sources */, + D284BE810ADD80800027CCDF /* uDNS.c in Sources */, + D284BE820ADD80800027CCDF /* DNSCommon.c in Sources */, + D284BE830ADD80800027CCDF /* DNSDigest.c in Sources */, + D284BE850ADD80800027CCDF /* mDNSDebug.c in Sources */, + D284BE860ADD80800027CCDF /* uds_daemon.c in Sources */, + D284BE870ADD80800027CCDF /* dnssd_ipc.c in Sources */, + D284BE880ADD80800027CCDF /* PlatformCommon.c in Sources */, + D284BE890ADD80800027CCDF /* mDNSMacOSX.c in Sources */, + D284BE8A0ADD80800027CCDF /* LegacyNATTraversal.c in Sources */, + D284BE8B0ADD80800027CCDF /* daemon.c in Sources */, + 2E0406200C3198B700F13B59 /* helpermsg.defs in Sources */, + 2E96A5300C39C1A50087C4D2 /* helper-stubs.c in Sources */, + 21A57F4D145B2AE100939099 /* CryptoAlg.c in Sources */, + 21A57F54145B2B1400939099 /* CryptoSupport.c in Sources */, + 2124FA341471E9DE0021D7BB /* nsec.c in Sources */, + 213BDC6E147319F400000896 /* dnssec.c in Sources */, + 218E8E52156D8C0300720DA0 /* dnsproxy.c in Sources */, + 21DED43615702C0F0060B6B9 /* DNSProxySupport.c in Sources */, + 216D9ACF1720C9F5008066E1 /* VPNService.c in Sources */, + 2127A47815C3C7B900A857FC /* nsec3.c in Sources */, + 21DD8FC0161E9A250033C8F8 /* anonymous.c in Sources */, + 21070E6016486B9000A69507 /* DNSSECSupport.c in Sources */, + 848DA5C8165477E000D2E8B4 /* xpc_services.c in Sources */, + 2120ABD616B71614007089B6 /* CUPolicy.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D284BEA70ADD80920027CCDF /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D284BEA80ADD80920027CCDF /* dns-sd.c in Sources */, + FFF589B70E37F66800EF515C /* ClientCommon.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D284BEB60ADD809A0027CCDF /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D284BEB70ADD809A0027CCDF /* JNISupport.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D284BEC40ADD80A20027CCDF /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 21DCD05C1461B23700702FC8 /* CryptoAlg.c in Sources */, + 21DCD05D1461B23700702FC8 /* CryptoAlg.h in Sources */, + D284BEC50ADD80A20027CCDF /* DNSCommon.c in Sources */, + D284BEC60ADD80A20027CCDF /* DNSDigest.c in Sources */, + D284BEC70ADD80A20027CCDF /* dnsextd.c in Sources */, + D284BEC80ADD80A20027CCDF /* mDNSDebug.c in Sources */, + D284BEC90ADD80A20027CCDF /* GenLinkedList.c in Sources */, + D284BECA0ADD80A20027CCDF /* mDNSMacOSX.c in Sources */, + D284BECC0ADD80A20027CCDF /* dnsextd_parser.y in Sources */, + D284BECB0ADD80A20027CCDF /* dnsextd_lexer.l in Sources */, + D284BECD0ADD80A20027CCDF /* PlatformCommon.c in Sources */, + 2EAE955A0C31F4D30021F738 /* helpermsg.defs in Sources */, + 2E35529E0C3A9E7600CA1CB7 /* helper-stubs.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D284BEDD0ADD80A70027CCDF /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D284BEDE0ADD80A70027CCDF /* ddnswriteconfig.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D284BEFD0ADD80B00027CCDF /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D284BEFE0ADD80B00027CCDF /* DNSServiceDiscoveryPref.m in Sources */, + D284BEFF0ADD80B00027CCDF /* PrivilegedOperations.c in Sources */, + D284BF000ADD80B00027CCDF /* ConfigurationAuthority.c in Sources */, + FFF589C10E37F67E00EF515C /* ClientCommon.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FFA572320AF18F1C0055A0F1 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FFA572330AF18F1C0055A0F1 /* dnssd_ipc.c in Sources */, + FFA572340AF18F1C0055A0F1 /* dnssd_clientlib.c in Sources */, + FFA572350AF18F1C0055A0F1 /* dnssd_clientstub.c in Sources */, + FFA5724A0AF18FCC0055A0F1 /* DNSServiceDiscovery.c in Sources */, + FFC22AA40B00F42C00BAB070 /* DNSServiceDiscoveryRequest.defs in Sources */, + FFC22AA60B00F43100BAB070 /* DNSServiceDiscoveryReply.defs in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FFA5723E0AF18F450055A0F1 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FFA5723F0AF18F450055A0F1 /* dnssd_ipc.c in Sources */, + FFA572400AF18F450055A0F1 /* dnssd_clientlib.c in Sources */, + FFA572410AF18F450055A0F1 /* dnssd_clientstub.c in Sources */, + FFA5724B0AF18FCC0055A0F1 /* DNSServiceDiscovery.c in Sources */, + FFC22AA30B00F42B00BAB070 /* DNSServiceDiscoveryRequest.defs in Sources */, + FFC22AA70B00F43100BAB070 /* DNSServiceDiscoveryReply.defs in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FFB765810AEED9C700583A2C /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FFFA38660AEEDB2B0065B80A /* dnssd_ipc.c in Sources */, + FFFA38630AEEDB090065B80A /* dnssd_clientlib.c in Sources */, + FFFA38650AEEDB130065B80A /* dnssd_clientstub.c in Sources */, + FFA572490AF18FCC0055A0F1 /* DNSServiceDiscovery.c in Sources */, + FFC22AA20B00F42A00BAB070 /* DNSServiceDiscoveryRequest.defs in Sources */, + FFC22AA50B00F43000BAB070 /* DNSServiceDiscoveryReply.defs in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 03067D680C83A3830022BE1F /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = D284BE500ADD80740027CCDF /* mDNSResponder */; + targetProxy = 03067D670C83A3830022BE1F /* PBXContainerItemProxy */; + }; + 03067D6A0C83A3890022BE1F /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = D284BE750ADD80800027CCDF /* mDNSResponder debug */; + targetProxy = 03067D690C83A3890022BE1F /* PBXContainerItemProxy */; + }; + 03067D6C0C83A3920022BE1F /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = D284BEA50ADD80920027CCDF /* dns-sd tool */; + targetProxy = 03067D6B0C83A3920022BE1F /* PBXContainerItemProxy */; + }; + 03067D6E0C83A39C0022BE1F /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 2E0405EF0C31955500F13B59 /* mDNSResponderHelper */; + targetProxy = 03067D6D0C83A39C0022BE1F /* PBXContainerItemProxy */; + }; + 03067D860C849CC30022BE1F /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 03067D640C83A3700022BE1F /* Build Some */; + targetProxy = 03067D850C849CC30022BE1F /* PBXContainerItemProxy */; + }; + 2130257112400E9300AC839F /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FFA572650AF190F10055A0F1 /* SystemLibrariesDynamic */; + targetProxy = 2130257012400E9300AC839F /* PBXContainerItemProxy */; + }; + 2141DCFD123FFB7D0086D23E /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 2141DCF8123FFB5D0086D23E /* SystemLibraries */; + targetProxy = 2141DCFC123FFB7D0086D23E /* PBXContainerItemProxy */; + }; + 2141DD0E123FFC960086D23E /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 2141DD0B123FFC7F0086D23E /* SystemLibrariesStatic */; + targetProxy = 2141DD0D123FFC960086D23E /* PBXContainerItemProxy */; + }; + 215FFB19124002C100470DE1 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 2141DD1C123FFCDB0086D23E /* libdns_sd_static */; + targetProxy = 215FFB18124002C100470DE1 /* PBXContainerItemProxy */; + }; + 215FFB1B124002C700470DE1 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 2141DD23123FFD0F0086D23E /* libdns_sd_debug_static */; + targetProxy = 215FFB1A124002C700470DE1 /* PBXContainerItemProxy */; + }; + 215FFB1D124002CC00470DE1 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 2141DD29123FFD2C0086D23E /* libdns_sd_profile_static */; + targetProxy = 215FFB1C124002CC00470DE1 /* PBXContainerItemProxy */; + }; + 217A4C49138EE14C000A5BA8 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 213FB21712028A7A002B3A08 /* BonjourEvents */; + targetProxy = 217A4C48138EE14C000A5BA8 /* PBXContainerItemProxy */; + }; + 4AE4716A0EAFF83800A6C5AD /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 4AE471670EAFF81900A6C5AD /* dns_sd.jar */; + targetProxy = 4AE471690EAFF83800A6C5AD /* PBXContainerItemProxy */; + }; + 72FB546A166D5FE40090B2D9 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 72FB545E166D5FB00090B2D9 /* dnsctl */; + targetProxy = 72FB5469166D5FE40090B2D9 /* PBXContainerItemProxy */; + }; + 84C5B3411665544B00C324A8 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 84C5B3341665529800C324A8 /* dns_services */; + targetProxy = 84C5B3401665544B00C324A8 /* PBXContainerItemProxy */; + }; + D284BF2C0ADD815A0027CCDF /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = D284BEBF0ADD80A20027CCDF /* dnsextd */; + targetProxy = D284BF2B0ADD815A0027CCDF /* PBXContainerItemProxy */; + }; + D284BF2E0ADD81600027CCDF /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = D284BEDB0ADD80A70027CCDF /* ddnswriteconfig */; + targetProxy = D284BF2D0ADD81600027CCDF /* PBXContainerItemProxy */; + }; + D284BF300ADD81630027CCDF /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = D284BEEA0ADD80B00027CCDF /* PreferencePane */; + targetProxy = D284BF2F0ADD81630027CCDF /* PBXContainerItemProxy */; + }; + FFA572690AF190FF0055A0F1 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FFB765830AEED9C700583A2C /* libdns_sd_dynamic */; + targetProxy = FFA572680AF190FF0055A0F1 /* PBXContainerItemProxy */; + }; + FFA5726B0AF191010055A0F1 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FFA572300AF18F1C0055A0F1 /* libdns_sd_debug_dynamic */; + targetProxy = FFA5726A0AF191010055A0F1 /* PBXContainerItemProxy */; + }; + FFA5726D0AF191020055A0F1 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FFA5723C0AF18F450055A0F1 /* libdns_sd_profile_dynamic */; + targetProxy = FFA5726C0AF191020055A0F1 /* PBXContainerItemProxy */; + }; + FFAE66F9105F0CF100162116 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = D284BEDB0ADD80A70027CCDF /* ddnswriteconfig */; + targetProxy = FFAE66F8105F0CF100162116 /* PBXContainerItemProxy */; + }; + FFB7657D0AEED97F00583A2C /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 00AD62BB032D7A0C0CCA2C71 /* Build More */; + targetProxy = FFB7657C0AEED97F00583A2C /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + FF260A4807B4475600CE10E5 /* DNSServiceDiscoveryPref.nib */ = { + isa = PBXVariantGroup; + children = ( + FF260A4907B4475600CE10E5 /* English */, + ); + name = DNSServiceDiscoveryPref.nib; + path = PreferencePane; + sourceTree = SOURCE_ROOT; + }; + FF260A4B07B4477F00CE10E5 /* InfoPlist.strings */ = { + isa = PBXVariantGroup; + children = ( + FF260A4C07B4477F00CE10E5 /* English */, + ); + name = InfoPlist.strings; + path = PreferencePane; + sourceTree = SOURCE_ROOT; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 03067D740C83A3CB0022BE1F /* Development */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_OPTIMIZATION_LEVEL = s; + PRODUCT_NAME = "Build Some"; + }; + name = Development; + }; + 213FB21A12028A7B002B3A08 /* Development */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + BUNDLE_LOADER = /usr/libexec/UserEventAgent; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_MODEL_TUNING = G5; + INFOPLIST_FILE = "BonjourEvents-Info.plist"; + INSTALL_PATH = /System/Library/UserEventPlugins/; + PREBINDING = NO; + PRODUCT_NAME = BonjourEvents; + PROVISIONING_PROFILE = ""; + WRAPPER_EXTENSION = plugin; + }; + name = Development; + }; + 2141DCF9123FFB5D0086D23E /* Development */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + PRODUCT_NAME = SystemLibraries; + }; + name = Development; + }; + 2141DD0C123FFC7F0086D23E /* Development */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = s; + PRODUCT_NAME = SystemLibrariesStatic; + }; + name = Development; + }; + 2141DD1E123FFCDB0086D23E /* Development */ = { + isa = XCBuildConfiguration; + buildSettings = { + CONFIGURATION_BUILD_DIR = "${BUILD_DIR}"; + CONFIGURATION_TEMP_DIR = "${BUILD_DIR}/mDNSResponder.build"; + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "__DARWIN_NON_CANCELABLE=1", + ); + HEADER_SEARCH_PATHS = "$(SDKROOT)/System/Library/Frameworks/System.framework/PrivateHeaders/"; + INSTALLHDRS_COPY_PHASE = YES; + INSTALLHDRS_SCRIPT_PHASE = YES; + INSTALL_PATH = /usr/local/lib/system; + PREBINDING = NO; + PRODUCT_NAME = dns_sd; + }; + name = Development; + }; + 2141DD25123FFD100086D23E /* Development */ = { + isa = XCBuildConfiguration; + buildSettings = { + CONFIGURATION_BUILD_DIR = "${BUILD_DIR}"; + CONFIGURATION_TEMP_DIR = "${BUILD_DIR}/mDNSResponder.build"; + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "__DARWIN_NON_CANCELABLE=1", + ); + HEADER_SEARCH_PATHS = "$(SDKROOT)/System/Library/Frameworks/System.framework/PrivateHeaders/"; + INSTALL_PATH = /usr/local/lib/system; + PREBINDING = NO; + PRODUCT_NAME = dns_sd_debug; + }; + name = Development; + }; + 2141DD2B123FFD2C0086D23E /* Development */ = { + isa = XCBuildConfiguration; + buildSettings = { + CONFIGURATION_BUILD_DIR = "${BUILD_DIR}"; + CONFIGURATION_TEMP_DIR = "${BUILD_DIR}/mDNSResponder.build"; + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "__DARWIN_NON_CANCELABLE=1", + ); + HEADER_SEARCH_PATHS = "$(SDKROOT)/System/Library/Frameworks/System.framework/PrivateHeaders/"; + INSTALL_PATH = /usr/local/lib/system; + PREBINDING = NO; + PRODUCT_NAME = dns_sd_profile; + }; + name = Development; + }; + 2E0405F20C31955500F13B59 /* Development */ = { + isa = XCBuildConfiguration; + buildSettings = { + "CODE_SIGN_ENTITLEMENTS[sdk=iphoneos*]" = "helper-entitlements.plist"; + CODE_SIGN_IDENTITY = "-"; + CONFIGURATION_BUILD_DIR = "$(BUILD_DIR)"; + CONFIGURATION_TEMP_DIR = "$(PROJECT_TEMP_DIR)"; + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_WARN_CHECK_SWITCH_STATEMENTS = NO; + HEADER_SEARCH_PATHS = ( + "${CONFIGURATION_TEMP_DIR}", + "$(SDKROOT)/System/Library/Frameworks/System.framework/PrivateHeaders", + ); + INSTALL_PATH = /usr/sbin; + LD_MAP_FILE_PATH = "$(TARGET_TEMP_DIR)/$(PRODUCT_NAME)-LinkMap-$(CURRENT_VARIANT)-$(CURRENT_ARCH).txt"; + LIBRARY_SEARCH_PATHS = "\"${CONFIGURATION_TEMP_DIR}\""; + MACOSX_DEPLOYMENT_TARGET = 10.5; + "OTHER_CFLAGS[sdk=iphoneos*][arch=*]" = "$(inherited)"; + OTHER_LDFLAGS = ( + "$(inherited)", + "-lipsec", + ); + "OTHER_LDFLAGS[sdk=iphoneos*] [arch=*]" = "-lipsec -Wl,-pie"; + "OTHER_LDFLAGS[sdk=macosx*][arch=*]" = ( + "-lipsec", + "-Wl,-pie", + ); + PREBINDING = NO; + PRODUCT_NAME = mDNSResponderHelper; + PROVISIONING_PROFILE = ""; + }; + name = Development; + }; + 4AE471680EAFF81900A6C5AD /* Development */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + PRODUCT_NAME = dns_sd.jar; + }; + name = Development; + }; + 72FB5466166D5FB00090B2D9 /* Development */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ARCHS = "$(ARCHS_STANDARD_64_BIT)"; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_ENTITLEMENTS[sdk=*]" = "dnsctl-entitlements.plist"; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_OBJC_EXCEPTIONS = YES; + GCC_OPTIMIZATION_LEVEL = s; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_STRICT_ALIASING = YES; + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = NO; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + INSTALL_PATH = /usr/bin; + MACOSX_DEPLOYMENT_TARGET = 10.9; + ONLY_ACTIVE_ARCH = NO; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE = ""; + SDKROOT = macosx; + }; + name = Development; + }; + 84C5B3371665529800C324A8 /* Development */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ARCHS = "$(ARCHS_STANDARD_64_BIT)"; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + EXECUTABLE_PREFIX = lib; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_OBJC_EXCEPTIONS = YES; + GCC_OPTIMIZATION_LEVEL = s; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = NO; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + INSTALL_PATH = /usr/lib; + MACOSX_DEPLOYMENT_TARGET = 10.8; + ONLY_ACTIVE_ARCH = NO; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + }; + name = Development; + }; + D284BE290ADD78180027CCDF /* Development */ = { + isa = XCBuildConfiguration; + buildSettings = { + CONFIGURATION_BUILD_DIR = "${BUILD_DIR}"; + CONFIGURATION_TEMP_DIR = "${BUILD_DIR}/mDNSResponder.build"; + GCC_DYNAMIC_NO_PIC = NO; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + OTHER_CFLAGS = ""; + OTHER_LDFLAGS = ""; + OTHER_REZFLAGS = ""; + PRODUCT_NAME = "Build All"; + SECTORDER_FLAGS = ""; + }; + name = Development; + }; + D284BE2C0ADD78180027CCDF /* Development */ = { + isa = XCBuildConfiguration; + buildSettings = { + CONFIGURATION_BUILD_DIR = "$(BUILD_DIR)"; + DEAD_CODE_STRIPPING = YES; + GCC_PREPROCESSOR_DEFINITIONS = ( + "__APPLE_USE_RFC_3542=1", + "_DNS_SD_LIBDISPATCH=1", + "APPLE_OSX_mDNSResponder=1", + "__MigTypeCheck=1", + "mDNSResponderVersion=${MVERS}", + _LEGACY_NAT_TRAVERSAL_, + "_BUILDING_XCODE_PROJECT_=1", + ); + GCC_TREAT_WARNINGS_AS_ERRORS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = NO; + MVERS = "\"(Engineering Build)\""; + OTHER_CFLAGS = ( + "-DUSE_SYSTEMCONFIGURATION_PRIVATE_HEADERS", + "-fwrapv", + ); + "OTHER_LDFLAGS[sdk=macosx*]" = ""; + PREBINDING = NO; + STRIP_STYLE = debugging; + WARNING_CFLAGS = ( + "-W", + "-Wall", + "-Wmissing-prototypes", + "-Wno-four-char-constants", + "-Wno-unknown-pragmas", + "-Wshadow", + ); + YACC_GENERATED_FILE_STEM = Standard; + }; + name = Development; + }; + D284BE6E0ADD80740027CCDF /* Development */ = { + isa = XCBuildConfiguration; + buildSettings = { + "CODE_SIGN_ENTITLEMENTS[sdk=*]" = "mDNSResponder-entitlements.plist"; + CODE_SIGN_IDENTITY = "-"; + CONFIGURATION_BUILD_DIR = "${BUILD_DIR}"; + CONFIGURATION_TEMP_DIR = "${BUILD_DIR}/mDNSResponder.build"; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(SDKROOT)/$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks", + ); + GCC_DYNAMIC_NO_PIC = NO; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + HEADER_SEARCH_PATHS = ( + "$(SDKROOT)/$(SYSTEM_LIBRARY_DIR)/Frameworks/Security.framework/PrivateHeaders", + ../mDNSShared, + "${SYSTEM_LIBRARY_DIR}/Frameworks/System.framework/PrivateHeaders", + "${APPLE_INTERNAL_DEVELOPER_DIR}/Headers", + "${CONFIGURATION_TEMP_DIR}", + "$(SDKROOT)/usr/include/libxml2", + ); + INSTALL_PATH = /usr/sbin; + LIBRARY_SEARCH_PATHS = "\"${CONFIGURATION_TEMP_DIR}\""; + MACOSX_DEPLOYMENT_TARGET = 10.5; + ORDER_FILE = "${SRCROOT}/mDNSResponder.order"; + OTHER_CFLAGS = ( + "$(inherited)", + "-no-cpp-precomp", + ); + "OTHER_CFLAGS[sdk=iphoneos*][arch=*]" = "$(inherited)"; + OTHER_LDFLAGS = ""; + "OTHER_LDFLAGS[sdk=iphoneos*][arch=*]" = ( + "-Wl,-pie", + "-weak_framework", + DeviceToDeviceManager, + "-lMobileGestalt", + "-lcupolicy", + ); + "OTHER_LDFLAGS[sdk=macosx*][arch=*]" = ( + "-Wl,-pie", + "-lAWACS", + "-weak_framework", + WebFilterDNS, + "-weak_framework", + DeviceToDeviceManager, + ); + "OTHER_LDFLAGS[sdk=macosx10.6][arch=*]" = "-lAWACS"; + OTHER_REZFLAGS = ""; + PRODUCT_NAME = mDNSResponder; + PROVISIONING_PROFILE = ""; + REZ_EXECUTABLE = YES; + }; + name = Development; + }; + D284BE930ADD80800027CCDF /* Development */ = { + isa = XCBuildConfiguration; + buildSettings = { + CONFIGURATION_BUILD_DIR = "${BUILD_DIR}"; + CONFIGURATION_TEMP_DIR = "${BUILD_DIR}/mDNSResponder.build"; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(SDKROOT)/$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks", + ); + GCC_DYNAMIC_NO_PIC = NO; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_OPTIMIZATION_LEVEL = s; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "MDNS_DEBUGMSGS=1", + ); + HEADER_SEARCH_PATHS = ( + "$(SDKROOT)/$(SYSTEM_LIBRARY_DIR)/Frameworks/Security.framework/PrivateHeaders", + ../mDNSShared, + "${SYSTEM_LIBRARY_DIR}/Frameworks/System.framework/PrivateHeaders", + "${APPLE_INTERNAL_DEVELOPER_DIR}/Headers", + "${CONFIGURATION_TEMP_DIR}", + "$(SDKROOT)/usr/include/libxml2", + ); + LIBRARY_SEARCH_PATHS = "\"${CONFIGURATION_TEMP_DIR}\""; + MACOSX_DEPLOYMENT_TARGET = 10.5; + OTHER_CFLAGS = ( + "$(inherited)", + "-no-cpp-precomp", + ); + OTHER_LDFLAGS = ""; + "OTHER_LDFLAGS[sdk=iphoneos*][arch=*]" = ( + "-Wl,-pie", + "-weak_framework", + DeviceToDeviceManager, + "-lMobileGestalt", + "-lcupolicy", + ); + "OTHER_LDFLAGS[sdk=macosx*][arch=*]" = ( + "-Wl,-pie", + "-lAWACS", + "-weak_framework", + WebFilterDNS, + "-weak_framework", + DeviceToDeviceManager, + ); + "OTHER_LDFLAGS[sdk=macosx10.6][arch=*]" = "-lAWACS"; + OTHER_REZFLAGS = ""; + PRODUCT_NAME = mDNSResponder.debug; + REZ_EXECUTABLE = YES; + SECTORDER_FLAGS = ( + "-sectorder", + __TEXT, + __text, + mDNSResponder.order, + ); + SKIP_INSTALL = YES; + }; + name = Development; + }; + D284BEAE0ADD80920027CCDF /* Development */ = { + isa = XCBuildConfiguration; + buildSettings = { + CONFIGURATION_BUILD_DIR = "${BUILD_DIR}"; + CONFIGURATION_TEMP_DIR = "${BUILD_DIR}/mDNSResponder.build"; + GCC_DYNAMIC_NO_PIC = NO; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + HEADER_SEARCH_PATHS = ( + ../mDNSShared, + "${SYSTEM_LIBRARY_DIR}/Frameworks/System.framework/PrivateHeaders", + ); + INSTALL_PATH = /usr/bin; + OTHER_CFLAGS = "-no-cpp-precomp"; + OTHER_LDFLAGS = ""; + OTHER_REZFLAGS = ""; + PRODUCT_NAME = "dns-sd"; + REZ_EXECUTABLE = YES; + SECTORDER_FLAGS = ""; + }; + name = Development; + }; + D284BEBC0ADD809A0027CCDF /* Development */ = { + isa = XCBuildConfiguration; + buildSettings = { + CONFIGURATION_BUILD_DIR = "${BUILD_DIR}"; + CONFIGURATION_TEMP_DIR = "${BUILD_DIR}/mDNSResponder.build"; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + EXECUTABLE_EXTENSION = jnilib; + GCC_DYNAMIC_NO_PIC = NO; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + HEADER_SEARCH_PATHS = ( + ../mDNSShared, + "${SYSTEM_LIBRARY_DIR}/Frameworks/JavaVM.framework/Versions/A/Headers", + "${SYSTEM_LIBRARY_DIR}/Frameworks/JavaVM.framework/Versions/1.3.1/Headers", + "${PROJECT_DERIVED_FILE_DIR}", + ); + INSTALL_PATH = /usr/lib/java; + LIBRARY_STYLE = DYNAMIC; + MACH_O_TYPE = mh_dylib; + OTHER_CFLAGS = ""; + OTHER_LIBTOOL_FLAGS = ""; + OTHER_REZFLAGS = ""; + PRODUCT_NAME = libjdns_sd; + REZ_EXECUTABLE = YES; + SECTORDER_FLAGS = ""; + }; + name = Development; + }; + D284BED70ADD80A20027CCDF /* Development */ = { + isa = XCBuildConfiguration; + buildSettings = { + CONFIGURATION_BUILD_DIR = "${BUILD_DIR}"; + CONFIGURATION_TEMP_DIR = "${BUILD_DIR}/mDNSResponder.build"; + FRAMEWORK_SEARCH_PATHS = ""; + GCC_DYNAMIC_NO_PIC = NO; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + HEADER_SEARCH_PATHS = ( + "${SYSTEM_LIBRARY_DIR}/Frameworks/System.framework/PrivateHeaders", + "${APPLE_INTERNAL_DEVELOPER_DIR}/Headers", + "${CONFIGURATION_TEMP_DIR}", + /System/Library/Frameworks/System.Framework/PrivateHeaders, + ); + INSTALL_PATH = /usr/sbin; + LIBRARY_SEARCH_PATHS = "\"${CONFIGURATION_TEMP_DIR}\""; + MACOSX_DEPLOYMENT_TARGET = 10.5; + OTHER_CFLAGS = ( + "-no-cpp-precomp", + "-UAPPLE_OSX_mDNSResponder", + ); + OTHER_LDFLAGS = ""; + "OTHER_LDFLAGS[sdk=macosx*][arch=*]" = "-Wl,-pie"; + PRODUCT_NAME = dnsextd; + SECTORDER_FLAGS = ""; + }; + name = Development; + }; + D284BEE60ADD80A70027CCDF /* Development */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; + CONFIGURATION_BUILD_DIR = "${BUILD_DIR}"; + CONFIGURATION_TEMP_DIR = "${BUILD_DIR}/mDNSResponder.build"; + GCC_DYNAMIC_NO_PIC = NO; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + INSTALL_PATH = "/Library/Application Support/Bonjour"; + MACOSX_DEPLOYMENT_TARGET = 10.5; + OTHER_CFLAGS = ""; + OTHER_LDFLAGS = ""; + "OTHER_LDFLAGS[sdk=macosx*]" = "-Wl,-pie"; + OTHER_REZFLAGS = ""; + PRODUCT_NAME = ddnswriteconfig; + REZ_EXECUTABLE = YES; + SECTORDER_FLAGS = ""; + }; + name = Development; + }; + D284BF090ADD80B00027CCDF /* Development */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; + CONFIGURATION_BUILD_DIR = "${BUILD_DIR}"; + CONFIGURATION_TEMP_DIR = "${BUILD_DIR}/mDNSResponder.build"; + EXPORTED_SYMBOLS_FILE = ""; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_OBJC_GC = supported; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + INFOPLIST_FILE = "PreferencePane/Info-PreferencePane.plist"; + INSTALL_PATH = /AppleInternal/Library/PreferencePanes; + MACOSX_DEPLOYMENT_TARGET = 10.5; + OTHER_CFLAGS = ""; + OTHER_LDFLAGS = "-twolevel_namespace"; + OTHER_REZFLAGS = ""; + PRODUCT_NAME = Bonjour; + SECTORDER_FLAGS = ""; + WRAPPER_EXTENSION = prefPane; + }; + name = Development; + }; + FFA572380AF18F1C0055A0F1 /* Development */ = { + isa = XCBuildConfiguration; + buildSettings = { + CONFIGURATION_BUILD_DIR = "${BUILD_DIR}"; + CONFIGURATION_TEMP_DIR = "${BUILD_DIR}/mDNSResponder.build"; + COPY_PHASE_STRIP = NO; + DYLIB_CURRENT_VERSION = "$(RC_ProjectSourceVersion)"; + EXECUTABLE_EXTENSION = dylib; + GCC_DYNAMIC_NO_PIC = NO; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "__DARWIN_NON_CANCELABLE=1", + ); + HEADER_SEARCH_PATHS = "$(SDKROOT)/System/Library/Frameworks/System.framework/PrivateHeaders/"; + INSTALL_PATH = /usr/lib/system; + "INSTALL_PATH[sdk=iphonesimulator*]" = "$(SDKROOT)/usr/lib/system"; + LINK_WITH_STANDARD_LIBRARIES = NO; + OTHER_LDFLAGS = ( + "-Wl,-umbrella,System", + "-L/usr/lib/system", + "-ldyld", + "-lcompiler_rt", + "-lsystem_kernel", + "-lsystem_platform", + "-lsystem_pthread", + "-lsystem_malloc", + "-lsystem_c", + "-lsystem_blocks", + "-ldispatch", + "-llaunch", + "-lsystem_asl", + ); + "OTHER_LDFLAGS[sdk=iphonesimulator*]" = ( + "-Wl,-umbrella,System", + "-L/usr/lib/system", + "-ldyld_sim", + "-lcompiler_rt_sim", + "-lsystem_sim_c", + "-lsystem_sim_blocks", + "-ldispatch", + "-Wl,-upward-lSystem", + ); + PRODUCT_NAME = libsystem_dnssd_debug; + "PRODUCT_NAME[sdk=iphonesimulator*]" = libsystem_sim_dnssd_debug; + }; + name = Development; + }; + FFA572440AF18F450055A0F1 /* Development */ = { + isa = XCBuildConfiguration; + buildSettings = { + CONFIGURATION_BUILD_DIR = "${BUILD_DIR}"; + CONFIGURATION_TEMP_DIR = "${BUILD_DIR}/mDNSResponder.build"; + COPY_PHASE_STRIP = NO; + DYLIB_CURRENT_VERSION = "$(RC_ProjectSourceVersion)"; + EXECUTABLE_EXTENSION = dylib; + GCC_DYNAMIC_NO_PIC = NO; + GCC_GENERATE_DEBUGGING_SYMBOLS = NO; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "__DARWIN_NON_CANCELABLE=1", + ); + GENERATE_PROFILING_CODE = YES; + HEADER_SEARCH_PATHS = "$(SDKROOT)/System/Library/Frameworks/System.framework/PrivateHeaders/"; + INSTALL_PATH = /usr/lib/system; + "INSTALL_PATH[sdk=iphonesimulator*]" = "$(SDKROOT)/usr/lib/system"; + LINK_WITH_STANDARD_LIBRARIES = NO; + OTHER_LDFLAGS = ( + "-Wl,-umbrella,System", + "-L/usr/lib/system", + "-ldyld", + "-lcompiler_rt", + "-lsystem_kernel", + "-lsystem_platform", + "-lsystem_pthread", + "-lsystem_malloc", + "-lsystem_c", + "-lsystem_blocks", + "-ldispatch", + "-llaunch", + "-lsystem_asl", + ); + "OTHER_LDFLAGS[sdk=iphonesimulator*]" = ( + "-Wl,-umbrella,System", + "-L/usr/lib/system", + "-ldyld_sim", + "-lcompiler_rt_sim", + "-lsystem_sim_c", + "-lsystem_sim_blocks", + "-ldispatch", + "-Wl,-upward-lSystem", + ); + PRODUCT_NAME = libsystem_dnssd_profile; + "PRODUCT_NAME[sdk=iphonesimulator*]" = libsystem_sim_dnssd_profile; + }; + name = Development; + }; + FFA5726F0AF191200055A0F1 /* Development */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + PRODUCT_NAME = SystemLibrariesDynamic; + }; + name = Development; + }; + FFB7657F0AEED99D00583A2C /* Development */ = { + isa = XCBuildConfiguration; + buildSettings = { + CONFIGURATION_BUILD_DIR = "${BUILD_DIR}"; + CONFIGURATION_TEMP_DIR = "${BUILD_DIR}/mDNSResponder.build"; + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + PRODUCT_NAME = "Build All"; + }; + name = Development; + }; + FFB7658A0AEED9FB00583A2C /* Development */ = { + isa = XCBuildConfiguration; + buildSettings = { + CONFIGURATION_BUILD_DIR = "${BUILD_DIR}"; + CONFIGURATION_TEMP_DIR = "${BUILD_DIR}/mDNSResponder.build"; + COPY_PHASE_STRIP = NO; + DYLIB_CURRENT_VERSION = "$(RC_ProjectSourceVersion)"; + EXECUTABLE_EXTENSION = dylib; + GCC_DYNAMIC_NO_PIC = NO; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "__DARWIN_NON_CANCELABLE=1", + ); + HEADER_SEARCH_PATHS = "$(SDKROOT)/System/Library/Frameworks/System.framework/PrivateHeaders/"; + INSTALLHDRS_COPY_PHASE = YES; + INSTALLHDRS_SCRIPT_PHASE = YES; + INSTALL_PATH = /usr/lib/system; + "INSTALL_PATH[sdk=iphonesimulator*]" = "$(SDKROOT)/usr/lib/system"; + LINK_WITH_STANDARD_LIBRARIES = NO; + OTHER_LDFLAGS = ( + "-Wl,-umbrella,System", + "-L/usr/lib/system", + "-ldyld", + "-lcompiler_rt", + "-lsystem_kernel", + "-lsystem_platform", + "-lsystem_pthread", + "-lsystem_malloc", + "-lsystem_c", + "-lsystem_blocks", + "-ldispatch", + "-llaunch", + "-lsystem_asl", + ); + "OTHER_LDFLAGS[sdk=iphonesimulator*]" = ( + "-Wl,-umbrella,System", + "-L/usr/lib/system", + "-ldyld_sim", + "-lcompiler_rt_sim", + "-lsystem_sim_c", + "-lsystem_sim_blocks", + "-ldispatch", + "-Wl,-upward-lSystem", + ); + PRODUCT_NAME = libsystem_dnssd; + "PRODUCT_NAME[sdk=iphonesimulator*]" = libsystem_sim_dnssd; + }; + name = Development; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 03067D730C83A3CB0022BE1F /* Build configuration list for PBXAggregateTarget "Build Some" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 03067D740C83A3CB0022BE1F /* Development */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Development; + }; + 213FB21B12028A7C002B3A08 /* Build configuration list for PBXNativeTarget "BonjourEvents" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 213FB21A12028A7B002B3A08 /* Development */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Development; + }; + 2141DD08123FFB830086D23E /* Build configuration list for PBXAggregateTarget "SystemLibraries" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 2141DCF9123FFB5D0086D23E /* Development */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Development; + }; + 2141DD18123FFC990086D23E /* Build configuration list for PBXAggregateTarget "SystemLibrariesStatic" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 2141DD0C123FFC7F0086D23E /* Development */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Development; + }; + 2141DD1F123FFCF90086D23E /* Build configuration list for PBXNativeTarget "libdns_sd_static" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 2141DD1E123FFCDB0086D23E /* Development */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Development; + }; + 2141DD35123FFD3B0086D23E /* Build configuration list for PBXNativeTarget "libdns_sd_debug_static" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 2141DD25123FFD100086D23E /* Development */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Development; + }; + 2141DD36123FFD3B0086D23E /* Build configuration list for PBXNativeTarget "libdns_sd_profile_static" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 2141DD2B123FFD2C0086D23E /* Development */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Development; + }; + 2E0405F30C31956600F13B59 /* Build configuration list for PBXNativeTarget "mDNSResponderHelper" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 2E0405F20C31955500F13B59 /* Development */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Development; + }; + 4AE471770EAFF84000A6C5AD /* Build configuration list for PBXLegacyTarget "dns_sd.jar" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 4AE471680EAFF81900A6C5AD /* Development */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Development; + }; + 72FB5465166D5FB00090B2D9 /* Build configuration list for PBXNativeTarget "dnsctl" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 72FB5466166D5FB00090B2D9 /* Development */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Development; + }; + 84C5B3361665529800C324A8 /* Build configuration list for PBXNativeTarget "dns_services" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 84C5B3371665529800C324A8 /* Development */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Development; + }; + D284BE280ADD78180027CCDF /* Build configuration list for PBXAggregateTarget "Build More" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + D284BE290ADD78180027CCDF /* Development */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Development; + }; + D284BE2B0ADD78180027CCDF /* Build configuration list for PBXProject "mDNSResponder" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + D284BE2C0ADD78180027CCDF /* Development */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Development; + }; + D284BE6D0ADD80740027CCDF /* Build configuration list for PBXNativeTarget "mDNSResponder" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + D284BE6E0ADD80740027CCDF /* Development */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Development; + }; + D284BE920ADD80800027CCDF /* Build configuration list for PBXNativeTarget "mDNSResponder debug" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + D284BE930ADD80800027CCDF /* Development */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Development; + }; + D284BEAD0ADD80920027CCDF /* Build configuration list for PBXNativeTarget "dns-sd tool" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + D284BEAE0ADD80920027CCDF /* Development */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Development; + }; + D284BEBB0ADD809A0027CCDF /* Build configuration list for PBXNativeTarget "libjdns_sd.jnilib" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + D284BEBC0ADD809A0027CCDF /* Development */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Development; + }; + D284BED60ADD80A20027CCDF /* Build configuration list for PBXNativeTarget "dnsextd" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + D284BED70ADD80A20027CCDF /* Development */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Development; + }; + D284BEE50ADD80A70027CCDF /* Build configuration list for PBXNativeTarget "ddnswriteconfig" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + D284BEE60ADD80A70027CCDF /* Development */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Development; + }; + D284BF080ADD80B00027CCDF /* Build configuration list for PBXNativeTarget "PreferencePane" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + D284BF090ADD80B00027CCDF /* Development */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Development; + }; + FFA572370AF18F1C0055A0F1 /* Build configuration list for PBXNativeTarget "libdns_sd_debug_dynamic" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FFA572380AF18F1C0055A0F1 /* Development */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Development; + }; + FFA572430AF18F450055A0F1 /* Build configuration list for PBXNativeTarget "libdns_sd_profile_dynamic" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FFA572440AF18F450055A0F1 /* Development */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Development; + }; + FFA5726E0AF191200055A0F1 /* Build configuration list for PBXAggregateTarget "SystemLibrariesDynamic" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FFA5726F0AF191200055A0F1 /* Development */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Development; + }; + FFB7657E0AEED99D00583A2C /* Build configuration list for PBXAggregateTarget "Build All" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FFB7657F0AEED99D00583A2C /* Development */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Development; + }; + FFB765890AEED9FB00583A2C /* Build configuration list for PBXNativeTarget "libdns_sd_dynamic" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FFB7658A0AEED9FB00583A2C /* Development */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Development; + }; +/* End XCConfigurationList section */ + }; + rootObject = 08FB7793FE84155DC02AAC07 /* Project object */; +} diff --git a/mDNSResponder/mDNSMacOSX/mDNSResponderHelper.8 b/mDNSResponder/mDNSMacOSX/mDNSResponderHelper.8 new file mode 100644 index 00000000..6e959dbd --- /dev/null +++ b/mDNSResponder/mDNSMacOSX/mDNSResponderHelper.8 @@ -0,0 +1,54 @@ +.\" -*- tab-width: 4 -*- +.\" +.\" Copyright (c) 2007 Apple Computer, 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. +.\" +.Dd August 2007 \" Date +.Dt mDNSResponderHelper 8 \" Document Title +.Os Darwin \" Operating System +.\" +.Sh NAME +.Nm mDNSResponderHelper +.Nd mDNS privilege separation helper \" Name Description for whatis database +.\" +.Sh SYNOPSIS +.Nm +.\" +.Sh DESCRIPTION +.Nm +is an executable invoked by +.Nm launchd +to provide privilege separation +to the +.Nm mDNSResponder +daemon. +.Pp +.Nm +has no user-specifiable command-line arguments, and users should not run +.Nm +manually. +.Sh FILES +.Pa /usr/sbin/mDNSResponderHelper \" Pathname +.\" +.Sh SEE ALSO +.Xr mDNSResponder 8 +.\" +.Sh BUGS +.Nm +bugs are tracked in Apple Radar component "mDNSResponder". +.\" +.Sh HISTORY +The +.Nm +first appeared in Mac OS X 10.5 (Leopard). diff --git a/mDNSResponder/mDNSMacOSX/mDNSResponderHelper.plist b/mDNSResponder/mDNSMacOSX/mDNSResponderHelper.plist new file mode 100644 index 00000000..22286d2e --- /dev/null +++ b/mDNSResponder/mDNSMacOSX/mDNSResponderHelper.plist @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<!-- To run mDNSResponderHelper on Tiger, place this file into /etc/mach_init.d --> +<plist version="1.0"> +<dict> + <key>ServiceName</key> + <string>com.apple.mDNSResponderHelper</string> + <key>Command</key> + <string>/usr/sbin/mDNSResponderHelper</string> + <key>OnDemand</key> + <true/> +</dict> +</plist> diff --git a/mDNSResponder/mDNSMacOSX/mDNSResponderLogging.mobileconfig b/mDNSResponder/mDNSMacOSX/mDNSResponderLogging.mobileconfig new file mode 100644 index 00000000..34ec0d9a --- /dev/null +++ b/mDNSResponder/mDNSMacOSX/mDNSResponderLogging.mobileconfig @@ -0,0 +1,57 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>PayloadIdentifier</key> + <string>com.apple.mDNSResponder</string> + <key>PayloadUUID</key> + <string>6D0962E1-558A-44FB-8FE0-1F4F0BD157F4</string> + <key>PayloadDescription</key> + <string>Turns on mDNSResponder Debug Logging</string> + <key>PayloadDisplayName</key> + <string>mDNSResponder Debug Logging</string> + <key>PayloadOrganization</key> + <string>Apple, Inc</string> + <key>PayloadType</key> + <string>Configuration</string> + <key>PayloadVersion</key> + <integer>2</integer> + + <key>ConsentText</key> + <dict> + <key>en</key> + <string>English consent text</string> + <key>jp</key> + <string>Japanese consent text</string> + <key>default</key> + <string>Default consent text - used if none of the other languages match</string> + </dict> + + <key>PayloadContent</key> + <array> + <dict> + <key>PayloadUUID</key> + <string>6D0962E1-558A-44FB-8FE0-1F4F0BD157F4</string> + <key>PayloadIdentifier</key> + <string>com.apple.defaults.1</string> + <key>PayloadType</key> + <string>com.apple.defaults.managed</string> + <key>PayloadVersion</key> + <integer>1</integer> + <key>PayloadContent</key> + <array> + <dict> + <key>DefaultsDomainName</key> + <string>com.apple.mDNSResponder</string> + <key>DefaultsData</key> + <dict> + <key>EnableLogging</key> + <true/> + </dict> + </dict> + </array> + </dict> + </array> +</dict> +</plist> + diff --git a/mDNSResponder/mDNSMacOSX/pfkey.c b/mDNSResponder/mDNSMacOSX/pfkey.c new file mode 100644 index 00000000..839c8d71 --- /dev/null +++ b/mDNSResponder/mDNSMacOSX/pfkey.c @@ -0,0 +1,2138 @@ +/* + * Copyright (c) 2003-2007 Apple Computer, 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. + */ +/* $FreeBSD: src/lib/libipsec/pfkey.c,v 1.1.2.2 2001/07/03 11:01:14 ume Exp $ */ +/* $KAME: pfkey.c,v 1.39 2001/03/05 18:22:17 thorpej Exp $ */ + +/* + * Copyright (C) 1995, 1996, 1997, 1998, and 1999 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/socket.h> +#include <net/pfkeyv2.h> +#include <netinet/in.h> +#include <netinet6/ipsec.h> + +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <stdio.h> +#include <TargetConditionals.h> + +#include "ipsec_strerror.h" +#include "libpfkey.h" +#include "ipsec_options.h" + +#if TARGET_OS_EMBEDDED +#ifndef MDNS_NO_IPSEC +#define MDNS_NO_IPSEC 1 +#endif +#endif + +#ifndef MDNS_NO_IPSEC + +#define CALLOC(size, cast) (cast)calloc(1, (size)) + +static int findsupportedmap __P((int)); +static int setsupportedmap __P((struct sadb_supported *)); +static struct sadb_alg *findsupportedalg __P((u_int, u_int)); +static int pfkey_send_x1 __P((int, u_int, u_int, u_int, struct sockaddr *, + struct sockaddr *, u_int32_t, u_int32_t, u_int, caddr_t, + u_int, u_int, u_int, u_int, u_int, u_int32_t, u_int32_t, + u_int32_t, u_int32_t, u_int32_t)); +static int pfkey_send_x2 __P((int, u_int, u_int, u_int, + struct sockaddr *, struct sockaddr *, u_int32_t)); +static int pfkey_send_x3 __P((int, u_int, u_int)); +static int pfkey_send_x4 __P((int, u_int, struct sockaddr *, u_int, + struct sockaddr *, u_int, u_int, u_int64_t, u_int64_t, + char *, int, u_int32_t)); +static int pfkey_send_x5 __P((int, u_int, u_int32_t)); + +static caddr_t pfkey_setsadbmsg __P((caddr_t, caddr_t, u_int, u_int, + u_int, u_int32_t, pid_t)); +static caddr_t pfkey_setsadbsa __P((caddr_t, caddr_t, u_int32_t, u_int, + u_int, u_int, u_int32_t)); +static caddr_t pfkey_setsadbaddr __P((caddr_t, caddr_t, u_int, + struct sockaddr *, u_int, u_int)); +static caddr_t pfkey_setsadbkey __P((caddr_t, caddr_t, u_int, caddr_t, u_int)); +static caddr_t pfkey_setsadblifetime __P((caddr_t, caddr_t, u_int, u_int32_t, + u_int32_t, u_int32_t, u_int32_t)); +static caddr_t pfkey_setsadbxsa2 __P((caddr_t, caddr_t, u_int32_t, u_int32_t)); + +/* + * make and search supported algorithm structure. + */ +static struct sadb_supported *ipsec_supported[] = { NULL, NULL, NULL, }; + +static int supported_map[] = { + SADB_SATYPE_AH, + SADB_SATYPE_ESP, + SADB_X_SATYPE_IPCOMP, +}; + +static int +findsupportedmap(satype) +int satype; +{ + int i; + + for (i = 0; (unsigned int)i < sizeof(supported_map)/sizeof(supported_map[0]); i++) + if (supported_map[i] == satype) + return i; + return -1; +} + +static struct sadb_alg * +findsupportedalg(satype, alg_id) +u_int satype, alg_id; +{ + int algno; + int tlen; + caddr_t p; + + /* validity check */ + algno = findsupportedmap(satype); + if (algno == -1) { + __ipsec_errcode = EIPSEC_INVAL_ARGUMENT; + return NULL; + } + if (ipsec_supported[algno] == NULL) { + __ipsec_errcode = EIPSEC_DO_GET_SUPP_LIST; + return NULL; + } + + tlen = ipsec_supported[algno]->sadb_supported_len + - sizeof(struct sadb_supported); + p = (caddr_t)(ipsec_supported[algno] + 1); + while (tlen > 0) { + if ((unsigned int)tlen < sizeof(struct sadb_alg)) { + /* invalid format */ + break; + } + if (((struct sadb_alg *)p)->sadb_alg_id == alg_id) + return (struct sadb_alg *)p; + + tlen -= sizeof(struct sadb_alg); + p += sizeof(struct sadb_alg); + } + + __ipsec_errcode = EIPSEC_NOT_SUPPORTED; + return NULL; +} + +static int +setsupportedmap(sup) +struct sadb_supported *sup; +{ + struct sadb_supported **ipsup; + + switch (sup->sadb_supported_exttype) { + case SADB_EXT_SUPPORTED_AUTH: + ipsup = &ipsec_supported[findsupportedmap(SADB_SATYPE_AH)]; + break; + case SADB_EXT_SUPPORTED_ENCRYPT: + ipsup = &ipsec_supported[findsupportedmap(SADB_SATYPE_ESP)]; + break; + default: + __ipsec_errcode = EIPSEC_INVAL_SATYPE; + return -1; + } + + if (*ipsup) + free(*ipsup); + + *ipsup = malloc(sup->sadb_supported_len); + if (!*ipsup) { + __ipsec_set_strerror(strerror(errno)); + return -1; + } + memcpy(*ipsup, sup, sup->sadb_supported_len); + + return 0; +} + +/* + * check key length against algorithm specified. + * This function is called with SADB_EXT_SUPPORTED_{AUTH,ENCRYPT} as the + * augument, and only calls to ipsec_check_keylen2(); + * keylen is the unit of bit. + * OUT: + * -1: invalid. + * 0: valid. + */ +int +ipsec_check_keylen(supported, alg_id, keylen) +u_int supported; +u_int alg_id; +u_int keylen; +{ + int satype; + + /* validity check */ + switch (supported) { + case SADB_EXT_SUPPORTED_AUTH: + satype = SADB_SATYPE_AH; + break; + case SADB_EXT_SUPPORTED_ENCRYPT: + satype = SADB_SATYPE_ESP; + break; + default: + __ipsec_errcode = EIPSEC_INVAL_ARGUMENT; + return -1; + } + + return ipsec_check_keylen2(satype, alg_id, keylen); +} + +/* + * check key length against algorithm specified. + * satype is one of satype defined at pfkeyv2.h. + * keylen is the unit of bit. + * OUT: + * -1: invalid. + * 0: valid. + */ +int +ipsec_check_keylen2(satype, alg_id, keylen) +u_int satype; +u_int alg_id; +u_int keylen; +{ + struct sadb_alg *alg; + + alg = findsupportedalg(satype, alg_id); + if (!alg) + return -1; + + if (keylen < alg->sadb_alg_minbits || keylen > alg->sadb_alg_maxbits) { + __ipsec_errcode = EIPSEC_INVAL_KEYLEN; + return -1; + } + + __ipsec_errcode = EIPSEC_NO_ERROR; + return 0; +} + +/* + * get max/min key length against algorithm specified. + * satype is one of satype defined at pfkeyv2.h. + * keylen is the unit of bit. + * OUT: + * -1: invalid. + * 0: valid. + */ +int +ipsec_get_keylen(supported, alg_id, alg0) +u_int supported, alg_id; +struct sadb_alg *alg0; +{ + struct sadb_alg *alg; + u_int satype; + + /* validity check */ + if (!alg0) { + __ipsec_errcode = EIPSEC_INVAL_ARGUMENT; + return -1; + } + + switch (supported) { + case SADB_EXT_SUPPORTED_AUTH: + satype = SADB_SATYPE_AH; + break; + case SADB_EXT_SUPPORTED_ENCRYPT: + satype = SADB_SATYPE_ESP; + break; + default: + __ipsec_errcode = EIPSEC_INVAL_ARGUMENT; + return -1; + } + + alg = findsupportedalg(satype, alg_id); + if (!alg) + return -1; + + memcpy(alg0, alg, sizeof(*alg0)); + + __ipsec_errcode = EIPSEC_NO_ERROR; + return 0; +} + +/* + * set the rate for SOFT lifetime against HARD one. + * If rate is more than 100 or equal to zero, then set to 100. + */ +static u_int soft_lifetime_allocations_rate = PFKEY_SOFT_LIFETIME_RATE; +static u_int soft_lifetime_bytes_rate = PFKEY_SOFT_LIFETIME_RATE; +static u_int soft_lifetime_addtime_rate = PFKEY_SOFT_LIFETIME_RATE; +static u_int soft_lifetime_usetime_rate = PFKEY_SOFT_LIFETIME_RATE; + +u_int +pfkey_set_softrate(type, rate) +u_int type, rate; +{ + __ipsec_errcode = EIPSEC_NO_ERROR; + + if (rate > 100 || rate == 0) + rate = 100; + + switch (type) { + case SADB_X_LIFETIME_ALLOCATIONS: + soft_lifetime_allocations_rate = rate; + return 0; + case SADB_X_LIFETIME_BYTES: + soft_lifetime_bytes_rate = rate; + return 0; + case SADB_X_LIFETIME_ADDTIME: + soft_lifetime_addtime_rate = rate; + return 0; + case SADB_X_LIFETIME_USETIME: + soft_lifetime_usetime_rate = rate; + return 0; + } + + __ipsec_errcode = EIPSEC_INVAL_ARGUMENT; + return 1; +} + +/* + * get current rate for SOFT lifetime against HARD one. + * ATTENTION: ~0 is returned if invalid type was passed. + */ +u_int +pfkey_get_softrate(type) +u_int type; +{ + switch (type) { + case SADB_X_LIFETIME_ALLOCATIONS: + return soft_lifetime_allocations_rate; + case SADB_X_LIFETIME_BYTES: + return soft_lifetime_bytes_rate; + case SADB_X_LIFETIME_ADDTIME: + return soft_lifetime_addtime_rate; + case SADB_X_LIFETIME_USETIME: + return soft_lifetime_usetime_rate; + } + + return ~0; +} + +/* + * sending SADB_GETSPI message to the kernel. + * OUT: + * positive: success and return length sent. + * -1 : error occured, and set errno. + */ +int +pfkey_send_getspi(so, satype, mode, src, dst, min, max, reqid, seq) +int so; +u_int satype, mode; +struct sockaddr *src, *dst; +u_int32_t min, max, reqid, seq; +{ + struct sadb_msg *newmsg; + caddr_t ep; + int len; + int need_spirange = 0; + caddr_t p; + int plen; + + /* validity check */ + if (src == NULL || dst == NULL) { + __ipsec_errcode = EIPSEC_INVAL_ARGUMENT; + return -1; + } + if (src->sa_family != dst->sa_family) { + __ipsec_errcode = EIPSEC_FAMILY_MISMATCH; + return -1; + } + if (min > max || (min > 0 && min <= 255)) { + __ipsec_errcode = EIPSEC_INVAL_SPI; + return -1; + } + switch (src->sa_family) { + case AF_INET: + plen = sizeof(struct in_addr) << 3; + break; + case AF_INET6: + plen = sizeof(struct in6_addr) << 3; + break; + default: + __ipsec_errcode = EIPSEC_INVAL_FAMILY; + return -1; + } + + /* create new sadb_msg to send. */ + len = sizeof(struct sadb_msg) + + sizeof(struct sadb_x_sa2) + + sizeof(struct sadb_address) + + PFKEY_ALIGN8(src->sa_len) + + sizeof(struct sadb_address) + + PFKEY_ALIGN8(dst->sa_len); + + if (min > (u_int32_t)255 && max < (u_int32_t) ~0) { + need_spirange++; + len += sizeof(struct sadb_spirange); + } + + if ((newmsg = CALLOC(len, struct sadb_msg *)) == NULL) { + __ipsec_set_strerror(strerror(errno)); + return -1; + } + ep = ((caddr_t)newmsg) + len; + + p = pfkey_setsadbmsg((caddr_t)newmsg, ep, SADB_GETSPI, + len, satype, seq, getpid()); + if (!p) { + free(newmsg); + return -1; + } + + p = pfkey_setsadbxsa2(p, ep, mode, reqid); + if (!p) { + free(newmsg); + return -1; + } + + /* set sadb_address for source */ + p = pfkey_setsadbaddr(p, ep, SADB_EXT_ADDRESS_SRC, src, plen, + IPSEC_ULPROTO_ANY); + if (!p) { + free(newmsg); + return -1; + } + + /* set sadb_address for destination */ + p = pfkey_setsadbaddr(p, ep, SADB_EXT_ADDRESS_DST, dst, plen, + IPSEC_ULPROTO_ANY); + if (!p) { + free(newmsg); + return -1; + } + + /* proccessing spi range */ + if (need_spirange) { + struct sadb_spirange spirange; + + if (p + sizeof(spirange) > ep) { + free(newmsg); + return -1; + } + + memset(&spirange, 0, sizeof(spirange)); + spirange.sadb_spirange_len = PFKEY_UNIT64(sizeof(spirange)); + spirange.sadb_spirange_exttype = SADB_EXT_SPIRANGE; + spirange.sadb_spirange_min = min; + spirange.sadb_spirange_max = max; + + memcpy(p, &spirange, sizeof(spirange)); + + p += sizeof(spirange); + } + if (p != ep) { + free(newmsg); + return -1; + } + + /* send message */ + len = pfkey_send(so, newmsg, len); + free(newmsg); + + if (len < 0) + return -1; + + __ipsec_errcode = EIPSEC_NO_ERROR; + return len; +} + +/* + * sending SADB_UPDATE message to the kernel. + * The length of key material is a_keylen + e_keylen. + * OUT: + * positive: success and return length sent. + * -1 : error occured, and set errno. + */ +int +pfkey_send_update(so, satype, mode, src, dst, spi, reqid, wsize, + keymat, e_type, e_keylen, a_type, a_keylen, flags, + l_alloc, l_bytes, l_addtime, l_usetime, seq) +int so; +u_int satype, mode, wsize; +struct sockaddr *src, *dst; +u_int32_t spi, reqid; +caddr_t keymat; +u_int e_type, e_keylen, a_type, a_keylen, flags; +u_int32_t l_alloc; +u_int64_t l_bytes, l_addtime, l_usetime; +u_int32_t seq; +{ + int len; + if ((len = pfkey_send_x1(so, SADB_UPDATE, satype, mode, src, dst, spi, + reqid, wsize, + keymat, e_type, e_keylen, a_type, a_keylen, flags, + l_alloc, l_bytes, l_addtime, l_usetime, seq)) < 0) + return -1; + + return len; +} + +/* + * sending SADB_ADD message to the kernel. + * The length of key material is a_keylen + e_keylen. + * OUT: + * positive: success and return length sent. + * -1 : error occured, and set errno. + */ +int +pfkey_send_add(so, satype, mode, src, dst, spi, reqid, wsize, + keymat, e_type, e_keylen, a_type, a_keylen, flags, + l_alloc, l_bytes, l_addtime, l_usetime, seq) +int so; +u_int satype, mode, wsize; +struct sockaddr *src, *dst; +u_int32_t spi, reqid; +caddr_t keymat; +u_int e_type, e_keylen, a_type, a_keylen, flags; +u_int32_t l_alloc; +u_int64_t l_bytes, l_addtime, l_usetime; +u_int32_t seq; +{ + int len; + if ((len = pfkey_send_x1(so, SADB_ADD, satype, mode, src, dst, spi, + reqid, wsize, + keymat, e_type, e_keylen, a_type, a_keylen, flags, + l_alloc, l_bytes, l_addtime, l_usetime, seq)) < 0) + return -1; + + return len; +} + +/* + * sending SADB_DELETE message to the kernel. + * OUT: + * positive: success and return length sent. + * -1 : error occured, and set errno. + */ +int +pfkey_send_delete(so, satype, mode, src, dst, spi) +int so; +u_int satype, mode; +struct sockaddr *src, *dst; +u_int32_t spi; +{ + int len; + if ((len = pfkey_send_x2(so, SADB_DELETE, satype, mode, src, dst, spi)) < 0) + return -1; + + return len; +} + +/* + * sending SADB_DELETE without spi to the kernel. This is + * the "delete all" request (an extension also present in + * Solaris). + * + * OUT: + * positive: success and return length sent + * -1 : error occured, and set errno + */ +int +pfkey_send_delete_all(so, satype, mode, src, dst) +int so; +u_int satype, mode; +struct sockaddr *src, *dst; +{ + struct sadb_msg *newmsg; + int len; + caddr_t p; + int plen; + caddr_t ep; + + (void)mode; + + /* validity check */ + if (src == NULL || dst == NULL) { + __ipsec_errcode = EIPSEC_INVAL_ARGUMENT; + return -1; + } + if (src->sa_family != dst->sa_family) { + __ipsec_errcode = EIPSEC_FAMILY_MISMATCH; + return -1; + } + switch (src->sa_family) { + case AF_INET: + plen = sizeof(struct in_addr) << 3; + break; + case AF_INET6: + plen = sizeof(struct in6_addr) << 3; + break; + default: + __ipsec_errcode = EIPSEC_INVAL_FAMILY; + return -1; + } + + /* create new sadb_msg to reply. */ + len = sizeof(struct sadb_msg) + + sizeof(struct sadb_address) + + PFKEY_ALIGN8(src->sa_len) + + sizeof(struct sadb_address) + + PFKEY_ALIGN8(dst->sa_len); + + if ((newmsg = CALLOC(len, struct sadb_msg *)) == NULL) { + __ipsec_set_strerror(strerror(errno)); + return -1; + } + ep = ((caddr_t)newmsg) + len; + + p = pfkey_setsadbmsg((caddr_t)newmsg, ep, SADB_DELETE, len, satype, 0, + getpid()); + if (!p) { + free(newmsg); + return -1; + } + p = pfkey_setsadbaddr(p, ep, SADB_EXT_ADDRESS_SRC, src, plen, + IPSEC_ULPROTO_ANY); + if (!p) { + free(newmsg); + return -1; + } + p = pfkey_setsadbaddr(p, ep, SADB_EXT_ADDRESS_DST, dst, plen, + IPSEC_ULPROTO_ANY); + if (!p || p != ep) { + free(newmsg); + return -1; + } + + /* send message */ + len = pfkey_send(so, newmsg, len); + free(newmsg); + + if (len < 0) + return -1; + + __ipsec_errcode = EIPSEC_NO_ERROR; + return len; +} + +/* + * sending SADB_GET message to the kernel. + * OUT: + * positive: success and return length sent. + * -1 : error occured, and set errno. + */ +int +pfkey_send_get(so, satype, mode, src, dst, spi) +int so; +u_int satype, mode; +struct sockaddr *src, *dst; +u_int32_t spi; +{ + int len; + if ((len = pfkey_send_x2(so, SADB_GET, satype, mode, src, dst, spi)) < 0) + return -1; + + return len; +} + +/* + * sending SADB_REGISTER message to the kernel. + * OUT: + * positive: success and return length sent. + * -1 : error occured, and set errno. + */ +int +pfkey_send_register(so, satype) +int so; +u_int satype; +{ + int len, algno; + + if (satype == PF_UNSPEC) { + for (algno = 0; + (unsigned int)algno < sizeof(supported_map)/sizeof(supported_map[0]); + algno++) { + if (ipsec_supported[algno]) { + free(ipsec_supported[algno]); + ipsec_supported[algno] = NULL; + } + } + } else { + algno = findsupportedmap(satype); + if (algno == -1) { + __ipsec_errcode = EIPSEC_INVAL_ARGUMENT; + return -1; + } + + if (ipsec_supported[algno]) { + free(ipsec_supported[algno]); + ipsec_supported[algno] = NULL; + } + } + + if ((len = pfkey_send_x3(so, SADB_REGISTER, satype)) < 0) + return -1; + + return len; +} + +/* + * receiving SADB_REGISTER message from the kernel, and copy buffer for + * sadb_supported returned into ipsec_supported. + * OUT: + * 0: success and return length sent. + * -1: error occured, and set errno. + */ +int +pfkey_recv_register(so) +int so; +{ + pid_t pid = getpid(); + struct sadb_msg *newmsg; + int error = -1; + + /* receive message */ + do { + if ((newmsg = pfkey_recv(so)) == NULL) + return -1; + } while (newmsg->sadb_msg_type != SADB_REGISTER + || (pid_t)newmsg->sadb_msg_pid != pid); + + /* check and fix */ + newmsg->sadb_msg_len = PFKEY_UNUNIT64(newmsg->sadb_msg_len); + + error = pfkey_set_supported(newmsg, newmsg->sadb_msg_len); + free(newmsg); + + if (error == 0) + __ipsec_errcode = EIPSEC_NO_ERROR; + + return error; +} + +/* + * receiving SADB_REGISTER message from the kernel, and copy buffer for + * sadb_supported returned into ipsec_supported. + * NOTE: sadb_msg_len must be host order. + * IN: + * tlen: msg length, it's to makeing sure. + * OUT: + * 0: success and return length sent. + * -1: error occured, and set errno. + */ +int +pfkey_set_supported(msg, tlen) +struct sadb_msg *msg; +int tlen; +{ + struct sadb_supported *sup; + caddr_t p; + caddr_t ep; + + /* validity */ + if (msg->sadb_msg_len != tlen) { + __ipsec_errcode = EIPSEC_INVAL_ARGUMENT; + return -1; + } + + p = (caddr_t)msg; + ep = p + tlen; + + p += sizeof(struct sadb_msg); + + while (p < ep) { + sup = (struct sadb_supported *)p; + if (ep < p + sizeof(*sup) || + (size_t)PFKEY_EXTLEN(sup) < sizeof(*sup) || + ep < p + sup->sadb_supported_len) { + /* invalid format */ + break; + } + + switch (sup->sadb_supported_exttype) { + case SADB_EXT_SUPPORTED_AUTH: + case SADB_EXT_SUPPORTED_ENCRYPT: + break; + default: + __ipsec_errcode = EIPSEC_INVAL_SATYPE; + return -1; + } + + /* fixed length */ + sup->sadb_supported_len = PFKEY_EXTLEN(sup); + + /* set supported map */ + if (setsupportedmap(sup) != 0) + return -1; + + p += sup->sadb_supported_len; + } + + if (p != ep) { + __ipsec_errcode = EIPSEC_INVAL_SATYPE; + return -1; + } + + __ipsec_errcode = EIPSEC_NO_ERROR; + + return 0; +} + +/* + * sending SADB_FLUSH message to the kernel. + * OUT: + * positive: success and return length sent. + * -1 : error occured, and set errno. + */ +int +pfkey_send_flush(so, satype) +int so; +u_int satype; +{ + int len; + + if ((len = pfkey_send_x3(so, SADB_FLUSH, satype)) < 0) + return -1; + + return len; +} + +/* + * sending SADB_DUMP message to the kernel. + * OUT: + * positive: success and return length sent. + * -1 : error occured, and set errno. + */ +int +pfkey_send_dump(so, satype) +int so; +u_int satype; +{ + int len; + + if ((len = pfkey_send_x3(so, SADB_DUMP, satype)) < 0) + return -1; + + return len; +} + +/* + * sending SADB_X_PROMISC message to the kernel. + * NOTE that this function handles promisc mode toggle only. + * IN: + * flag: set promisc off if zero, set promisc on if non-zero. + * OUT: + * positive: success and return length sent. + * -1 : error occured, and set errno. + * 0 : error occured, and set errno. + * others: a pointer to new allocated buffer in which supported + * algorithms is. + */ +int +pfkey_send_promisc_toggle(so, flag) +int so; +int flag; +{ + int len; + + if ((len = pfkey_send_x3(so, SADB_X_PROMISC, (flag ? 1 : 0))) < 0) + return -1; + + return len; +} + +/* + * sending SADB_X_SPDADD message to the kernel. + * OUT: + * positive: success and return length sent. + * -1 : error occured, and set errno. + */ +int +pfkey_send_spdadd(so, src, prefs, dst, prefd, proto, policy, policylen, seq) +int so; +struct sockaddr *src, *dst; +u_int prefs, prefd, proto; +caddr_t policy; +int policylen; +u_int32_t seq; +{ + int len; + + if ((len = pfkey_send_x4(so, SADB_X_SPDADD, + src, prefs, dst, prefd, proto, + 0, 0, + policy, policylen, seq)) < 0) + return -1; + + return len; +} + +/* + * sending SADB_X_SPDADD message to the kernel. + * OUT: + * positive: success and return length sent. + * -1 : error occured, and set errno. + */ +int +pfkey_send_spdadd2(so, src, prefs, dst, prefd, proto, ltime, vtime, + policy, policylen, seq) +int so; +struct sockaddr *src, *dst; +u_int prefs, prefd, proto; +u_int64_t ltime, vtime; +caddr_t policy; +int policylen; +u_int32_t seq; +{ + int len; + + if ((len = pfkey_send_x4(so, SADB_X_SPDADD, + src, prefs, dst, prefd, proto, + ltime, vtime, + policy, policylen, seq)) < 0) + return -1; + + return len; +} + +/* + * sending SADB_X_SPDUPDATE message to the kernel. + * OUT: + * positive: success and return length sent. + * -1 : error occured, and set errno. + */ +int +pfkey_send_spdupdate(so, src, prefs, dst, prefd, proto, policy, policylen, seq) +int so; +struct sockaddr *src, *dst; +u_int prefs, prefd, proto; +caddr_t policy; +int policylen; +u_int32_t seq; +{ + int len; + + if ((len = pfkey_send_x4(so, SADB_X_SPDUPDATE, + src, prefs, dst, prefd, proto, + 0, 0, + policy, policylen, seq)) < 0) + return -1; + + return len; +} + +/* + * sending SADB_X_SPDUPDATE message to the kernel. + * OUT: + * positive: success and return length sent. + * -1 : error occured, and set errno. + */ +int +pfkey_send_spdupdate2(so, src, prefs, dst, prefd, proto, ltime, vtime, + policy, policylen, seq) +int so; +struct sockaddr *src, *dst; +u_int prefs, prefd, proto; +u_int64_t ltime, vtime; +caddr_t policy; +int policylen; +u_int32_t seq; +{ + int len; + + if ((len = pfkey_send_x4(so, SADB_X_SPDUPDATE, + src, prefs, dst, prefd, proto, + ltime, vtime, + policy, policylen, seq)) < 0) + return -1; + + return len; +} + +/* + * sending SADB_X_SPDDELETE message to the kernel. + * OUT: + * positive: success and return length sent. + * -1 : error occured, and set errno. + */ +int +pfkey_send_spddelete(so, src, prefs, dst, prefd, proto, policy, policylen, seq) +int so; +struct sockaddr *src, *dst; +u_int prefs, prefd, proto; +caddr_t policy; +int policylen; +u_int32_t seq; +{ + int len; + + if (policylen != sizeof(struct sadb_x_policy)) { + __ipsec_errcode = EIPSEC_INVAL_ARGUMENT; + return -1; + } + + if ((len = pfkey_send_x4(so, SADB_X_SPDDELETE, + src, prefs, dst, prefd, proto, + 0, 0, + policy, policylen, seq)) < 0) + return -1; + + return len; +} + +/* + * sending SADB_X_SPDDELETE message to the kernel. + * OUT: + * positive: success and return length sent. + * -1 : error occured, and set errno. + */ +int +pfkey_send_spddelete2(so, spid) +int so; +u_int32_t spid; +{ + int len; + + if ((len = pfkey_send_x5(so, SADB_X_SPDDELETE2, spid)) < 0) + return -1; + + return len; +} + +/* + * sending SADB_X_SPDGET message to the kernel. + * OUT: + * positive: success and return length sent. + * -1 : error occured, and set errno. + */ +int +pfkey_send_spdget(so, spid) +int so; +u_int32_t spid; +{ + int len; + + if ((len = pfkey_send_x5(so, SADB_X_SPDGET, spid)) < 0) + return -1; + + return len; +} + +/* + * sending SADB_X_SPDSETIDX message to the kernel. + * OUT: + * positive: success and return length sent. + * -1 : error occured, and set errno. + */ +int +pfkey_send_spdsetidx(so, src, prefs, dst, prefd, proto, policy, policylen, seq) +int so; +struct sockaddr *src, *dst; +u_int prefs, prefd, proto; +caddr_t policy; +int policylen; +u_int32_t seq; +{ + int len; + + if (policylen != sizeof(struct sadb_x_policy)) { + __ipsec_errcode = EIPSEC_INVAL_ARGUMENT; + return -1; + } + + if ((len = pfkey_send_x4(so, SADB_X_SPDSETIDX, + src, prefs, dst, prefd, proto, + 0, 0, + policy, policylen, seq)) < 0) + return -1; + + return len; +} + +/* + * sending SADB_SPDFLUSH message to the kernel. + * OUT: + * positive: success and return length sent. + * -1 : error occured, and set errno. + */ +int +pfkey_send_spdflush(so) +int so; +{ + int len; + + if ((len = pfkey_send_x3(so, SADB_X_SPDFLUSH, SADB_SATYPE_UNSPEC)) < 0) + return -1; + + return len; +} + +/* + * sending SADB_SPDDUMP message to the kernel. + * OUT: + * positive: success and return length sent. + * -1 : error occured, and set errno. + */ +int +pfkey_send_spddump(so) +int so; +{ + int len; + + if ((len = pfkey_send_x3(so, SADB_X_SPDDUMP, SADB_SATYPE_UNSPEC)) < 0) + return -1; + + return len; +} + +/* sending SADB_ADD or SADB_UPDATE message to the kernel */ +static int +pfkey_send_x1(so, type, satype, mode, src, dst, spi, reqid, wsize, + keymat, e_type, e_keylen, a_type, a_keylen, flags, + l_alloc, l_bytes, l_addtime, l_usetime, seq) +int so; +u_int type, satype, mode; +struct sockaddr *src, *dst; +u_int32_t spi, reqid; +u_int wsize; +caddr_t keymat; +u_int e_type, e_keylen, a_type, a_keylen, flags; +u_int32_t l_alloc, l_bytes, l_addtime, l_usetime, seq; +{ + struct sadb_msg *newmsg; + int len; + caddr_t p; + int plen; + caddr_t ep; + + /* validity check */ + if (src == NULL || dst == NULL) { + __ipsec_errcode = EIPSEC_INVAL_ARGUMENT; + return -1; + } + if (src->sa_family != dst->sa_family) { + __ipsec_errcode = EIPSEC_FAMILY_MISMATCH; + return -1; + } + switch (src->sa_family) { + case AF_INET: + plen = sizeof(struct in_addr) << 3; + break; + case AF_INET6: + plen = sizeof(struct in6_addr) << 3; + break; + default: + __ipsec_errcode = EIPSEC_INVAL_FAMILY; + return -1; + } + + switch (satype) { + case SADB_SATYPE_ESP: + if (e_type == SADB_EALG_NONE) { + __ipsec_errcode = EIPSEC_NO_ALGS; + return -1; + } + break; + case SADB_SATYPE_AH: + if (e_type != SADB_EALG_NONE) { + __ipsec_errcode = EIPSEC_INVAL_ALGS; + return -1; + } + if (a_type == SADB_AALG_NONE) { + __ipsec_errcode = EIPSEC_NO_ALGS; + return -1; + } + break; + case SADB_X_SATYPE_IPCOMP: + if (e_type == SADB_X_CALG_NONE) { + __ipsec_errcode = EIPSEC_INVAL_ALGS; + return -1; + } + if (a_type != SADB_AALG_NONE) { + __ipsec_errcode = EIPSEC_NO_ALGS; + return -1; + } + break; + default: + __ipsec_errcode = EIPSEC_INVAL_SATYPE; + return -1; + } + + /* create new sadb_msg to reply. */ + len = sizeof(struct sadb_msg) + + sizeof(struct sadb_sa) + + sizeof(struct sadb_x_sa2) + + sizeof(struct sadb_address) + + PFKEY_ALIGN8(src->sa_len) + + sizeof(struct sadb_address) + + PFKEY_ALIGN8(dst->sa_len) + + sizeof(struct sadb_lifetime) + + sizeof(struct sadb_lifetime); + + if (e_type != SADB_EALG_NONE) + len += (sizeof(struct sadb_key) + PFKEY_ALIGN8(e_keylen)); + if (a_type != SADB_AALG_NONE) + len += (sizeof(struct sadb_key) + PFKEY_ALIGN8(a_keylen)); + + if ((newmsg = CALLOC(len, struct sadb_msg *)) == NULL) { + __ipsec_set_strerror(strerror(errno)); + return -1; + } + ep = ((caddr_t)newmsg) + len; + + p = pfkey_setsadbmsg((caddr_t)newmsg, ep, type, len, + satype, seq, getpid()); + if (!p) { + free(newmsg); + return -1; + } + p = pfkey_setsadbsa(p, ep, spi, wsize, a_type, e_type, flags); + if (!p) { + free(newmsg); + return -1; + } + p = pfkey_setsadbxsa2(p, ep, mode, reqid); + if (!p) { + free(newmsg); + return -1; + } + p = pfkey_setsadbaddr(p, ep, SADB_EXT_ADDRESS_SRC, src, plen, + IPSEC_ULPROTO_ANY); + if (!p) { + free(newmsg); + return -1; + } + p = pfkey_setsadbaddr(p, ep, SADB_EXT_ADDRESS_DST, dst, plen, + IPSEC_ULPROTO_ANY); + if (!p) { + free(newmsg); + return -1; + } + + if (e_type != SADB_EALG_NONE) { + p = pfkey_setsadbkey(p, ep, SADB_EXT_KEY_ENCRYPT, + keymat, e_keylen); + if (!p) { + free(newmsg); + return -1; + } + } + if (a_type != SADB_AALG_NONE) { + p = pfkey_setsadbkey(p, ep, SADB_EXT_KEY_AUTH, + keymat + e_keylen, a_keylen); + if (!p) { + free(newmsg); + return -1; + } + } + + /* set sadb_lifetime for destination */ + p = pfkey_setsadblifetime(p, ep, SADB_EXT_LIFETIME_HARD, + l_alloc, l_bytes, l_addtime, l_usetime); + if (!p) { + free(newmsg); + return -1; + } + p = pfkey_setsadblifetime(p, ep, SADB_EXT_LIFETIME_SOFT, + l_alloc, l_bytes, l_addtime, l_usetime); + if (!p || p != ep) { + free(newmsg); + return -1; + } + + /* send message */ + len = pfkey_send(so, newmsg, len); + free(newmsg); + + if (len < 0) + return -1; + + __ipsec_errcode = EIPSEC_NO_ERROR; + return len; +} + +/* sending SADB_DELETE or SADB_GET message to the kernel */ +static int +pfkey_send_x2(so, type, satype, mode, src, dst, spi) +int so; +u_int type, satype, mode; +struct sockaddr *src, *dst; +u_int32_t spi; +{ + struct sadb_msg *newmsg; + int len; + caddr_t p; + int plen; + caddr_t ep; + + (void)mode; + + /* validity check */ + if (src == NULL || dst == NULL) { + __ipsec_errcode = EIPSEC_INVAL_ARGUMENT; + return -1; + } + if (src->sa_family != dst->sa_family) { + __ipsec_errcode = EIPSEC_FAMILY_MISMATCH; + return -1; + } + switch (src->sa_family) { + case AF_INET: + plen = sizeof(struct in_addr) << 3; + break; + case AF_INET6: + plen = sizeof(struct in6_addr) << 3; + break; + default: + __ipsec_errcode = EIPSEC_INVAL_FAMILY; + return -1; + } + + /* create new sadb_msg to reply. */ + len = sizeof(struct sadb_msg) + + sizeof(struct sadb_sa) + + sizeof(struct sadb_address) + + PFKEY_ALIGN8(src->sa_len) + + sizeof(struct sadb_address) + + PFKEY_ALIGN8(dst->sa_len); + + if ((newmsg = CALLOC(len, struct sadb_msg *)) == NULL) { + __ipsec_set_strerror(strerror(errno)); + return -1; + } + ep = ((caddr_t)newmsg) + len; + + p = pfkey_setsadbmsg((caddr_t)newmsg, ep, type, len, satype, 0, + getpid()); + if (!p) { + free(newmsg); + return -1; + } + p = pfkey_setsadbsa(p, ep, spi, 0, 0, 0, 0); + if (!p) { + free(newmsg); + return -1; + } + p = pfkey_setsadbaddr(p, ep, SADB_EXT_ADDRESS_SRC, src, plen, + IPSEC_ULPROTO_ANY); + if (!p) { + free(newmsg); + return -1; + } + p = pfkey_setsadbaddr(p, ep, SADB_EXT_ADDRESS_DST, dst, plen, + IPSEC_ULPROTO_ANY); + if (!p || p != ep) { + free(newmsg); + return -1; + } + + /* send message */ + len = pfkey_send(so, newmsg, len); + free(newmsg); + + if (len < 0) + return -1; + + __ipsec_errcode = EIPSEC_NO_ERROR; + return len; +} + +/* + * sending SADB_REGISTER, SADB_FLUSH, SADB_DUMP or SADB_X_PROMISC message + * to the kernel + */ +static int +pfkey_send_x3(so, type, satype) +int so; +u_int type, satype; +{ + struct sadb_msg *newmsg; + int len; + caddr_t p; + caddr_t ep; + + /* validity check */ + switch (type) { + case SADB_X_PROMISC: + if (satype != 0 && satype != 1) { + __ipsec_errcode = EIPSEC_INVAL_SATYPE; + return -1; + } + break; + default: + switch (satype) { + case SADB_SATYPE_UNSPEC: + case SADB_SATYPE_AH: + case SADB_SATYPE_ESP: + case SADB_X_SATYPE_IPCOMP: + break; + default: + __ipsec_errcode = EIPSEC_INVAL_SATYPE; + return -1; + } + } + + /* create new sadb_msg to send. */ + len = sizeof(struct sadb_msg); + + if ((newmsg = CALLOC(len, struct sadb_msg *)) == NULL) { + __ipsec_set_strerror(strerror(errno)); + return -1; + } + ep = ((caddr_t)newmsg) + len; + + p = pfkey_setsadbmsg((caddr_t)newmsg, ep, type, len, satype, 0, + getpid()); + if (!p || p != ep) { + free(newmsg); + return -1; + } + + /* send message */ + len = pfkey_send(so, newmsg, len); + free(newmsg); + + if (len < 0) + return -1; + + __ipsec_errcode = EIPSEC_NO_ERROR; + return len; +} + +/* sending SADB_X_SPDADD message to the kernel */ +static int +pfkey_send_x4(so, type, src, prefs, dst, prefd, proto, + ltime, vtime, policy, policylen, seq) +int so; +struct sockaddr *src, *dst; +u_int type, prefs, prefd, proto; +u_int64_t ltime, vtime; +char *policy; +int policylen; +u_int32_t seq; +{ + struct sadb_msg *newmsg; + int len; + caddr_t p; + int plen; + caddr_t ep; + + /* validity check */ + if (src == NULL || dst == NULL) { + __ipsec_errcode = EIPSEC_INVAL_ARGUMENT; + return -1; + } + if (src->sa_family != dst->sa_family) { + __ipsec_errcode = EIPSEC_FAMILY_MISMATCH; + return -1; + } + + switch (src->sa_family) { + case AF_INET: + plen = sizeof(struct in_addr) << 3; + break; + case AF_INET6: + plen = sizeof(struct in6_addr) << 3; + break; + default: + __ipsec_errcode = EIPSEC_INVAL_FAMILY; + return -1; + } + if (prefs > (u_int)plen || prefd > (u_int)plen) { + __ipsec_errcode = EIPSEC_INVAL_PREFIXLEN; + return -1; + } + + /* create new sadb_msg to reply. */ + len = sizeof(struct sadb_msg) + + sizeof(struct sadb_address) + + PFKEY_ALIGN8(src->sa_len) + + sizeof(struct sadb_address) + + PFKEY_ALIGN8(src->sa_len) + + sizeof(struct sadb_lifetime) + + policylen; + + if ((newmsg = CALLOC(len, struct sadb_msg *)) == NULL) { + __ipsec_set_strerror(strerror(errno)); + return -1; + } + ep = ((caddr_t)newmsg) + len; + + p = pfkey_setsadbmsg((caddr_t)newmsg, ep, type, len, + SADB_SATYPE_UNSPEC, seq, getpid()); + if (!p) { + free(newmsg); + return -1; + } + p = pfkey_setsadbaddr(p, ep, SADB_EXT_ADDRESS_SRC, src, prefs, proto); + if (!p) { + free(newmsg); + return -1; + } + p = pfkey_setsadbaddr(p, ep, SADB_EXT_ADDRESS_DST, dst, prefd, proto); + if (!p) { + free(newmsg); + return -1; + } + p = pfkey_setsadblifetime(p, ep, SADB_EXT_LIFETIME_HARD, + 0, 0, ltime, vtime); + if (!p || p + policylen != ep) { + free(newmsg); + return -1; + } + memcpy(p, policy, policylen); + + /* send message */ + len = pfkey_send(so, newmsg, len); + free(newmsg); + + if (len < 0) + return -1; + + __ipsec_errcode = EIPSEC_NO_ERROR; + return len; +} + +/* sending SADB_X_SPDGET or SADB_X_SPDDELETE message to the kernel */ +static int +pfkey_send_x5(so, type, spid) +int so; +u_int type; +u_int32_t spid; +{ + struct sadb_msg *newmsg; + struct sadb_x_policy xpl; + int len; + caddr_t p; + caddr_t ep; + + /* create new sadb_msg to reply. */ + len = sizeof(struct sadb_msg) + + sizeof(xpl); + + if ((newmsg = CALLOC(len, struct sadb_msg *)) == NULL) { + __ipsec_set_strerror(strerror(errno)); + return -1; + } + ep = ((caddr_t)newmsg) + len; + + p = pfkey_setsadbmsg((caddr_t)newmsg, ep, type, len, + SADB_SATYPE_UNSPEC, 0, getpid()); + if (!p) { + free(newmsg); + return -1; + } + + if (p + sizeof(xpl) != ep) { + free(newmsg); + return -1; + } + memset(&xpl, 0, sizeof(xpl)); + xpl.sadb_x_policy_len = PFKEY_UNUNIT64(sizeof(xpl)); + xpl.sadb_x_policy_exttype = SADB_X_EXT_POLICY; + xpl.sadb_x_policy_id = spid; + memcpy(p, &xpl, sizeof(xpl)); + + /* send message */ + len = pfkey_send(so, newmsg, len); + free(newmsg); + + if (len < 0) + return -1; + + __ipsec_errcode = EIPSEC_NO_ERROR; + return len; +} + +/* + * open a socket. + * OUT: + * -1: fail. + * others : success and return value of socket. + */ +int +pfkey_open() +{ + int so; + const int bufsiz = 128 * 1024; /*is 128K enough?*/ + + if ((so = socket(PF_KEY, SOCK_RAW, PF_KEY_V2)) < 0) { + __ipsec_set_strerror(strerror(errno)); + return -1; + } + + /* + * This is a temporary workaround for KAME PR 154. + * Don't really care even if it fails. + */ + (void)setsockopt(so, SOL_SOCKET, SO_SNDBUF, &bufsiz, sizeof(bufsiz)); + (void)setsockopt(so, SOL_SOCKET, SO_RCVBUF, &bufsiz, sizeof(bufsiz)); + + __ipsec_errcode = EIPSEC_NO_ERROR; + return so; +} + +/* + * close a socket. + * OUT: + * 0: success. + * -1: fail. + */ +void +pfkey_close(so) +int so; +{ + (void)close(so); + + __ipsec_errcode = EIPSEC_NO_ERROR; + return; +} + +/* + * receive sadb_msg data, and return pointer to new buffer allocated. + * Must free this buffer later. + * OUT: + * NULL : error occured. + * others : a pointer to sadb_msg structure. + * + * XXX should be rewritten to pass length explicitly + */ +struct sadb_msg * +pfkey_recv(so) +int so; +{ + struct sadb_msg buf, *newmsg; + int len, reallen; + + while ((len = recv(so, (caddr_t)&buf, sizeof(buf), MSG_PEEK)) < 0) { + if (errno == EINTR) + continue; + __ipsec_set_strerror(strerror(errno)); + return NULL; + } + + if ((size_t)len < sizeof(buf)) { + recv(so, (caddr_t)&buf, sizeof(buf), 0); + __ipsec_errcode = EIPSEC_MAX; + return NULL; + } + + /* read real message */ + reallen = PFKEY_UNUNIT64(buf.sadb_msg_len); + if ((newmsg = CALLOC(reallen, struct sadb_msg *)) == 0) { + __ipsec_set_strerror(strerror(errno)); + return NULL; + } + + while ((len = recv(so, (caddr_t)newmsg, reallen, 0)) < 0) { + if (errno == EINTR) + continue; + __ipsec_set_strerror(strerror(errno)); + free(newmsg); + return NULL; + } + + if (len != reallen) { + __ipsec_errcode = EIPSEC_SYSTEM_ERROR; + free(newmsg); + return NULL; + } + + /* don't trust what the kernel says, validate! */ + if (PFKEY_UNUNIT64(newmsg->sadb_msg_len) != len) { + __ipsec_errcode = EIPSEC_SYSTEM_ERROR; + free(newmsg); + return NULL; + } + + __ipsec_errcode = EIPSEC_NO_ERROR; + return newmsg; +} + +/* + * send message to a socket. + * OUT: + * others: success and return length sent. + * -1 : fail. + */ +int +pfkey_send(so, msg, len) +int so; +struct sadb_msg *msg; +int len; +{ + if ((len = send(so, (caddr_t)msg, len, 0)) < 0) { + __ipsec_set_strerror(strerror(errno)); + return -1; + } + + __ipsec_errcode = EIPSEC_NO_ERROR; + return len; +} + +/* + * %%% Utilities + * NOTE: These functions are derived from netkey/key.c in KAME. + */ +/* + * set the pointer to each header in this message buffer. + * IN: msg: pointer to message buffer. + * mhp: pointer to the buffer initialized like below: + * caddr_t mhp[SADB_EXT_MAX + 1]; + * OUT: -1: invalid. + * 0: valid. + * + * XXX should be rewritten to obtain length explicitly + */ +int +pfkey_align(msg, mhp) +struct sadb_msg *msg; +caddr_t *mhp; +{ + struct sadb_ext *ext; + int i; + caddr_t p; + caddr_t ep; /* XXX should be passed from upper layer */ + + /* validity check */ + if (msg == NULL || mhp == NULL) { + __ipsec_errcode = EIPSEC_INVAL_ARGUMENT; + return -1; + } + + /* initialize */ + for (i = 0; i < SADB_EXT_MAX + 1; i++) + mhp[i] = NULL; + + mhp[0] = (caddr_t)msg; + + /* initialize */ + p = (caddr_t) msg; + ep = p + PFKEY_UNUNIT64(msg->sadb_msg_len); + + /* skip base header */ + p += sizeof(struct sadb_msg); + + while (p < ep) { + ext = (struct sadb_ext *)p; + if (ep < p + sizeof(*ext) || (size_t)PFKEY_EXTLEN(ext) < sizeof(*ext) || + ep < p + PFKEY_EXTLEN(ext)) { + /* invalid format */ + break; + } + + /* duplicate check */ + /* XXX Are there duplication either KEY_AUTH or KEY_ENCRYPT ?*/ + if (mhp[ext->sadb_ext_type] != NULL) { + __ipsec_errcode = EIPSEC_INVAL_EXTTYPE; + return -1; + } + + /* set pointer */ + switch (ext->sadb_ext_type) { + case SADB_EXT_SA: + case SADB_EXT_LIFETIME_CURRENT: + case SADB_EXT_LIFETIME_HARD: + case SADB_EXT_LIFETIME_SOFT: + case SADB_EXT_ADDRESS_SRC: + case SADB_EXT_ADDRESS_DST: + case SADB_EXT_ADDRESS_PROXY: + case SADB_EXT_KEY_AUTH: + /* XXX should to be check weak keys. */ + case SADB_EXT_KEY_ENCRYPT: + /* XXX should to be check weak keys. */ + case SADB_EXT_IDENTITY_SRC: + case SADB_EXT_IDENTITY_DST: + case SADB_EXT_SENSITIVITY: + case SADB_EXT_PROPOSAL: + case SADB_EXT_SUPPORTED_AUTH: + case SADB_EXT_SUPPORTED_ENCRYPT: + case SADB_EXT_SPIRANGE: + case SADB_X_EXT_POLICY: + case SADB_X_EXT_SA2: + mhp[ext->sadb_ext_type] = (caddr_t)ext; + break; + default: + __ipsec_errcode = EIPSEC_INVAL_EXTTYPE; + return -1; + } + + p += PFKEY_EXTLEN(ext); + } + + if (p != ep) { + __ipsec_errcode = EIPSEC_INVAL_SADBMSG; + return -1; + } + + __ipsec_errcode = EIPSEC_NO_ERROR; + return 0; +} + +/* + * check basic usage for sadb_msg, + * NOTE: This routine is derived from netkey/key.c in KAME. + * IN: msg: pointer to message buffer. + * mhp: pointer to the buffer initialized like below: + * + * caddr_t mhp[SADB_EXT_MAX + 1]; + * + * OUT: -1: invalid. + * 0: valid. + */ +int +pfkey_check(mhp) +caddr_t *mhp; +{ + struct sadb_msg *msg; + + /* validity check */ + if (mhp == NULL || mhp[0] == NULL) { + __ipsec_errcode = EIPSEC_INVAL_ARGUMENT; + return -1; + } + + msg = (struct sadb_msg *)mhp[0]; + + /* check version */ + if (msg->sadb_msg_version != PF_KEY_V2) { + __ipsec_errcode = EIPSEC_INVAL_VERSION; + return -1; + } + + /* check type */ + if (msg->sadb_msg_type > SADB_MAX) { + __ipsec_errcode = EIPSEC_INVAL_MSGTYPE; + return -1; + } + + /* check SA type */ + switch (msg->sadb_msg_satype) { + case SADB_SATYPE_UNSPEC: + switch (msg->sadb_msg_type) { + case SADB_GETSPI: + case SADB_UPDATE: + case SADB_ADD: + case SADB_DELETE: + case SADB_GET: + case SADB_ACQUIRE: + case SADB_EXPIRE: + __ipsec_errcode = EIPSEC_INVAL_SATYPE; + return -1; + } + break; + case SADB_SATYPE_ESP: + case SADB_SATYPE_AH: + case SADB_X_SATYPE_IPCOMP: + switch (msg->sadb_msg_type) { + case SADB_X_SPDADD: + case SADB_X_SPDDELETE: + case SADB_X_SPDGET: + case SADB_X_SPDDUMP: + case SADB_X_SPDFLUSH: + __ipsec_errcode = EIPSEC_INVAL_SATYPE; + return -1; + } + break; + case SADB_SATYPE_RSVP: + case SADB_SATYPE_OSPFV2: + case SADB_SATYPE_RIPV2: + case SADB_SATYPE_MIP: + __ipsec_errcode = EIPSEC_NOT_SUPPORTED; + return -1; + case 1: /* XXX: What does it do ? */ + if (msg->sadb_msg_type == SADB_X_PROMISC) + break; + /*FALLTHROUGH*/ + default: + __ipsec_errcode = EIPSEC_INVAL_SATYPE; + return -1; + } + + /* check field of upper layer protocol and address family */ + if (mhp[SADB_EXT_ADDRESS_SRC] != NULL + && mhp[SADB_EXT_ADDRESS_DST] != NULL) { + struct sadb_address *src0, *dst0; + + src0 = (struct sadb_address *)(mhp[SADB_EXT_ADDRESS_SRC]); + dst0 = (struct sadb_address *)(mhp[SADB_EXT_ADDRESS_DST]); + + if (src0->sadb_address_proto != dst0->sadb_address_proto) { + __ipsec_errcode = EIPSEC_PROTO_MISMATCH; + return -1; + } + + if (PFKEY_ADDR_SADDR(src0)->sa_family + != PFKEY_ADDR_SADDR(dst0)->sa_family) { + __ipsec_errcode = EIPSEC_FAMILY_MISMATCH; + return -1; + } + + switch (PFKEY_ADDR_SADDR(src0)->sa_family) { + case AF_INET: + case AF_INET6: + break; + default: + __ipsec_errcode = EIPSEC_INVAL_FAMILY; + return -1; + } + + /* + * prefixlen == 0 is valid because there must be the case + * all addresses are matched. + */ + } + + __ipsec_errcode = EIPSEC_NO_ERROR; + return 0; +} + +/* + * set data into sadb_msg. + * `buf' must has been allocated sufficiently. + */ +static caddr_t +pfkey_setsadbmsg(buf, lim, type, tlen, satype, seq, pid) +caddr_t buf; +caddr_t lim; +u_int type, satype; +u_int tlen; +u_int32_t seq; +pid_t pid; +{ + struct sadb_msg *p; + u_int len; + + p = (struct sadb_msg *)buf; + len = sizeof(struct sadb_msg); + + if (buf + len > lim) + return NULL; + + memset(p, 0, len); + p->sadb_msg_version = PF_KEY_V2; + p->sadb_msg_type = type; + p->sadb_msg_errno = 0; + p->sadb_msg_satype = satype; + p->sadb_msg_len = PFKEY_UNIT64(tlen); + p->sadb_msg_reserved = 0; + p->sadb_msg_seq = seq; + p->sadb_msg_pid = (u_int32_t)pid; + + return(buf + len); +} + +/* + * copy secasvar data into sadb_address. + * `buf' must has been allocated sufficiently. + */ +static caddr_t +pfkey_setsadbsa(buf, lim, spi, wsize, auth, enc, flags) +caddr_t buf; +caddr_t lim; +u_int32_t spi, flags; +u_int wsize, auth, enc; +{ + struct sadb_sa *p; + u_int len; + + p = (struct sadb_sa *)buf; + len = sizeof(struct sadb_sa); + + if (buf + len > lim) + return NULL; + + memset(p, 0, len); + p->sadb_sa_len = PFKEY_UNIT64(len); + p->sadb_sa_exttype = SADB_EXT_SA; + p->sadb_sa_spi = spi; + p->sadb_sa_replay = wsize; + p->sadb_sa_state = SADB_SASTATE_LARVAL; + p->sadb_sa_auth = auth; + p->sadb_sa_encrypt = enc; + p->sadb_sa_flags = flags; + + return(buf + len); +} + +/* + * set data into sadb_address. + * `buf' must has been allocated sufficiently. + * prefixlen is in bits. + */ +static caddr_t +pfkey_setsadbaddr(buf, lim, exttype, saddr, prefixlen, ul_proto) +caddr_t buf; +caddr_t lim; +u_int exttype; +struct sockaddr *saddr; +u_int prefixlen; +u_int ul_proto; +{ + struct sadb_address *p; + u_int len; + + p = (struct sadb_address *)buf; + len = sizeof(struct sadb_address) + PFKEY_ALIGN8(saddr->sa_len); + + if (buf + len > lim) + return NULL; + + memset(p, 0, len); + p->sadb_address_len = PFKEY_UNIT64(len); + p->sadb_address_exttype = exttype & 0xffff; + p->sadb_address_proto = ul_proto & 0xff; + p->sadb_address_prefixlen = prefixlen; + p->sadb_address_reserved = 0; + + memcpy(p + 1, saddr, saddr->sa_len); + + return(buf + len); +} + +/* + * set sadb_key structure after clearing buffer with zero. + * OUT: the pointer of buf + len. + */ +static caddr_t +pfkey_setsadbkey(buf, lim, type, key, keylen) +caddr_t buf; +caddr_t lim; +caddr_t key; +u_int type, keylen; +{ + struct sadb_key *p; + u_int len; + + p = (struct sadb_key *)buf; + len = sizeof(struct sadb_key) + PFKEY_ALIGN8(keylen); + + if (buf + len > lim) + return NULL; + + memset(p, 0, len); + p->sadb_key_len = PFKEY_UNIT64(len); + p->sadb_key_exttype = type; + p->sadb_key_bits = keylen << 3; + p->sadb_key_reserved = 0; + + memcpy(p + 1, key, keylen); + + return buf + len; +} + +/* + * set sadb_lifetime structure after clearing buffer with zero. + * OUT: the pointer of buf + len. + */ +static caddr_t +pfkey_setsadblifetime(buf, lim, type, l_alloc, l_bytes, l_addtime, l_usetime) +caddr_t buf; +caddr_t lim; +u_int type; +u_int32_t l_alloc, l_bytes, l_addtime, l_usetime; +{ + struct sadb_lifetime *p; + u_int len; + + p = (struct sadb_lifetime *)buf; + len = sizeof(struct sadb_lifetime); + + if (buf + len > lim) + return NULL; + + memset(p, 0, len); + p->sadb_lifetime_len = PFKEY_UNIT64(len); + p->sadb_lifetime_exttype = type; + + switch (type) { + case SADB_EXT_LIFETIME_SOFT: + p->sadb_lifetime_allocations + = (l_alloc * soft_lifetime_allocations_rate) /100; + p->sadb_lifetime_bytes + = (l_bytes * soft_lifetime_bytes_rate) /100; + p->sadb_lifetime_addtime + = (l_addtime * soft_lifetime_addtime_rate) /100; + p->sadb_lifetime_usetime + = (l_usetime * soft_lifetime_usetime_rate) /100; + break; + case SADB_EXT_LIFETIME_HARD: + p->sadb_lifetime_allocations = l_alloc; + p->sadb_lifetime_bytes = l_bytes; + p->sadb_lifetime_addtime = l_addtime; + p->sadb_lifetime_usetime = l_usetime; + break; + } + + return buf + len; +} + +/* + * copy secasvar data into sadb_address. + * `buf' must has been allocated sufficiently. + */ +static caddr_t +pfkey_setsadbxsa2(buf, lim, mode0, reqid) +caddr_t buf; +caddr_t lim; +u_int32_t mode0; +u_int32_t reqid; +{ + struct sadb_x_sa2 *p; + u_int8_t mode = mode0 & 0xff; + u_int len; + + p = (struct sadb_x_sa2 *)buf; + len = sizeof(struct sadb_x_sa2); + + if (buf + len > lim) + return NULL; + + memset(p, 0, len); + p->sadb_x_sa2_len = PFKEY_UNIT64(len); + p->sadb_x_sa2_exttype = SADB_X_EXT_SA2; + p->sadb_x_sa2_mode = mode; + p->sadb_x_sa2_reqid = reqid; + + return(buf + len); +} + +#endif /* ndef MDNS_NO_IPSEC */ |